-
Notifications
You must be signed in to change notification settings - Fork 1
307 lines (307 loc) · 14.2 KB
/
_reusable-package-release.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
---
name: Publish to GitHub & PyPI
on:
workflow_call:
inputs:
package-name:
description: The name of the package to release.
required: true
type: string
repo-name:
description: The full name of the repository to use to gate uploads, in the
format `owner/repo`.
required: true
type: string
commit-user-name:
description: The name of the user to use when committing changes to the repository.
required: true
type: string
commit-user-email:
description: The email of the user to use when committing changes to the repository.
required: true
type: string
release-level:
description: |
Select the release level:
patch for backward compatible minor changes and bug fixes,
minor for backward compatible larger changes,
major for non-backward compatible changes.
required: true
type: string
build-and-publish-python-package:
description: A boolean value that determines whether to build and publish
the Python package. If set to `false`, the package binaries will not be
built or published to PyPI, TestPyPI, or GitHub Releases.
required: false
default: true
type: boolean
python-versions-array:
description: A valid JSON array of Python versions to validate the installation
with. If `build-and-publish-python-package` is set to `true`, this input
must be provided or the build will fail.
required: false
type: string
operating-systems-array:
description: A valid JSON array of operating system names to validate the
installation on.
required: false
default: '["ubuntu", "windows", "macos"]'
type: string
previous-changelog-filepath:
description: The full path of the file to copy the contents of the changelog
into for use in the `python-semantic-release` templates.
required: false
type: string
default: .previous_changelog_for_template.md
previous-release-notes-filepath:
description: The full path of the file to copy the contents of the `## Unreleased`
section of the changelog into for use in the GitHub Release Notes.
required: false
type: string
default: .previous_release_notes_for_template.md
secrets:
checkout-token:
description: The token to use for checking out the repository, must have permissions
to write back to the repository.
required: true
ssh-signing-key-private:
description: A private SSH key associated with the account that owns the `checkout-token`
that will be used to sign the commit and tag created by `python-semantic-release`.
required: true
ssh-signing-key-public:
description: The public SSH key linked to the `secrets.ssh-signing-key-private`
key that will be used to sign the commit and tag created by `python-semantic-release`.
required: true
pypi-api-token:
description: The API token for the package on pypi.org. If `build-and-publish-python-package`
is set to `true`, this input must be provided or the build will fail.
required: false
test-pypi-api-token:
description: The API token for the package on test.pypi.org. If `build-and-publish-python-package`
is set to `true`, this input must be provided or the build will fail.
required: false
concurrency:
group: pypi (Reusable Workflows)
env:
PACKAGE_NAME: ${{ inputs.package-name }}
jobs:
# Print the inputs to the summary page for easy User Review
print-inputs:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
fetch-tags: true
- name: python-versions-array input missing
if: ${{ inputs.build-and-publish-python-package == true && (inputs.python-versions-array == null || inputs.python-versions-array == '') }}
run: |
echo "The python-versions-array input is required when the build-and-publish-python-package input is set to true."
exit 1
- if: ${{ endsWith(github.repository, '/python-package-ci-cd') }} # Run the local action when this is run in the python-package-ci-cd repository
uses: ./actions/find_unreleased_changelog_items
id: find-changes-local
continue-on-error: true
with:
release-level: ${{ inputs.release-level }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository
uses: tektronix/python-package-ci-cd/actions/[email protected]
id: find-changes-public
continue-on-error: true
with:
release-level: ${{ inputs.release-level }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- name: Python Semantic Release (noop for preview)
uses: python-semantic-release/python-semantic-release@fd8c509df1f16daf3f71a9a6fac49247017017b2 # v9.9.0
with:
force: ${{ inputs.release-level }}
root_options: --verbose --strict --noop
- name: Add note to summary
run: |
echo "## Python Semantic Release Preview (noop)" >> "$GITHUB_STEP_SUMMARY"
echo "See the stdout from the \`Python Semantic Release (noop for preview)\` step in the \`package-release / print-inputs\` job for more details." >> "$GITHUB_STEP_SUMMARY"
outputs:
found-changes: ${{ (steps.find-changes-local.outputs.found-changes && fromJSON(steps.find-changes-local.outputs.found-changes)) || (steps.find-changes-public.outputs.found-changes && fromJSON(steps.find-changes-public.outputs.found-changes)) }}
# Update the package version using the python-semantic-release package (https://github.com/python-semantic-release/python-semantic-release)
# This job requires a Personal Access Token (Classic) with
# the public_repo permission. It also needs a private/public
# ssh key pair that can be used for signing. The public key must
# be attached to the account as an SSH signing key.
bump-version:
name: Update package version
needs: [print-inputs]
if: github.repository == inputs.repo-name && github.ref == 'refs/heads/main' &&
needs.print-inputs.outputs.found-changes == 'true'
runs-on: ubuntu-latest
environment: package-release-gate
continue-on-error: true
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.checkout-token }}
- if: ${{ endsWith(github.repository, '/python-package-ci-cd') }} # Run the local action when this is run in the python-package-ci-cd repository
uses: ./actions/find_unreleased_changelog_items
with:
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository
uses: tektronix/python-package-ci-cd/actions/[email protected]
with:
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@fd8c509df1f16daf3f71a9a6fac49247017017b2 # v9.9.0
id: release
with:
force: ${{ inputs.release-level }}
root_options: --verbose --strict
github_token: ${{ secrets.checkout-token }}
git_committer_email: ${{ inputs.commit-user-email }}
git_committer_name: ${{ inputs.commit-user-name }}
ssh_public_signing_key: ${{ secrets.ssh-signing-key-public }}
ssh_private_signing_key: ${{ secrets.ssh-signing-key-private }}
- name: Mark the release as approved
id: set-approved
if: ${{ steps.release.conclusion == 'success' }}
run: echo "approved=true" >> "$GITHUB_OUTPUT"
outputs:
built-version: ${{ steps.release.outputs.version }}
approved: ${{ steps.set-approved.outputs.approved }}
# Build the newly updated package
pypi-build:
name: Build package
needs: [print-inputs, bump-version]
if: inputs.build-and-publish-python-package && github.repository == inputs.repo-name
&& github.ref == 'refs/heads/main' && needs.bump-version.outputs.approved ==
'true' && needs.print-inputs.outputs.found-changes == 'true'
runs-on: ubuntu-latest
permissions:
id-token: write
attestations: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main # Make sure to check out the latest commit on main, not the original commit that triggered the workflow
fetch-depth: 0
- name: Build package
uses: hynek/build-and-inspect-python-package@b5076c307dc91924a82ad150cdd1533b444d3310 # v2.12.0
with:
attest-build-provenance-github: 'true'
# Upload the official package version to TestPyPI
upload-testpypi:
name: Upload package to TestPyPI
needs: [print-inputs, bump-version, pypi-build]
if: inputs.build-and-publish-python-package && github.repository == inputs.repo-name
&& github.ref == 'refs/heads/main' && needs.bump-version.outputs.approved ==
'true' && needs.print-inputs.outputs.found-changes == 'true'
runs-on: ubuntu-latest
environment: package-testpypi
permissions:
id-token: write
steps:
- name: Download built packages
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: Packages
path: dist
- name: Upload package to Test PyPI
uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0
with:
password: ${{ secrets.test-pypi-api-token }}
repository-url: https://test.pypi.org/legacy/
# Upload the official package version to PyPI
upload-pypi:
name: Upload package to PyPI
needs: [print-inputs, bump-version, upload-testpypi]
if: inputs.build-and-publish-python-package && github.repository == inputs.repo-name
&& github.ref == 'refs/heads/main' && needs.bump-version.outputs.approved ==
'true' && needs.print-inputs.outputs.found-changes == 'true'
runs-on: ubuntu-latest
environment: package-release
permissions:
id-token: write
steps:
- name: Download built packages
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: Packages
path: dist
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0
with:
password: ${{ secrets.pypi-api-token }}
# Upload the official package binaries to the GitHub Release
upload-github:
name: Upload package to GitHub Release
needs: [print-inputs, bump-version, upload-pypi]
if: inputs.build-and-publish-python-package && github.repository == inputs.repo-name
&& github.ref == 'refs/heads/main' && needs.bump-version.outputs.approved ==
'true' && needs.print-inputs.outputs.found-changes == 'true'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main # Make sure to check out the latest commit on main, not the original commit that triggered the workflow
fetch-depth: 0
- name: Download built packages
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: Packages
path: dist
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@0a92b5d7ebfc15a84f9801ebd1bf706343d43711 # v9.8.9
with:
root_options: -v --strict
github_token: ${{ secrets.GITHUB_TOKEN }}
# Verify the package can be installed on all necessary python versions and operating systems from both TestPyPI and PyPI
pypi-install:
name: Install package
needs:
- print-inputs
- bump-version
- pypi-build
- upload-testpypi
- upload-pypi
- upload-github
if: inputs.build-and-publish-python-package && github.repository == inputs.repo-name
&& github.ref == 'refs/heads/main' && needs.bump-version.outputs.approved ==
'true' && needs.print-inputs.outputs.found-changes == 'true'
runs-on: ${{ matrix.os-name }}-latest
permissions: {}
strategy:
fail-fast: false
matrix:
os-name: ${{ fromJSON(inputs.operating-systems-array) }}
python-version: ${{ fromJSON(inputs.python-versions-array) }}
index_urls:
- ''
- ' --index-url=https://test.pypi.org/simple/ --extra-index-url=https://pypi.org/simple'
steps:
- name: Set up Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Test installing package
# A retry is used to allow for some downtime before the package is installable
uses: nick-fields/retry@c97818ca39074beaea45180dba704f92496a0082 # v3.0.1
with:
timeout_minutes: 10
max_attempts: 5
retry_wait_seconds: 30
warning_on_retry: false
command: pip install${{ matrix.index_urls }} "${{ env.PACKAGE_NAME }}==${{
needs.bump-version.outputs.built-version }}"