name: Release
on:
push:
tags: ["v*"]
env:
CARGO_TERM_COLOR: always
jobs:
verify:
name: Verify tag and tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Check tag matches Cargo.toml version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
CARGO_VERSION="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')"
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "Tag $GITHUB_REF_NAME does not match Cargo.toml version $CARGO_VERSION" >&2
exit 1
fi
- name: Install qpdf (test validator)
run: sudo apt-get update && sudo apt-get install -y qpdf
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test --all-targets
build:
name: Build (${{ matrix.target }})
needs: verify
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-musl
os: ubuntu-24.04
- target: aarch64-unknown-linux-musl
os: ubuntu-24.04-arm
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@v5
- name: Install musl toolchain
if: contains(matrix.target, 'musl')
run: sudo apt-get update && sudo apt-get install -y musl-tools
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Build
run: cargo build --release --bin pdq --target ${{ matrix.target }}
- name: Package
shell: bash
run: |
VERSION="${GITHUB_REF_NAME#v}"
NAME="pdq-${VERSION}-${{ matrix.target }}"
mkdir "$NAME"
cp README.md "$NAME/"
if [ "$RUNNER_OS" = "Windows" ]; then
cp "target/${{ matrix.target }}/release/pdq.exe" "$NAME/"
7z a "$NAME.zip" "$NAME"
echo "ASSET=$NAME.zip" >> "$GITHUB_ENV"
else
cp "target/${{ matrix.target }}/release/pdq" "$NAME/"
tar czf "$NAME.tar.gz" "$NAME"
echo "ASSET=$NAME.tar.gz" >> "$GITHUB_ENV"
fi
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}
path: ${{ env.ASSET }}
if-no-files-found: error
publish:
name: Publish to crates.io
needs: verify
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Check every [patch.crates-io] override is publish-safe
run: |
allowlist=.github/publish-patch-allowlist.txt
patched="$(awk '
/^\[patch\.crates-io\]/ { in_patch = 1; next }
/^\[/ { in_patch = 0 }
in_patch && /=/ { print $1 }
' Cargo.toml)"
status=0
for crate in $patched; do
if grep -qxF "$crate" "$allowlist"; then
echo "note: $crate is allowlisted; publishing without its patch."
else
echo "Cargo.toml patches $crate, which is not in $allowlist." >&2
echo "The published crate would silently drop that patch." >&2
echo "Upstream it, or add it to the allowlist with a rationale" >&2
echo "for why losing it is safe for crates.io consumers." >&2
status=1
fi
done
exit $status
- name: Check if version is already on crates.io
id: check
run: |
VERSION="${GITHUB_REF_NAME#v}"
# crates.io rejects curl's default User-Agent with a 403
if curl -sf -A "pdq-release-workflow (github.com/meistrari/pdq)" \
"https://crates.io/api/v1/crates/pdq/$VERSION" > /dev/null; then
echo "Version $VERSION already published; skipping." >&2
echo "published=true" >> "$GITHUB_OUTPUT"
else
echo "published=false" >> "$GITHUB_OUTPUT"
fi
- name: Authenticate with crates.io (Trusted Publishing)
if: steps.check.outputs.published == 'false'
uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish
if: steps.check.outputs.published == 'false'
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
release:
name: Publish GitHub release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Generate checksums
run: |
cd artifacts
sha256sum -- * > SHA256SUMS
- name: Create release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--title "pdq $GITHUB_REF_NAME" \
--generate-notes \
artifacts/*