Implement allowed-skips input

This change lets the end-users mark certain jobs as allowed to be non-
voting when skipped only but their failures will affect the outcome.

Resolves #1
This commit is contained in:
Sviatoslav Sydorenko
2021-12-14 04:02:06 +01:00
parent 290bb0f92a
commit 3a2de129f0
3 changed files with 59 additions and 11 deletions
+8 -4
View File
@@ -78,6 +78,7 @@ jobs:
uses: re-actors/alls-green@release/v1 uses: re-actors/alls-green@release/v1
with: with:
allowed-failures: docs, linters allowed-failures: docs, linters
allowed-skips: non-voting-flaky-job
jobs: ${{ toJSON(needs) }} jobs: ${{ toJSON(needs) }}
... ...
``` ```
@@ -85,10 +86,13 @@ jobs:
## Options ## Options
There are two options — `allowed-failures` and `jobs`. The former is There are three options — `allowed-failures`, `allowed-skips` and
optional but the later is mandatory. `allowed-failures` tells the action `jobs`. The first two are optional but `jobs` is mandatory.
which jobs should not affect the outcome, by default all the jobs will `allowed-failures` tells the action which jobs should not affect the
be "voting". `jobs` is an object representing the jobs that should outcome if they don't succeed, by default all the jobs will
be "voting". Same goes for `allowed-skips` — it won't allow the listed
jobs to affect the outcome if they are skipped but are still "voting" in
case they run. `jobs` is an object representing the jobs that should
affect the decision of whether the pipeline failed or not, it is affect the decision of whether the pipeline failed or not, it is
important to pass a JSON-serialized `needs` context to this argument. important to pass a JSON-serialized `needs` context to this argument.
+11
View File
@@ -23,6 +23,13 @@ inputs:
Job names that are allowed to fail and not affect the outcome, Job names that are allowed to fail and not affect the outcome,
as a comma-separated list or serialized as a JSON string as a comma-separated list or serialized as a JSON string
required: false required: false
allowed-skips:
default: >-
[]
description: >-
Job names that are allowed to be skipped and not affect the
outcome, as a comma-separated list or serialized as a JSON string
required: false
outputs: outputs:
failure: failure:
@@ -48,6 +55,10 @@ runs:
${{ inputs.allowed-failures }} ${{ inputs.allowed-failures }}
EOM EOM
)" \ )" \
"$(cat << EOM
${{ inputs.allowed-skips }}
EOM
)" \
"$(cat << EOM "$(cat << EOM
${{ inputs.jobs }} ${{ inputs.jobs }}
EOM EOM
+40 -7
View File
@@ -35,13 +35,14 @@ def parse_as_list(input_text):
return [s.strip() for s in input_text.split(',')] return [s.strip() for s in input_text.split(',')]
def parse_inputs(raw_allowed_failures, raw_jobs): def parse_inputs(raw_allowed_failures, raw_allowed_skips, raw_jobs):
"""Normalize the action inputs by turning them into data.""" """Normalize the action inputs by turning them into data."""
allowed_failures_input = parse_as_list(raw_allowed_failures) allowed_failures_input = parse_as_list(raw_allowed_failures)
allowed_skips_input = parse_as_list(raw_allowed_skips)
return { return {
'allowed_failures': allowed_failures_input, 'allowed_failures': allowed_failures_input,
'allowed_skips': allowed_skips_input,
'jobs': json.loads(raw_jobs), 'jobs': json.loads(raw_jobs),
} }
@@ -49,7 +50,9 @@ def parse_inputs(raw_allowed_failures, raw_jobs):
def log_decision_details( def log_decision_details(
job_matrix_succeeded, job_matrix_succeeded,
jobs_allowed_to_fail, jobs_allowed_to_fail,
jobs_allowed_to_be_skipped,
allowed_to_fail_jobs_succeeded, allowed_to_fail_jobs_succeeded,
allowed_to_be_skipped_jobs_succeeded,
jobs, jobs,
): ):
"""Record the decisions made into console output.""" """Record the decisions made into console output."""
@@ -73,27 +76,46 @@ def log_decision_details(
) )
if jobs_allowed_to_be_skipped and allowed_to_be_skipped_jobs_succeeded:
print_to_stderr(
'🛈 All of the allowed to be skipped dependency jobs succeeded.',
)
elif jobs_allowed_to_fail:
print_to_stderr(
'🛈 Some of the allowed to be skipped jobs did not succeed.',
)
print_to_stderr('📝 Job statuses:') print_to_stderr('📝 Job statuses:')
for name, job in jobs.items(): for name, job in jobs.items():
print_to_stderr( print_to_stderr(
'📝 {name} → {emoji} {result} [{status}]'. '📝 {name} → {emoji} {result} [{status}]'.
format( format(
emoji='✓' if job['result'] == 'success' else '❌', emoji='✓' if job['result'] == 'success'
else '❌' if job['result'] == 'failure'
else '⬜',
name=name, name=name,
result=job['result'], result=job['result'],
status='allowed to fail' if name in jobs_allowed_to_fail status='allowed to fail' if name in jobs_allowed_to_fail
else 'required to succeed', else 'required to succeed'
if name not in jobs_allowed_to_be_skipped
else 'required to succeed or be skipped',
), ),
) )
def main(argv): def main(argv):
"""Decide whether the needed jobs got satisfactory results.""" """Decide whether the needed jobs got satisfactory results."""
inputs = parse_inputs(raw_allowed_failures=argv[1], raw_jobs=argv[2]) inputs = parse_inputs(
raw_allowed_failures=argv[1],
raw_allowed_skips=argv[2],
raw_jobs=argv[3],
)
jobs = inputs['jobs'] or {} jobs = inputs['jobs'] or {}
jobs_allowed_to_fail = inputs['allowed_failures'] or [] jobs_allowed_to_fail = set(inputs['allowed_failures'] or [])
jobs_allowed_to_be_skipped = set(inputs['allowed_skips'] or [])
if not jobs: if not jobs:
sys.exit( sys.exit(
@@ -104,7 +126,10 @@ def main(argv):
job_matrix_succeeded = all( job_matrix_succeeded = all(
job['result'] == 'success' for name, job in jobs.items() job['result'] == 'success' for name, job in jobs.items()
if name not in jobs_allowed_to_fail if name not in (jobs_allowed_to_fail | jobs_allowed_to_be_skipped)
) and all(
job['result'] in {'skipped', 'success'} for name, job in jobs.items()
if name in jobs_allowed_to_be_skipped
) )
set_final_result_outputs(job_matrix_succeeded) set_final_result_outputs(job_matrix_succeeded)
@@ -115,10 +140,18 @@ def main(argv):
) )
allowed_to_be_skipped_jobs_succeeded = all(
job['result'] == 'success' for name, job in jobs.items()
if name in jobs_allowed_to_be_skipped
)
log_decision_details( log_decision_details(
job_matrix_succeeded, job_matrix_succeeded,
jobs_allowed_to_fail, jobs_allowed_to_fail,
jobs_allowed_to_be_skipped,
allowed_to_fail_jobs_succeeded, allowed_to_fail_jobs_succeeded,
allowed_to_be_skipped_jobs_succeeded,
jobs, jobs,
) )