Skip to content

Release

Release #54

Workflow file for this run

name: Release
on:
workflow_run: # would only fire after file is merged to master
workflows: ["Lint and test"]
types:
- completed
branches:
- master
- 'ci/**' # ci testing, pre-releases
#- develop # can emit -dev releases but we do not want to
workflow_dispatch:
inputs:
dry_run:
description: "Run in dry-run mode (no publish)"
required: false
default: "true"
push: # only temporary, until this file lands on master (see above)
branches:
- 'ci/**'
# MUSTHAVE: Trusted publisher access for both repos.
# NOTE: according to docs, 'test' repo accounts are ephemeral and can be wiped at any time
# NOTE: 'test' accs are not that ephmeperal -- losing access to sandbox account (2FA issue) effectively locked us out of project; good test for workarounds though
# NOTE: as a part of regaining-control scenario we may use distinct project names in pyroject.toml (e.g. appmap-dev, appmap-ng)
env:
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
pypi_project: appmap
#testpypi_project: appmap-dev # workaround for lost-access scenario
testpypi_project: appmapcitest
jobs:
setup:
runs-on: ubuntu-latest
outputs:
distribution_name: ${{ steps.configure.outputs.distribution_name }}
publish_to: ${{ steps.configure.outputs.publish_to }}
publish_env: ${{ steps.configure.outputs.publish_env }}
steps:
- id: configure
shell: bash
run: |
case "${{ github.ref_name }}" in
ci/*)
echo "publish_env=testpypi" >> $GITHUB_OUTPUT
echo "distribution_name=${{ env.testpypi_project }}" >> $GITHUB_OUTPUT
echo "publish_to=https://test.pypi.org/project/${{ env.testpypi_project }}" >> $GITHUB_OUTPUT
;;
master)
echo "publish_env=pypi" >> $GITHUB_OUTPUT
echo "distribution_name=${{ env.pypi_project }}" >> $GITHUB_OUTPUT
echo "publish_to=https://pypi.org/project/${{ env.pypi_project }}" >> $GITHUB_OUTPUT
;;
*)
echo "publish_env=SKIP" >> $GITHUB_OUTPUT
echo "distribution_name=${{ env.pypi_project }}" >> $GITHUB_OUTPUT
echo "publish_to=https://test.pypi.org/project/${{ env.pypi_project }}" >> $GITHUB_OUTPUT
;;
esac
release:
runs-on: ubuntu-latest
needs: setup
if: github.event_name == 'workflow_dispatch' || (github.event_name=='push' && startsWith(github.ref_name,'ci/') ) || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && (github.event.workflow_run.head_branch == 'master' || startsWith(github.event.workflow_run.head_branch, 'ci/') ) )
steps:
- name: Generate token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
- uses: actions/checkout@v5
with:
token: ${{ steps.app-token.outputs.token }}
- uses: ./.github/actions/setup-semantic-release # node+semantic-release
- uses: ./.github/actions/setup # poetry
- name: Prepare release # branch policies defined in .releaserc
env:
GIT_AUTHOR_NAME: appland-release
GIT_AUTHOR_EMAIL: release@app.land
GIT_COMMITTER_NAME: appland-release
GIT_COMMITTER_EMAIL: release@app.land
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
DISTRIBUTION_NAME: ${{ needs.setup.outputs.distribution_name }}
run: |
if [ "$DRY_RUN" = "true" ]; then
semantic-release --dry-run
else
semantic-release
fi
- name: Get version
if: env.DRY_RUN != 'true'
id: version
run: |
VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: dockerhub login (for seamless docker pulling)
if: env.DRY_RUN != 'true'
uses: ./.github/actions/dockerhub-login
env:
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
continue-on-error: true
- name: Smoke test
if: env.DRY_RUN != 'true'
run: ci/scripts/run_tests.sh
env:
SMOKETEST_DOCKER_IMAGE: python:3.12-slim
DISTRIBUTION_NAME: ${{ needs.setup.outputs.distribution_name }}
- name: Extract release notes
if: env.DRY_RUN != 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
sed -n "/^## \[$VERSION\]/,/^## \[/p" CHANGELOG.md | sed '$d' > release-notes.md
# Verify we got something
if [ ! -s release-notes.md ]; then
echo "Error: Could not extract release notes for version $VERSION"
exit 1
fi
echo "Extracted release notes:"
cat release-notes.md
- name: Commit, tag, and push
if: env.DRY_RUN != 'true'
run: |
git config user.name "appland-release"
git config user.email "release@app.land"
git add pyproject.toml CHANGELOG.md
# Create commit with release notes in body
git commit -F- <<EOF
chore(release): ${{ steps.version.outputs.version }} [skip ci]
$(cat release-notes.md)
EOF
git tag "v${{ steps.version.outputs.version }}"
git push --follow-tags
- name: Upload wheel
if: env.DRY_RUN != 'true'
uses: actions/upload-artifact@v4
with:
name: wheel
path: dist/*.whl
- name: Upload sdist
if: env.DRY_RUN != 'true'
uses: actions/upload-artifact@v4
with:
name: sdist
path: dist/*.tar.gz
outputs:
version: ${{ steps.version.outputs.version }}
# as a workaround to ownership issues (lost access to project)
publish:
name: publish package on PyPI and create GitHub release
needs: ['setup', 'release']
if: (( github.event.inputs.dry_run != 'true' ) && ( (needs.setup.outputs.publish_env == 'pypi') || (needs.setup.outputs.publish_env == 'testpypi') ) )
runs-on: ubuntu-latest
environment:
name: ${{ needs.setup.outputs.publish_env }}
url: ${{ needs.setup.outputs.publish_to }}
permissions:
id-token: write
contents: write # needed for creating GitHub releases
steps:
- uses: actions/checkout@v5
with:
ref: v${{ needs.release.outputs.version }} # checkout the tag
- uses: ./.github/actions/refetch-artifacts
- name: Create GitHub release
run: |
gh release create "v${{ needs.release.outputs.version }}" \
dist/*.whl dist/*.tar.gz \
--notes-from-tag
env:
GH_TOKEN: ${{ github.token }}
- name: Publish to PyPI
if: needs.setup.outputs.publish_env=='pypi'
uses: pypa/gh-action-pypi-publish@release/v1
- name: Publish to TestPyPI
if: needs.setup.outputs.publish_env=='testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/ # trailing slash matters!