-
Notifications
You must be signed in to change notification settings - Fork 1
405 lines (357 loc) · 17.4 KB
/
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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# This is a workflow for releasing packages in GitHub, publishing to PyPI, and building/pushing new Docker images.
# This workflow is only triggered manually, from the Actions tab. It is limited to those with `write` access
# to the repo (e.g., collaborators and orgs, people, teams given write access)
#
# The release process leans heavily on the Python Semantic Release (PSR) package, which in turn is dependent on
# conventional commits to determine release versions. Poetry is used to build the release distributions in order to
# use them for "verification" purposes *before* creating a GitHub release and publishing to PyPI. Currently, the
# verification process is simply uploading the distributions to TestPyPI and then confirming that the package can be
# accessed/used from there. In the future, a more robust end-to-end (E2E) pipeline could be triggered to run instead.
#
# References:
# https://github.community/t/who-has-permission-to-workflow-dispatch/133981
# https://github.community/t/who-can-manually-trigger-a-workflow-using-workflow-dispatch/128592
# https://python-semantic-release.readthedocs.io/en/latest/index.html
# https://www.conventionalcommits.org/en/v1.0.0/
---
name: Release
concurrency: Production
on:
workflow_dispatch:
inputs:
prerelease:
description: "Make this a pre-release"
type: boolean
required: true
default: false
env:
PYTHON_VERSION: "3.12"
POETRY_VERSION: "1.8.3"
jobs:
build_dist:
name: Build and verify Python distribution
runs-on: ubuntu-latest
outputs:
phylum_rel_ver: ${{ steps.get_vers.outputs.phylum_rel_ver }}
phylum_rel_ver_nuitka: ${{ steps.get_vers.outputs.phylum_rel_ver_nuitka }}
psr_expected_next_ver: ${{ steps.get_vers.outputs.psr_expected_next_ver }}
steps:
- name: Checkout the repo
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
# `python-semantic-release` needs full history to properly determine the next release version
fetch-depth: 0
- name: Install poetry
run: pipx install poetry==${{ env.POETRY_VERSION }}
- name: Configure poetry
run: |
poetry config virtualenvs.in-project true
poetry config repositories.testpypi https://test.pypi.org/legacy/
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'poetry'
- name: Install the project with poetry
run: |
poetry env use python${{ env.PYTHON_VERSION }}
poetry check --lock
poetry lock --no-update --no-cache
poetry install --verbose --sync --with test,ci
- name: Set to next version for build
id: get_vers
run: |
curr_ver=$(poetry version --short)
next_ver=$(poetry run semantic-release -v version --print)
echo "psr_expected_next_ver=${next_ver}" >> "${GITHUB_OUTPUT}"
if [ "${curr_ver}" = "${next_ver}" ]; then
if [ "${{ inputs.prerelease }}" = "true" ]; then
next_ver=$(poetry run semantic-release -v --strict version --print --as-prerelease --patch)
else
next_ver=$(poetry run semantic-release -v --strict version --print --patch)
fi
else
if [ "${{ inputs.prerelease }}" = "true" ]; then
next_ver=$(poetry run semantic-release -v --strict version --print --as-prerelease)
fi
fi
poetry version "${next_ver}"
phylum_rel_ver=$(poetry version --short)
phylum_rel_ver_nuitka=${phylum_rel_ver//-rc/}
echo "phylum_rel_ver=${phylum_rel_ver}" >> "${GITHUB_OUTPUT}"
echo "phylum_rel_ver_nuitka=${phylum_rel_ver_nuitka}" >> "${GITHUB_OUTPUT}"
# Run the tests for the active Python version as a sanity check.
- name: Run tox via poetry
run: poetry run tox
- name: Build wheel and source distribution
run: poetry build -vvv
- name: Upload build artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: dist
path: ./dist/
if-no-files-found: error
- name: Publish to TestPyPI
if: github.ref_name == 'main'
# Allow for re-running the workflow, with the same release package version, for situations where that package is
# already published to TestPyPI (e.g., failures due to TestPyPI being slow to recognize a published package).
continue-on-error: true
run: poetry publish -vvv --repository testpypi --username __token__ --password ${{ secrets.TESTPYPI_API_TOKEN }}
# This step is currently only verifying that the package uploaded to TestPyPI can be installed and run from there.
# This would be a good spot to trigger a more holistic E2E or integration (as it were) level testing suite. It is
# also possible to use the `Production` environment here as a manual gating function. That is, the required
# "reviewers" for that environment would be notified of the release and could check that the package verification
# step(s) worked as intended before approving the release to proceed.
- name: Verify the package
if: github.ref_name == 'main'
run: |
pipx run \
--index-url https://test.pypi.org/simple/ \
--spec "phylum==${{ steps.get_vers.outputs.phylum_rel_ver }}" \
--pip-args="--extra-index-url=https://pypi.org/simple/" \
phylum-init -h
pipx run \
--index-url https://test.pypi.org/simple/ \
--spec "phylum==${{ steps.get_vers.outputs.phylum_rel_ver }}" \
--pip-args="--extra-index-url=https://pypi.org/simple/" \
phylum-ci -h
build_windows:
name: Build Windows standalone binary
needs: build_dist
runs-on: windows-latest
steps:
- name: Checkout the repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
# Nuitka needs the packaged form and not the editable install Poetry provides
# Ref: https://github.com/Nuitka/Nuitka/issues/2965
- name: Download build artifacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install poetry
run: pipx install --python ${{ env.PYTHON_VERSION }} poetry==${{ env.POETRY_VERSION }}
- name: Configure poetry
run: poetry config virtualenvs.in-project true
- name: Install the project with poetry
run: |
poetry check --lock
poetry lock --no-update --no-cache
poetry install --verbose --no-root --sync --with compile
poetry run python -m pip install --find-links dist --no-index phylum
- name: Compile binary with Nuitka
env:
PHYLUM_REL_VER: ${{ needs.build_dist.outputs.phylum_rel_ver_nuitka }}
run: |
poetry run python -m nuitka `
--onefile `
--output-dir=build `
--output-filename="phylum-ci.exe" `
--include-package=phylum `
--include-package-data=phylum `
--include-distribution-metadata=phylum `
--onefile-tempdir-spec="{CACHE_DIR}/{PRODUCT}/{VERSION}" `
--product-name=phylum-ci `
--product-version=${env:PHYLUM_REL_VER} `
--file-version=${env:GITHUB_RUN_NUMBER} `
--company-name="Phylum, Inc." `
--copyright="Copyright (C) 2024 Phylum, Inc." `
--file-description="Analyze dependencies in CI with Phylum" `
--windows-icon-from-ico="docs/img/favicon.ico" `
--warn-implicit-exceptions `
--warn-unusual-code `
--assume-yes-for-downloads `
--report=nuitka-compilation-report.xml `
--deployment `
src/phylum/ci/cli.py
- name: Confirm operation of binary
env:
PHYLUM_API_KEY: ${{ secrets.PHYLUM_TOKEN }}
PHYLUM_BYPASS_CI_DETECTION: true
run: |
./build/phylum-ci.exe -h
./build/phylum-ci.exe -vvaf
- name: Upload standalone binary
if: always()
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: phylum-ci.exe
path: ./build/phylum-ci.exe
if-no-files-found: error
- name: Upload compilation report
if: always()
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: nuitka-compilation-report.xml
path: ./nuitka-compilation-report.xml
if-no-files-found: warn
# Nuitka will create a crash report with a static name when there are failures
- name: Upload crash report
if: always()
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: nuitka-crash-report.xml
path: ./nuitka-crash-report.xml
if-no-files-found: ignore
release:
name: Release and publish artifacts
needs: [build_dist, build_windows]
if: github.ref_name == 'main'
environment:
name: Production
url: ${{ steps.pypi_release.outputs.url }}
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: 1
PHYLUM_REL_VER: ${{ needs.build_dist.outputs.phylum_rel_ver }}
PSR_EXPECTED_NEXT_VER: ${{ needs.build_dist.outputs.psr_expected_next_ver }}
steps:
- name: Checkout the repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
# `python-semantic-release` needs full history to properly determine the next release version
fetch-depth: 0
# `python-semantic-release` needs this personal access token (PAT) in order to push to a protected branch.
# This PAT is for the `phylum-bot` account and only has the `public_repo` scope to limit privileges.
token: ${{ secrets.GH_RELEASE_PAT }}
# This GPG key is for the `phylum-bot` account and used in order to ensure commits and tags are signed/verified.
- name: Import GPG key for bot account
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
with:
gpg_private_key: ${{ secrets.PHYLUM_BOT_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PHYLUM_BOT_GPG_PASSPHRASE }}
git_user_signingkey: true
git_commit_gpgsign: true
git_tag_gpgsign: true
- name: Download build artifacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- name: Install poetry
run: pipx install poetry==${{ env.POETRY_VERSION }}
- name: Configure poetry
run: poetry config virtualenvs.in-project true
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'poetry'
- name: Install the project with poetry
run: |
poetry env use python${{ env.PYTHON_VERSION }}
poetry check --lock
poetry lock --no-update --no-cache
poetry install --verbose --sync --with ci
- name: Update script options documentation
run: poetry run rich-codex --verbose --skip-git-checks --no-confirm
- name: Use Python Semantic Release to create release
env:
GH_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
run: |
curr_ver=$(poetry version --short)
if [ "${curr_ver}" = "${{ env.PSR_EXPECTED_NEXT_VER }}" ]; then
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry run semantic-release -v --strict version --as-prerelease --patch
else
poetry run semantic-release -v --strict version --patch
fi
else
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry run semantic-release -v --strict version --as-prerelease
else
poetry run semantic-release -v version
fi
fi
- name: Use Python Semantic Release to publish release
env:
GH_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
run: poetry run semantic-release -v --strict publish --tag "v${{ env.PHYLUM_REL_VER }}"
- name: Publish to PyPI
id: pypi_release
# Allow for re-running the workflow, with the same release package version,
# for situations where that package is already published to PyPI.
continue-on-error: true
run: |
poetry publish -vvv --username __token__ --password ${{ secrets.PYPI_API_TOKEN }}
echo "url=https://pypi.org/project/phylum/${{ env.PHYLUM_REL_VER }}/" >> "${GITHUB_OUTPUT}"
# This is the safety net for the previous step allowing "continue-on-error"
- name: Verify PyPI release
run: |
pipx run --spec "phylum==${{ env.PHYLUM_REL_VER }}" phylum-init -h
pipx run --spec "phylum==${{ env.PHYLUM_REL_VER }}" phylum-ci -h
- name: Trigger documentation update
# NOTE: This is an instance where the expression syntax (`${{ }}`) is required for the `if` conditional,
# contrary to the GitHub workflow syntax documentation. Do not remove the expression syntax.
if: ${{ !inputs.prerelease }}
# Reference: https://docs.github.com/en/rest/repos/repos#create-a-repository-dispatch-event
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.GH_RELEASE_PAT }}
script: |
const response = await github.rest.repos.createDispatchEvent({
owner: "phylum-dev",
repo: "documentation",
event_type: "trigger-update-submodule",
client_payload: {
repo_name: context.repo.repo,
tag_name: "v${{ env.PHYLUM_REL_VER }}"
},
});
console.log(response);
- name: Build default docker image
run: |
docker build \
--tag phylum-ci \
--build-arg PKG_SRC=dist/phylum-*.whl \
--build-arg PKG_NAME=phylum-*.whl \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
.
- name: Test default docker image with pre-built distributions
run: scripts/docker_tests.sh --image phylum-ci
- name: Build slim docker image
run: |
docker build \
--tag phylum-ci-slim \
--build-arg PKG_SRC=dist/phylum-*.whl \
--build-arg PKG_NAME=phylum-*.whl \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
--file Dockerfile.slim \
.
- name: Test slim docker image with pre-built distributions
run: scripts/docker_tests.sh --image phylum-ci-slim --slim
- name: Login to Docker Hub
run: docker login --username ${{ secrets.DOCKER_HUB_USERNAME }} --password ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GitHub Container Registry
run: docker login --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} ghcr.io
- name: Create specific docker tags and push them
run: |
CLI_REL_VER=$(docker run --rm phylum-ci phylum --version | sed 's/phylum //')
docker tag phylum-ci "phylumio/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}"
docker tag phylum-ci-slim "phylumio/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}-slim"
docker tag phylum-ci "ghcr.io/phylum-dev/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}"
docker tag phylum-ci-slim "ghcr.io/phylum-dev/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}-slim"
docker push "phylumio/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}"
docker push "phylumio/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}-slim"
docker push "ghcr.io/phylum-dev/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}"
docker push "ghcr.io/phylum-dev/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI${CLI_REL_VER}-slim"
- name: Tag and push latest docker images
# Only tag and push `latest` and `slim` images when it's not a phylum-ci pre-release.
# NOTE: This is an instance where the expression syntax (`${{ }}`) is required for the `if` conditional,
# contrary to the GitHub workflow syntax documentation. Do not remove the expression syntax.
if: ${{ !inputs.prerelease }}
run: |
docker tag phylum-ci phylumio/phylum-ci:latest
docker tag phylum-ci-slim phylumio/phylum-ci:slim
docker tag phylum-ci ghcr.io/phylum-dev/phylum-ci:latest
docker tag phylum-ci-slim ghcr.io/phylum-dev/phylum-ci:slim
docker push phylumio/phylum-ci:latest
docker push phylumio/phylum-ci:slim
docker push ghcr.io/phylum-dev/phylum-ci:latest
docker push ghcr.io/phylum-dev/phylum-ci:slim
- name: Logout of Docker Hub
if: always()
run: docker logout
- name: Logout of GitHub Container Registry
if: always()
run: docker logout ghcr.io