Skip to content

Release

Release #65

Workflow file for this run

# 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.psr_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.11"]
defaults:
run:
shell: bash
env:
DOCKER_BUILDKIT: 1
steps:
- name: Checkout the repo
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
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@82a020f1f7f605c65dd2449b392a52c3fcfef7ef # v6.0.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.6.1
- 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@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.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: |
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry version $(poetry run semantic-release print-version --next --prerelease)
else
poetry version $(poetry run semantic-release print-version --next)
fi
- 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
id: publish_test_pypi
# 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 --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 `publish` 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 publish release
id: psr_release
env:
GH_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
REPOSITORY_USERNAME: __token__
REPOSITORY_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
if [ "${{ inputs.prerelease }}" = "true" ]; then
poetry run semantic-release publish -v DEBUG --prerelease
else
poetry run semantic-release publish -v DEBUG
fi
echo "url=https://pypi.org/project/phylum/${{ env.PHYLUM_REL_VER }}/" >> $GITHUB_OUTPUT
- name: Build 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 docker image with pre-built distributions
run: |
docker run --rm phylum-ci git --version
docker run --rm phylum-ci phylum-ci --version
docker run --rm phylum-ci phylum-ci --help
docker run --rm phylum-ci phylum-init --help
docker run --rm phylum-ci phylum --help
- 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: Tag and push unique docker image
run: |
export 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 ghcr.io/phylum-dev/phylum-ci:${{ env.PHYLUM_REL_VER }}-CLI$CLI_REL_VER
docker push phylumio/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
- name: Tag and push latest docker image
# 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 ghcr.io/phylum-dev/phylum-ci:latest
docker push phylumio/phylum-ci:latest
docker push ghcr.io/phylum-dev/phylum-ci:latest
- name: Logout of Docker Hub
if: always()
run: docker logout
- name: Logout of GitHub Container Registry
if: always()
run: docker logout ghcr.io