Skip to content

Commit

Permalink
Implement allowed-skips input
Browse files Browse the repository at this point in the history
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
  • Loading branch information
webknjaz committed Dec 14, 2021
1 parent 290bb0f commit 3a2de12
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 11 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,21 @@ jobs:
uses: re-actors/alls-green@release/v1
with:
allowed-failures: docs, linters
allowed-skips: non-voting-flaky-job
jobs: ${{ toJSON(needs) }}
...
```


## Options

There are two options — `allowed-failures` and `jobs`. The former is
optional but the later is mandatory. `allowed-failures` tells the action
which jobs should not affect the outcome, by default all the jobs will
be "voting". `jobs` is an object representing the jobs that should
There are three options — `allowed-failures`, `allowed-skips` and
`jobs`. The first two are optional but `jobs` is mandatory.
`allowed-failures` tells the action which jobs should not affect the
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
important to pass a JSON-serialized `needs` context to this argument.

Expand Down
11 changes: 11 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ inputs:
Job names that are allowed to fail and not affect the outcome,
as a comma-separated list or serialized as a JSON string
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:
failure:
Expand All @@ -48,6 +55,10 @@ runs:
${{ inputs.allowed-failures }}
EOM
)" \
"$(cat << EOM
${{ inputs.allowed-skips }}
EOM
)" \
"$(cat << EOM
${{ inputs.jobs }}
EOM
Expand Down
47 changes: 40 additions & 7 deletions src/normalize_needed_jobs_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,24 @@ def parse_as_list(input_text):
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."""
allowed_failures_input = parse_as_list(raw_allowed_failures)

allowed_skips_input = parse_as_list(raw_allowed_skips)

return {
'allowed_failures': allowed_failures_input,
'allowed_skips': allowed_skips_input,
'jobs': json.loads(raw_jobs),
}


def log_decision_details(
job_matrix_succeeded,
jobs_allowed_to_fail,
jobs_allowed_to_be_skipped,
allowed_to_fail_jobs_succeeded,
allowed_to_be_skipped_jobs_succeeded,
jobs,
):
"""Record the decisions made into console output."""
Expand All @@ -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:')
for name, job in jobs.items():
print_to_stderr(
'📝 {name} → {emoji} {result} [{status}]'.
format(
emoji='✓' if job['result'] == 'success' else '❌',
emoji='✓' if job['result'] == 'success'
else '❌' if job['result'] == 'failure'
else '⬜',
name=name,
result=job['result'],
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):
"""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_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:
sys.exit(
Expand All @@ -104,7 +126,10 @@ def main(argv):

job_matrix_succeeded = all(
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)

Expand All @@ -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(
job_matrix_succeeded,
jobs_allowed_to_fail,
jobs_allowed_to_be_skipped,
allowed_to_fail_jobs_succeeded,
allowed_to_be_skipped_jobs_succeeded,
jobs,
)

Expand Down

0 comments on commit 3a2de12

Please sign in to comment.