diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index 2754a7da14e2dbe4ddc3ace1d9fde62c5fc3d71a..8ff39da581321f181ba06744d8adb27920460d11 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -1540,6 +1540,24 @@ } } ] + }, + "exit_codes": { + "markdownDescription": "Either a single or array of exit codes to trigger job retry on. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#retryexit_codes).", + "oneOf": [ + { + "description": "Retry when the job exit code is included in the array's values.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "integer" + } + }, + { + "description": "Retry when the job exit code is equal to.", + "type": "integer" + } + ] } } } diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 83976bc0cfb7dd68bac0ed03cdb33657c0635d20..2535432cca647052228408077700d535a9d5834d 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -3793,7 +3793,7 @@ If not defined, defaults to `0` and jobs do not retry. When a job fails, the job is processed up to two more times, until it succeeds or reaches the maximum number of retries. -By default, all failure types cause the job to be retried. Use [`retry:when`](#retrywhen) +By default, all failure types cause the job to be retried. Use [`retry:when`](#retrywhen) or [`retry:exit_codes`](#retryexit_codes) to select which failures to retry on. **Keyword type**: Job keyword. You can use it only as part of a job or in the @@ -3809,8 +3809,20 @@ to select which failures to retry on. test: script: rspec retry: 2 + +test_advanced: + script: + - echo "Run a script that results in exit code 137." + - exit 137 + retry: + max: 2 + when: runner_system_failure + exit_codes: 137 ``` +`test_advanced` will be retried up to 2 times if the exit code is `137` or if it had +a runner system failure. + #### `retry:when` Use `retry:when` with `retry:max` to retry jobs for only specific failure cases. @@ -3872,6 +3884,48 @@ test: - stuck_or_timeout_failure ``` +#### `retry:exit_codes` + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/430037) in GitLab 16.10 [with a flag](../../administration/feature_flags.md) named `ci_retry_on_exit_codes`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, +an administrator can [enable the feature flag](../../administration/feature_flags.md) named `ci_retry_on_exit_codes`. + +Use `retry:exit_codes` with `retry:max` to retry jobs for only specific failure cases. +`retry:max` is the maximum number of retries, like [`retry`](#retry), and can be +`0`, `1`, or `2`. + +**Keyword type**: Job keyword. You can use it only as part of a job or in the +[`default` section](#default). + +**Possible inputs**: + +- A single exit code. +- An array of exit codes. + +**Example of `retry:exit_codes`**: + +```yaml +test_job_1: + script: + - echo "Run a script that results in exit code 1. This job isn't retried." + - exit 1 + retry: + max: 2 + exit_codes: 137 + +test_job_2: + script: + - echo "Run a script that results in exit code 137. This job will be retried." + - exit 137 + retry: + max: 1 + exit_codes: + - 255 + - 137 +``` + **Related topics**: You can specify the number of [retry attempts for certain stages of job execution](../runners/configure_runners.md#job-stages-attempts) diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js index d6424336f43baafe35f24023c0d6d89c99253b15..0d74355d3053671c4a15191bb5f916a09f64bb04 100644 --- a/spec/frontend/editor/schema/ci/ci_schema_spec.js +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -43,6 +43,7 @@ import WorkflowAutoCancelOnNewCommitYaml from './yaml_tests/positive_tests/workf import WorkflowRulesAutoCancelOnJobFailureYaml from './yaml_tests/positive_tests/workflow/rules/auto_cancel/on_job_failure.yml'; import WorkflowRulesAutoCancelOnNewCommitYaml from './yaml_tests/positive_tests/workflow/rules/auto_cancel/on_new_commit.yml'; import StagesYaml from './yaml_tests/positive_tests/stages.yml'; +import RetryYaml from './yaml_tests/positive_tests/retry.yml'; // YAML NEGATIVE TEST import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml'; @@ -74,6 +75,7 @@ import WorkflowAutoCancelOnNewCommitNegativeYaml from './yaml_tests/negative_tes import WorkflowRulesAutoCancelOnJobFailureNegativeYaml from './yaml_tests/negative_tests/workflow/rules/auto_cancel/on_job_failure.yml'; import WorkflowRulesAutoCancelOnNewCommitNegativeYaml from './yaml_tests/negative_tests/workflow/rules/auto_cancel/on_new_commit.yml'; import StagesNegativeYaml from './yaml_tests/negative_tests/stages.yml'; +import RetryNegativeYaml from './yaml_tests/negative_tests/retry.yml'; const ajv = new Ajv({ strictTypes: false, @@ -122,6 +124,7 @@ describe('positive tests', () => { WorkflowRulesAutoCancelOnJobFailureYaml, WorkflowRulesAutoCancelOnNewCommitYaml, StagesYaml, + RetryYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a @@ -172,6 +175,7 @@ describe('negative tests', () => { WorkflowRulesAutoCancelOnJobFailureNegativeYaml, WorkflowRulesAutoCancelOnNewCommitNegativeYaml, StagesNegativeYaml, + RetryNegativeYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/retry.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/retry.yml new file mode 100644 index 0000000000000000000000000000000000000000..7258521ec647dbaae288fa5ee3075607671fedc8 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/retry.yml @@ -0,0 +1,60 @@ +# invalid retry +invalid_job_with_retry_int: + stage: "test" + script: "rspec" + retry: -1 + +invalid_job_with_retry_type: + stage: "test" + script: "rspec" + retry: "2" + +invalid_job_with_retry_object_type: + stage: "test" + script: "rspec" + retry: + max: 2 + unknown: 2 + +# invalid retry:when +invalid_job_with_retry_single_when_reason: + stage: "test" + script: "rspec" + retry: + max: 2 + when: "gitlab-ci-retry-object-unknown-when" + +invalid_job_with_retry_when_reason: + stage: "test" + script: "rspec" + retry: + max: 2 + when: + - "api_failure" + - "gitlab-ci-retry-object-unknown-when" + +# invalid retry:exit_codes +invalid_job_with_retry_single_exit_codes_type: + stage: "test" + script: "rspec" + retry: + max: 2 + exit_codes: "137" + +invalid_job_with_retry_exit_codes_type: + stage: "test" + script: "rspec" + retry: + max: 2 + exit_codes: + - 137 + - "1" + +invalid_job_with_retry_exit_codes_duplicate: + stage: "test" + script: "rspec" + retry: + max: 2 + exit_codes: + - 137 + - 137 diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/retry.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/retry.yml new file mode 100644 index 0000000000000000000000000000000000000000..c6c1f105531f2118ffcf568ad66ba95f0c65ff4a --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/retry.yml @@ -0,0 +1,94 @@ +# valid retry +valid_job_with_retry_int: + stage: "test" + script: "rspec" + retry: 2 + +valid_job_with_retry_object_max: + stage: "test" + script: "rspec" + retry: + max: 2 + +valid_job_with_retry_object_when: + stage: "test" + script: "rspec" + retry: + when: "runner_system_failure" + +valid_job_with_retry_object_exit_codes: + stage: "test" + script: "rspec" + retry: + exit_codes: 137 + +valid_job_with_retry_object_all_properties: + stage: "test" + script: "rspec" + retry: + max: 1 + when: "runner_system_failure" + exit_codes: 137 + +# valid retry:when +valid_job_with_retry_single_when: + stage: "test" + script: "rspec" + retry: + max: 2 + when: "runner_system_failure" + +valid_job_with_retry_multiple_when: + stage: "test" + script: "rspec" + retry: + max: 2 + when: + - "runner_system_failure" + - "stuck_or_timeout_failure" + +valid_job_with_retry_all_when: + stage: "test" + script: "rspec" + retry: + max: 2 + when: + - "always" + - "unknown_failure" + - "script_failure" + - "api_failure" + - "stuck_or_timeout_failure" + - "runner_system_failure" + - "runner_unsupported" + - "stale_schedule" + - "job_execution_timeout" + - "archived_failure" + - "unmet_prerequisites" + - "scheduler_failure" + - "data_integrity_failure" + +valid_job_with_retry_duplicate_when: + stage: "test" + script: "rspec" + retry: + max: 2 + when: + - "runner_system_failure" + - "runner_system_failure" + +# valid retry:exit_codes +valid_job_with_retry_single_exit_codes: + stage: "test" + script: "rspec" + retry: + max: 2 + exit_codes: 137 + +valid_job_with_retry_multiple_exit_codes: + stage: "test" + script: "rspec" + retry: + max: 2 + exit_codes: + - 137 + - 255