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:
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user