From 2a20b4f2ed4866f45175dd6f63d40c01e8132eb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:28:08 +0000 Subject: [PATCH 1/2] Release automatically when the version in Cargo.toml changes Add a release job to ci.yml that runs after the tests on every push to the default branch. It reads the version from Cargo.toml, derives the tag vX.Y.Z and, when that tag does not exist yet, creates the tag and the GitHub release in one step with gh release create. A merge whose version is already tagged creates nothing; a failed tag lookup fails the job instead of guessing. A release created with the workflow's own token does not trigger the publish workflow, so publish.yml becomes callable (workflow_call with a tag input; the release trigger is kept for releases published by hand) and ci.yml calls it from the same run with secrets: inherit when a release was created. The publish steps and the secret they use are unchanged; the checkout now names the tag being published explicitly. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 56 +++++++++++++++++++++++++++++++++++ .github/workflows/publish.yml | 10 +++++++ 2 files changed, 66 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54aa445..d0c7a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,59 @@ jobs: - run: cargo clippy --all-targets -- -D warnings - run: cargo test + + # Tags and releases the version in Cargo.toml once a push to the default + # branch is green. Nothing happens when that version is already tagged. + release: + needs: test + if: github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + runs-on: ubuntu-latest + permissions: + contents: write + concurrency: + group: release + cancel-in-progress: false + outputs: + tag: ${{ steps.release.outputs.tag }} + created: ${{ steps.release.outputs.created }} + steps: + - uses: actions/checkout@v5 + + - id: release + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION="$(sed -nE 's/^version *= *"([^"]+)".*/\1/p' Cargo.toml | head -1)" + if [ -z "$VERSION" ]; then + echo "::error::No version found in Cargo.toml" + exit 1 + fi + TAG="v$VERSION" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + STATUS=0 + git ls-remote --exit-code --tags origin "refs/tags/$TAG" > /dev/null || STATUS=$? + case "$STATUS" in + 0) + echo "Tag $TAG already exists; nothing to release." + echo "created=false" >> "$GITHUB_OUTPUT" + ;; + 2) + gh release create "$TAG" --target "$GITHUB_SHA" --title "$TAG" --generate-notes + echo "created=true" >> "$GITHUB_OUTPUT" + ;; + *) + echo "::error::Could not check whether tag $TAG exists (git ls-remote exited with $STATUS)" + exit "$STATUS" + ;; + esac + + # A release created with the workflow's own token does not trigger the + # publish workflow, so it is called from this run instead. + publish: + needs: release + if: needs.release.outputs.created == 'true' + uses: ./.github/workflows/publish.yml + with: + tag: ${{ needs.release.outputs.tag }} + secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index abb8481..2a1c0d8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,12 @@ name: Publish to crates.io on: release: types: [published] + workflow_call: + inputs: + tag: + description: Tag of the release to publish, for example v0.1.2 + required: true + type: string env: CARGO_TERM_COLOR: always @@ -12,6 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 + with: + # The tag being published: the input when called from the release job + # in ci.yml, the release's own tag when a release is published by hand. + ref: refs/tags/${{ inputs.tag || github.event.release.tag_name }} - uses: dtolnay/rust-toolchain@stable with: From 408f1bc3693cde1037ea15710a06ab59fcb2f72f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:28:09 +0000 Subject: [PATCH 2/2] Document the release process in the README Add a Releasing section for maintainers: bump the version in Cargo.toml in a pull request and merge it; CI tags vX.Y.Z, creates the release and publishes the crate, and a merge whose version is already tagged publishes nothing. Co-Authored-By: Claude Fable 5.1 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 97fa449..c78584e 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,10 @@ The main response type is `IpInfo` which includes: All response structs ignore unknown keys and decode a missing key and an explicit `null` alike: `Option` fields read `None`, every other field reads its default (`""`, `false`, `0`, `0.0` or an empty list), so a `null` anywhere in a response does not fail the lookup. +## Releasing + +Releases are cut by CI; nothing is tagged by hand. Bump `version` in `Cargo.toml` in a pull request and merge it: once the tests on the merge commit pass, CI tags it `vX.Y.Z`, creates the GitHub release with generated notes and publishes the crate to [crates.io](https://crates.io/crates/ipdata). A merge whose version is already tagged publishes nothing. A release published by hand on GitHub still runs the same publish workflow. + ## License MIT