diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cb87f05 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,116 @@ +name: Release + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag to publish, for example v0.6.0" + required: true + type: string + +permissions: + contents: read + +concurrency: + group: release-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }} + +jobs: + publish: + name: Publish crates.io packages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name || inputs.tag }} + + - uses: dtolnay/rust-toolchain@stable + + - name: Validate release version + run: | + set -euo pipefail + + version="${RELEASE_TAG#v}" + metadata="$(cargo metadata --no-deps --format-version 1)" + lib_version="$(jq -r '.packages[] | select(.name == "office2pdf") | .version' <<<"${metadata}")" + cli_version="$(jq -r '.packages[] | select(.name == "office2pdf-cli") | .version' <<<"${metadata}")" + cli_dependency="$(jq -r '.packages[] | select(.name == "office2pdf-cli") | .dependencies[] | select(.name == "office2pdf") | .req' <<<"${metadata}")" + + if [[ "${lib_version}" != "${version}" ]]; then + echo "::error::office2pdf version ${lib_version} does not match ${RELEASE_TAG}" + exit 1 + fi + + if [[ "${cli_version}" != "${version}" ]]; then + echo "::error::office2pdf-cli version ${cli_version} does not match ${RELEASE_TAG}" + exit 1 + fi + + case "${cli_dependency}" in + "${version}"|"^${version}") ;; + *) + echo "::error::office2pdf-cli depends on office2pdf ${cli_dependency}, expected ${version}" + exit 1 + ;; + esac + + - name: Check crates.io token + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + set -euo pipefail + if [[ -z "${CARGO_REGISTRY_TOKEN}" ]]; then + echo "::error::CARGO_REGISTRY_TOKEN secret is required to publish crates" + exit 1 + fi + + - name: Publish office2pdf + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + set -euo pipefail + + version="${RELEASE_TAG#v}" + if cargo search office2pdf --limit 1 | grep -F "office2pdf = \"${version}\"" > /dev/null; then + echo "office2pdf ${version} is already published" + exit 0 + fi + + cargo publish -p office2pdf --token "${CARGO_REGISTRY_TOKEN}" + + - name: Wait for office2pdf index update + run: | + set -euo pipefail + + version="${RELEASE_TAG#v}" + for attempt in {1..30}; do + if cargo search office2pdf --limit 1 | grep -F "office2pdf = \"${version}\"" > /dev/null; then + exit 0 + fi + + echo "office2pdf ${version} is not visible in the crates.io index yet; retry ${attempt}/30" + sleep 10 + done + + echo "::error::office2pdf ${version} did not appear in the crates.io index" + exit 1 + + - name: Publish office2pdf-cli + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + set -euo pipefail + + version="${RELEASE_TAG#v}" + if cargo search office2pdf-cli --limit 1 | grep -F "office2pdf-cli = \"${version}\"" > /dev/null; then + echo "office2pdf-cli ${version} is already published" + exit 0 + fi + + cargo publish -p office2pdf-cli --token "${CARGO_REGISTRY_TOKEN}" diff --git a/CLAUDE.md b/CLAUDE.md index b85103f..ee52cbe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -128,7 +128,7 @@ When asked to "release", always perform **both** GitHub Release and crates.io pu 1. **Version bump** — Create a PR (`chore/publish-`) that bumps `version` in both `crates/office2pdf/Cargo.toml` and `crates/office2pdf-cli/Cargo.toml`, and updates the CLI's `office2pdf` dependency version. Merge via standard PR workflow. 2. **GitHub Release** — `gh release create v` with changelog and contributors section. - Use `git log ..HEAD --format='%an' | sort -u` to find contributors. List each with their GitHub profile link. -3. **crates.io publish** — Publish lib first, then CLI: - - `cargo publish -p office2pdf` - - `cargo publish -p office2pdf-cli` +3. **crates.io publish** — `.github/workflows/release.yml` publishes lib first, then CLI. It requires the `CARGO_REGISTRY_TOKEN` repo secret. + - New releases publish automatically on `release.published`. + - Existing releases can be published with `gh workflow run release.yml -f tag=v`. 4. **Tag alignment** — Ensure the GitHub release tag (`v`) and Cargo.toml versions match.