fix: prevent premature token revocation in reusable workflows
This commit is contained in:
parent
66a7e82c43
commit
d013126979
2 changed files with 54 additions and 17 deletions
|
|
@ -180,31 +180,35 @@ func setJobResult(ctx context.Context, info jobInfo, rc *RunContext, success boo
|
|||
jobResult = "failure"
|
||||
}
|
||||
|
||||
// Set local result on current job (child or parent)
|
||||
info.result(jobResult)
|
||||
|
||||
if rc.caller != nil {
|
||||
// set reusable workflow job result
|
||||
// Child reusable workflow:
|
||||
// 1) propagate result to parent job state
|
||||
rc.caller.runContext.result(jobResult)
|
||||
|
||||
// 2) copy workflow_call outputs from child to parent (as in upstream)
|
||||
jobOutputs := make(map[string]string)
|
||||
ee := rc.NewExpressionEvaluator(ctx)
|
||||
if wfcc := rc.Run.Workflow.WorkflowCallConfig(); wfcc != nil {
|
||||
for k, v := range wfcc.Outputs {
|
||||
jobOutputs[k] = ee.Interpolate(ctx, ee.Interpolate(ctx, v.Value))
|
||||
}
|
||||
}
|
||||
rc.caller.runContext.Run.Job().Outputs = jobOutputs
|
||||
|
||||
// 3) DO NOT print banner in child job (prevents premature token revocation)
|
||||
logger.Debugf("Reusable job result=%s (parent will finalize, no banner)", jobResult)
|
||||
return
|
||||
}
|
||||
|
||||
// Parent job: print the final banner ONCE (job-level)
|
||||
jobResultMessage := "succeeded"
|
||||
if jobResult != "success" {
|
||||
jobResultMessage = "failed"
|
||||
}
|
||||
|
||||
jobOutputs := rc.Run.Job().Outputs
|
||||
if rc.caller != nil {
|
||||
// Rewrite the job's outputs into the workflow_call outputs...
|
||||
jobOutputs = make(map[string]string)
|
||||
ee := rc.NewExpressionEvaluator(ctx)
|
||||
for k, v := range rc.Run.Workflow.WorkflowCallConfig().Outputs {
|
||||
jobOutputs[k] = ee.Interpolate(ctx, ee.Interpolate(ctx, v.Value))
|
||||
}
|
||||
// When running as a daemon and receiving jobs from Forgejo, the next job (and any of it's `needs` outputs) will
|
||||
// be provided by Forgejo based upon the data sent to the logger below. However, when running `forgejo-runner
|
||||
// exec` with a reusable workflow, the next job will only be able to read outputs if those outputs are stored on
|
||||
// the workflow -- that's what is accomplished here:
|
||||
rc.caller.runContext.Run.Job().Outputs = jobOutputs
|
||||
}
|
||||
|
||||
logger.
|
||||
WithFields(logrus.Fields{
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.forgejo.org/forgejo/runner/v11/act/common"
|
||||
"code.forgejo.org/forgejo/runner/v11/act/common/git"
|
||||
"code.forgejo.org/forgejo/runner/v11/act/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||
|
|
@ -115,7 +116,10 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
|
|||
return err
|
||||
}
|
||||
|
||||
return runner.NewPlanExecutor(plan)(ctx)
|
||||
planErr := runner.NewPlanExecutor(plan)(ctx)
|
||||
|
||||
// Finalize from parent context: one job-level banner
|
||||
return finalizeReusableWorkflow(ctx, rc, planErr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +175,10 @@ func newReusableWorkflowExecutor(rc *RunContext, directory, workflow string) com
|
|||
return err
|
||||
}
|
||||
|
||||
return runner.NewPlanExecutor(plan)(ctx)
|
||||
planErr := runner.NewPlanExecutor(plan)(ctx)
|
||||
|
||||
// Finalize from parent context: one job-level banner
|
||||
return finalizeReusableWorkflow(ctx, rc, planErr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,3 +236,29 @@ func newRemoteReusableWorkflowWithPlat(url, uses string) *remoteReusableWorkflow
|
|||
URL: url,
|
||||
}
|
||||
}
|
||||
|
||||
// finalizeReusableWorkflow prints the final job banner from the parent job context.
|
||||
//
|
||||
// The Forgejo reporter waits for this banner (log entry with "jobResult"
|
||||
// field and without stage="Main") before marking the job as complete and revoking
|
||||
// tokens. Printing this banner from the child reusable workflow would cause
|
||||
// premature token revocation, breaking subsequent steps in the parent workflow.
|
||||
func finalizeReusableWorkflow(ctx context.Context, rc *RunContext, planErr error) error {
|
||||
jobResult := "success"
|
||||
jobResultMessage := "succeeded"
|
||||
if planErr != nil {
|
||||
jobResult = "failure"
|
||||
jobResultMessage = "failed"
|
||||
}
|
||||
|
||||
// Outputs should already be present in the parent context:
|
||||
// - copied by child's setJobResult branch (rc.caller != nil)
|
||||
jobOutputs := rc.Run.Job().Outputs
|
||||
|
||||
common.Logger(ctx).WithFields(logrus.Fields{
|
||||
"jobResult": jobResult,
|
||||
"jobOutputs": jobOutputs,
|
||||
}).Infof("\U0001F3C1 Job %s", jobResultMessage)
|
||||
|
||||
return planErr
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue