Skip to content

Add scripts for managing release process #2

Add scripts for managing release process

Add scripts for managing release process #2

Workflow file for this run

# This is heavily inspired on
# https://github.com/PyO3/maturin/blob/main/.github/workflows/release.yml
# Maturin is a tool to build Rust crates as Python packages with minimal configuration,
# which uses cargo under the hood and ensures the binaries are compatible with a wide
# array of platforms and architectures.
# Although fontc itself is a pure Rust project and as such doesn't use Python,
# making it available on PyPI as a binary wheel package suitable to be installed by pip
# increases its reach among font developers accustomed to Python tools.
#
# The Release workflow uses maturin to build fontc for Linux (x86_64 and aarch64),
# Windows (i686 and x86_64) and macOS (universal2 'fat' binary supporting both
# x86_64 and arm64 architectures), package these as Python wheels, and upload to PyPI.
# The same fontc binaries are also uploaded as Github Release assets which can be used
# with `cargo binstall` command.
# The workflow runs automatically when a fontc version tag (i.e. starting with 'fontc-v')
# gets pushed, or can be manually triggered from Github Actions interface (in which case
# artifacts don't get published, just temporarily uploaded to Github Actions).
name: Release
on:
# only run on fontc-specific, version-tagged commits or manually
push:
tags: ["fontc-v*.*.*"]
# enables workflow to be run manually
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
workflow_dispatch:
# read-only access to repo contents by default; specific jobs may override it
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
permissions:
contents: read
jobs:
build:
name: Build ${{ matrix.platform.target }}
strategy:
fail-fast: false
matrix:
platform:
- target: x86_64-apple-darwin
os: macos-latest
python-architecture: x64
archive: fontc-x86_64-apple-darwin.tar.gz
- target: x86_64-pc-windows-msvc
os: windows-latest
python-architecture: x64
archive: fontc-x86_64-pc-windows-msvc.zip
- target: i686-pc-windows-msvc
os: windows-latest
python-architecture: x86
archive: fontc-i686-pc-windows-msvc.zip
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
architecture: ${{ matrix.platform.python-architecture }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform.target }}
# Install gnu-tar because BSD tar is buggy
# https://github.com/actions/cache/issues/403
- name: Install GNU tar (macOS)
if: matrix.platform.os == 'macos-latest'
run: |
brew install gnu-tar
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
- name: Build wheel (macOS universal2) and sdist
if: matrix.platform.target == 'x86_64-apple-darwin'
uses: PyO3/maturin-action@v1
with:
target: universal2-apple-darwin
args: --release -o dist --sdist
- name: Build wheel (without sdist)
if: matrix.platform.target != 'x86_64-apple-darwin'
uses: PyO3/maturin-action@v1
with:
target: ${{matrix.platform.target}}
args: --release -o dist
- name: Install wheel
shell: bash
run: |
pip install --no-index --find-links dist/ --force-reinstall fontc
which fontc
fontc --version
- name: Check sdist metadata
if: matrix.platform.target == 'x86_64-apple-darwin'
run: pipx run twine check dist/*.tar.gz
- name: Archive binary
if: matrix.platform.os != 'windows-latest'
run: |
cd target/${{ matrix.platform.target }}/release
tar czvf ../../../${{ matrix.platform.archive }} fontc
cd -
- name: Archive binary (windows)
if: matrix.platform.os == 'windows-latest'
run: |
cd target/${{ matrix.platform.target }}/release
7z a ../../../${{ matrix.platform.archive }} fontc.exe
cd -
- name: Archive binary (macOS aarch64)
if: matrix.platform.os == 'macos-latest'
run: |
cd target/aarch64-apple-darwin/release
tar czvf ../../../fontc-aarch64-apple-darwin.tar.gz fontc
cd -
- name: Upload wheel artifacts
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.platform.target }}
path: dist
- name: Upload binary artifacts
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.platform.target }}
path: |
*.tar.gz
*.zip
build_linux:
name: Build ${{ matrix.platform.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- target: "x86_64-unknown-linux-musl"
image_tag: "x86_64-musl"
compatibility: "manylinux2010 musllinux_1_1"
- target: "aarch64-unknown-linux-musl"
image_tag: "aarch64-musl"
compatibility: "manylinux2014 musllinux_1_1"
container:
image: docker://ghcr.io/rust-cross/rust-musl-cross:${{ matrix.platform.image_tag }}
steps:
# checkout@v3 doesn't work inside the manylinux container:
# https://github.com/actions/checkout/issues/1487
- uses: actions/checkout@v3
- name: Build Wheels
uses: PyO3/maturin-action@main
with:
target: ${{ matrix.platform.target }}
manylinux: ${{ matrix.platform.compatibility }}
container: off
args: --release -o dist
- name: Install x86_64 wheel
if: startsWith(matrix.platform.target, 'x86_64')
run: |
/usr/bin/python3 -m pip install --no-index --find-links dist/ --force-reinstall fontc
which fontc
fontc --version
- name: Archive binary
run: tar czvf target/release/fontc-${{ matrix.platform.target }}.tar.gz -C target/${{ matrix.platform.target }}/release fontc
- name: Upload wheel artifacts
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.platform.target }}
path: dist
- name: Upload binary artifacts
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.platform.target }}
path: target/release/fontc-${{ matrix.platform.target }}.tar.gz
release-pypi:
name: Publish to PyPI
if: startsWith(github.ref, 'refs/tags/fontc-v')
needs: [build, build_linux]
runs-on: ubuntu-latest
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: wheels-*
# so that all artifacts are downloaded in the same directory specified by 'path'
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
release-github:
permissions:
# Needed to upload release artifacts.
contents: write
name: Publish to GitHub releases
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/fontc-v')"
needs: [build, build_linux]
steps:
- uses: actions/download-artifact@v4
with:
pattern: binaries-*
merge-multiple: true
- uses: actions/download-artifact@v4
with:
pattern: wheels-*
merge-multiple: true
path: wheels
# this requirements.txt can be used to pip install fontc more securely
# https://pip.pypa.io/en/stable/topics/secure-installs/
- name: Generate requirements.txt with SHA256 hashes
run: |
echo fontc | pipx run --spec pip-tools pip-compile - --no-index --find-links wheels/ --no-emit-find-links --generate-hashes --pip-args '--only-binary=:all:' --no-annotate --no-header --output-file requirements.txt
- name: Compute checksums of release assets
run: |
sha256sum *.tar.gz *.zip requirements.txt > checksums.txt
- name: Detect if tag is a pre-release
id: before_release
env:
# e.g. v0.1.0a1, v1.2.0b2 or v2.3.0rc3, but not v1.0.0
PRERELEASE_TAG_PATTERN: "fontc-v[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+([ab]|rc)[[:digit:]]+"
run: |
# strip leading 'refs/tags/' to get the tag name
TAG_NAME="${GITHUB_REF##*/}"
if egrep -q "$PRERELEASE_TAG_PATTERN" <<< "$TAG_NAME"; then
echo "Tag ${TAG_NAME} contains a pre-release suffix"
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "Tag ${TAG_NAME} does not contain a pre-release suffix"
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
fi
# strip the redundant 'fontc-v' prefix from the Github Release title
echo "release_title=${TAG_NAME#fontc-v}" >> "$GITHUB_OUTPUT"
- name: Release
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.before_release.outputs.release_title }}
files: |
*.tar.gz
*.zip
checksums.txt
requirements.txt
prerelease: ${{ steps.before_release.outputs.is_prerelease }}
generate_release_notes: true