From 49656699eb8983ffc31815dd78cc859e82fdf147 Mon Sep 17 00:00:00 2001 From: meh Date: Sun, 23 Aug 2026 23:16:12 +0700 Subject: [PATCH] ci: publish workspace version bumps --- .github/workflows/ci.yml | 39 +++++++++++ .github/workflows/publish.yml | 120 ++++++++++++++++++++++++++++++++++ README.md | 8 +++ 3 files changed, 167 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3c8cb41 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + name: Build and test + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v2 + + - name: Format + run: cargo fmt --check + + - name: Clippy + run: | + rustc --version + cargo clippy --version + cargo clippy --all-targets -- -D warnings + + - name: Test + run: cargo test --locked diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..d0d87f6 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,120 @@ +name: Publish + +# Publish on a version bump, not on every push to master. Source paths are +# included so a re-run after a transient registry failure does not need a +# dummy version change. Existing versions exit cleanly. +on: + push: + branches: [master] + paths: + - Cargo.toml + - Cargo.lock + - crates/** + workflow_dispatch: + inputs: + dry_run: + description: 'Package and verify, but do not upload' + required: false + type: boolean + default: false + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + publish: + name: crates.io + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Are these versions already on crates.io? + id: check + shell: bash + run: | + set -euo pipefail + version="" + missing="" + for crate_name in agency-proxy-protocol agency-proxy-client agency-proxy; do + package_version=$(cargo metadata --no-deps --format-version 1 \ + | jq -r --arg name "$crate_name" '.packages[] | select(.name == $name) | .version') + if [ -n "$version" ] && [ "$package_version" != "$version" ]; then + echo "workspace package versions disagree: $version and $package_version" >&2 + exit 1 + fi + version=$package_version + published=$(curl -sS -H 'User-Agent: pathscale-ci' \ + "https://crates.io/api/v1/crates/$crate_name/versions" \ + | jq -r --arg v "$version" '.versions[]? | select(.num == $v) | .num' || true) + if [ -n "$published" ]; then + echo "$crate_name $version is already published" + else + missing="$missing $crate_name" + echo "$crate_name $version is new" + fi + done + echo "version=$version" >> "$GITHUB_OUTPUT" + if [ -n "$missing" ]; then + echo "already=false" >> "$GITHUB_OUTPUT" + else + echo "already=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build + if: steps.check.outputs.already == 'false' + run: cargo build --release + + - name: Test + if: steps.check.outputs.already == 'false' + run: cargo test --release + + - name: Package dry run + if: steps.check.outputs.already == 'false' && inputs.dry_run + run: | + cargo package -p agency-proxy-protocol --locked + cargo package -p agency-proxy-client --locked --no-verify + cargo package -p agency-proxy --locked --no-verify + + - name: Publish + if: steps.check.outputs.already == 'false' && !inputs.dry_run + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + VERSION: ${{ steps.check.outputs.version }} + run: | + set -euo pipefail + if [ -z "$CARGO_REGISTRY_TOKEN" ]; then + echo "CARGO_REGISTRY_TOKEN is not set on this repository" >&2 + echo "Add it under Settings -> Secrets -> Actions." >&2 + exit 1 + fi + for crate_name in agency-proxy-protocol agency-proxy-client agency-proxy; do + published=$(curl -sS -H 'User-Agent: pathscale-ci' \ + "https://crates.io/api/v1/crates/$crate_name/versions" \ + | jq -r --arg v "$VERSION" '.versions[]? | select(.num == $v) | .num' || true) + if [ -n "$published" ]; then + echo "$crate_name $VERSION is already published; skipping" + else + cargo publish -p "$crate_name" --locked --registry crates-io + fi + done + + - name: Summary + if: always() + run: | + { + if [ '${{ steps.check.outputs.already }}' = 'true' ]; then + echo "Nothing to publish: all AgencyProxy crates at \`${{ steps.check.outputs.version }}\` are already on crates.io." + echo + echo 'Bump `version` in Cargo.toml to cut a new release.' + elif [ '${{ inputs.dry_run }}' = 'true' ]; then + echo "Dry run: packaged AgencyProxy \`${{ steps.check.outputs.version }}\` without uploading." + else + echo "Published AgencyProxy \`${{ steps.check.outputs.version }}\`." + fi + } >> "$GITHUB_STEP_SUMMARY" diff --git a/README.md b/README.md index 574018d..025598d 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,11 @@ Run it with `agency-proxy --config /path/to/agency-proxy.json`. TLS is optional for local development, but an HTTPS-hosted frontend should use the configured certificate and connect with `wss://`. Browser clients authenticate with the WebSocket subprotocol `agency-proxy.`. + +## Publishing + +The three crates share the workspace version. A merge to `master` that changes +the root `Cargo.toml` runs the full CI checks, then publishes any missing crates +in dependency order: protocol, client, and daemon. The repository must provide +an Actions secret named `CARGO_REGISTRY_TOKEN` with permission to publish all +three crates.