gha-workflow-linter 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. gha_workflow_linter-0.1.0/.dockerignore +87 -0
  2. gha_workflow_linter-0.1.0/.editorconfig +23 -0
  3. gha_workflow_linter-0.1.0/.github/actionlint.yaml +11 -0
  4. gha_workflow_linter-0.1.0/.github/dependabot.yml +12 -0
  5. gha_workflow_linter-0.1.0/.github/release-drafter.yml +68 -0
  6. gha_workflow_linter-0.1.0/.github/workflows/build-test-release.yaml +366 -0
  7. gha_workflow_linter-0.1.0/.github/workflows/build-test.yaml +149 -0
  8. gha_workflow_linter-0.1.0/.github/workflows/release-drafter.yaml +63 -0
  9. gha_workflow_linter-0.1.0/.github/workflows/semantic-pull-request.yaml +55 -0
  10. gha_workflow_linter-0.1.0/.github/workflows/sha-pinned-actions.yaml +29 -0
  11. gha_workflow_linter-0.1.0/.github/workflows/testing.yaml +53 -0
  12. gha_workflow_linter-0.1.0/.gitignore +60 -0
  13. gha_workflow_linter-0.1.0/.gitlint +37 -0
  14. gha_workflow_linter-0.1.0/.pre-commit-config.yaml +127 -0
  15. gha_workflow_linter-0.1.0/.readthedocs.yml +32 -0
  16. gha_workflow_linter-0.1.0/.yamllint +13 -0
  17. gha_workflow_linter-0.1.0/Dockerfile +58 -0
  18. gha_workflow_linter-0.1.0/LICENSE +201 -0
  19. gha_workflow_linter-0.1.0/LICENSES/Apache-2.0.txt +201 -0
  20. gha_workflow_linter-0.1.0/PKG-INFO +693 -0
  21. gha_workflow_linter-0.1.0/README.md +650 -0
  22. gha_workflow_linter-0.1.0/REUSE.toml +15 -0
  23. gha_workflow_linter-0.1.0/action.yaml +176 -0
  24. gha_workflow_linter-0.1.0/pyproject.toml +180 -0
  25. gha_workflow_linter-0.1.0/scripts/setup-dev.sh +122 -0
  26. gha_workflow_linter-0.1.0/src/gha_workflow_linter/__init__.py +11 -0
  27. gha_workflow_linter-0.1.0/src/gha_workflow_linter/_version.py +34 -0
  28. gha_workflow_linter-0.1.0/src/gha_workflow_linter/cli.py +521 -0
  29. gha_workflow_linter-0.1.0/src/gha_workflow_linter/config.py +256 -0
  30. gha_workflow_linter-0.1.0/src/gha_workflow_linter/github_api.py +572 -0
  31. gha_workflow_linter-0.1.0/src/gha_workflow_linter/models.py +303 -0
  32. gha_workflow_linter-0.1.0/src/gha_workflow_linter/patterns.py +198 -0
  33. gha_workflow_linter-0.1.0/src/gha_workflow_linter/py.typed +0 -0
  34. gha_workflow_linter-0.1.0/src/gha_workflow_linter/scanner.py +273 -0
  35. gha_workflow_linter-0.1.0/src/gha_workflow_linter/validator.py +409 -0
  36. gha_workflow_linter-0.1.0/tests/__init__.py +4 -0
  37. gha_workflow_linter-0.1.0/tests/conftest.py +274 -0
  38. gha_workflow_linter-0.1.0/tests/test_patterns.py +277 -0
  39. gha_workflow_linter-0.1.0/tests/test_validator_dedup.py +518 -0
  40. gha_workflow_linter-0.1.0/uv.lock +864 -0
@@ -0,0 +1,87 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
3
+
4
+ # Git files
5
+ .git
6
+ .gitignore
7
+ .gitattributes
8
+
9
+ # Python cache and build artifacts
10
+ __pycache__
11
+ *.pyc
12
+ *.pyo
13
+ *.pyd
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+
31
+ # Virtual environments
32
+ venv/
33
+ .venv/
34
+ ENV/
35
+ env/
36
+ .env
37
+
38
+ # IDE and editor files
39
+ .vscode/
40
+ .idea/
41
+ *.swp
42
+ *.swo
43
+ *~
44
+ .DS_Store
45
+
46
+ # Testing and coverage
47
+ .pytest_cache/
48
+ .coverage
49
+ htmlcov/
50
+ .tox/
51
+ .nox/
52
+
53
+ # Documentation
54
+ docs/_build/
55
+ .readthedocs.yml
56
+
57
+ # CI/CD
58
+ .github/
59
+ .pre-commit-config.yaml
60
+
61
+ # Local development files
62
+ *.log
63
+ .cache/
64
+ temp/
65
+ tmp/
66
+
67
+ # OS generated files
68
+ Thumbs.db
69
+ ehthumbs.db
70
+ Desktop.ini
71
+
72
+ # Package manager files
73
+ poetry.lock
74
+ Pipfile.lock
75
+ requirements*.txt
76
+
77
+ # Jupyter notebooks
78
+ .ipynb_checkpoints
79
+
80
+ # mypy
81
+ .mypy_cache/
82
+ .dmypy.json
83
+ dmypy.json
84
+
85
+ # Misc
86
+ *.orig
87
+ *.rej
@@ -0,0 +1,23 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
3
+
4
+ root = true
5
+
6
+ [*]
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+ indent_style = space
11
+ indent_size = 4
12
+
13
+ [*.{json,yaml,yml}]
14
+ indent_size = 2
15
+
16
+ [*.markdown]
17
+ max_line_length = 80
18
+
19
+ [*.py]
20
+ max_line_legth = 120
21
+
22
+ [*.sh]
23
+ max_line_length = 80
@@ -0,0 +1,11 @@
1
+ ---
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
4
+
5
+ # Path-specific configurations.
6
+ paths:
7
+ .github/workflows/testing.yaml:
8
+ ignore:
9
+ # Ignore deliberate test failure
10
+ - 'input "invalid_input" is not defined in action .+'
11
+ - 'missing input "input" which is required by action .+'
@@ -0,0 +1,12 @@
1
+ ---
2
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ version: 2
6
+ updates:
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
11
+ commit-message:
12
+ prefix: "Chore"
@@ -0,0 +1,68 @@
1
+ ---
2
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ name-template: "v$RESOLVED_VERSION"
6
+ tag-template: "v$RESOLVED_VERSION"
7
+ change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
8
+ sort-direction: ascending
9
+ categories:
10
+ - title: ":boom: Breaking Change :boom:"
11
+ labels:
12
+ - "breaking-change"
13
+ - title: ":zap: Enhancements :zap:"
14
+ labels:
15
+ - "enhancement"
16
+ - title: ":sparkles: New Features :sparkles:"
17
+ labels:
18
+ - "feature"
19
+ - title: ":bug: Bug Fixes :bug:"
20
+ labels:
21
+ - "fix"
22
+ - "bugfix"
23
+ - "bug"
24
+ - title: ":wrench: Maintenance :wrench:"
25
+ labels:
26
+ - "chore"
27
+ - "documentation"
28
+ - "maintenance"
29
+ - "repo"
30
+ - "dependencies"
31
+ - "github_actions"
32
+ - "refactor"
33
+ - title: ":mortar_board: Code Quality :mortar_board:"
34
+ labels:
35
+ - "code-quality"
36
+ - "CI"
37
+ - "test"
38
+ autolabeler:
39
+ - label: "breaking-change"
40
+ title:
41
+ - "/!:/i"
42
+ - label: "feature"
43
+ title:
44
+ - "/feat:/i"
45
+ - label: "bug"
46
+ title:
47
+ - "/fix:/i"
48
+ - label: "refactor"
49
+ title:
50
+ - "/refactor:/i"
51
+ - label: "code-quality"
52
+ title:
53
+ - "/test:/i"
54
+ - label: "CI"
55
+ title:
56
+ - "/ci:/i"
57
+ - label: "chore"
58
+ title:
59
+ - "/chore:/i"
60
+ - label: "documentation"
61
+ title:
62
+ - "/docs:/i"
63
+ # yamllint disable rule:line-length
64
+ template: |
65
+ $CHANGES
66
+
67
+ ## Links
68
+ - [Submit bugs/feature requests](https://github.com/$OWNER/$REPOSITORY/issues)
@@ -0,0 +1,366 @@
1
+ ---
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
4
+
5
+ # Runs on a new pull request, performs build and runs tests
6
+ name: 'Python Build/Test/Release'
7
+
8
+ # yamllint disable-line rule:truthy
9
+ on:
10
+ # Trigger on tag push events
11
+ push:
12
+ tags:
13
+ - '**'
14
+
15
+ permissions: {}
16
+
17
+ jobs:
18
+ tag-validate:
19
+ name: 'Validate Tag Push'
20
+ runs-on: 'ubuntu-latest'
21
+ permissions:
22
+ contents: read
23
+ timeout-minutes: 1
24
+ outputs:
25
+ tag: "${{ steps.tag-validate.outputs.tag }}"
26
+ should_promote: "${{ steps.check-release.outputs.should_promote }}"
27
+ steps:
28
+ # Harden the runner used by this workflow
29
+ # yamllint disable-line rule:line-length
30
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
31
+ with:
32
+ egress-policy: 'audit'
33
+
34
+ - name: 'Verify Pushed Tag'
35
+ id: 'tag-validate'
36
+ # yamllint disable-line rule:line-length
37
+ uses: lfreleng-actions/tag-push-verify-action@80e2bdbbb9ee7b67557a31705892b75e75d2859e # v0.1.1
38
+ with:
39
+ versioning: 'semver'
40
+
41
+ - name: 'Reject Development Tags'
42
+ if: steps.tag-validate.outputs.dev_version == 'true'
43
+ shell: bash
44
+ run: |
45
+ # Reject Development Tags
46
+ echo "Development tag pushed; aborting release workflow 🛑"
47
+ echo "Development tag pushed; aborting release workflow 🛑" \
48
+ >> "$GITHUB_STEP_SUMMARY"
49
+ exit 1
50
+
51
+ - name: 'Check if release exists'
52
+ id: 'check-release'
53
+ shell: bash
54
+ env:
55
+ GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
56
+ run: |
57
+ TAG="${{ steps.tag-validate.outputs.tag }}"
58
+
59
+ # Check if release exists and get its draft status
60
+ if RELEASE_INFO=$(gh release view "$TAG" --json isDraft \
61
+ 2>/dev/null); then
62
+ IS_DRAFT=$(echo "$RELEASE_INFO" | jq -r '.isDraft')
63
+ if [ "$IS_DRAFT" = "false" ]; then
64
+ echo "should_promote=false" >> "$GITHUB_OUTPUT"
65
+ echo "Published release already exists for tag $TAG, " \
66
+ "skipping promotion"
67
+ else
68
+ echo "should_promote=true" >> "$GITHUB_OUTPUT"
69
+ echo "Draft release exists for tag $TAG, " \
70
+ "will proceed with promotion"
71
+ fi
72
+ else
73
+ echo "should_promote=true" >> "$GITHUB_OUTPUT"
74
+ echo "No release found for tag $TAG, will proceed with promotion"
75
+ fi
76
+
77
+ python-build:
78
+ name: 'Python Build'
79
+ needs: 'tag-validate'
80
+ runs-on: 'ubuntu-latest'
81
+ outputs:
82
+ matrix_json: "${{ steps.python-build.outputs.matrix_json }}"
83
+ artefact_name: "${{ steps.python-build.outputs.artefact_name }}"
84
+ artefact_path: "${{ steps.python-build.outputs.artefact_path }}"
85
+ permissions:
86
+ contents: read
87
+ id-token: write # Needed for attestations
88
+ attestations: write # Needed for attestations
89
+ timeout-minutes: 12
90
+ env:
91
+ GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
92
+ steps:
93
+ # Harden the runner used by this workflow
94
+ # yamllint disable-line rule:line-length
95
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
96
+ with:
97
+ egress-policy: 'audit'
98
+
99
+ # yamllint disable-line rule:line-length
100
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
101
+
102
+ - name: 'Build Python project'
103
+ id: 'python-build'
104
+ # yamllint disable-line rule:line-length
105
+ uses: lfreleng-actions/python-build-action@48381cece78a990a6ba93bd5924bcd40bf0d1a7d # v0.1.20
106
+ with:
107
+ sigstore_sign: true
108
+ attestations: true
109
+
110
+ python-tests:
111
+ name: 'Python Tests'
112
+ runs-on: 'ubuntu-latest'
113
+ needs: 'python-build'
114
+ # Matrix job
115
+ strategy:
116
+ fail-fast: false
117
+ matrix: "${{ fromJson(needs.python-build.outputs.matrix_json) }}"
118
+ permissions:
119
+ contents: read
120
+ timeout-minutes: 12
121
+ steps:
122
+ # Harden the runner used by this workflow
123
+ # yamllint disable-line rule:line-length
124
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
125
+ with:
126
+ egress-policy: 'audit'
127
+
128
+ # yamllint disable-line rule:line-length
129
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
130
+
131
+ - name: 'Test Python project [PYTEST]'
132
+ # yamllint disable-line rule:line-length
133
+ uses: lfreleng-actions/python-test-action@bdde9e4e6221e858359f9036bd4f41ab3b1af90e # v0.1.11
134
+ with:
135
+ python_version: "${{ matrix.python-version }}"
136
+
137
+ python-audit:
138
+ name: 'Python Audit'
139
+ runs-on: 'ubuntu-latest'
140
+ needs: 'python-build'
141
+ # Matrix job
142
+ strategy:
143
+ fail-fast: false
144
+ matrix: "${{ fromJson(needs.python-build.outputs.matrix_json) }}"
145
+ permissions:
146
+ contents: read
147
+ timeout-minutes: 10
148
+ steps:
149
+ # Harden the runner used by this workflow
150
+ # yamllint disable-line rule:line-length
151
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
152
+ with:
153
+ egress-policy: 'audit'
154
+
155
+ # yamllint disable-line rule:line-length
156
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
157
+
158
+ - name: 'Audit Python project'
159
+ # yamllint disable-line rule:line-length
160
+ uses: lfreleng-actions/python-audit-action@bab5316468c108870eb759ef0de622bae9239aad # v0.2.2
161
+ with:
162
+ python_version: "${{ matrix.python-version }}"
163
+ permit_fail: true
164
+
165
+ sbom:
166
+ name: 'Generate SBOM'
167
+ runs-on: ubuntu-latest
168
+ needs: 'python-build'
169
+ timeout-minutes: 10
170
+ permissions:
171
+ contents: read
172
+ steps:
173
+ # yamllint disable-line rule:line-length
174
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
175
+
176
+ - name: "Generate SBOM"
177
+ id: sbom
178
+ # yamllint disable-line rule:line-length
179
+ uses: lfreleng-actions/python-sbom-action@ae4aca2ef28d7da4ec95049cc78be43e632d322a # v0.1.0
180
+ with:
181
+ include_dev: "false"
182
+ sbom_format: "both"
183
+
184
+ - name: "Upload SBOM artifacts"
185
+ # yamllint disable-line rule:line-length
186
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
187
+ with:
188
+ name: sbom-files
189
+ path: |
190
+ sbom-cyclonedx.json
191
+ sbom-cyclonedx.xml
192
+ retention-days: 45
193
+
194
+ - name: "Security scan with Grype"
195
+ # yamllint disable-line rule:line-length
196
+ uses: anchore/scan-action@f6601287cdb1efc985d6b765bbf99cb4c0ac29d8 # v7.0.0
197
+ with:
198
+ sbom: "${{ steps.sbom.outputs.sbom_json_path }}"
199
+
200
+ - name: "Summary step"
201
+ run: |
202
+ # Summary step
203
+ echo "SBOM count: ${{ steps.sbom.outputs.component_count }}"
204
+ echo "Tool used: ${{ steps.sbom.outputs.dependency_manager }}"
205
+
206
+ test-pypi:
207
+ name: 'Test PyPI Publishing'
208
+ runs-on: 'ubuntu-latest'
209
+ needs:
210
+ - 'tag-validate'
211
+ - 'python-tests'
212
+ - 'python-audit'
213
+ environment:
214
+ name: 'development'
215
+ permissions:
216
+ contents: read
217
+ id-token: write # IMPORTANT: mandatory for trusted publishing
218
+ timeout-minutes: 5
219
+ steps:
220
+ # Harden the runner used by this workflow
221
+ # yamllint disable-line rule:line-length
222
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
223
+ with:
224
+ egress-policy: 'audit'
225
+
226
+ # yamllint disable-line rule:line-length
227
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
228
+
229
+ - name: 'Test PyPI publishing'
230
+ # yamllint disable-line rule:line-length
231
+ uses: lfreleng-actions/pypi-publish-action@81a056957ed050f8305760055b1fd8103a916989 # v0.1.1
232
+ with:
233
+ environment: 'development'
234
+ tag: "${{ needs.tag-validate.outputs.tag }}"
235
+ pypi_credential: "${{ secrets.TEST_PYPI_CREDENTIAL }}"
236
+
237
+ pypi:
238
+ name: 'Release PyPI Package'
239
+ runs-on: 'ubuntu-latest'
240
+ needs:
241
+ - 'tag-validate'
242
+ - 'test-pypi'
243
+ environment:
244
+ name: 'production'
245
+ permissions:
246
+ contents: read
247
+ id-token: write # IMPORTANT: mandatory for trusted publishing
248
+ timeout-minutes: 5
249
+ steps:
250
+ # Harden the runner used by this workflow
251
+ # yamllint disable-line rule:line-length
252
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
253
+ with:
254
+ egress-policy: 'audit'
255
+
256
+ # yamllint disable-line rule:line-length
257
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
258
+
259
+ - name: 'PyPI release'
260
+ # yamllint disable-line rule:line-length
261
+ uses: lfreleng-actions/pypi-publish-action@81a056957ed050f8305760055b1fd8103a916989 # v0.1.1
262
+ with:
263
+ environment: 'production'
264
+ attestations: true
265
+ tag: "${{ needs.tag-validate.outputs.tag }}"
266
+ pypi_credential: "${{ secrets.PYPI_CREDENTIAL }}"
267
+
268
+
269
+ promote-release:
270
+ name: 'Promote Draft Release'
271
+ # yamllint disable-line rule:line-length
272
+ if: needs.tag-validate.outputs.should_promote == 'true'
273
+ needs:
274
+ - 'tag-validate'
275
+ - 'pypi'
276
+ runs-on: 'ubuntu-latest'
277
+ permissions:
278
+ contents: write # IMPORTANT: needed to edit a draft release and promote it
279
+ timeout-minutes: 2
280
+ outputs:
281
+ release_url: "${{ steps.promote-release.outputs.release_url }}"
282
+ steps:
283
+ # Harden the runner used by this workflow
284
+ # yamllint disable-line rule:line-length
285
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
286
+ with:
287
+ egress-policy: 'audit'
288
+
289
+ # yamllint disable-line rule:line-length
290
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
291
+
292
+ - name: 'Check if release is already promoted'
293
+ id: 'check-promoted'
294
+ shell: bash
295
+ env:
296
+ GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
297
+ run: |
298
+ TAG="${{ needs.tag-validate.outputs.tag }}"
299
+ if gh release view "$TAG" --json isDraft --jq '.isDraft' \
300
+ 2>/dev/null | grep -q "false"; then
301
+ echo "Release $TAG is already promoted, skipping promotion"
302
+ echo "already_promoted=true" >> "$GITHUB_OUTPUT"
303
+ else
304
+ echo "Release $TAG is draft or doesn't exist, " \
305
+ "proceeding with promotion"
306
+ echo "already_promoted=false" >> "$GITHUB_OUTPUT"
307
+ fi
308
+
309
+ - name: 'Promote draft release'
310
+ id: 'promote-release'
311
+ if: steps.check-promoted.outputs.already_promoted == 'false'
312
+ # yamllint disable-line rule:line-length
313
+ uses: lfreleng-actions/draft-release-promote-action@d7e7df12e32fa26b28dbc2f18a12766482785399 # v0.1.2
314
+ with:
315
+ token: "${{ secrets.GITHUB_TOKEN }}"
316
+ tag: "${{ needs.tag-validate.outputs.tag }}"
317
+ latest: true
318
+
319
+ - name: 'Set release URL for already promoted release'
320
+ if: steps.check-promoted.outputs.already_promoted == 'true'
321
+ shell: bash
322
+ env:
323
+ GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
324
+ run: |
325
+ TAG="${{ needs.tag-validate.outputs.tag }}"
326
+ RELEASE_URL=$(gh release view "$TAG" --json url --jq '.url')
327
+ echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT"
328
+
329
+ # Need to attach build artefacts to the release
330
+ # This step could potentially be moved
331
+ # (May be better to when/where the release is still in draft state)
332
+ attach-artefacts:
333
+ name: 'Attach Artefacts to Release'
334
+ runs-on: 'ubuntu-latest'
335
+ needs:
336
+ - 'tag-validate'
337
+ - 'python-build'
338
+ - 'promote-release'
339
+ # yamllint disable-line rule:line-length
340
+ if: always() && (needs.promote-release.result == 'success' || needs.promote-release.result == 'skipped')
341
+ permissions:
342
+ contents: write # IMPORTANT: needed to edit release, attach artefacts
343
+ timeout-minutes: 5
344
+ steps:
345
+ # Harden the runner used by this workflow
346
+ # yamllint disable-line rule:line-length
347
+ - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
348
+ with:
349
+ egress-policy: 'audit'
350
+
351
+ # Note: no need for a checkout step in this job
352
+
353
+ - name: '⬇ Download build artefacts'
354
+ # yamllint disable-line rule:line-length
355
+ uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
356
+ with:
357
+ name: "${{ needs.python-build.outputs.artefact_name }}"
358
+ path: "${{ needs.python-build.outputs.artefact_path }}"
359
+
360
+ - name: 'Attach build artefacts to release'
361
+ # yamllint disable-line rule:line-length
362
+ uses: alexellis/upload-assets@13926a61cdb2cb35f5fdef1c06b8b591523236d3 # 0.4.1
363
+ env:
364
+ GITHUB_TOKEN: "${{ github.token }}"
365
+ with:
366
+ asset_paths: '["${{ needs.python-build.outputs.artefact_path }}/**"]'