From 8c49ba0cec0bd62f13f3dadd5ae556c7dd571436 Mon Sep 17 00:00:00 2001 From: Casey Lee <cplee@nektos.com> Date: Tue, 4 Feb 2020 16:38:41 -0800 Subject: [PATCH] initial load of yaml working --- .github/workflows/basic.yml | 8 + actions/action.go | 7 +- actions/api.go | 51 ----- actions/graph.go | 64 ------ actions/runner.go | 160 -------------- cmd/graph.go | 40 ++++ cmd/input.go | 40 ++++ cmd/root.go | 157 +++++--------- go.mod | 15 +- go.sum | 45 ++-- {common => pkg/common}/draw.go | 0 {common => pkg/common}/executor.go | 0 {common => pkg/common}/executor_test.go | 0 {common => pkg/common}/file.go | 0 {common => pkg/common}/git.go | 0 {common => pkg/common}/git_test.go | 0 {container => pkg/container}/docker_build.go | 2 +- {container => pkg/container}/docker_common.go | 0 {container => pkg/container}/docker_images.go | 0 .../container}/docker_images_test.go | 0 {container => pkg/container}/docker_pull.go | 2 +- .../container}/docker_pull_test.go | 0 {container => pkg/container}/docker_run.go | 2 +- .../container}/docker_run_test.go | 7 +- pkg/model/planner.go | 196 ++++++++++++++++++ pkg/model/workflow.go | 67 ++++++ pkg/runner/api.go | 5 + pkg/runner/runner.go | 88 ++++++++ {actions => pkg/runner}/runner_exec.go | 17 +- {actions => pkg/runner}/runner_test.go | 2 +- 30 files changed, 560 insertions(+), 415 deletions(-) create mode 100644 .github/workflows/basic.yml delete mode 100644 actions/api.go delete mode 100644 actions/graph.go delete mode 100644 actions/runner.go create mode 100644 cmd/graph.go create mode 100644 cmd/input.go rename {common => pkg/common}/draw.go (100%) rename {common => pkg/common}/executor.go (100%) rename {common => pkg/common}/executor_test.go (100%) rename {common => pkg/common}/file.go (100%) rename {common => pkg/common}/git.go (100%) rename {common => pkg/common}/git_test.go (100%) rename {container => pkg/container}/docker_build.go (98%) rename {container => pkg/container}/docker_common.go (100%) rename {container => pkg/container}/docker_images.go (100%) rename {container => pkg/container}/docker_images_test.go (100%) rename {container => pkg/container}/docker_pull.go (97%) rename {container => pkg/container}/docker_pull_test.go (100%) rename {container => pkg/container}/docker_run.go (99%) rename {container => pkg/container}/docker_run_test.go (97%) create mode 100644 pkg/model/planner.go create mode 100644 pkg/model/workflow.go create mode 100644 pkg/runner/api.go create mode 100644 pkg/runner/runner.go rename {actions => pkg/runner}/runner_exec.go (93%) rename {actions => pkg/runner}/runner_test.go (99%) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml new file mode 100644 index 0000000..7cdb3b4 --- /dev/null +++ b/.github/workflows/basic.yml @@ -0,0 +1,8 @@ +name: basic +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: echo hello world! diff --git a/actions/action.go b/actions/action.go index 2a3136a..4b32916 100644 --- a/actions/action.go +++ b/actions/action.go @@ -5,17 +5,18 @@ import ( "log" "os" - "github.com/actions/workflow-parser/model" "github.com/howeyc/gopass" ) var secretCache map[string]string type actionEnvironmentApplier struct { - *model.Action + *Action } -func newActionEnvironmentApplier(action *model.Action) environmentApplier { +type Action struct{} + +func newActionEnvironmentApplier(action *Action) environmentApplier { return &actionEnvironmentApplier{action} } diff --git a/actions/api.go b/actions/api.go deleted file mode 100644 index 0ebdc2d..0000000 --- a/actions/api.go +++ /dev/null @@ -1,51 +0,0 @@ -package actions - -import ( - "context" - "io" -) - -// Runner provides capabilities to run GitHub actions -type Runner interface { - EventGrapher - EventLister - EventRunner - ActionRunner - io.Closer -} - -// EventGrapher to list the actions -type EventGrapher interface { - GraphEvent(eventName string) ([][]string, error) -} - -// EventLister to list the events -type EventLister interface { - ListEvents() []string -} - -// EventRunner to run the actions for a given event -type EventRunner interface { - RunEvent() error -} - -// ActionRunner to run a specific actions -type ActionRunner interface { - RunActions(actionNames ...string) error -} - -// RunnerConfig contains the config for a new runner -type RunnerConfig struct { - Ctx context.Context // context to use for the run - Dryrun bool // don't start any of the containers - WorkingDir string // base directory to use - WorkflowPath string // path to load main.workflow file, relative to WorkingDir - EventName string // name of event to run - EventPath string // path to JSON file to use for event.json in containers, relative to WorkingDir - ReuseContainers bool // reuse containers to maintain state - ForcePull bool // force pulling of the image, if already present -} - -type environmentApplier interface { - applyEnvironment(map[string]string) -} diff --git a/actions/graph.go b/actions/graph.go deleted file mode 100644 index 1e1bbc9..0000000 --- a/actions/graph.go +++ /dev/null @@ -1,64 +0,0 @@ -package actions - -import ( - "log" - - "github.com/actions/workflow-parser/model" -) - -// return a pipeline that is run in series. pipeline is a list of steps to run in parallel -func newExecutionGraph(workflowConfig *model.Configuration, actionNames ...string) [][]string { - // first, build a list of all the necessary actions to run, and their dependencies - actionDependencies := make(map[string][]string) - for len(actionNames) > 0 { - newActionNames := make([]string, 0) - for _, aName := range actionNames { - // make sure we haven't visited this action yet - if _, ok := actionDependencies[aName]; !ok { - action := workflowConfig.GetAction(aName) - if action != nil { - actionDependencies[aName] = action.Needs - newActionNames = append(newActionNames, action.Needs...) - } - } - } - actionNames = newActionNames - } - - // next, build an execution graph - graph := make([][]string, 0) - for len(actionDependencies) > 0 { - stage := make([]string, 0) - for aName, aDeps := range actionDependencies { - // make sure all deps are in the graph already - if listInLists(aDeps, graph...) { - stage = append(stage, aName) - delete(actionDependencies, aName) - } - } - if len(stage) == 0 { - log.Fatalf("Unable to build dependency graph!") - } - graph = append(graph, stage) - } - - return graph -} - -// return true iff all strings in srcList exist in at least one of the searchLists -func listInLists(srcList []string, searchLists ...[]string) bool { - for _, src := range srcList { - found := false - for _, searchList := range searchLists { - for _, search := range searchList { - if src == search { - found = true - } - } - } - if !found { - return false - } - } - return true -} diff --git a/actions/runner.go b/actions/runner.go deleted file mode 100644 index 0509747..0000000 --- a/actions/runner.go +++ /dev/null @@ -1,160 +0,0 @@ -package actions - -import ( - "io/ioutil" - "os" - "path/filepath" - "sort" - - "github.com/actions/workflow-parser/model" - "github.com/actions/workflow-parser/parser" - "github.com/nektos/act/common" - log "github.com/sirupsen/logrus" -) - -type runnerImpl struct { - config *RunnerConfig - workflowConfig *model.Configuration - tempDir string - eventJSON string -} - -// NewRunner Creates a new Runner -func NewRunner(runnerConfig *RunnerConfig) (Runner, error) { - runner := &runnerImpl{ - config: runnerConfig, - } - - init := common.NewPipelineExecutor( - runner.setupTempDir, - runner.setupWorkingDir, - runner.setupWorkflows, - runner.setupEvent, - ) - - return runner, init() -} - -func (runner *runnerImpl) setupTempDir() error { - var err error - runner.tempDir, err = ioutil.TempDir("", "act-") - return err -} - -func (runner *runnerImpl) setupWorkingDir() error { - var err error - runner.config.WorkingDir, err = filepath.Abs(runner.config.WorkingDir) - log.Debugf("Setting working dir to %s", runner.config.WorkingDir) - return err -} - -func (runner *runnerImpl) setupWorkflows() error { - runner.config.WorkflowPath = runner.resolvePath(runner.config.WorkflowPath) - log.Debugf("Loading workflow config from %s", runner.config.WorkflowPath) - workflowReader, err := os.Open(runner.config.WorkflowPath) - if err != nil { - return err - } - defer workflowReader.Close() - - runner.workflowConfig, err = parser.Parse(workflowReader) - return err -} - -func (runner *runnerImpl) setupEvent() error { - runner.eventJSON = "{}" - if runner.config.EventPath != "" { - runner.config.EventPath = runner.resolvePath(runner.config.EventPath) - log.Debugf("Reading event.json from %s", runner.config.EventPath) - eventJSONBytes, err := ioutil.ReadFile(runner.config.EventPath) - if err != nil { - return err - } - runner.eventJSON = string(eventJSONBytes) - } - return nil -} - -func (runner *runnerImpl) resolvePath(path string) string { - if path == "" { - return path - } - if !filepath.IsAbs(path) { - path = filepath.Join(runner.config.WorkingDir, path) - } - return path -} - -// ListEvents gets all the events in the workflows file -func (runner *runnerImpl) ListEvents() []string { - log.Debugf("Listing all events") - events := make([]string, 0) - for _, w := range runner.workflowConfig.Workflows { - events = append(events, w.On) - } - - // sort the list based on depth of dependencies - sort.Slice(events, func(i, j int) bool { - return events[i] < events[j] - }) - - return events -} - -// GraphEvent builds an execution path -func (runner *runnerImpl) GraphEvent(eventName string) ([][]string, error) { - log.Debugf("Listing actions for event '%s'", eventName) - resolves := runner.resolveEvent(eventName) - return newExecutionGraph(runner.workflowConfig, resolves...), nil -} - -// RunAction runs a set of actions in parallel, and their dependencies -func (runner *runnerImpl) RunActions(actionNames ...string) error { - log.Debugf("Running actions %+q", actionNames) - graph := newExecutionGraph(runner.workflowConfig, actionNames...) - - pipeline := make([]common.Executor, 0) - for _, actions := range graph { - stage := make([]common.Executor, 0) - for _, actionName := range actions { - stage = append(stage, runner.newActionExecutor(actionName)) - } - pipeline = append(pipeline, common.NewParallelExecutor(stage...)) - } - - executor := common.NewPipelineExecutor(pipeline...) - return executor() -} - -// RunEvent runs the actions for a single event -func (runner *runnerImpl) RunEvent() error { - log.Debugf("Running event '%s'", runner.config.EventName) - resolves := runner.resolveEvent(runner.config.EventName) - log.Debugf("Running actions %s -> %s", runner.config.EventName, resolves) - return runner.RunActions(resolves...) -} - -func (runner *runnerImpl) Close() error { - return os.RemoveAll(runner.tempDir) -} - -// get list of resolves for an event -func (runner *runnerImpl) resolveEvent(eventName string) []string { - workflows := runner.workflowConfig.GetWorkflows(eventName) - resolves := make([]string, 0) - for _, workflow := range workflows { - for _, resolve := range workflow.Resolves { - found := false - for _, r := range resolves { - if r == resolve { - found = true - break - } - } - if !found { - resolves = append(resolves, resolve) - } - } - } - return resolves -} diff --git a/cmd/graph.go b/cmd/graph.go new file mode 100644 index 0000000..e8d0204 --- /dev/null +++ b/cmd/graph.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/nektos/act/pkg/common" + "github.com/nektos/act/pkg/model" +) + +func drawGraph(plan *model.Plan) error { + + drawings := make([]*common.Drawing, 0) + + jobPen := common.NewPen(common.StyleSingleLine, 96) + arrowPen := common.NewPen(common.StyleNoLine, 97) + for i, stage := range plan.Stages { + if i > 0 { + drawings = append(drawings, arrowPen.DrawArrow()) + } + + ids := make([]string, 0) + for _, r := range stage.Runs { + ids = append(ids, fmt.Sprintf("%s/%s", r.Workflow.Name, r.JobID)) + } + drawings = append(drawings, jobPen.DrawBoxes(ids...)) + } + + maxWidth := 0 + for _, d := range drawings { + if d.GetWidth() > maxWidth { + maxWidth = d.GetWidth() + } + } + + for _, d := range drawings { + d.Draw(os.Stdout, maxWidth) + } + return nil +} diff --git a/cmd/input.go b/cmd/input.go new file mode 100644 index 0000000..9f6ea5c --- /dev/null +++ b/cmd/input.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "log" + "path/filepath" +) + +// Input contains the input for the root command +type Input struct { + workingDir string + workflowsPath string + eventPath string + reuseContainers bool + dryrun bool + forcePull bool +} + +func (i *Input) resolve(path string) string { + basedir, err := filepath.Abs(i.workingDir) + if err != nil { + log.Fatal(err) + } + if path == "" { + return path + } + if !filepath.IsAbs(path) { + path = filepath.Join(basedir, path) + } + return path +} + +// WorkflowsPath returns path to workflows +func (i *Input) WorkflowsPath() string { + return i.resolve(i.workflowsPath) +} + +// EventPath returns the path to events file +func (i *Input) EventPath() string { + return i.resolve(i.eventPath) +} diff --git a/cmd/root.go b/cmd/root.go index dc6ea16..afe9a73 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,13 +2,11 @@ package cmd import ( "context" - "fmt" "os" "path/filepath" fswatch "github.com/andreaskoch/go-fswatch" - "github.com/nektos/act/actions" - "github.com/nektos/act/common" + "github.com/nektos/act/pkg/model" gitignore "github.com/sabhiram/go-gitignore" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -16,26 +14,26 @@ import ( // Execute is the entry point to running the CLI func Execute(ctx context.Context, version string) { - runnerConfig := &actions.RunnerConfig{Ctx: ctx} + input := new(Input) var rootCmd = &cobra.Command{ Use: "act [event name to run]", Short: "Run Github actions locally by specifying the event name (e.g. `push`) or an action name directly.", Args: cobra.MaximumNArgs(1), - RunE: newRunCommand(runnerConfig), + RunE: newRunCommand(ctx, input), PersistentPreRun: setupLogging, Version: version, SilenceUsage: true, } rootCmd.Flags().BoolP("watch", "w", false, "watch the contents of the local repo and run when files change") - rootCmd.Flags().BoolP("list", "l", false, "list actions") - rootCmd.Flags().StringP("action", "a", "", "run action") - rootCmd.Flags().BoolVarP(&runnerConfig.ReuseContainers, "reuse", "r", false, "reuse action containers to maintain state") - rootCmd.Flags().StringVarP(&runnerConfig.EventPath, "event", "e", "", "path to event JSON file") - rootCmd.Flags().BoolVarP(&runnerConfig.ForcePull, "pull", "p", false, "pull docker image(s) if already present") + rootCmd.Flags().BoolP("list", "l", false, "list workflows") + rootCmd.Flags().StringP("job", "j", "", "run job") + rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "reuse action containers to maintain state") + rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) if already present") + rootCmd.Flags().StringVarP(&input.eventPath, "event", "e", "", "path to event JSON file") + rootCmd.PersistentFlags().StringVarP(&input.workflowsPath, "workflows", "W", "./.github/workflows/", "path to workflow files") + rootCmd.PersistentFlags().StringVarP(&input.workingDir, "directory", "C", ".", "working directory") rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output") - rootCmd.PersistentFlags().BoolVarP(&runnerConfig.Dryrun, "dryrun", "n", false, "dryrun mode") - rootCmd.PersistentFlags().StringVarP(&runnerConfig.WorkflowPath, "file", "f", "./.github/main.workflow", "path to workflow file") - rootCmd.PersistentFlags().StringVarP(&runnerConfig.WorkingDir, "directory", "C", ".", "working directory") + rootCmd.PersistentFlags().BoolVarP(&input.dryrun, "dryrun", "n", false, "dryrun mode") if err := rootCmd.Execute(); err != nil { os.Exit(1) } @@ -49,67 +47,63 @@ func setupLogging(cmd *cobra.Command, args []string) { } } -func newRunCommand(runnerConfig *actions.RunnerConfig) func(*cobra.Command, []string) error { +func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - if len(args) > 0 { - runnerConfig.EventName = args[0] - } - - watch, err := cmd.Flags().GetBool("watch") + planner, err := model.NewWorkflowPlanner(input.WorkflowsPath()) if err != nil { return err } - if watch { - return watchAndRun(runnerConfig.Ctx, func() error { - return parseAndRun(cmd, runnerConfig) - }) + + // Determine the event name + var eventName string + if len(args) > 0 { + eventName = args[0] + } else { + // set default event type if we only have a single workflow in the file. + // this way user dont have to specify the event. + if events := planner.GetEvents(); len(events) == 1 { + log.Debugf("Using detected workflow event: %s", events[0]) + eventName = events[0] + } } - return parseAndRun(cmd, runnerConfig) - } -} -func parseAndRun(cmd *cobra.Command, runnerConfig *actions.RunnerConfig) error { - // create the runner - runner, err := actions.NewRunner(runnerConfig) - if err != nil { - return err - } - defer runner.Close() - - // set default event type if we only have a single workflow in the file. - // this way user dont have to specify the event. - if runnerConfig.EventName == "" { - if events := runner.ListEvents(); len(events) == 1 { - log.Debugf("Using detected workflow event: %s", events[0]) - runnerConfig.EventName = events[0] + // build the plan for this run + var plan *model.Plan + if jobID, err := cmd.Flags().GetString("job"); err != nil { + return err + } else if jobID != "" { + log.Debugf("Planning job: %s", jobID) + plan = planner.PlanJob(jobID) + } else { + log.Debugf("Planning event: %s", eventName) + plan = planner.PlanEvent(eventName) } - } - // fall back to default event name if we could not detect one. - if runnerConfig.EventName == "" { - runnerConfig.EventName = "push" - } + // check if we should just print the graph + if list, err := cmd.Flags().GetBool("list"); err != nil { + return err + } else if list { + return drawGraph(plan) + } - // check if we should just print the graph - list, err := cmd.Flags().GetBool("list") - if err != nil { - return err - } - if list { - return drawGraph(runner) - } + // run the plan + // runner, err := runner.New(config) + // if err != nil { + // return err + // } + // defer runner.Close() - // check if we are running just a single action - actionName, err := cmd.Flags().GetString("action") - if err != nil { - return err - } - if actionName != "" { - return runner.RunActions(actionName) - } + // if watch, err := cmd.Flags().GetBool("watch"); err != nil { + // return err + // } else if watch { + // return watchAndRun(ctx, func() error { + // return runner.RunPlan(plan) + // }) + // } - // run the event in the RunnerRonfig - return runner.RunEvent() + // return runner.RunPlan(plan) + return nil + } } func watchAndRun(ctx context.Context, fn func() error) error { @@ -155,40 +149,3 @@ func watchAndRun(ctx context.Context, fn func() error) error { folderWatcher.Stop() return err } - -func drawGraph(runner actions.Runner) error { - eventNames := runner.ListEvents() - for _, eventName := range eventNames { - graph, err := runner.GraphEvent(eventName) - if err != nil { - return err - } - - drawings := make([]*common.Drawing, 0) - eventPen := common.NewPen(common.StyleDoubleLine, 91 /*34*/) - - drawings = append(drawings, eventPen.DrawBoxes(fmt.Sprintf("EVENT: %s", eventName))) - - actionPen := common.NewPen(common.StyleSingleLine, 96) - arrowPen := common.NewPen(common.StyleNoLine, 97) - drawings = append(drawings, arrowPen.DrawArrow()) - for i, stage := range graph { - if i > 0 { - drawings = append(drawings, arrowPen.DrawArrow()) - } - drawings = append(drawings, actionPen.DrawBoxes(stage...)) - } - - maxWidth := 0 - for _, d := range drawings { - if d.GetWidth() > maxWidth { - maxWidth = d.GetWidth() - } - } - - for _, d := range drawings { - d.Draw(os.Stdout, maxWidth) - } - } - return nil -} diff --git a/go.mod b/go.mod index 75d3143..5aa814e 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.11 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/actions/workflow-parser v1.0.0 github.com/andreaskoch/go-fswatch v1.0.0 github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect @@ -14,9 +13,10 @@ require ( github.com/emirpasic/gods v1.12.0 // indirect github.com/go-ini/ini v1.41.0 github.com/gogo/protobuf v1.2.0 // indirect + github.com/gophercloud/gophercloud v0.7.0 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect github.com/gorilla/mux v1.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93 github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect @@ -29,15 +29,10 @@ require ( github.com/sirupsen/logrus v1.3.0 github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect - github.com/soniakeys/graph v0.0.0 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect github.com/stretchr/testify v1.3.0 - golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 - golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect - golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533 // indirect - golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect + golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca // indirect google.golang.org/grpc v1.18.0 // indirect @@ -45,8 +40,10 @@ require ( gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 // indirect gopkg.in/src-d/go-git.v4 v4.9.1 - gopkg.in/yaml.v2 v2.2.2 // indirect + gopkg.in/yaml.v2 v2.2.8 gotest.tools v2.2.0+incompatible ) replace github.com/docker/docker => github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb + +go 1.13 diff --git a/go.sum b/go.sum index 8ff9c36..30170e1 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/actions/workflow-parser v1.0.0 h1:Zz2Ke31f3OMYCSzU2pqZSsk/Oz+lWXfEiXMisjxgGcc= -github.com/actions/workflow-parser v1.0.0/go.mod h1:jz9ZVl8zUIcjMfDQearQjvUHIBhx9l1ys4keDd6be34= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/andreaskoch/go-fswatch v1.0.0 h1:la8nP/HiaFCxP2IM6NZNUCoxgLWuyNFgH0RligBbnJU= @@ -21,8 +19,6 @@ 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/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= -github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb h1:PyjxRdW1mqCmSoxy/6uP01P7CGbsD+woX+oOWbaUPwQ= github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -49,12 +45,14 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/gophercloud/gophercloud v0.7.0 h1:vhmQQEM2SbnGCg2/3EzQnQZ3V7+UCGy9s8exQCprNYg= +github.com/gophercloud/gophercloud v0.7.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93 h1:T1Q6ag9tCwun16AW+XK3tAql24P4uTGUMIn1/92WsQQ= +github.com/hashicorp/hil v0.0.0-20190212132231-97b3a9cdfa93/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -79,6 +77,10 @@ github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnG github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= @@ -103,10 +105,6 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PX github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/soniakeys/bits v1.0.0 h1:Rune9VFefdJvLE0Q5iRCVGiKdSu2iDihs2I6SCm7evw= -github.com/soniakeys/bits v1.0.0/go.mod h1:7yJHB//UizrUr64VFneewK6SX5oeCf0SMbDYe2ey1JA= -github.com/soniakeys/graph v0.0.0 h1:C/Rr8rv9wbhZIsYHcWJFoI84pkipJocMYdRteE+/PQA= -github.com/soniakeys/graph v0.0.0/go.mod h1:lxpIbor/bIzWUAqvt1Dx92Hr63uWeyuEAbPnsjYbVwM= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= @@ -126,6 +124,9 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= +golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -134,25 +135,41 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 h1:e6HwijUxhDe+hPNjZQQn9bA5PW3vNmnN64U2ZW759Lk= +golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533 h1:bLfqnzrpeG4usq5OvMCrwTdmMJ6aTmlCuo1eKl0mhkI= golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -161,10 +178,11 @@ google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca/go.mod h1:L3J43x8/ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo= @@ -179,8 +197,9 @@ gopkg.in/src-d/go-git.v4 v4.9.1 h1:0oKHJZY8tM7B71378cfTg2c5jmWyNlXvestTT6WfY+4= gopkg.in/src-d/go-git.v4 v4.9.1/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/common/draw.go b/pkg/common/draw.go similarity index 100% rename from common/draw.go rename to pkg/common/draw.go diff --git a/common/executor.go b/pkg/common/executor.go similarity index 100% rename from common/executor.go rename to pkg/common/executor.go diff --git a/common/executor_test.go b/pkg/common/executor_test.go similarity index 100% rename from common/executor_test.go rename to pkg/common/executor_test.go diff --git a/common/file.go b/pkg/common/file.go similarity index 100% rename from common/file.go rename to pkg/common/file.go diff --git a/common/git.go b/pkg/common/git.go similarity index 100% rename from common/git.go rename to pkg/common/git.go diff --git a/common/git_test.go b/pkg/common/git_test.go similarity index 100% rename from common/git_test.go rename to pkg/common/git_test.go diff --git a/container/docker_build.go b/pkg/container/docker_build.go similarity index 98% rename from container/docker_build.go rename to pkg/container/docker_build.go index 8fac5f4..9d3c621 100644 --- a/container/docker_build.go +++ b/pkg/container/docker_build.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/fileutils" - "github.com/nektos/act/common" + "github.com/nektos/act/pkg/common" log "github.com/sirupsen/logrus" ) diff --git a/container/docker_common.go b/pkg/container/docker_common.go similarity index 100% rename from container/docker_common.go rename to pkg/container/docker_common.go diff --git a/container/docker_images.go b/pkg/container/docker_images.go similarity index 100% rename from container/docker_images.go rename to pkg/container/docker_images.go diff --git a/container/docker_images_test.go b/pkg/container/docker_images_test.go similarity index 100% rename from container/docker_images_test.go rename to pkg/container/docker_images_test.go diff --git a/container/docker_pull.go b/pkg/container/docker_pull.go similarity index 97% rename from container/docker_pull.go rename to pkg/container/docker_pull.go index 99fcd94..c324598 100644 --- a/container/docker_pull.go +++ b/pkg/container/docker_pull.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" - "github.com/nektos/act/common" + "github.com/nektos/act/pkg/common" ) // NewDockerPullExecutorInput the input for the NewDockerPullExecutor function diff --git a/container/docker_pull_test.go b/pkg/container/docker_pull_test.go similarity index 100% rename from container/docker_pull_test.go rename to pkg/container/docker_pull_test.go diff --git a/container/docker_run.go b/pkg/container/docker_run.go similarity index 99% rename from container/docker_run.go rename to pkg/container/docker_run.go index 246fca2..0397ec0 100644 --- a/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" - "github.com/nektos/act/common" + "github.com/nektos/act/pkg/common" "golang.org/x/crypto/ssh/terminal" ) diff --git a/container/docker_run_test.go b/pkg/container/docker_run_test.go similarity index 97% rename from container/docker_run_test.go rename to pkg/container/docker_run_test.go index a1dfd9c..9d92b22 100644 --- a/container/docker_run_test.go +++ b/pkg/container/docker_run_test.go @@ -3,11 +3,12 @@ package container import ( "bytes" "context" - "github.com/nektos/act/common" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" "io/ioutil" "testing" + + "github.com/nektos/act/pkg/common" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" ) type rawFormatter struct{} diff --git a/pkg/model/planner.go b/pkg/model/planner.go new file mode 100644 index 0000000..854bf81 --- /dev/null +++ b/pkg/model/planner.go @@ -0,0 +1,196 @@ +package model + +import ( + "io/ioutil" + "math" + "os" + "path/filepath" + "sort" + + log "github.com/sirupsen/logrus" +) + +// WorkflowPlanner contains methods for creating plans +type WorkflowPlanner interface { + PlanEvent(eventName string) *Plan + PlanJob(jobName string) *Plan + GetEvents() []string +} + +// Plan contains a list of stages to run in series +type Plan struct { + Stages []*Stage +} + +// Stage contains a list of runs to execute in parallel +type Stage struct { + Runs []*Run +} + +// Run represents a job from a workflow that needs to be run +type Run struct { + Workflow *Workflow + JobID string +} + +// NewWorkflowPlanner will load all workflows from a directory +func NewWorkflowPlanner(dirname string) (WorkflowPlanner, error) { + log.Debugf("Loading workflows from '%s'", dirname) + files, err := ioutil.ReadDir(dirname) + if err != nil { + return nil, err + } + + wp := new(workflowPlanner) + for _, file := range files { + ext := filepath.Ext(file.Name()) + if ext == ".yml" || ext == ".yaml" { + f, err := os.Open(filepath.Join(dirname, file.Name())) + if err != nil { + return nil, err + } + + workflow, err := ReadWorkflow(f) + if err != nil { + f.Close() + return nil, err + } + wp.workflows = append(wp.workflows, workflow) + f.Close() + } + } + + return wp, nil +} + +type workflowPlanner struct { + workflows []*Workflow +} + +// PlanEvent builds a new list of runs to execute in parallel for an event name +func (wp *workflowPlanner) PlanEvent(eventName string) *Plan { + plan := new(Plan) + for _, w := range wp.workflows { + if w.On == eventName { + plan.mergeStages(createStages(w, w.GetJobIDs()...)) + } + } + return plan +} + +// PlanJob builds a new run to execute in parallel for a job name +func (wp *workflowPlanner) PlanJob(jobName string) *Plan { + plan := new(Plan) + for _, w := range wp.workflows { + plan.mergeStages(createStages(w, jobName)) + } + return plan +} + +// GetEvents gets all the events in the workflows file +func (wp *workflowPlanner) GetEvents() []string { + events := make([]string, 0) + for _, w := range wp.workflows { + found := false + for _, e := range events { + if e == w.On { + found = true + break + } + } + + if !found { + events = append(events, w.On) + } + } + + // sort the list based on depth of dependencies + sort.Slice(events, func(i, j int) bool { + return events[i] < events[j] + }) + + return events +} + +// GetJobIDs will get all the job names in the stage +func (s *Stage) GetJobIDs() []string { + names := make([]string, 0) + for _, r := range s.Runs { + names = append(names, r.JobID) + } + return names +} + +// Merge stages with existing stages in plan +func (p *Plan) mergeStages(stages []*Stage) { + newStages := make([]*Stage, int(math.Max(float64(len(p.Stages)), float64(len(stages))))) + for i := 0; i < len(newStages); i++ { + newStages[i] = new(Stage) + if i >= len(p.Stages) { + newStages[i].Runs = append(stages[i].Runs) + } else if i >= len(stages) { + newStages[i].Runs = append(p.Stages[i].Runs) + } else { + newStages[i].Runs = append(p.Stages[i].Runs, stages[i].Runs...) + } + } + p.Stages = newStages +} + +func createStages(w *Workflow, jobIDs ...string) []*Stage { + // first, build a list of all the necessary jobs to run, and their dependencies + jobDependencies := make(map[string][]string) + for len(jobIDs) > 0 { + newJobIDs := make([]string, 0) + for _, jID := range jobIDs { + // make sure we haven't visited this job yet + if _, ok := jobDependencies[jID]; !ok { + if job := w.GetJob(jID); job != nil { + jobDependencies[jID] = job.Needs + newJobIDs = append(newJobIDs, job.Needs...) + } + } + } + jobIDs = newJobIDs + } + + // next, build an execution graph + stages := make([]*Stage, 0) + for len(jobDependencies) > 0 { + stage := new(Stage) + for jID, jDeps := range jobDependencies { + // make sure all deps are in the graph already + if listInStages(jDeps, stages...) { + stage.Runs = append(stage.Runs, &Run{ + Workflow: w, + JobID: jID, + }) + delete(jobDependencies, jID) + } + } + if len(stage.Runs) == 0 { + log.Fatalf("Unable to build dependency graph!") + } + stages = append(stages, stage) + } + + return stages +} + +// return true iff all strings in srcList exist in at least one of the stages +func listInStages(srcList []string, stages ...*Stage) bool { + for _, src := range srcList { + found := false + for _, stage := range stages { + for _, search := range stage.GetJobIDs() { + if src == search { + found = true + } + } + } + if !found { + return false + } + } + return true +} diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go new file mode 100644 index 0000000..9be0dfa --- /dev/null +++ b/pkg/model/workflow.go @@ -0,0 +1,67 @@ +package model + +import ( + "io" + + "gopkg.in/yaml.v2" +) + +// Workflow is the structure of the files in .github/workflows +type Workflow struct { + Name string `yaml:"name"` + On string `yaml:"on"` + Env map[string]string `yaml:"env"` + Jobs map[string]*Job `yaml:"jobs"` +} + +// Job is the structure of one job in a workflow +type Job struct { + Name string `yaml:"name"` + Needs []string `yaml:"needs"` + RunsOn string `yaml:"runs-on"` + Env map[string]string `yaml:"env"` + If string `yaml:"if"` + Steps []*Step `yaml:"steps"` + TimeoutMinutes int64 `yaml:"timeout-minutes"` +} + +// Step is the structure of one step in a job +type Step struct { + ID string `yaml:"id"` + If string `yaml:"if"` + Name string `yaml:"name"` + Uses string `yaml:"uses"` + Run string `yaml:"run"` + WorkingDirectory string `yaml:"working-directory"` + Shell string `yaml:"shell"` + Env map[string]string `yaml:"env"` + With map[string]string `yaml:"with"` + ContinueOnError bool `yaml:"continue-on-error"` + TimeoutMinutes int64 `yaml:"timeout-minutes"` +} + +// ReadWorkflow returns a list of jobs for a given workflow file reader +func ReadWorkflow(in io.Reader) (*Workflow, error) { + w := new(Workflow) + err := yaml.NewDecoder(in).Decode(w) + return w, err +} + +// GetJob will get a job by name in the workflow +func (w *Workflow) GetJob(jobID string) *Job { + for id, j := range w.Jobs { + if jobID == id { + return j + } + } + return nil +} + +// GetJobIDs will get all the job names in the workflow +func (w *Workflow) GetJobIDs() []string { + ids := make([]string, 0) + for id := range w.Jobs { + ids = append(ids, id) + } + return ids +} diff --git a/pkg/runner/api.go b/pkg/runner/api.go new file mode 100644 index 0000000..b9f342b --- /dev/null +++ b/pkg/runner/api.go @@ -0,0 +1,5 @@ +package runner + +type environmentApplier interface { + applyEnvironment(map[string]string) +} diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go new file mode 100644 index 0000000..c242e67 --- /dev/null +++ b/pkg/runner/runner.go @@ -0,0 +1,88 @@ +package runner + +import ( + "io" + "io/ioutil" + "os" + + "github.com/nektos/act/pkg/common" + "github.com/nektos/act/pkg/model" + log "github.com/sirupsen/logrus" +) + +// Runner provides capabilities to run GitHub actions +type Runner interface { + PlanRunner + io.Closer +} + +// PlanRunner to run a specific actions +type PlanRunner interface { + RunPlan(plan *model.Plan) error +} + +// Config contains the config for a new runner +type Config struct { + Dryrun bool // don't start any of the containers + EventName string // name of event to run + EventPath string // path to JSON file to use for event.json in containers + ReuseContainers bool // reuse containers to maintain state + ForcePull bool // force pulling of the image, if already present +} + +type runnerImpl struct { + config *Config + tempDir string + eventJSON string +} + +// NewRunner Creates a new Runner +func NewRunner(runnerConfig *Config) (Runner, error) { + runner := &runnerImpl{ + config: runnerConfig, + } + + init := common.NewPipelineExecutor( + runner.setupTempDir, + runner.setupEvent, + ) + + return runner, init() +} + +func (runner *runnerImpl) setupTempDir() error { + var err error + runner.tempDir, err = ioutil.TempDir("", "act-") + return err +} + +func (runner *runnerImpl) setupEvent() error { + runner.eventJSON = "{}" + if runner.config.EventPath != "" { + log.Debugf("Reading event.json from %s", runner.config.EventPath) + eventJSONBytes, err := ioutil.ReadFile(runner.config.EventPath) + if err != nil { + return err + } + runner.eventJSON = string(eventJSONBytes) + } + return nil +} + +func (runner *runnerImpl) RunPlan(plan *model.Plan) error { + pipeline := make([]common.Executor, 0) + for _, stage := range plan.Stages { + stageExecutor := make([]common.Executor, 0) + for _, run := range stage.Runs { + stageExecutor = append(stageExecutor, runner.newRunExecutor(run)) + } + pipeline = append(pipeline, common.NewParallelExecutor(stageExecutor...)) + } + + executor := common.NewPipelineExecutor(pipeline...) + return executor() +} + +func (runner *runnerImpl) Close() error { + return os.RemoveAll(runner.tempDir) +} diff --git a/actions/runner_exec.go b/pkg/runner/runner_exec.go similarity index 93% rename from actions/runner_exec.go rename to pkg/runner/runner_exec.go index aec5151..0a52fab 100644 --- a/actions/runner_exec.go +++ b/pkg/runner/runner_exec.go @@ -1,21 +1,20 @@ -package actions +package runner import ( "archive/tar" "bytes" "fmt" "io" - "os" "path/filepath" "regexp" - "github.com/actions/workflow-parser/model" - "github.com/nektos/act/common" - "github.com/nektos/act/container" + "github.com/nektos/act/pkg/common" + "github.com/nektos/act/pkg/container" + "github.com/nektos/act/pkg/model" log "github.com/sirupsen/logrus" ) -func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { +func (runner *runnerImpl) newRunExecutor(run *model.Run) common.Executor { action := runner.workflowConfig.GetAction(actionName) if action == nil { return common.NewErrorExecutor(fmt.Errorf("Unable to find action named '%s'", actionName)) @@ -35,7 +34,8 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { return common.NewPipelineExecutor(executors...) } -func (runner *runnerImpl) addImageExecutor(action *model.Action, executors *[]common.Executor) (string, error) { +/* +func (runner *runnerImpl) addImageExecutor(action *Action, executors *[]common.Executor) (string, error) { var image string logger := newActionLogger(action.Identifier, runner.config.Dryrun) log.Debugf("Using '%s' for action '%s'", action.Uses, action.Identifier) @@ -111,8 +111,9 @@ func (runner *runnerImpl) addImageExecutor(action *model.Action, executors *[]co return image, nil } +*/ -func (runner *runnerImpl) addRunExecutor(action *model.Action, image string, executors *[]common.Executor) error { +func (runner *runnerImpl) addRunExecutor(action *Action, image string, executors *[]common.Executor) error { logger := newActionLogger(action.Identifier, runner.config.Dryrun) log.Debugf("Using '%s' for action '%s'", action.Uses, action.Identifier) diff --git a/actions/runner_test.go b/pkg/runner/runner_test.go similarity index 99% rename from actions/runner_test.go rename to pkg/runner/runner_test.go index cd96aae..5ca1d0f 100644 --- a/actions/runner_test.go +++ b/pkg/runner/runner_test.go @@ -1,4 +1,4 @@ -package actions +package runner import ( "context"