Improve the support for reusable workflows (#122)

Fix [#32439](https://github.com/go-gitea/gitea/issues/32439)

- Support reusable workflows with conditional jobs
- Support nesting reusable workflows

Reviewed-on: https://gitea.com/gitea/act/pulls/122
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
This commit is contained in:
Zettat123
2024-11-23 14:14:17 +00:00
committed by Jason Song
parent 6cdf1e5788
commit 1656206765
4 changed files with 71 additions and 34 deletions

View File

@@ -73,10 +73,6 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
preExec := step.pre() preExec := step.pre()
preSteps = append(preSteps, useStepLogger(rc, stepModel, stepStagePre, func(ctx context.Context) error { preSteps = append(preSteps, useStepLogger(rc, stepModel, stepStagePre, func(ctx context.Context) error {
if rc.caller != nil { // For Gitea
rc.caller.reusedWorkflowJobResults[rc.JobName] = "pending"
}
logger := common.Logger(ctx) logger := common.Logger(ctx)
preErr := preExec(ctx) preErr := preExec(ctx)
if preErr != nil { if preErr != nil {
@@ -189,34 +185,7 @@ func setJobResult(ctx context.Context, info jobInfo, rc *RunContext, success boo
info.result(jobResult) info.result(jobResult)
if rc.caller != nil { if rc.caller != nil {
// set reusable workflow job result // set reusable workflow job result
rc.caller.setReusedWorkflowJobResult(rc.JobName, jobResult) // For Gitea
rc.caller.updateResultLock.Lock()
rc.caller.reusedWorkflowJobResults[rc.JobName] = jobResult
allJobDone := true
hasFailure := false
for _, result := range rc.caller.reusedWorkflowJobResults {
if result == "pending" {
allJobDone = false
break
}
if result == "failure" {
hasFailure = true
}
}
if allJobDone {
reusedWorkflowJobResult := "success"
reusedWorkflowJobResultMessage := "succeeded"
if hasFailure {
reusedWorkflowJobResult = "failure"
reusedWorkflowJobResultMessage = "failed"
}
rc.caller.runContext.result(reusedWorkflowJobResult)
logger.WithField("jobResult", reusedWorkflowJobResult).Infof("\U0001F3C1 Job %s", reusedWorkflowJobResultMessage)
}
rc.caller.updateResultLock.Unlock()
return return
} }

View File

@@ -175,7 +175,11 @@ func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow stri
return err return err
} }
return runner.NewPlanExecutor(plan)(ctx) // return runner.NewPlanExecutor(plan)(ctx)
return common.NewPipelineExecutor( // For Gitea
runner.NewPlanExecutor(plan),
setReusedWorkflowCallerResult(rc, runner),
)(ctx)
} }
} }
@@ -271,3 +275,47 @@ func newRemoteReusableWorkflow(uses string) *remoteReusableWorkflow {
URL: "https://github.com", URL: "https://github.com",
} }
} }
// For Gitea
func setReusedWorkflowCallerResult(rc *RunContext, runner Runner) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
runnerImpl, ok := runner.(*runnerImpl)
if !ok {
logger.Warn("Failed to get caller from runner")
return nil
}
caller := runnerImpl.caller
allJobDone := true
hasFailure := false
for _, result := range caller.reusedWorkflowJobResults {
if result == "pending" {
allJobDone = false
break
}
if result == "failure" {
hasFailure = true
}
}
if allJobDone {
reusedWorkflowJobResult := "success"
reusedWorkflowJobResultMessage := "succeeded"
if hasFailure {
reusedWorkflowJobResult = "failure"
reusedWorkflowJobResultMessage = "failed"
}
if rc.caller != nil {
rc.caller.setReusedWorkflowJobResult(rc.JobName, reusedWorkflowJobResult)
} else {
rc.result(reusedWorkflowJobResult)
logger.WithField("jobResult", reusedWorkflowJobResult).Infof("\U0001F3C1 Job %s", reusedWorkflowJobResultMessage)
}
}
return nil
}
}

View File

@@ -92,7 +92,12 @@ func (rc *RunContext) GetEnv() map[string]string {
} }
func (rc *RunContext) jobContainerName() string { func (rc *RunContext) jobContainerName() string {
return createSimpleContainerName(rc.Config.ContainerNamePrefix, "WORKFLOW-"+rc.Run.Workflow.Name, "JOB-"+rc.Name) nameParts := []string{rc.Config.ContainerNamePrefix, "WORKFLOW-" + rc.Run.Workflow.Name, "JOB-" + rc.Name}
if rc.caller != nil {
nameParts = append(nameParts, "CALLED-BY-"+rc.caller.runContext.JobName)
}
// return createSimpleContainerName(rc.Config.ContainerNamePrefix, "WORKFLOW-"+rc.Run.Workflow.Name, "JOB-"+rc.Name)
return createSimpleContainerName(nameParts...) // For Gitea
} }
// Deprecated: use `networkNameForGitea` // Deprecated: use `networkNameForGitea`
@@ -653,6 +658,7 @@ func (rc *RunContext) Executor() (common.Executor, error) {
return func(ctx context.Context) error { return func(ctx context.Context) error {
res, err := rc.isEnabled(ctx) res, err := rc.isEnabled(ctx)
if err != nil { if err != nil {
rc.caller.setReusedWorkflowJobResult(rc.JobName, "failure") // For Gitea
return err return err
} }
if res { if res {
@@ -748,6 +754,10 @@ func (rc *RunContext) isEnabled(ctx context.Context) (bool, error) {
} }
if !runJob { if !runJob {
if rc.caller != nil { // For Gitea
rc.caller.setReusedWorkflowJobResult(rc.JobName, "skipped")
return false, nil
}
l.WithField("jobResult", "skipped").Debugf("Skipping job '%s' due to '%s'", job.Name, job.If.Value) l.WithField("jobResult", "skipped").Debugf("Skipping job '%s' due to '%s'", job.Name, job.If.Value)
return false, nil return false, nil
} }

View File

@@ -210,6 +210,9 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
if len(rc.String()) > maxJobNameLen { if len(rc.String()) > maxJobNameLen {
maxJobNameLen = len(rc.String()) maxJobNameLen = len(rc.String())
} }
if rc.caller != nil { // For Gitea
rc.caller.setReusedWorkflowJobResult(rc.JobName, "pending")
}
stageExecutor = append(stageExecutor, func(ctx context.Context) error { stageExecutor = append(stageExecutor, func(ctx context.Context) error {
jobName := fmt.Sprintf("%-*s", maxJobNameLen, rc.String()) jobName := fmt.Sprintf("%-*s", maxJobNameLen, rc.String())
executor, err := rc.Executor() executor, err := rc.Executor()
@@ -281,3 +284,10 @@ func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, mat
return rc return rc
} }
// For Gitea
func (c *caller) setReusedWorkflowJobResult(jobName string, result string) {
c.updateResultLock.Lock()
defer c.updateResultLock.Unlock()
c.reusedWorkflowJobResults[jobName] = result
}