diff --git a/cmd/input.go b/cmd/input.go index 6cde2f0c..164d8484 100644 --- a/cmd/input.go +++ b/cmd/input.go @@ -34,6 +34,7 @@ type Input struct { githubInstance string containerCapAdd []string containerCapDrop []string + autoRemove bool } func (i *Input) resolve(path string) string { diff --git a/cmd/root.go b/cmd/root.go index bfde1688..25f2b825 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -52,6 +52,7 @@ func Execute(ctx context.Context, version string) { rootCmd.Flags().BoolVar(&input.useGitIgnore, "use-gitignore", true, "Controls whether paths specified in .gitignore should be copied into container") rootCmd.Flags().StringArrayVarP(&input.containerCapAdd, "container-cap-add", "", []string{}, "kernel capabilities to add to the workflow containers (e.g. --container-cap-add SYS_PTRACE)") rootCmd.Flags().StringArrayVarP(&input.containerCapDrop, "container-cap-drop", "", []string{}, "kernel capabilities to remove from the workflow containers (e.g. --container-cap-drop SYS_PTRACE)") + rootCmd.Flags().BoolVar(&input.autoRemove, "rm", false, "automatically remove containers just before exit") rootCmd.PersistentFlags().StringVarP(&input.actor, "actor", "a", "nektos/act", "user that triggered the event") rootCmd.PersistentFlags().StringVarP(&input.workflowsPath, "workflows", "W", "./.github/workflows/", "path to workflow file(s)") rootCmd.PersistentFlags().BoolVarP(&input.noWorkflowRecurse, "no-recurse", "", false, "Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag") @@ -263,6 +264,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str GitHubInstance: input.githubInstance, ContainerCapAdd: input.containerCapAdd, ContainerCapDrop: input.containerCapDrop, + AutoRemove: input.autoRemove, } r, err := runner.New(config) if err != nil { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index cf76d9df..f34f9e80 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -42,6 +42,7 @@ type Config struct { GitHubInstance string // GitHub instance to use, default "github.com" ContainerCapAdd []string // list of kernel capabilities to add to the containers ContainerCapDrop []string // list of kernel capabilities to remove from the containers + AutoRemove bool // controls if the container is automatically removed upon workflow completion } // Resolves the equivalent host path inside the container @@ -111,9 +112,9 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { maxJobNameLen := 0 pipeline := make([]common.Executor, 0) - for _, stage := range plan.Stages { + for s, stage := range plan.Stages { stageExecutor := make([]common.Executor, 0) - for _, run := range stage.Runs { + for r, run := range stage.Runs { job := run.Job() matrixes := job.GetMatrixes() @@ -128,7 +129,20 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { } stageExecutor = append(stageExecutor, func(ctx context.Context) error { jobName := fmt.Sprintf("%-*s", maxJobNameLen, rc.String()) - return rc.Executor()(WithJobLogger(ctx, jobName, rc.Config.Secrets, rc.Config.InsecureSecrets)) + return rc.Executor().Finally(func(ctx context.Context) error { + isLastRunningContainer := func(currentStage int, currentRun int) bool { + return currentStage == len(plan.Stages)-1 && currentRun == len(stage.Runs)-1 + } + + if runner.config.AutoRemove && isLastRunningContainer(s, r) { + log.Infof("Cleaning up container for job %s", rc.JobName) + if err := rc.stopJobContainer()(ctx); err != nil { + log.Errorf("Error while cleaning container: %v", err) + } + } + + return nil + })(WithJobLogger(ctx, jobName, rc.Config.Secrets, rc.Config.InsecureSecrets)) }) } }