diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 0000000..6252bd6 --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,18 @@ +name: PR check + +on: + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: cargo check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo check --workspace --all-features diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1fe7e2f --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,87 @@ +name: Release + +on: + push: + branches: [main] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + release: + name: Tag, publish, release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + + - name: Read crate version + id: version + run: | + VERSION=$(cargo metadata --format-version 1 --no-deps \ + | jq -r '.packages[] | select(.name == "postcrate-core") | .version') + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" + echo "Detected postcrate-core version: $VERSION" + + - name: Check if tag already exists + id: tag_check + run: | + TAG="${{ steps.version.outputs.tag }}" + if git rev-parse "refs/tags/$TAG" >/dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Tag $TAG already exists. Nothing to do." + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Tag $TAG does not exist. Will release." + fi + + - name: Create and push tag + if: steps.tag_check.outputs.exists == 'false' + run: | + TAG="${{ steps.version.outputs.tag }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "$TAG" -m "Release $TAG" + git push origin "$TAG" + + - name: Publish to crates.io + if: steps.tag_check.outputs.exists == 'false' + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish -p postcrate-core + + - name: Create GitHub release + if: steps.tag_check.outputs.exists == 'false' + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="${{ steps.version.outputs.tag }}" + VERSION="${{ steps.version.outputs.version }}" + + # Build release notes: GitHub's auto-generated changelog + a crates.io footer + gh api \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -f tag_name="$TAG" \ + -f target_commitish=main \ + "repos/${{ github.repository }}/releases/generate-notes" \ + --jq .body > /tmp/release-notes.md + + { + echo "" + echo "---" + echo "" + echo "**Published**: [\`postcrate-core $VERSION\` on crates.io](https://crates.io/crates/postcrate-core/$VERSION) — [docs.rs](https://docs.rs/postcrate-core/$VERSION)" + } >> /tmp/release-notes.md + + gh release create "$TAG" \ + --title "$TAG" \ + --notes-file /tmp/release-notes.md diff --git a/Cargo.toml b/Cargo.toml index e305ebf..7f93bfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ exclude = ["fuzz"] [workspace.package] -version = "0.1.0" +version = "0.1.1" edition = "2021" rust-version = "1.78" license = "MIT" @@ -21,7 +21,7 @@ homepage = "https://github.com/postcrate/core" readme = "README.md" [workspace.dependencies] -postcrate-core = { path = "crates/postcrate-core", version = "0.1.0" } +postcrate-core = { path = "crates/postcrate-core", version = "0.1.1" } tokio = { version = "1.41", features = [ "macros", diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..9bd267e --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# +# Open a release PR for postcrate-core. +# +# ./scripts/release.sh 0.2.0 +# +# What it does: +# 1. Verifies you are on the `dev` branch with a clean working tree. +# 2. Pulls latest `dev`. +# 3. Bumps the workspace version across Cargo.toml (workspace.package and +# workspace.dependencies path-dep stay in sync). +# 4. Runs `cargo check --workspace` to confirm the bump compiles. +# 5. Commits as "release: vX.Y.Z" and pushes `dev`. +# 6. Opens a PR `dev` -> `main`. +# +# After merging the PR, the Release workflow on `main` tags, publishes to +# crates.io, and creates the GitHub release automatically. +# +# Requires: cargo-edit (`cargo install cargo-edit`), gh CLI authenticated. + +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " >&2 + echo "Example: $0 0.2.0" >&2 + exit 1 +fi + +VERSION="$1" + +if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$ ]]; then + echo "Error: '$VERSION' is not valid semver (e.g. 0.2.0, 1.0.0-beta.1)" >&2 + exit 1 +fi + +if ! cargo set-version --help >/dev/null 2>&1; then + echo "Error: 'cargo set-version' not found. Install with:" >&2 + echo " cargo install cargo-edit" >&2 + exit 1 +fi + +if ! command -v gh >/dev/null 2>&1; then + echo "Error: 'gh' CLI not found. Install from https://cli.github.com/" >&2 + exit 1 +fi + +REPO_ROOT="$(git rev-parse --show-toplevel)" +cd "$REPO_ROOT" + +CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +if [[ "$CURRENT_BRANCH" != "dev" ]]; then + echo "Error: must be on the 'dev' branch (currently on '$CURRENT_BRANCH')." >&2 + echo "Switch with: git checkout dev" >&2 + exit 1 +fi + +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "Error: working tree has uncommitted changes. Commit or stash first." >&2 + exit 1 +fi + +echo "==> Pulling latest dev" +git pull --ff-only + +echo "==> Bumping workspace version to $VERSION" +cargo set-version --workspace "$VERSION" + +echo "==> Verifying the bump compiles" +cargo check --workspace --quiet + +echo "==> Committing and pushing" +git add Cargo.toml crates/*/Cargo.toml +git commit -m "release: v$VERSION" +git push origin dev + +echo "==> Opening release PR" +PR_URL=$(gh pr create \ + --base main \ + --head dev \ + --title "release: v$VERSION" \ + --body "Bumps \`postcrate-core\` to \`v$VERSION\`. + +After merge, the Release workflow on \`main\` will: +1. Create and push tag \`v$VERSION\` +2. Publish \`postcrate-core $VERSION\` to crates.io +3. Create the GitHub release") + +echo "" +echo "Release PR opened: $PR_URL" +echo "Review and merge. The Release workflow on main will do the rest."