Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/release-allowed-signers
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Maintainer and fleet release-tag signing keys. Public keys only.
steipete@gmail.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINJpNX7Qza7z78FxujtboBJZC73Co1cgWJ5xrFXaek+W
steipete@gmail.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII9XsaCcr8TInPnHcuTVfvXXcsoUFrOE7menfbEIHFW9
41 changes: 41 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Release

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
version:
description: Version with a dated CHANGELOG section (X.Y.Z)
required: true
type: string
dry_run:
description: Validate, build, and verify without publishing
default: true
type: boolean

permissions: {}

concurrency:
group: release-${{ github.repository }}-${{ inputs.version || github.ref_name }}
cancel-in-progress: false

jobs:
release:
permissions:
actions: read
checks: read
contents: write
statuses: read
uses: openclaw/release-workflows/.github/workflows/release-source.yml@v1
with:
version: ${{ inputs.version || github.ref_name }}
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
require-signed-tag: true
strict-checks: true
ci-check-events: '["push"]'
source-archives: true
test-command: >-
npx --yes --package=node@24.15.0 --package=pnpm@11.28.4
--call 'bash scripts/verify-release.sh'
release-name: ClawRouter
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add verified, signed-tag GitHub source releases with a no-publish rehearsal; keep service deployment separately dispatched.

- Select scoped inference grants in the admission transaction using fenced, secretless authority projections; keep credential-owner materialization authoritative for revocation and retain legacy projection fallback.

- Collapse proxy-key admission into one canonical authority read and preserve atomic grant selection, migration fences and revocation checks; reuse compiled discovery metadata and instrument local authority RPC counts.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pnpm check

`pnpm build` compiles the provider snapshot and admin UI, then typechecks the Worker. `pnpm check` runs Worker, admin, and script tests.

Numbered GitHub source releases use the [release workflow](RELEASING.md), independently of service deployments.

## License

MIT. See [LICENSE](LICENSE).
58 changes: 58 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Releasing ClawRouter

Numbered releases publish the versioned source on GitHub. The package is private;
there is no npm publication, Homebrew handoff, or application-signing credential.
Deployment is separate from release publication.

## Prepare

1. Update `package.json` and any lockfile root version for the next release.
2. Finalize one `## X.Y.Z - YYYY-MM-DD` section in `CHANGELOG.md`, preserving
contributor credit and release highlights. Keep an empty `## Unreleased` above it.
3. Land the reviewed release preparation PR on protected `main`. Wait for every
independent push CI check on that exact merged commit to pass.

## No-publish proof

Run the **Release** workflow on current `main`, with the version from `package.json`
and `dry_run=true` (the dispatch default):

```sh
gh workflow run release.yml --repo openclaw/clawrouter --ref main -f version=X.Y.Z -F dry_run=true
```

A rehearsal freezes current protected `main`, even if that version already has a
historical tag or release. It validates the version and dated changelog, requires
exact-commit push CI, runs `scripts/verify-release.sh` with Node.js 24.15.0, assembles
source archives, and independently verifies their inventory and SHA-256 checksums.
It creates no tag, draft, release, or deployment. No secrets are needed.

The local verification script only installs dependencies, builds, and tests. Run
it with Node.js 24.15+ and the repository's package manager. It does not publish.

## Publish an explicitly authorized release

After the rehearsal succeeds, create an SSH-signed annotated `vX.Y.Z` tag on the
verified commit using a key listed in `.github/release-allowed-signers`. Verify it
locally and push the tag. The tag push is the publication entrypoint; do not run
`gh release create` or a local publish helper.

`.github/workflows/release.yml` calls
`openclaw/release-workflows/.github/workflows/release-source.yml@v1`. It verifies
the signed tag, protected-branch ancestry, exact version metadata, dated notes,
and green push CI, then builds and verifies the frozen source before publishing.
Live dispatches also require a pre-existing signed tag; an unsigned workflow-created
tag cannot satisfy this repository's policy. Never move tags or republish a version.

The GitHub Release contains the exact changelog section, commit-based `.tar.gz`
and `.zip` source archives, `ASSET-INVENTORY.json`, and `SHA256SUMS`. The two source
verifiers run without publication credentials. The publisher checks the frozen
inventory and tag again before publishing. Read back the release and verify the
checksums and tag commit after completion. Leave `main` current and clean.

## Deployment boundary

Cloudflare production and FakeCo deployments remain in their existing explicit
`workflow_dispatch` workflows with their `expected_sha` checks. The release proof
renders a local config and uses Wrangler `--dry-run` only; it sends no deployment.
Docker/self-hosting remains source-based, with no new image registry publication.
16 changes: 16 additions & 0 deletions scripts/verify-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail

pnpm install --frozen-lockfile
pnpm build
pnpm --dir admin check
pnpm --dir admin test
pnpm test:scripts
pnpm worker:e2e
pnpm provider:smoke-plan
# Render and pack locally; neither command sends a deployment.
CLAWROUTER_STRICT_CONFIG=0 pnpm cf:config
build_dir="$(mktemp -d)"
trap 'rm -rf "$build_dir"' EXIT
WRANGLER_SEND_METRICS=false pnpm exec wrangler deploy --dry-run --outdir "$build_dir" --config .wrangler.generated.toml
test -s "$build_dir/index.js"
Loading