[wip]: starting to work on the runner application
This commit is contained in:
parent
352810c309
commit
b21b352923
25
README.md
Normal file
25
README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# QBitRun
|
||||||
|
|
||||||
|
Runner application for qBittorrent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Anything Below This Line is not ready!!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Start by copying the following command into the "Run external program on torrent completion" field in qBittorrent `Settings > Downloads`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/path/to/qbitrun handle -N "%N" -L "%L" -G "%G" -F "%F" -R "%R" -D "%D" -C "%C" -Z "%Z" -T "%T" -I "%I" -J "%J" -K "%K"
|
||||||
|
```
|
||||||
|
|
||||||
|
If wanting to handle "Run external program on torrent torrent added", add `--add-event` (or `-a` for short) to the command. This is for the qbitrun and you to be able to distinguish whether this run is triggered by "torrent added" event.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/path/to/qbitrun handle --add-event -N "%N" -L "%L" -G "%G" -F "%F" -R "%R" -D "%D" -C "%C" -Z "%Z" -T "%T" -I "%I" -J "%J" -K "%K"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can filter out which events to handle later in the runner .yaml files.
|
|
@ -1,24 +1,11 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tigorlazuardi/qbitrun/lib/qbitrun"
|
||||||
|
)
|
||||||
|
|
||||||
type handleArgsS struct {
|
var handleArgs = qbitrun.RunnerContext{}
|
||||||
torrentName string
|
|
||||||
category string
|
|
||||||
tags []string
|
|
||||||
contentPath string
|
|
||||||
rootPath string
|
|
||||||
savePath string
|
|
||||||
numberOfFiles int
|
|
||||||
torrentSize uint64
|
|
||||||
currentTracker string
|
|
||||||
infoHashV1 string
|
|
||||||
infoHashV2 string
|
|
||||||
torrentID string
|
|
||||||
addEvent bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var handleArgs = handleArgsS{}
|
|
||||||
|
|
||||||
var handleCmd = &cobra.Command{
|
var handleCmd = &cobra.Command{
|
||||||
Use: "handle",
|
Use: "handle",
|
||||||
|
@ -30,43 +17,43 @@ var handleCmd = &cobra.Command{
|
||||||
func init() {
|
func init() {
|
||||||
flags := handleCmd.Flags()
|
flags := handleCmd.Flags()
|
||||||
|
|
||||||
flags.BoolVarP(&handleArgs.addEvent, "add-event", "a", false, "mark this event as add torrent event")
|
flags.BoolVarP(&handleArgs.AddEvent, "add-event", "a", false, "mark this event as add torrent event")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.torrentName, "torrent-name", "N", "", "Torrent Name")
|
flags.StringVarP(&handleArgs.TorrentName, "torrent-name", "N", "", "Torrent Name")
|
||||||
handleCmd.MarkFlagRequired("torrent-name")
|
_ = handleCmd.MarkFlagRequired("torrent-name")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.category, "category", "L", "", "Category")
|
flags.StringVarP(&handleArgs.Category, "category", "L", "", "Category")
|
||||||
handleCmd.MarkFlagRequired("category")
|
_ = handleCmd.MarkFlagRequired("category")
|
||||||
|
|
||||||
flags.StringSliceVarP(&handleArgs.tags, "tags", "G", []string{}, "Tags (seperated by comma)")
|
flags.StringSliceVarP(&handleArgs.Tags, "tags", "G", []string{}, "Tags (seperated by comma)")
|
||||||
handleCmd.MarkFlagRequired("tags")
|
_ = handleCmd.MarkFlagRequired("tags")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.contentPath, "content-path", "F", "", "Content Path (same as root path for multifile torrent")
|
flags.StringVarP(&handleArgs.ContentPath, "content-path", "F", "", "Content Path (same as root path for multifile torrent)")
|
||||||
handleCmd.MarkFlagRequired("content-path")
|
_ = handleCmd.MarkFlagRequired("content-path")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.rootPath, "root-path", "R", "", "Root path (first torrent subdirectory path)")
|
flags.StringVarP(&handleArgs.RootPath, "root-path", "R", "", "Root path (first torrent subdirectory path)")
|
||||||
handleCmd.MarkFlagRequired("root-path")
|
_ = handleCmd.MarkFlagRequired("root-path")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.savePath, "save-path", "D", "", "Save path")
|
flags.StringVarP(&handleArgs.SavePath, "save-path", "D", "", "Save path")
|
||||||
handleCmd.MarkFlagRequired("save-path")
|
_ = handleCmd.MarkFlagRequired("save-path")
|
||||||
|
|
||||||
flags.IntVarP(&handleArgs.numberOfFiles, "number-of-files", "C", 0, "Number of files")
|
flags.IntVarP(&handleArgs.NumberOfFiles, "number-of-files", "C", 0, "Number of files")
|
||||||
handleCmd.MarkFlagRequired("number-of-files")
|
_ = handleCmd.MarkFlagRequired("number-of-files")
|
||||||
|
|
||||||
flags.Uint64VarP(&handleArgs.torrentSize, "torrent-size", "Z", 0, "Torrent size")
|
flags.Uint64VarP(&handleArgs.TorrentSize, "torrent-size", "Z", 0, "Torrent size")
|
||||||
handleCmd.MarkFlagRequired("torrent-size")
|
_ = handleCmd.MarkFlagRequired("torrent-size")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.currentTracker, "current-tracker", "T", "", "Current tracker")
|
flags.StringVarP(&handleArgs.CurrentTracker, "current-tracker", "T", "", "Current tracker")
|
||||||
handleCmd.MarkFlagRequired("current-tracker")
|
_ = handleCmd.MarkFlagRequired("current-tracker")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.infoHashV1, "info-hash-v1", "I", "", "Info hash v1")
|
flags.StringVarP(&handleArgs.InfoHashV1, "info-hash-v1", "I", "", "Info hash v1")
|
||||||
handleCmd.MarkFlagRequired("info-hash-v1")
|
_ = handleCmd.MarkFlagRequired("info-hash-v1")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.infoHashV2, "info-hash-v2", "J", "", "Info hash v2")
|
flags.StringVarP(&handleArgs.InfoHashV2, "info-hash-v2", "J", "", "Info hash v2")
|
||||||
handleCmd.MarkFlagRequired("info-hash-v2")
|
_ = handleCmd.MarkFlagRequired("info-hash-v2")
|
||||||
|
|
||||||
flags.StringVarP(&handleArgs.torrentID, "torrent-id", "K", "", "Torrent ID")
|
flags.StringVarP(&handleArgs.TorrentID, "torrent-id", "K", "", "Torrent ID")
|
||||||
handleCmd.MarkFlagRequired("torrent-id")
|
_ = handleCmd.MarkFlagRequired("torrent-id")
|
||||||
|
|
||||||
RootCmd.AddCommand(handleCmd)
|
RootCmd.AddCommand(handleCmd)
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,7 +3,11 @@ module github.com/tigorlazuardi/qbitrun
|
||||||
go 1.22.3
|
go 1.22.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/spf13/cobra v1.8.1 // indirect
|
github.com/spf13/cobra v1.8.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -1,10 +1,17 @@
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
37
lib/qbitrun/qbitrun.go
Normal file
37
lib/qbitrun/qbitrun.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package qbitrun
|
||||||
|
|
||||||
|
type QBitRun struct {
|
||||||
|
context RunnerContext
|
||||||
|
workflow WorkflowDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a QBitRun instance intended for single session.
|
||||||
|
//
|
||||||
|
// QBitRun assumes ownership of the WorkflowDir and will close all files
|
||||||
|
// when the session is done.
|
||||||
|
//
|
||||||
|
// Users must not use or modify the []io.ReadCloser in the WorkflowDir after
|
||||||
|
// calling this function, especially if the WorkflowDir is created using
|
||||||
|
// WorkflowFromReadClosers or WorkflowFromReaders.
|
||||||
|
func New(workflows WorkflowDir, ctx RunnerContext) *QBitRun {
|
||||||
|
return &QBitRun{
|
||||||
|
context: ctx,
|
||||||
|
workflow: workflows,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunnerContext struct {
|
||||||
|
TorrentName string
|
||||||
|
Category string
|
||||||
|
Tags []string
|
||||||
|
ContentPath string
|
||||||
|
RootPath string
|
||||||
|
SavePath string
|
||||||
|
NumberOfFiles int
|
||||||
|
TorrentSize uint64
|
||||||
|
CurrentTracker string
|
||||||
|
InfoHashV1 string
|
||||||
|
InfoHashV2 string
|
||||||
|
TorrentID string
|
||||||
|
AddEvent bool
|
||||||
|
}
|
74
lib/qbitrun/workflow.go
Normal file
74
lib/qbitrun/workflow.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package qbitrun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WorkflowDir interface {
|
||||||
|
Files() []io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
type workflowFiles []io.ReadCloser
|
||||||
|
|
||||||
|
func (wf workflowFiles) Files() []io.ReadCloser {
|
||||||
|
return wf
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenDir opens a directory and returns a WorkflowDir.
|
||||||
|
//
|
||||||
|
// Set filter to filter out unwanted files.
|
||||||
|
func OpenDir(dir string, filters ...func(path string, d fs.FileInfo) bool) (WorkflowDir, error) {
|
||||||
|
var files []io.ReadCloser
|
||||||
|
err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ok := true
|
||||||
|
for _, filter := range filters {
|
||||||
|
ok = filter(path, info)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return workflowFiles(files), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowFromReadClosers creates a WorkflowDir from a list of io.ReadCloser.
|
||||||
|
func WorkflowFromReadClosers(rcs ...io.ReadCloser) WorkflowDir {
|
||||||
|
return workflowFiles(rcs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowFromReaders creates a WorkflowDir from a list of io.Reader.
|
||||||
|
//
|
||||||
|
// If the Reader implements io.ReadCloser, it will be used as is.
|
||||||
|
// If not, it will be wrapped with io.NopCloser.
|
||||||
|
func WorkflowFromReaders(readers ...io.Reader) WorkflowDir {
|
||||||
|
var rcs []io.ReadCloser
|
||||||
|
for _, r := range readers {
|
||||||
|
if rc, ok := r.(io.ReadCloser); ok {
|
||||||
|
rcs = append(rcs, rc)
|
||||||
|
} else {
|
||||||
|
rcs = append(rcs, io.NopCloser(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return workflowFiles(rcs)
|
||||||
|
}
|
59
lib/workflow/job.go
Normal file
59
lib/workflow/job.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package workflow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Job struct {
|
||||||
|
After []string `yaml:"after"`
|
||||||
|
Steps []Step `yaml:"steps"`
|
||||||
|
// Error is the action taken when a step fails.
|
||||||
|
Error ErrorAction `yaml:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorAction string
|
||||||
|
|
||||||
|
func (ea *ErrorAction) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
if value.IsZero() {
|
||||||
|
*ea = ErrorActionStopJob
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
if err := value.Decode(&s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
trimmed := strings.TrimSpace(s)
|
||||||
|
lowered := strings.ToLower(trimmed)
|
||||||
|
switch lowered {
|
||||||
|
case "continue":
|
||||||
|
*ea = ErrorActionContinue
|
||||||
|
case "stop-job":
|
||||||
|
*ea = ErrorActionStopJob
|
||||||
|
case "stop-action":
|
||||||
|
*ea = ErrorActionStopAction
|
||||||
|
case "stop-run":
|
||||||
|
*ea = ErrorActionStopRun
|
||||||
|
default:
|
||||||
|
return &yaml.TypeError{
|
||||||
|
Errors: []string{fmt.Sprintf("invalid job error action: %q", trimmed)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ea ErrorAction) String() string {
|
||||||
|
if ea == ErrorActionStopJob {
|
||||||
|
return "stop-job"
|
||||||
|
}
|
||||||
|
return string(ea)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrorActionStopJob ErrorAction = "" // default case
|
||||||
|
ErrorActionContinue ErrorAction = "continue"
|
||||||
|
ErrorActionStopAction ErrorAction = "stop-action"
|
||||||
|
ErrorActionStopRun ErrorAction = "stop-run"
|
||||||
|
)
|
70
lib/workflow/job_test.go
Normal file
70
lib/workflow/job_test.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package workflow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorAction_UnmarshalYAML(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
want ErrorAction
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "continue",
|
||||||
|
input: []byte(`E: ConTinue`),
|
||||||
|
want: ErrorActionContinue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stop-job",
|
||||||
|
input: []byte(`E: stop-job`),
|
||||||
|
want: ErrorActionStopJob,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stop-job",
|
||||||
|
input: []byte(`E: stop-job`),
|
||||||
|
want: ErrorActionStopJob,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "null is stop-job",
|
||||||
|
input: []byte(`E: null`),
|
||||||
|
want: ErrorActionStopJob,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unset is stop-job",
|
||||||
|
input: []byte(`E: `),
|
||||||
|
want: ErrorActionStopJob,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unset is stop-job 2",
|
||||||
|
input: []byte(`foo: bar`),
|
||||||
|
want: ErrorActionStopJob,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stop-action",
|
||||||
|
input: []byte(`E: stop-action`),
|
||||||
|
want: ErrorActionStopAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stop-run",
|
||||||
|
input: []byte(`E: stop-run`),
|
||||||
|
want: ErrorActionStopRun,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
type placeholder struct {
|
||||||
|
E ErrorAction `yaml:"E"`
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var placeholder placeholder
|
||||||
|
err := yaml.Unmarshal(tt.input, &placeholder)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, placeholder.E)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
31
lib/workflow/sequence.go
Normal file
31
lib/workflow/sequence.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package workflow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sequencer handles sequencing of jobs.
|
||||||
|
//
|
||||||
|
// It handles the Lifecycle of a Workflow.
|
||||||
|
//
|
||||||
|
// When Jobs have no dependencies (defined via After), they are run in parallel.
|
||||||
|
//
|
||||||
|
// Sequencer's instance job is to keep track of the order of Jobs and Steps. NOT RUNNING THEM.
|
||||||
|
//
|
||||||
|
// Sequencer is thread-safe.
|
||||||
|
type Sequencer struct {
|
||||||
|
queued Jobs
|
||||||
|
finished Jobs
|
||||||
|
|
||||||
|
reports []SequenceReport
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type SequenceReport struct {
|
||||||
|
started time.Time
|
||||||
|
end time.Time
|
||||||
|
job Job
|
||||||
|
err error
|
||||||
|
}
|
15
lib/workflow/workflow.go
Normal file
15
lib/workflow/workflow.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package workflow
|
||||||
|
|
||||||
|
type Workflow struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Disabled bool `yaml:"disabled"`
|
||||||
|
When string `yaml:"when"`
|
||||||
|
Jobs Jobs `yaml:"jobs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Jobs map[string]Job
|
||||||
|
|
||||||
|
type Step struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Run string `yaml:"run"`
|
||||||
|
}
|
Loading…
Reference in a new issue