diff --git a/.github/workflows/bindings-python.yml b/.github/workflows/bindings-python.yml index 14cb5427..77ff6070 100644 --- a/.github/workflows/bindings-python.yml +++ b/.github/workflows/bindings-python.yml @@ -143,30 +143,3 @@ jobs: steps: - run: echo "Python CI jobs completed successfully." - # TODO: Should we move this to a separate workflow? - release-pypi: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - needs: - - build-pyproject - - python-complete - runs-on: ubuntu-latest - environment: - name: release-pypi - url: "https://pypi.org/p/pathrs" - permissions: - id-token: write - steps: - - name: download built python-pathrs - uses: actions/download-artifact@v8 - with: - name: python-3.x-pathrs-dist - path: ${{ env.PYTHON_DIST }} - # PyPI doesn't let us upload our native wheel because we aren't building - # using the restrictive manylinux set of libraries (because we depend on - # libpathrs.so). - - name: remove wheel from python-pathrs - run: rm -fv ${{ env.PYTHON_DIST }}/*.whl - - name: upload python-pathrs to pypi - uses: pypa/gh-action-pypi-publish@release/v1 - with: - packages-dir: ${{ env.PYTHON_DIST }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..398e64c6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: MPL-2.0 +# +# libpathrs: safe path resolution on Linux +# Copyright (C) 2026 Aleksa Sarai +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +on: + push: + tags: + - 'v*' + +name: release + +jobs: + check-release-tag: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - run: rustup update stable + - name: validate git tag + run: >- + ./hack/ci-check-release-tag.sh + + release-crate: + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + needs: + - check-release-tag + runs-on: ubuntu-latest + environment: + name: release-crate + url: "https://crates.io/crates/pathrs" + permissions: + id-token: write + steps: + - uses: actions/checkout@v6 + - run: rustup update stable + - run: cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # NOTE: Copied from bindings-python.yml. + # TODO: Should this be a reusable workflow...? + build-pyproject: + strategy: + matrix: + python-version: ["3.x"] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + # Build and install libpathrs.so. + - uses: dtolnay/rust-toolchain@stable + - name: build libpathrs + run: make release + - name: install libpathrs + run: sudo ./install.sh --libdir=/usr/lib + # Set up python venv. + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + - name: install pypa/build + run: >- + python3 -m pip install --user build twine + # Build and install our bindings. + - name: build python-pathrs bindings + run: make -C contrib/bindings/python dist + - run: twine check contrib/bindings/python/dist/* + - name: install python-pathrs bindings + run: make -C contrib/bindings/python install + # Verify that the crate and python bindings have the same version. + # TODO: Move this to a "make check" we can run locally as well. + - name: check crate and python binding versions match + run: | + CRATE_VERSION="$(cargo metadata --no-deps --format-version=1 | jq -rM '.packages[] | select(.name == "pathrs") | "\(.name)-\(.version)"')" + PY_VERSION="$(python3 -c 'import importlib.metadata; print("pathrs-" + importlib.metadata.version("pathrs"))')" + + echo "rust crate version: $CRATE_VERSION"; + echo "python module version: $PY_VERSION"; + + [[ "$CRATE_VERSION" == "$PY_VERSION" ]] || exit 1 + # Include the dist/ in our artefacts. + - name: upload python-pathrs bindings dist/ + uses: actions/upload-artifact@v7 + with: + name: python-${{ matrix.python-version }}-pathrs-dist + path: contrib/bindings/python/dist/ + + release-pypi: + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + needs: + - check-release-tag + - build-pyproject + runs-on: ubuntu-latest + environment: + name: release-pypi + url: "https://pypi.org/p/pathrs" + permissions: + id-token: write + steps: + - name: download built python-pathrs + uses: actions/download-artifact@v8 + with: + name: python-3.x-pathrs-dist + path: ${{ env.PYTHON_DIST }} + # PyPI doesn't let us upload our native wheel because we aren't building + # using the restrictive manylinux set of libraries (because we depend on + # libpathrs.so). + - name: remove wheel from python-pathrs + run: rm -fv ${{ env.PYTHON_DIST }}/*.whl + - name: upload python-pathrs to pypi + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: ${{ env.PYTHON_DIST }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ef5e86dc..d39e325a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -655,20 +655,3 @@ jobs: runs-on: ubuntu-latest steps: - run: echo "Rust CI jobs completed successfully." - - release-crate: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - needs: - - rust-complete - runs-on: ubuntu-latest - environment: - name: release-crate - url: "https://crates.io/crates/pathrs" - permissions: - id-token: write - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable - - run: cargo publish - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/hack/ci-check-release-tag.sh b/hack/ci-check-release-tag.sh new file mode 100755 index 00000000..e97a0de7 --- /dev/null +++ b/hack/ci-check-release-tag.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# SPDX-License-Identifier: MPL-2.0 +# +# libpathrs: safe path resolution on Linux +# Copyright (C) 2026 Aleksa Sarai +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -Eeuxo pipefail + +SRC_ROOT="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" +TAG="$1" + +# Make sure that the tag is actually signed by the maintainer (me). +# TODO: Support checking the exact email addresses in libpathrs.keyring. +git show -- "$TAG" | grep -Fx "Author: Aleksa Sarai " +git show -- "$TAG" | grep -Fx "Tagger: Aleksa Sarai " + +# Use a temporary GNUPGHOME home to make sure we are only permitting +# libpathrs-approved keys for signing the release commit. +export GNUPGHOME="$(mkdir -d --tmpdir gpg-home-libpathrs.XXXXXXX)" +trap 'rm -rf "$GNUPGHOME"' EXIT +gpg --import <"$SRC_ROOT/libpathrs.keyring" + +git verify-tag -- "$TAG" +git tag -v -- "$TAG"