Release workflow (pdm-project/pdm)
The Release workflow from pdm-project/pdm, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Release workflow from the pdm-project/pdm repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Release
on:
push:
tags:
- "*"
defaults:
run:
# make sure to work on Windows
shell: bash
jobs:
release-pypi:
name: release-pypi
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v7.0.0
- uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- name: Check prerelease
id: check_version
run: |
if [[ "${{ github.ref }}" =~ ^refs/tags/[0-9.]+$ ]]; then
echo "PRERELEASE=false" >> $GITHUB_OUTPUT
else
echo "PRERELEASE=true" >> $GITHUB_OUTPUT
fi
- name: Build artifacts
run: |
pipx run build
- name: Upload artifacts
uses: actions/upload-artifact@v7.0.1
with:
name: pdm-wheel
path: dist/*.whl
if-no-files-found: error
retention-days: 15
- name: Test Build
run: |
python -m pip install "pdm[locked] @ file://$(ls ${GITHUB_WORKSPACE}/dist/*.whl)"
pdm --help
- name: Publish package distributions to PyPI
run: pdm publish --no-build
- name: Get Changelog
id: get-changelog
run: |
awk '/## Release/{if (flag==1)exit;else;flag=1;next} flag' CHANGELOG.md > .changelog.md
- name: Create Release
uses: actions/create-release@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: v${{ github.ref }}
body_path: .changelog.md
draft: false
prerelease: ${{ steps.check_version.outputs.PRERELEASE }}
- name: Trigger Bucket Update
uses: benc-uk/workflow-dispatch@v1.3.2
with:
workflow: Excavator
repo: frostming/scoop-frostming
token: ${{ secrets.G_T }}
ref: master
binary:
needs: release-pypi
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
[
"ubuntu-24.04",
"ubuntu-24.04-arm",
"windows-2025",
"macos-15-intel",
"macos-15",
]
env:
PYAPP_REPO: pyapp
PYAPP_VERSION: "0.27.0"
PYAPP_PROJECT_NAME: pdm
PYAPP_PROJECT_VERSION: ${{ github.ref_name }}
PYAPP_SELF_COMMAND: app # since `self` has been taken in `pdm`
PYAPP_DISTRIBUTION_EMBED: true
PYAPP_PROJECT_FEATURES: locked
SOURCE_FILE: ${{ matrix.os != 'windows-2025' && 'pyapp' || 'pyapp.exe' }}
TARGET_FILE: ${{ matrix.os != 'windows-2025' && 'pdm' || 'pdm.exe' }}
steps:
- name: Checkout
uses: actions/checkout@v7.0.0
- name: Fetch PyApp
run: >-
mkdir $PYAPP_REPO && curl -L
https://github.com/ofek/pyapp/releases/download/v$PYAPP_VERSION/source.tar.gz
|
tar --strip-components=1 -xzf - -C $PYAPP_REPO
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.10
if: matrix.os != 'macos-15-intel'
- name: Set sccache env
if: matrix.os != 'macos-15-intel'
run: |
echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
- name: Download artifacts
uses: actions/download-artifact@v8.0.1
with:
name: pdm-wheel
path: dist
- name: Configure embedded wheel
run: |
cd dist
wheel="$(echo *.whl)"
mv $wheel ../$PYAPP_REPO
echo "PYAPP_PROJECT_PATH=$wheel" >> $GITHUB_ENV
echo "TARGET_TRIPLE=$(rustc --version --verbose | grep "host" | awk '{print $2}')" >> $GITHUB_ENV
- name: Build
run: |
cd $PYAPP_REPO
cargo build --release
mv target/release/$SOURCE_FILE ../$TARGET_FILE
- name: Upload Assets
uses: actions/upload-artifact@v7.0.1
with:
name: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}
path: ${{ env.TARGET_FILE }}
if-no-files-found: error
retention-days: 15
- name: Create and Upload Archive with Checksum
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
UPLOAD_FILE: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}.tar.gz
CHECKSUM_FILE: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}.tar.gz.sha256
run: |
tar -czf $UPLOAD_FILE $TARGET_FILE
if command -v sha256sum &> /dev/null; then
sha256sum $UPLOAD_FILE > $CHECKSUM_FILE
else
shasum -a 256 $UPLOAD_FILE > $CHECKSUM_FILE
fi
gh release upload ${{ github.ref_name }} $UPLOAD_FILE $CHECKSUM_FILE --clobber
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Release on: push: tags: - "*" defaults: run: # make sure to work on Windows shell: bash concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: release-pypi: timeout-minutes: 30 name: release-pypi runs-on: latchkey-small permissions: id-token: write contents: write steps: - uses: actions/checkout@v7.0.0 - uses: actions/setup-python@v6 with: python-version: "3.11" cache: pip - name: Check prerelease id: check_version run: | if [[ "${{ github.ref }}" =~ ^refs/tags/[0-9.]+$ ]]; then echo "PRERELEASE=false" >> $GITHUB_OUTPUT else echo "PRERELEASE=true" >> $GITHUB_OUTPUT fi - name: Build artifacts run: | pipx run build - name: Upload artifacts uses: actions/upload-artifact@v7.0.1 with: name: pdm-wheel path: dist/*.whl if-no-files-found: error retention-days: 15 - name: Test Build run: | python -m pip install "pdm[locked] @ file://$(ls ${GITHUB_WORKSPACE}/dist/*.whl)" pdm --help - name: Publish package distributions to PyPI run: pdm publish --no-build - name: Get Changelog id: get-changelog run: | awk '/## Release/{if (flag==1)exit;else;flag=1;next} flag' CHANGELOG.md > .changelog.md - name: Create Release uses: actions/create-release@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: v${{ github.ref }} body_path: .changelog.md draft: false prerelease: ${{ steps.check_version.outputs.PRERELEASE }} - name: Trigger Bucket Update uses: benc-uk/workflow-dispatch@v1.3.2 with: workflow: Excavator repo: frostming/scoop-frostming token: ${{ secrets.G_T }} ref: master binary: timeout-minutes: 30 needs: release-pypi runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ "ubuntu-24.04", "ubuntu-24.04-arm", "windows-2025", "macos-15-intel", "macos-15", ] env: PYAPP_REPO: pyapp PYAPP_VERSION: "0.27.0" PYAPP_PROJECT_NAME: pdm PYAPP_PROJECT_VERSION: ${{ github.ref_name }} PYAPP_SELF_COMMAND: app # since `self` has been taken in `pdm` PYAPP_DISTRIBUTION_EMBED: true PYAPP_PROJECT_FEATURES: locked SOURCE_FILE: ${{ matrix.os != 'windows-2025' && 'pyapp' || 'pyapp.exe' }} TARGET_FILE: ${{ matrix.os != 'windows-2025' && 'pdm' || 'pdm.exe' }} steps: - name: Checkout uses: actions/checkout@v7.0.0 - name: Fetch PyApp run: >- mkdir $PYAPP_REPO && curl -L https://github.com/ofek/pyapp/releases/download/v$PYAPP_VERSION/source.tar.gz | tar --strip-components=1 -xzf - -C $PYAPP_REPO - name: Setup Rust uses: dtolnay/rust-toolchain@stable - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.10 if: matrix.os != 'macos-15-intel' - name: Set sccache env if: matrix.os != 'macos-15-intel' run: | echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV - name: Download artifacts uses: actions/download-artifact@v8.0.1 with: name: pdm-wheel path: dist - name: Configure embedded wheel run: | cd dist wheel="$(echo *.whl)" mv $wheel ../$PYAPP_REPO echo "PYAPP_PROJECT_PATH=$wheel" >> $GITHUB_ENV echo "TARGET_TRIPLE=$(rustc --version --verbose | grep "host" | awk '{print $2}')" >> $GITHUB_ENV - name: Build run: | cd $PYAPP_REPO cargo build --release mv target/release/$SOURCE_FILE ../$TARGET_FILE - name: Upload Assets uses: actions/upload-artifact@v7.0.1 with: name: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }} path: ${{ env.TARGET_FILE }} if-no-files-found: error retention-days: 15 - name: Create and Upload Archive with Checksum env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} UPLOAD_FILE: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}.tar.gz CHECKSUM_FILE: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}.tar.gz.sha256 run: | tar -czf $UPLOAD_FILE $TARGET_FILE if command -v sha256sum &> /dev/null; then sha256sum $UPLOAD_FILE > $CHECKSUM_FILE else shasum -a 256 $UPLOAD_FILE > $CHECKSUM_FILE fi gh release upload ${{ github.ref_name }} $UPLOAD_FILE $CHECKSUM_FILE --clobber
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
3 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Dependency installs
- Network fetches
This workflow runs 2 jobs (6 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.