-
Notifications
You must be signed in to change notification settings - Fork 1
265 lines (234 loc) · 11.9 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
# This is a workflow for releasing packages in GitHub and publishing to PyPI.
# 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
jobs:
release:
name: Build, Verify, Release, and Publish
if: github.ref_name == 'main'
environment:
name: Production
url: ${{ steps.pypi_release.outputs.url }}
runs-on: ubuntu-latest
strategy:
matrix:
# It's only one Python version specified in a "matrix", but on purpose to stay DRY
python-version: ["3.12"]
defaults:
run:
shell: bash
env:
DOCKER_BUILDKIT: 1
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: Install poetry
run: pipx install poetry==1.8.3
- 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@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install the project with poetry
run: |
poetry env use python${{ matrix.python-version }}
poetry check --lock
poetry lock --no-update --no-cache
poetry install --verbose --sync --with test,ci
- name: Set PHYLUM_ORIGINAL_VER value
run: echo "PHYLUM_ORIGINAL_VER=$(poetry version --short)" >> "${GITHUB_ENV}"
- name: Set to next version for build
run: |
next_ver=$(poetry run semantic-release -vv version --print)
echo "PSR_EXPECTED_NEXT_VER=${next_ver}" >> "${GITHUB_ENV}"
if [ "${{ env.PHYLUM_ORIGINAL_VER }}" = "${next_ver}" ]; then
if [ "${{ inputs.prerelease }}" = "true" ]; then
next_ver=$(poetry run semantic-release -vv --strict version --print --as-prerelease --patch)
else
next_ver=$(poetry run semantic-release -vv --strict version --print --patch)
fi
else
if [ "${{ inputs.prerelease }}" = "true" ]; then
next_ver=$(poetry run semantic-release -vv --strict version --print --as-prerelease)
fi
fi
poetry version "${next_ver}"
- name: Set PHYLUM_REL_VER value
run: echo "PHYLUM_REL_VER=$(poetry version --short)" >> "${GITHUB_ENV}"
# NOTE: Run the tests for the current 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: Publish to TestPyPI
# 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
run: |
pipx run \
--index-url https://test.pypi.org/simple/ \
--spec "phylum==${{ env.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==${{ env.PHYLUM_REL_VER }}" \
--pip-args="--extra-index-url=https://pypi.org/simple/" \
phylum-ci -h
# This step is needed b/c otherwise the Python Semantic Release `version` cmd would bump the version a 2nd time.
- name: Revert to original version
run: poetry version ${{ env.PHYLUM_ORIGINAL_VER }}
- 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: |
if [ "${{ env.PHYLUM_ORIGINAL_VER }}" = "${{ env.PSR_EXPECTED_NEXT_VER }}" ]; then
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry run semantic-release -vv --strict version --as-prerelease --patch
else
poetry run semantic-release -vv --strict version --patch
fi
else
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry run semantic-release -vv --strict version --as-prerelease
else
poetry run semantic-release -vv version
fi
fi
- 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}"
- name: Use Python Semantic Release to publish release
env:
GH_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
run: poetry run semantic-release -vv --strict publish --tag "v${{ env.PHYLUM_REL_VER }}"
- name: Trigger documentation update
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` 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