From 21484b5c1ecbd9e1ddb6a57debd7731a3e1fc25f Mon Sep 17 00:00:00 2001 From: Alex Savchuk Date: Thu, 29 Sep 2022 08:59:52 +0300 Subject: [PATCH] fix: show workflow info even if on.push is not defined (#1329) (#1335) * fix: show workflow info even if on.push is not defined (#1329) To fix listing of workflows in such cases list/graph filtering was split with planning. Now act supports one of the following list (-l)/graph (-g) cases: * show all jobs of loaded workflows: act -l * show specific job JOBNAME: act -l -j JOBNAME * show jobs of loaded workflows in which event EVENTNAME is set up: act -l EVENTNAME * show jobs of loaded workflows in which first defined workflow event is set up: act -l --detect-event For planning it supports: * running specific job JOBNAME with triggered event determined from: ** CLI argument: act -j JOBNAME EVENTNAME ** first defined in loaded workflows event: act -j JOBNAME --detect-event ** only defined in loaded workflows event: act -j JOBNAME ** push event by default: act -j JOBNAME * running jobs of loaded workflows in which event is set up, event is determined from: ** CLI argument: act EVENTNAME ** first defined in loaded workflows event: act --detect-event ** only defined in loaded workflows event: act ** push event by default: act Except #1329 this PR fixes #1332, #1318 * Update docs/help --- README.md | 6 ++- cmd/root.go | 110 ++++++++++++++++++++++++++++++------------- pkg/model/planner.go | 15 ++++++ 3 files changed, 97 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 823c04b..8fe500e 100644 --- a/README.md +++ b/README.md @@ -128,13 +128,17 @@ Download the [latest release](https://github.com/nektos/act/releases/latest) and # Command structure: act [] [options] If no event name passed, will default to "on: push" +If actions handles only one event it will be used as default instead of "on: push" -# List the actions for the default event: +# List all actions for all events: act -l # List the actions for a specific event: act workflow_dispatch -l +# List the actions for a specific job: +act -j test -l + # Run the default (`push`) event: act diff --git a/cmd/root.go b/cmd/root.go index 646ca99..d56d2bb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,7 +30,7 @@ import ( func Execute(ctx context.Context, version string) { input := new(Input) var rootCmd = &cobra.Command{ - Use: "act [event name to run]\nIf no event name passed, will default to \"on: push\"", + Use: "act [event name to run] [flags]\n\nIf no event name passed, will default to \"on: push\"\nIf actions handles only one event it will be used as default instead of \"on: push\"", Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.", Args: cobra.MaximumNArgs(1), RunE: newRunCommand(ctx, input), @@ -304,46 +304,90 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str return err } - // Determine the event name - var eventName string - events := planner.GetEvents() - if input.autodetectEvent && len(events) > 0 { - // set default event type to first event - // this way user dont have to specify the event. - log.Debugf("Using detected workflow event: %s", events[0]) - eventName = events[0] - } else { - if len(args) > 0 { - eventName = args[0] - } else if plan := planner.PlanEvent("push"); plan != nil { - eventName = "push" - } - } - - // build the plan for this run - var plan *model.Plan - if jobID, err := cmd.Flags().GetString("job"); err != nil { + jobID, err := cmd.Flags().GetString("job") + if 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) } // check if we should just list the workflows - if list, err := cmd.Flags().GetBool("list"); err != nil { + list, err := cmd.Flags().GetBool("list") + if err != nil { return err - } else if list { - return printList(plan) } - // check if we should just print the graph - if list, err := cmd.Flags().GetBool("graph"); err != nil { + // check if we should just draw the graph + graph, err := cmd.Flags().GetBool("graph") + if err != nil { return err - } else if list { - return drawGraph(plan) + } + + // collect all events from loaded workflows + events := planner.GetEvents() + + // plan with filtered jobs - to be used for filtering only + var filterPlan *model.Plan + + // Determine the event name to be filtered + var filterEventName string = "" + + if len(args) > 0 { + log.Debugf("Using first passed in arguments event for filtering: %s", args[0]) + filterEventName = args[0] + } else if input.autodetectEvent && len(events) > 0 && len(events[0]) > 0 { + // set default event type to first event from many available + // this way user dont have to specify the event. + log.Debugf("Using first detected workflow event for filtering: %s", events[0]) + filterEventName = events[0] + } + + if jobID != "" { + log.Debugf("Preparing plan with a job: %s", jobID) + filterPlan = planner.PlanJob(jobID) + } else if filterEventName != "" { + log.Debugf("Preparing plan for a event: %s", filterEventName) + filterPlan = planner.PlanEvent(filterEventName) + } else { + log.Debugf("Preparing plan with all jobs") + filterPlan = planner.PlanAll() + } + + if list { + return printList(filterPlan) + } + + if graph { + return drawGraph(filterPlan) + } + + // plan with triggered jobs + var plan *model.Plan + + // Determine the event name to be triggered + var eventName string + + if len(args) > 0 { + log.Debugf("Using first passed in arguments event: %s", args[0]) + eventName = args[0] + } else if len(events) == 1 && len(events[0]) > 0 { + log.Debugf("Using the only detected workflow event: %s", events[0]) + eventName = events[0] + } else if input.autodetectEvent && len(events) > 0 && len(events[0]) > 0 { + // set default event type to first event from many available + // this way user dont have to specify the event. + log.Debugf("Using first detected workflow event: %s", events[0]) + eventName = events[0] + } else { + log.Debugf("Using default workflow event: push") + eventName = "push" + } + + // build the plan for this run + if jobID != "" { + log.Debugf("Planning job: %s", jobID) + plan = planner.PlanJob(jobID) + } else { + log.Debugf("Planning jobs for event: %s", eventName) + plan = planner.PlanEvent(eventName) } // check to see if the main branch was defined diff --git a/pkg/model/planner.go b/pkg/model/planner.go index 16c8928..7f8af6f 100644 --- a/pkg/model/planner.go +++ b/pkg/model/planner.go @@ -17,6 +17,7 @@ import ( type WorkflowPlanner interface { PlanEvent(eventName string) *Plan PlanJob(jobName string) *Plan + PlanAll() *Plan GetEvents() []string } @@ -196,6 +197,20 @@ func (wp *workflowPlanner) PlanJob(jobName string) *Plan { return plan } +// PlanAll builds a new run to execute in parallel all +func (wp *workflowPlanner) PlanAll() *Plan { + plan := new(Plan) + if len(wp.workflows) == 0 { + log.Debugf("no jobs found for loaded workflows") + } + + for _, w := range wp.workflows { + plan.mergeStages(createStages(w, w.GetJobIDs()...)) + } + + return plan +} + // GetEvents gets all the events in the workflows file func (wp *workflowPlanner) GetEvents() []string { events := make([]string, 0)