diff --git a/.github/workflows/manifest.yml b/.github/workflows/manifest.yml new file mode 100644 index 0000000..8d9775e --- /dev/null +++ b/.github/workflows/manifest.yml @@ -0,0 +1,22 @@ +name: manifest + +# The bundled adapter manifest must match the one seamless-auth-api main +# publishes. The schedule catches API route changes that land between PRs here. +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '17 6 * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + drift: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - run: scripts/check-manifest.sh diff --git a/AGENTS.md b/AGENTS.md index c17f060..4d93d5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,7 @@ guidance may extend them but must not contradict them. | Tests | `cargo test` | | MSRV | `cargo +1.88 test` | | Conformance | see README, "Conformance" | +| Manifest | `scripts/check-manifest.sh` (fix with `scripts/sync-manifest.sh`) | The crate targets Rust 1.88 (`rust-version` in `Cargo.toml`), set by jsonwebtoken 11. Do not use language or standard library features newer than that (CI runs 1.88). `Cargo.lock` is committed @@ -100,5 +101,8 @@ version, edit the changelog's released sections, or tag by hand: did not declare. - Merging that PR tests the crate, publishes it to crates.io through trusted publishing (no token in the repository), tags `vX.Y.Z`, and creates the GitHub release. +- The bundled manifest must match the one seamless-auth-api main publishes. The `manifest` + workflow checks it on every PR, on `main` and daily. Update it with `scripts/sync-manifest.sh` + in a `fix:` commit so the new routes ship in a patch release. Pre-1.0: a breaking change is a minor bump, and 1.0 is a deliberate decision, not a side effect. diff --git a/manifest.json b/manifest.json index 5eda92a..05919e8 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "apiVersion": "0.19.4", + "apiVersion": "0.21.1", "session": { "subject": "sub", "token": "token", @@ -9,6 +9,15 @@ "refreshTtl": "refreshTtl" }, "routes": [ + { "method": "GET", "path": "/admin/applications", "credential": "access" }, + { "method": "POST", "path": "/admin/applications", "credential": "access" }, + { "method": "DELETE", "path": "/admin/applications/{id}", "credential": "access" }, + { "method": "GET", "path": "/admin/applications/{id}", "credential": "access" }, + { "method": "PATCH", "path": "/admin/applications/{id}", "credential": "access" }, + { "method": "PUT", "path": "/admin/applications/{id}/keys", "credential": "access" }, + { "method": "GET", "path": "/admin/applications/{id}/resources", "credential": "access" }, + { "method": "PUT", "path": "/admin/applications/{id}/resources", "credential": "access" }, + { "method": "POST", "path": "/admin/applications/{id}/secret", "credential": "access" }, { "method": "GET", "path": "/admin/auth-events", "credential": "access" }, { "method": "GET", "path": "/admin/auth-events/export", "credential": "access" }, { "method": "GET", "path": "/admin/auth-events/integrity", "credential": "access" }, @@ -51,6 +60,11 @@ "credential": "access" }, { "method": "GET", "path": "/admin/reports/authentication-coverage", "credential": "access" }, + { "method": "GET", "path": "/admin/resources", "credential": "access" }, + { "method": "POST", "path": "/admin/resources", "credential": "access" }, + { "method": "DELETE", "path": "/admin/resources/{id}", "credential": "access" }, + { "method": "GET", "path": "/admin/resources/{id}", "credential": "access" }, + { "method": "PATCH", "path": "/admin/resources/{id}", "credential": "access" }, { "method": "GET", "path": "/admin/review-accounts", "credential": "access" }, { "method": "GET", "path": "/admin/sessions", "credential": "access" }, { "method": "GET", "path": "/admin/sessions/{userId}", "credential": "access" }, @@ -219,6 +233,11 @@ { "method": "POST", "path": "/system-config/oauth-providers", "credential": "access" }, { "method": "DELETE", "path": "/system-config/oauth-providers/{id}", "credential": "access" }, { "method": "PATCH", "path": "/system-config/oauth-providers/{id}", "credential": "access" }, + { + "method": "POST", + "path": "/system-config/oauth-providers/{id}/discovery", + "credential": "access" + }, { "method": "GET", "path": "/system-config/policy", "credential": "access" }, { "method": "DELETE", "path": "/system-config/policy/draft", "credential": "access" }, { "method": "PUT", "path": "/system-config/policy/draft", "credential": "access" }, diff --git a/scripts/check-manifest.sh b/scripts/check-manifest.sh new file mode 100755 index 0000000..84896ab --- /dev/null +++ b/scripts/check-manifest.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env sh +# Fails when the bundled adapter manifest differs from the one the auth API +# publishes. The bundled copy is what the adapter serves whenever the live fetch +# fails, so a route missing from it answers 404 in that case. apiVersion is +# ignored: it changes on every API release, routes do not. +# +# scripts/check-manifest.sh [source] +# +# source defaults to seamless-auth-api main. Fix a failure with +# scripts/sync-manifest.sh. +set -eu + +source="${1:-https://raw.githubusercontent.com/fells-code/seamless-auth-api/main/adapter-manifest.json}" +bundled="$(dirname "$0")/../manifest.json" + +dir="$(mktemp -d)" +trap 'rm -rf "$dir"' EXIT + +case "$source" in + http://*|https://*) curl -fsSL "$source" -o "$dir/upstream.json" ;; + *) cp "$source" "$dir/upstream.json" ;; +esac + +normalize() { jq -S 'del(.apiVersion) | .routes |= sort_by(.path, .method)' "$1"; } +normalize "$bundled" > "$dir/bundled.norm" +normalize "$dir/upstream.json" > "$dir/upstream.norm" + +if cmp -s "$dir/bundled.norm" "$dir/upstream.norm"; then + echo "Bundled manifest matches $source" + exit 0 +fi + +routes() { jq -r '.routes[] | "\(.method) \(.path)"' "$1" | sort; } +routes "$bundled" > "$dir/bundled.routes" +routes "$dir/upstream.json" > "$dir/upstream.routes" + +echo "Bundled manifest (manifest.json) differs from $source" >&2 +echo "Missing from the bundled copy:" >&2 +comm -13 "$dir/bundled.routes" "$dir/upstream.routes" | sed 's/^/ /' >&2 +echo "Only in the bundled copy:" >&2 +comm -23 "$dir/bundled.routes" "$dir/upstream.routes" | sed 's/^/ /' >&2 +echo "Full difference:" >&2 +diff -u "$dir/bundled.norm" "$dir/upstream.norm" >&2 || true +echo "Run scripts/sync-manifest.sh to update it." >&2 +exit 1