Skip to content

Commit 7c5c68e

Browse files
committed
feat(release): two-channel release tooling (latest + dev)
1 parent dbb7863 commit 7c5c68e

8 files changed

Lines changed: 696 additions & 180 deletions

File tree

.github/workflows/publish.yml

Lines changed: 80 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
name: publish
2-
run-name: "${{ format('{0} {1}', inputs.channel || (inputs.pre_release && 'rc' || 'latest'), inputs.version || inputs.bump) }}"
2+
run-name: "${{ format('{0} {1}', inputs.channel || 'latest', inputs.version || inputs.bump || 'auto') }}"
33

44
on:
5-
# Automated releases are intentionally disabled.
65
# Releases are manual only — trigger via workflow_dispatch.
7-
#
8-
# push:
9-
# branches:
10-
# - main
6+
# Both "latest" and "dev" channels are dispatched by hand. There is
7+
# no auto-publish on push.
118
workflow_dispatch:
129
inputs:
10+
channel:
11+
description: 'npm dist-tag channel — "latest" (public stable) or "dev" (internal)'
12+
required: true
13+
type: choice
14+
default: latest
15+
options:
16+
- latest
17+
- dev
1318
bump:
14-
description: "Bump major, minor, or patch"
19+
description: "Bump major/minor/patch — for latest, bumps stable; for dev, resets dev cycle"
1520
required: false
1621
type: choice
1722
options:
@@ -20,25 +25,11 @@ on:
2025
- minor
2126
- major
2227
version:
23-
description: "Override version (optional, takes precedence over bump)"
28+
description: "Override version (X.Y.Z for latest, X.Y.Z-dev.N for dev). Wins over bump."
2429
required: false
2530
type: string
26-
channel:
27-
description: "npm dist-tag channel (explicit; wins over pre_release)"
28-
required: false
29-
type: choice
30-
options:
31-
- ""
32-
- latest
33-
- rc
34-
- beta
35-
pre_release:
36-
description: "Publish to rc channel (ignored when channel is set)"
37-
required: false
38-
type: boolean
39-
default: false
4031

41-
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }}
32+
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.channel }}-${{ inputs.version || inputs.bump }}
4233

4334
# id-token:write is required for npm provenance (SLSA attestation).
4435
# This workflow must run on GitHub-hosted runners (not Blacksmith) for
@@ -78,14 +69,21 @@ jobs:
7869
- name: Format check
7970
run: bun run format:check
8071

72+
# Fail fast on bad/missing NPM_TOKEN before any side effects
73+
# (version.ts writes to package.json, network calls to GH, etc.)
74+
# Surfaces auth issues in ~2s instead of mid-publish.
75+
- name: Verify npm auth
76+
run: npm whoami --registry=https://registry.npmjs.org/
77+
env:
78+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
79+
8180
- name: Resolve version
8281
id: version
8382
run: bun script/version.ts
8483
env:
84+
KILO_CHANNEL: ${{ inputs.channel }}
8585
KILO_BUMP: ${{ inputs.bump }}
8686
KILO_VERSION: ${{ inputs.version }}
87-
KILO_CHANNEL: ${{ inputs.channel }}
88-
KILO_PRE_RELEASE: ${{ inputs.pre_release && 'true' || 'false' }}
8987
GH_REPO: ${{ github.repository }}
9088
GH_TOKEN: ${{ github.token }}
9189

@@ -96,17 +94,71 @@ jobs:
9694
NPM_CONFIG_PROVENANCE: "true"
9795
KILO_CHANNEL: ${{ steps.version.outputs.channel }}
9896

99-
- name: Commit version bump and tag
97+
# Smoke test: confirm the version actually appeared on the npm
98+
# registry. `npm publish` has been known to report success while
99+
# the registry's eventual-consistency layer drops the new version
100+
# (rare, but real). Catching this here means we fail BEFORE
101+
# creating tags / GH releases that would point at a non-existent
102+
# version. Retries 3x with 5s backoff to absorb normal replication
103+
# lag.
104+
- name: Verify publish landed on registry
100105
env:
101-
TAG: ${{ steps.version.outputs.tag }}
102106
VERSION: ${{ steps.version.outputs.version }}
107+
run: |
108+
MAX_ATTEMPTS=3
109+
for i in $(seq 1 $MAX_ATTEMPTS); do
110+
PUBLISHED=$(npm view "@kilocode/openclaw-security-advisor@$VERSION" version 2>/dev/null || echo "")
111+
if [ "$PUBLISHED" = "$VERSION" ]; then
112+
echo "Verified: $VERSION is live on npm"
113+
exit 0
114+
fi
115+
if [ "$i" -lt "$MAX_ATTEMPTS" ]; then
116+
echo "Attempt $i/$MAX_ATTEMPTS: registry returned '$PUBLISHED', expected '$VERSION'. Retrying in 5s..."
117+
sleep 5
118+
else
119+
echo "Attempt $i/$MAX_ATTEMPTS: registry returned '$PUBLISHED', expected '$VERSION'."
120+
fi
121+
done
122+
echo "::error::npm publish reported success but $VERSION did not appear on the registry after $MAX_ATTEMPTS attempts"
123+
exit 1
124+
125+
- name: Configure git identity
103126
run: |
104127
git config user.name "github-actions[bot]"
105128
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
129+
130+
# Stable releases get the version bump committed back to main so
131+
# package.json on main always reflects the latest published stable.
132+
# Dev releases skip this: dev publishes are ephemeral and would
133+
# otherwise pollute main with snapshot commits.
134+
- name: Commit version bump (stable only)
135+
if: steps.version.outputs.channel == 'latest'
136+
env:
137+
TAG: ${{ steps.version.outputs.tag }}
138+
run: |
106139
git add package.json
107140
git commit -m "release: $TAG"
141+
git push origin HEAD
142+
143+
# Tag is created for both channels so future `gh release list`
144+
# queries can find the highest dev version. For dev releases the
145+
# tag points at an orphan commit (the package.json bump made in
146+
# this CI runner) — pushing the tag carries the orphan commit too.
147+
- name: Tag release
148+
env:
149+
TAG: ${{ steps.version.outputs.tag }}
150+
CHANNEL: ${{ steps.version.outputs.channel }}
151+
run: |
152+
# For dev, we haven't committed the bump yet — do it now so the
153+
# tag points at the bumped tree. version.ts is guaranteed to
154+
# have modified package.json before this step runs, so this
155+
# commit always has changes (no --allow-empty needed).
156+
if [ "$CHANNEL" = "dev" ]; then
157+
git add package.json
158+
git commit -m "release: $TAG"
159+
fi
108160
git tag "$TAG"
109-
git push origin HEAD --tags
161+
git push origin "$TAG"
110162
111163
- name: Create GitHub release
112164
env:

AGENTS.md

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,48 @@ the published tarball.
4949
## Release flow
5050

5151
Releases are triggered manually from GitHub Actions → `publish` workflow →
52-
"Run workflow". Two common paths:
52+
"Run workflow". Two channels exist and they map to npm dist-tags:
5353

54-
- **Explicit version (today's path)**: dispatch with `version=0.1.0-beta.2`,
55-
`channel=beta`, leave bump blank. Use this for pre-release / beta / rc cuts.
56-
- **Auto-bump stable**: dispatch with `bump=patch|minor|major`, leave version
57-
blank, leave channel blank. CI queries the highest existing `vX.Y.Z` tag on
58-
the repo, bumps it, publishes to the `latest` npm dist-tag.
54+
- **`latest`** — public stable releases (`X.Y.Z`). Default for `npm install`.
55+
- **`dev`** — internal dogfood snapshots (`X.Y.Z-dev.N`). Available via
56+
`npm install @kilocode/openclaw-security-advisor@dev`.
5957

60-
`script/version.ts` handles both. See the top-of-file docstring for full env
61-
var semantics. The workflow fails fast if the target tag already exists on
62-
GitHub.
58+
There is no `beta`, `rc`, `next`, or `canary`. Two channels, that's it.
59+
60+
Common dispatch paths:
61+
62+
- **Auto-bump stable**: `channel=latest`, `bump=patch|minor|major`. Queries
63+
the highest existing `vX.Y.Z` tag, bumps it, publishes to `latest`.
64+
- **Continue dev cycle**: `channel=dev`, leave bump and version blank.
65+
Increments the dev counter on the highest existing `*-dev.N` tag.
66+
- **Reset dev cycle**: `channel=dev`, `bump=minor` (or major/patch). Seeds
67+
`${next-stable}-dev.1`. Use after shipping a stable release to start
68+
the next dev cycle.
69+
- **Explicit version**: any channel, `version=X.Y.Z` or `X.Y.Z-dev.N`.
70+
Wins over bump.
71+
72+
`script/version.ts` handles all of the above. See the top-of-file docstring
73+
for full env var semantics. The workflow fails fast if the target tag
74+
already exists on GitHub.
75+
76+
For full step-by-step release instructions see [RELEASING.md](./RELEASING.md).
6377

6478
### Branch protection and the release commit
6579

66-
The publish workflow's final step pushes a `release: vX.Y.Z` commit + tag
67-
directly to `main` as `github-actions[bot]`, using the default `GITHUB_TOKEN`.
80+
The publish workflow pushes commits and/or tags to `main` as
81+
`github-actions[bot]`, using the default `GITHUB_TOKEN`.
82+
83+
- **Stable releases** (`channel=latest`) commit the `package.json` version
84+
bump back to `main` AND push the tag.
85+
- **Dev releases** (`channel=dev`) push only the tag (pointing at an
86+
orphan commit). `main` history stays clean.
6887

6988
Once branch protection / repository rulesets are enabled on `main`, the
7089
`github-actions[bot]` actor **must be added to the ruleset's bypass actors
71-
list**, otherwise the release workflow will fail at the push step _after_
90+
list**, otherwise stable releases will fail at the push step _after_
7291
`npm publish` has already succeeded — leaving npm and GitHub out of sync.
92+
Dev releases are less affected (no commit to `main`) but still need tag
93+
push to be allowed, which most rulesets permit by default.
7394

7495
This is a stopgap. The long-term plan is to adopt the same `kilo-maintainer`
7596
GitHub App pattern used by the kilocode monorepo

CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
## [0.1.0-beta.1] - 2026-04-15
10+
## [0.1.0-dev.1] - 2026-04-15
1111

12-
Initial beta release.
12+
Initial dev release.
1313

1414
### Added
1515

@@ -21,5 +21,5 @@ Initial beta release.
2121
- Audit output validated with a Zod schema at the plugin boundary.
2222
- Public IP detection via `ifconfig.me` with IPv4/IPv6 validation.
2323

24-
[Unreleased]: https://github.com/Kilo-Org/openclaw-security-advisor/compare/v0.1.0-beta.1...HEAD
25-
[0.1.0-beta.1]: https://github.com/Kilo-Org/openclaw-security-advisor/releases/tag/v0.1.0-beta.1
24+
[Unreleased]: https://github.com/Kilo-Org/openclaw-security-advisor/compare/v0.1.0-dev.1...HEAD
25+
[0.1.0-dev.1]: https://github.com/Kilo-Org/openclaw-security-advisor/releases/tag/v0.1.0-dev.1

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ openclaw gateway restart
2222
That's it. On first use, the plugin will walk you through a one-time
2323
device auth flow to connect your KiloCode account.
2424

25+
### Channels
26+
27+
The plugin ships on two npm dist-tags:
28+
29+
- `latest` (default) — public stable releases (`X.Y.Z`).
30+
- `dev` — internal dogfood snapshots (`X.Y.Z-dev.N`). Install with:
31+
32+
```bash
33+
openclaw plugins install @kilocode/openclaw-security-advisor@dev
34+
```
35+
2536
---
2637

2738
## Usage

0 commit comments

Comments
 (0)