From 24d7cf17e0ab4f763f35522c57be7b4872722e6f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:26:19 +0000 Subject: [PATCH] Release automatically when the version in the README changes Add a release job to ci.yml that runs on pushes to the default branch after both test matrices succeed. It reads the version from the `from:` argument of the README install snippet and, when the tag vX.Y.Z does not exist yet, creates the tag and the GitHub release at the pushed commit with generated notes. Swift Package Manager and the Swift Package Index resolve releases from the tags, so no further publishing step is needed. Document the flow in a Releasing section of the README. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 45 ++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 49 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df2fdb7..5626f9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,48 @@ jobs: run: swift build - name: Test run: swift test + + release: + name: Tag and release + needs: [test-linux, test-macos] + 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.version.outputs.tag }} + created: ${{ steps.release.outputs.created }} + steps: + - uses: actions/checkout@v7 + - name: Read the version from the README install snippet + id: version + run: | + VERSION="$(sed -nE 's/.*from: *"([^"]+)".*/\1/p' README.md | head -1)" + if [ -z "$VERSION" ]; then + echo "No 'from:' version found in the README.md install snippet." >&2 + exit 1 + fi + echo "Version in README.md: $VERSION" + echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" + - name: Create the tag and the GitHub release when the tag is missing + id: release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.version.outputs.tag }} + run: | + status=0 + git ls-remote --exit-code --tags origin "refs/tags/$TAG" > /dev/null || status=$? + if [ "$status" -eq 0 ]; then + echo "Tag $TAG already exists; nothing to release." + echo "created=false" >> "$GITHUB_OUTPUT" + exit 0 + elif [ "$status" -ne 2 ]; then + echo "Could not check origin for tag $TAG (git ls-remote exited with $status)." >&2 + exit "$status" + fi + echo "Tag $TAG does not exist; creating it and the GitHub release at $GITHUB_SHA." + gh release create "$TAG" --target "$GITHUB_SHA" --title "$TAG" --generate-notes + echo "created=true" >> "$GITHUB_OUTPUT" diff --git a/README.md b/README.md index 5c8fc52..d659353 100644 --- a/README.md +++ b/README.md @@ -244,3 +244,7 @@ One entry per list in `threat.blocklists`: Some feeds, such as ipdata's own VPN feed, omit `firstSeen`, `lastSeen` and `daysSeen`; those entries decode with the three properties set to `nil`. The dates are kept as strings exactly as the API sends them. All model types conform to `Codable`, `Sendable`, and `Equatable`. + +## Releasing + +The package version is the one in the `from:` argument of the Swift Package Manager snippet under [Installation](#installation); a Swift package has no other version file. To release, bump that version in a pull request and merge it. On every push to `main` that passes the test matrices, CI reads the version from `README.md` and, when the tag `vx.y.z` does not exist yet, creates the tag and the GitHub release at the merge commit with generated release notes. Swift Package Manager and the [Swift Package Index](https://swiftpackageindex.com) pick new versions up from the Git tags, so nothing else is published. A merge whose version is already tagged releases nothing.