Skip to content

Commit 988a195

Browse files
echoixHuidaeCho
authored andcommitted
CI: Add a verify-success reusable workflow to use for required checks (OSGeo#3320)
* CI: Add a verify success job to use in required checks * Limit line length in verify-success.yml and Python Code Quality workflow * CI: Use pipx to run tools in python-code-quality.yml, cache pip * CI: Add verify success for pytest workflow * CI: Add verify-success to Ubuntu * Update .github/workflows/python-code-quality.yml
1 parent 4b1f29f commit 988a195

File tree

4 files changed

+193
-12
lines changed

4 files changed

+193
-12
lines changed

.github/workflows/pytest.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,11 @@ jobs:
8080
- name: Print installed versions
8181
if: always()
8282
run: .github/workflows/print_versions.sh
83+
pytest-success:
84+
name: pytest Result
85+
needs:
86+
- pytest
87+
if: ${{ always() }}
88+
uses: ./.github/workflows/verify-success.yml
89+
with:
90+
needs_context: ${{ toJson(needs) }}

.github/workflows/python-code-quality.yml

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ on:
1313

1414
jobs:
1515
python-checks:
16-
name: Python Code Quality
16+
name: Python Code Quality Checks
1717

1818
concurrency:
19-
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}-${{
20-
matrix.pylint-version }}
19+
group: ${{ github.workflow }}-${{ github.job }}-${{
20+
github.event_name == 'pull_request' &&
21+
github.head_ref || github.sha }}-${{ matrix.pylint-version }}
2122
cancel-in-progress: true
2223

2324
# Using matrix just to get variables which are not environmental variables
@@ -26,11 +27,11 @@ jobs:
2627
matrix:
2728
include:
2829
- os: ubuntu-22.04
29-
python-version: '3.10'
30-
min-python-version: '3.7'
31-
black-version: '23.1.0'
32-
flake8-version: '3.9.2'
33-
pylint-version: '2.12.2'
30+
python-version: "3.10"
31+
min-python-version: "3.7"
32+
black-version: "23.1.0"
33+
flake8-version: "3.9.2"
34+
pylint-version: "2.12.2"
3435

3536
runs-on: ${{ matrix.os }}
3637

@@ -39,7 +40,7 @@ jobs:
3940
run: |
4041
echo OS: ${{ matrix.os }}
4142
echo Python: ${{ matrix.python-version }}
42-
echo Minimimal Python version: ${{ matrix.min-python-version }}
43+
echo Minimal Python version: ${{ matrix.min-python-version }}
4344
echo Black: ${{ matrix.black-version }}
4445
echo Flake8: ${{ matrix.flake8-version }}
4546
echo Pylint: ${{ matrix.pylint-version }}
@@ -50,13 +51,14 @@ jobs:
5051
uses: actions/setup-python@v5
5152
with:
5253
python-version: ${{ matrix.python-version }}
54+
cache: pip
5355

5456
- name: Install non-Python dependencies
5557
run: |
5658
sudo apt-get update -y
5759
sudo apt-get install -y wget git gawk findutils
5860
xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \
59-
sudo apt-get install -y --no-install-recommends --no-install-suggests
61+
sudo apt-get install -y --no-install-recommends --no-install-suggests
6062
6163
- name: Install Python dependencies
6264
run: |
@@ -146,3 +148,11 @@ jobs:
146148
name: sphinx-grass
147149
path: sphinx-grass
148150
retention-days: 3
151+
python-success:
152+
name: Python Code Quality Result
153+
needs:
154+
- python-checks
155+
if: ${{ always() }}
156+
uses: ./.github/workflows/verify-success.yml
157+
with:
158+
needs_context: ${{ toJson(needs) }}

.github/workflows/ubuntu.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
- releasebranch_*
1515

1616
jobs:
17-
build-and-test:
17+
ubuntu:
1818
name: ${{ matrix.name }} tests
1919

2020
concurrency:
@@ -26,7 +26,7 @@ jobs:
2626
strategy:
2727
matrix:
2828
include:
29-
- name: '22.04'
29+
- name: "22.04"
3030
os: ubuntu-22.04
3131
config: ubuntu-22.04
3232
# This is without optional things but it still keeps things useful,
@@ -95,3 +95,11 @@ jobs:
9595
- name: Print installed versions
9696
if: always()
9797
run: .github/workflows/print_versions.sh
98+
build-and-test-success:
99+
name: Build & Test Result
100+
needs:
101+
- ubuntu
102+
if: ${{ always() }}
103+
uses: ./.github/workflows/verify-success.yml
104+
with:
105+
needs_context: ${{ toJson(needs) }}

.github/workflows/verify-success.yml

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
name: Verify Success reusable workflow
3+
4+
# Use this reusable workflow as a job of workflow to check that
5+
# all jobs, including ones ran through a matrix, were successful.
6+
# This job can then be used as a required status check in the
7+
# repo's rulesets, that allows to change the required jobs or
8+
# the matrix values of a required job without needing to change
9+
# the rulesets settings. In the future, GitHub might have a
10+
# solution to this natively.
11+
12+
# This reusable workflow has inputs to change what is required
13+
# to have this workflow pass. It handles the cases were there were
14+
# skipped jobs, and no successful jobs.
15+
16+
# The jobs to check must set as the `needs` for the job calling this
17+
# reusable workflow. This also means that the job ids should be in the
18+
# same workflow file. The calling job must be set to always run to be
19+
# triggered when jobs are skipped or cancelled.
20+
# Then, set the `needs_context` input like:
21+
# `needs_context: ${{ toJson(needs) }}`
22+
23+
# Example usage, as a job inside a workflow:
24+
# ```
25+
# jobs:
26+
# a-job-id:
27+
# ...
28+
# another-job-id:
29+
# ...
30+
# some-job-success:
31+
# name: Some Job Result
32+
# needs:
33+
# - a-job-id
34+
# - another-job-id
35+
# if: ${{ always() }}
36+
# uses: ./.github/workflows/verify-success.yml
37+
# with:
38+
# needs_context: ${{ toJson(needs) }}
39+
# ```
40+
41+
on:
42+
workflow_call:
43+
inputs:
44+
needs_context:
45+
type: string
46+
required: true
47+
# Can't escape the handlebars in the description
48+
description:
49+
In the calling job that defines all the needed jobs,
50+
send `toJson(needs)` inside `$` followed by `{{ }}`
51+
fail_if_failure:
52+
type: boolean
53+
default: true
54+
description:
55+
If true, this workflow will fail if any job from 'needs_context was
56+
failed
57+
fail_if_cancelled:
58+
type: boolean
59+
default: true
60+
description:
61+
If true, this workflow will fail if any job from 'needs_context' was
62+
cancelled
63+
fail_if_skipped:
64+
type: boolean
65+
default: false
66+
description:
67+
If true, this workflow will fail if any job from 'needs_context' was
68+
skipped
69+
require_success:
70+
type: boolean
71+
default: true
72+
description:
73+
If true, this workflow will fail if no job from 'needs_context' was
74+
successful
75+
76+
jobs:
77+
verify-success:
78+
name: Success
79+
runs-on: ubuntu-latest
80+
continue-on-error: true
81+
steps:
82+
- name: Set outputs for each job result type
83+
id: has-result
84+
run: |
85+
echo "failure=${{
86+
contains(env.NEEDS_RESULT, 'failure') }}" >> "$GITHUB_OUTPUT"
87+
echo "cancelled=${{
88+
contains(env.NEEDS_RESULT, 'cancelled') }}" >> "$GITHUB_OUTPUT"
89+
echo "skipped=${{
90+
contains(env.NEEDS_RESULT, 'skipped') }}" >> "$GITHUB_OUTPUT"
91+
echo "success=${{
92+
contains(env.NEEDS_RESULT, 'success') }}" >> "$GITHUB_OUTPUT"
93+
env:
94+
NEEDS_RESULT: ${{ toJson(fromJson(inputs.needs_context).*.result) }}
95+
- name: Set exit codes for each job result type
96+
id: exit-code
97+
run: |
98+
echo "failure=${{ inputs.fail_if_failure &&
99+
fromJson(steps.has-result.outputs.failure) && 1 || 0
100+
}}" >> "$GITHUB_OUTPUT"
101+
echo "cancelled=${{ inputs.fail_if_cancelled &&
102+
fromJson(steps.has-result.outputs.cancelled) && 1 || 0
103+
}}" >> "$GITHUB_OUTPUT"
104+
echo "skipped=${{ inputs.fail_if_skipped &&
105+
fromJson(steps.has-result.outputs.skipped) && 1 || 0
106+
}}" >> "$GITHUB_OUTPUT"
107+
echo "success=${{ inputs.require_success &&
108+
!fromJson(steps.has-result.outputs.success) && 1 || 0
109+
}}" >> "$GITHUB_OUTPUT"
110+
- name: Set messages for each job result type
111+
id: message
112+
run: |
113+
echo "failure=${{ format('{0}{1} were failed',
114+
(steps.exit-code.outputs.failure == 1) && env.P1 || env.P2,
115+
(steps.has-result.outputs.failure == 'true') && env.M1 || env.M2)
116+
}}" >> "$GITHUB_OUTPUT"
117+
echo "cancelled=${{ format('{0}{1} were cancelled',
118+
(steps.exit-code.outputs.cancelled == 1) && env.P1 || env.P2,
119+
(steps.has-result.outputs.cancelled == 'true') && env.M1 || env.M2)
120+
}}" >> "$GITHUB_OUTPUT"
121+
echo "skipped=${{ format('{0}{1} were skipped',
122+
(steps.exit-code.outputs.skipped == 1) && env.P1 || env.P2,
123+
(steps.has-result.outputs.skipped == 'true') && env.M1 || env.M2)
124+
}}" >> "$GITHUB_OUTPUT"
125+
echo "success=${{ format('{0}{1} were successful',
126+
(steps.exit-code.outputs.success == 1) && env.P1 || env.P2,
127+
(steps.has-result.outputs.success == 'true') && env.M1 || env.M2)
128+
}}" >> "$GITHUB_OUTPUT"
129+
env:
130+
P1: "::error ::" # Common message prefix if step will fail
131+
P2: "" # Common message prefix if step will not fail
132+
M1: "Some jobs" # Common message if result is true
133+
M2: "No jobs" # Common message if result is false
134+
135+
- name: Check for failed jobs
136+
run: |
137+
echo "${{ steps.message.outputs.failure }}"
138+
exit ${{ steps.exit-code.outputs.failure }}
139+
- name: Check for cancelled jobs
140+
run: |
141+
echo "${{ steps.message.outputs.cancelled }}"
142+
exit ${{ steps.exit-code.outputs.cancelled }}
143+
- name: Check for skipped jobs
144+
run: |
145+
echo "${{ steps.message.outputs.skipped }}"
146+
exit ${{ steps.exit-code.outputs.skipped }}
147+
- name: Check for successful jobs
148+
run: |
149+
echo "${{ steps.message.outputs.success }}"
150+
exit ${{ steps.exit-code.outputs.success }}
151+
152+
- run: echo "Checks passed successfully"
153+
if: ${{ success() }}
154+
- run: echo "Checks failed"
155+
if: ${{ !success() }}

0 commit comments

Comments
 (0)