mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 14:03:24 +09:00
feat(actions): implement jobs.<job_id>.continue-on-error (#38100)
Support `continue-on-error` for workflow jobs when aggregating an Actions workflow run status. Previously, `continue-on-error` was parsed from workflow YAML but was not persisted or used when calculating the overall run result. As a result, a failed job could incorrectly fail the entire workflow even when the workflow explicitly allowed that job to fail. This PR stores the parsed `continue-on-error` value on each action run job and treats failed jobs with `continue-on-error: true` as successful when computing the workflow run status, matching GitHub Actions behavior. ## Changes - Add `ContinueOnError` to `jobparser.Job`. - Add `continue_on_error` to `ActionRunJob` with a `NOT NULL DEFAULT FALSE` migration. - Populate `ActionRunJob.ContinueOnError` when creating workflow run jobs. - Update workflow status aggregation so failed `continue-on-error` jobs do not fail the overall run. - Leave `resolveCheckNeeds` unchanged so dependent jobs still see the job result as `failure` and are skipped by default. ## Compatibility This is backward compatible. If only the runner or only the server is updated, `continue-on-error` continues to degrade to the previous behavior and is effectively ignored until both sides support it. Related runner PR: https://gitea.com/gitea/runner/pulls/1032 --------- Signed-off-by: bircni <bircni@icloud.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
@@ -79,23 +79,37 @@ func (w *SingleWorkflow) Marshal() ([]byte, error) {
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
RawNeeds yaml.Node `yaml:"needs,omitempty"`
|
||||
RawRunsOn yaml.Node `yaml:"runs-on,omitempty"`
|
||||
Env yaml.Node `yaml:"env,omitempty"`
|
||||
If yaml.Node `yaml:"if,omitempty"`
|
||||
Steps []*Step `yaml:"steps,omitempty"`
|
||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
||||
Services map[string]*ContainerSpec `yaml:"services,omitempty"`
|
||||
Strategy Strategy `yaml:"strategy,omitempty"`
|
||||
RawContainer yaml.Node `yaml:"container,omitempty"`
|
||||
Defaults Defaults `yaml:"defaults,omitempty"`
|
||||
Outputs map[string]string `yaml:"outputs,omitempty"`
|
||||
Uses string `yaml:"uses,omitempty"`
|
||||
With map[string]any `yaml:"with,omitempty"`
|
||||
RawSecrets yaml.Node `yaml:"secrets,omitempty"`
|
||||
RawConcurrency *model.RawConcurrency `yaml:"concurrency,omitempty"`
|
||||
RawPermissions yaml.Node `yaml:"permissions,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
RawNeeds yaml.Node `yaml:"needs,omitempty"`
|
||||
RawRunsOn yaml.Node `yaml:"runs-on,omitempty"`
|
||||
Env yaml.Node `yaml:"env,omitempty"`
|
||||
If yaml.Node `yaml:"if,omitempty"`
|
||||
Steps []*Step `yaml:"steps,omitempty"`
|
||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
||||
RawContinueOnError yaml.Node `yaml:"continue-on-error,omitempty"`
|
||||
Services map[string]*ContainerSpec `yaml:"services,omitempty"`
|
||||
Strategy Strategy `yaml:"strategy,omitempty"`
|
||||
RawContainer yaml.Node `yaml:"container,omitempty"`
|
||||
Defaults Defaults `yaml:"defaults,omitempty"`
|
||||
Outputs map[string]string `yaml:"outputs,omitempty"`
|
||||
Uses string `yaml:"uses,omitempty"`
|
||||
With map[string]any `yaml:"with,omitempty"`
|
||||
RawSecrets yaml.Node `yaml:"secrets,omitempty"`
|
||||
RawConcurrency *model.RawConcurrency `yaml:"concurrency,omitempty"`
|
||||
RawPermissions yaml.Node `yaml:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
// GetContinueOnError decodes the continue-on-error field to a bool.
|
||||
// The field may be a literal bool or an already-evaluated expression node.
|
||||
func (j *Job) GetContinueOnError() bool {
|
||||
if j.RawContinueOnError.Kind == 0 {
|
||||
return false
|
||||
}
|
||||
var v bool
|
||||
if err := j.RawContinueOnError.Decode(&v); err != nil {
|
||||
return false
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (j *Job) Clone() *Job {
|
||||
@@ -103,23 +117,24 @@ func (j *Job) Clone() *Job {
|
||||
return nil
|
||||
}
|
||||
return &Job{
|
||||
Name: j.Name,
|
||||
RawNeeds: j.RawNeeds,
|
||||
RawRunsOn: j.RawRunsOn,
|
||||
Env: j.Env,
|
||||
If: j.If,
|
||||
Steps: j.Steps,
|
||||
TimeoutMinutes: j.TimeoutMinutes,
|
||||
Services: j.Services,
|
||||
Strategy: j.Strategy,
|
||||
RawContainer: j.RawContainer,
|
||||
Defaults: j.Defaults,
|
||||
Outputs: j.Outputs,
|
||||
Uses: j.Uses,
|
||||
With: j.With,
|
||||
RawSecrets: j.RawSecrets,
|
||||
RawConcurrency: j.RawConcurrency,
|
||||
RawPermissions: j.RawPermissions,
|
||||
Name: j.Name,
|
||||
RawNeeds: j.RawNeeds,
|
||||
RawRunsOn: j.RawRunsOn,
|
||||
Env: j.Env,
|
||||
If: j.If,
|
||||
Steps: j.Steps,
|
||||
TimeoutMinutes: j.TimeoutMinutes,
|
||||
RawContinueOnError: j.RawContinueOnError,
|
||||
Services: j.Services,
|
||||
Strategy: j.Strategy,
|
||||
RawContainer: j.RawContainer,
|
||||
Defaults: j.Defaults,
|
||||
Outputs: j.Outputs,
|
||||
Uses: j.Uses,
|
||||
With: j.With,
|
||||
RawSecrets: j.RawSecrets,
|
||||
RawConcurrency: j.RawConcurrency,
|
||||
RawPermissions: j.RawPermissions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user