diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9bd73b8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,171 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + # Verify the pushed tag matches every artifact's declared version: + # workspace.package.version, each member crate's manifest, and the + # ts-client package.json. `shared-version = true` in release.toml is + # meant to keep these aligned but doesn't enforce it on `cargo + # release` failures or hand-edits, so check defensively. + version-check: + name: Verify tag matches every artifact version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check all manifest versions match the tag + run: | + set -euo pipefail + tag="${GITHUB_REF_NAME#v}" + fail=0 + + check() { + local label="$1" version="$2" + if [ "${version}" != "${tag}" ]; then + echo "::error::${label}: '${version}' does not match tag '${tag}'" + fail=1 + else + echo "ok ${label}: ${version}" + fi + } + + ws=$(awk '/^\[workspace\.package\]/{f=1; next} f && /^version = /{gsub(/"/, "", $3); print $3; exit}' Cargo.toml) + check "workspace.package" "${ws}" + + for manifest in crates/*/Cargo.toml; do + crate=$(awk -F'"' '/^name = /{print $2; exit}' "${manifest}") + # `version.workspace = true` carries the workspace version + # and matches by construction; only check explicit values. + if grep -Eq '^version = "' "${manifest}"; then + v=$(awk -F'"' '/^version = "/{print $2; exit}' "${manifest}") + check "${crate}" "${v}" + fi + done + + tsv=$(awk -F'"' '/"version"/{print $4; exit}' ts-client/package.json) + check "ts-client/package.json" "${tsv}" + + exit "${fail}" + + # Publish in dependency order so each crate's verification build can + # resolve the previous one from the index. partly-proxy-echo is + # publish=false and skipped automatically. + publish-crates: + name: Publish to crates.io + needs: [version-check] + runs-on: ubuntu-latest + # `environment: release` lets the repo gate this job behind approval + # rules and bind crates.io Trusted Publishers to this scope. + environment: release + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Authenticate to crates.io (OIDC) + id: crates-io-auth + uses: rust-lang/crates-io-auth-action@v1 + + - name: Publish partly-proxy-types + run: cargo publish --package partly-proxy-types + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }} + + # Sparse-index propagation isn't synchronous on publish ack. + - name: Wait for index propagation + run: sleep 30 + + - name: Publish partly-proxy-storage-jsonl + run: cargo publish --package partly-proxy-storage-jsonl + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }} + + - name: Wait for index propagation + run: sleep 30 + + - name: Publish partly-proxy-storage-sqlite + run: cargo publish --package partly-proxy-storage-sqlite + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }} + + - name: Wait for index propagation + run: sleep 30 + + - name: Publish partly-proxy-lib + run: cargo publish --package partly-proxy-lib + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }} + + # Publish @partly/proxy-client to npm with provenance. + # + # Uses npm's OIDC Trusted Publishing flow (GA Aug 2025) — configure a + # Trusted Publisher on npmjs.com for `@partly/proxy-client` bound to + # this repo, workflow filename, and the `release` environment. With + # that in place `--provenance` issues a Sigstore attestation linking + # the published tarball to this workflow run; no NPM_TOKEN secret + # required. + publish-npm: + name: Publish to npm + needs: [version-check] + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + contents: read + defaults: + run: + working-directory: ts-client + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + registry-url: "https://registry.npmjs.org" + cache: "npm" + cache-dependency-path: ts-client/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Publish to npm + run: npm publish --provenance --access public + + # Sequenced after every publish job so the release page never + # advertises a version that failed to upload to either registry. + github-release: + name: Create GitHub release + needs: [publish-crates, publish-npm] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + # --generate-notes walks back to the previous release tag. + fetch-depth: 0 + + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "${GITHUB_REF_NAME}" \ + --title "${GITHUB_REF_NAME}" \ + --generate-notes \ + --verify-tag diff --git a/Cargo.toml b/Cargo.toml index 9d161cd..5a5280c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ members = ["crates/*"] version = "0.3.0" edition = "2024" license = "MIT OR Apache-2.0" -repository = "https://github.com/thepartly/api-proxy" +repository = "https://github.com/thepartly/partly-proxy" +homepage = "https://github.com/thepartly/partly-proxy" authors = ["Partly "] rust-version = "1.85" diff --git a/README.md b/README.md index 326fbc4..897ed93 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # partly-proxy +[![CI](https://github.com/thepartly/partly-proxy/actions/workflows/ci.yml/badge.svg)](https://github.com/thepartly/partly-proxy/actions/workflows/ci.yml) +[![crates.io](https://img.shields.io/crates/v/partly-proxy-lib.svg)](https://crates.io/crates/partly-proxy-lib) +[![docs.rs](https://img.shields.io/docsrs/partly-proxy-lib)](https://docs.rs/partly-proxy-lib) +[![npm](https://img.shields.io/npm/v/@partly/proxy-client.svg)](https://www.npmjs.com/package/@partly/proxy-client) +[![MSRV](https://img.shields.io/badge/MSRV-1.85-blue)](https://github.com/thepartly/partly-proxy/blob/main/Cargo.toml) +![License](https://img.shields.io/crates/l/partly-proxy-lib) + A programmable HTTP/HTTPS proxy library for integration testing. Record real upstream traffic, replay it deterministically, inject stubbed responses, intercept and modify requests/responses via reqwest-style middleware, diff --git a/crates/partly-proxy-lib/Cargo.toml b/crates/partly-proxy-lib/Cargo.toml index 975558b..4da17dd 100644 --- a/crates/partly-proxy-lib/Cargo.toml +++ b/crates/partly-proxy-lib/Cargo.toml @@ -36,6 +36,10 @@ similar_names = "allow" too_many_lines = "allow" struct_field_names = "allow" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] # Shared type surface (SnapshotStorage trait, RecordedExchange, ProxyError). # Re-exported under the original module paths via the shim files in diff --git a/crates/partly-proxy-storage-jsonl/Cargo.toml b/crates/partly-proxy-storage-jsonl/Cargo.toml index 3d8aff3..cbdf345 100644 --- a/crates/partly-proxy-storage-jsonl/Cargo.toml +++ b/crates/partly-proxy-storage-jsonl/Cargo.toml @@ -18,6 +18,10 @@ missing_errors_doc = "allow" missing_panics_doc = "allow" must_use_candidate = "allow" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] # Depend on the types crate, NOT on partly-proxy-lib — depending on the # lib would create a Cargo package-graph cycle since the lib has an diff --git a/crates/partly-proxy-storage-sqlite/Cargo.toml b/crates/partly-proxy-storage-sqlite/Cargo.toml index 5dd4fbc..baa40e3 100644 --- a/crates/partly-proxy-storage-sqlite/Cargo.toml +++ b/crates/partly-proxy-storage-sqlite/Cargo.toml @@ -18,6 +18,10 @@ missing_errors_doc = "allow" missing_panics_doc = "allow" must_use_candidate = "allow" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] # Trait + RecordedExchange + ProxyError come from the types crate; # depending on it here keeps the SQLite backend usable without pulling diff --git a/crates/partly-proxy-types/Cargo.toml b/crates/partly-proxy-types/Cargo.toml index d2e320e..24d80b1 100644 --- a/crates/partly-proxy-types/Cargo.toml +++ b/crates/partly-proxy-types/Cargo.toml @@ -23,6 +23,10 @@ missing_errors_doc = "allow" missing_panics_doc = "allow" must_use_candidate = "allow" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] async-trait = { workspace = true } base64 = { workspace = true }