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
108 changes: 80 additions & 28 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
name: publish
run-name: "${{ format('{0} {1}', inputs.channel || (inputs.pre_release && 'rc' || 'latest'), inputs.version || inputs.bump) }}"
run-name: "${{ format('{0} {1}', inputs.channel || 'latest', inputs.version || inputs.bump || 'auto') }}"

on:
# Automated releases are intentionally disabled.
# Releases are manual only — trigger via workflow_dispatch.
#
# push:
# branches:
# - main
# Both "latest" and "dev" channels are dispatched by hand. There is
# no auto-publish on push.
workflow_dispatch:
inputs:
channel:
description: 'npm dist-tag channel — "latest" (public stable) or "dev" (internal)'
required: true
type: choice
default: latest
options:
- latest
- dev
bump:
description: "Bump major, minor, or patch"
description: "Bump major/minor/patch — for latest, bumps stable; for dev, resets dev cycle"
required: false
type: choice
options:
Expand All @@ -20,25 +25,11 @@ on:
- minor
- major
version:
description: "Override version (optional, takes precedence over bump)"
description: "Override version (X.Y.Z for latest, X.Y.Z-dev.N for dev). Wins over bump."
required: false
type: string
channel:
description: "npm dist-tag channel (explicit; wins over pre_release)"
required: false
type: choice
options:
- ""
- latest
- rc
- beta
pre_release:
description: "Publish to rc channel (ignored when channel is set)"
required: false
type: boolean
default: false

concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }}
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.channel }}-${{ inputs.version || inputs.bump }}

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

# Fail fast on bad/missing NPM_TOKEN before any side effects
# (version.ts writes to package.json, network calls to GH, etc.)
# Surfaces auth issues in ~2s instead of mid-publish.
- name: Verify npm auth
run: npm whoami --registry=https://registry.npmjs.org/
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Resolve version
id: version
run: bun script/version.ts
env:
KILO_CHANNEL: ${{ inputs.channel }}
KILO_BUMP: ${{ inputs.bump }}
KILO_VERSION: ${{ inputs.version }}
KILO_CHANNEL: ${{ inputs.channel }}
KILO_PRE_RELEASE: ${{ inputs.pre_release && 'true' || 'false' }}
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}

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

- name: Commit version bump and tag
# Smoke test: confirm the version actually appeared on the npm
# registry. `npm publish` has been known to report success while
# the registry's eventual-consistency layer drops the new version
# (rare, but real). Catching this here means we fail BEFORE
# creating tags / GH releases that would point at a non-existent
# version. Retries 3x with 5s backoff to absorb normal replication
# lag.
- name: Verify publish landed on registry
env:
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
MAX_ATTEMPTS=3
for i in $(seq 1 $MAX_ATTEMPTS); do
PUBLISHED=$(npm view "@kilocode/openclaw-security-advisor@$VERSION" version 2>/dev/null || echo "")
if [ "$PUBLISHED" = "$VERSION" ]; then
echo "Verified: $VERSION is live on npm"
exit 0
fi
if [ "$i" -lt "$MAX_ATTEMPTS" ]; then
echo "Attempt $i/$MAX_ATTEMPTS: registry returned '$PUBLISHED', expected '$VERSION'. Retrying in 5s..."
sleep 5
else
echo "Attempt $i/$MAX_ATTEMPTS: registry returned '$PUBLISHED', expected '$VERSION'."
fi
done
echo "::error::npm publish reported success but $VERSION did not appear on the registry after $MAX_ATTEMPTS attempts"
exit 1

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

# Stable releases get the version bump committed back to main so
# package.json on main always reflects the latest published stable.
# Dev releases skip this: dev publishes are ephemeral and would
# otherwise pollute main with snapshot commits.
- name: Commit version bump (stable only)
if: steps.version.outputs.channel == 'latest'
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
git add package.json
git commit -m "release: $TAG"
git push origin HEAD

# Tag is created for both channels so future `gh release list`
# queries can find the highest dev version. For dev releases the
# tag points at an orphan commit (the package.json bump made in
# this CI runner) — pushing the tag carries the orphan commit too.
- name: Tag release
env:
TAG: ${{ steps.version.outputs.tag }}
CHANNEL: ${{ steps.version.outputs.channel }}
run: |
# For dev, we haven't committed the bump yet — do it now so the
# tag points at the bumped tree. version.ts is guaranteed to
# have modified package.json before this step runs, so this
# commit always has changes (no --allow-empty needed).
if [ "$CHANNEL" = "dev" ]; then
git add package.json
git commit -m "release: $TAG"
fi
git tag "$TAG"
git push origin HEAD --tags
git push origin "$TAG"

- name: Create GitHub release
env:
Expand Down
45 changes: 33 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,48 @@ the published tarball.
## Release flow

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

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

`script/version.ts` handles both. See the top-of-file docstring for full env
var semantics. The workflow fails fast if the target tag already exists on
GitHub.
There is no `beta`, `rc`, `next`, or `canary`. Two channels, that's it.

Common dispatch paths:

- **Auto-bump stable**: `channel=latest`, `bump=patch|minor|major`. Queries
the highest existing `vX.Y.Z` tag, bumps it, publishes to `latest`.
- **Continue dev cycle**: `channel=dev`, leave bump and version blank.
Increments the dev counter on the highest existing `*-dev.N` tag.
- **Reset dev cycle**: `channel=dev`, `bump=minor` (or major/patch). Seeds
`${next-stable}-dev.1`. Use after shipping a stable release to start
the next dev cycle.
- **Explicit version**: any channel, `version=X.Y.Z` or `X.Y.Z-dev.N`.
Wins over bump.

`script/version.ts` handles all of the above. See the top-of-file docstring
for full env var semantics. The workflow fails fast if the target tag
already exists on GitHub.

For full step-by-step release instructions see [RELEASING.md](./RELEASING.md).

### Branch protection and the release commit

The publish workflow's final step pushes a `release: vX.Y.Z` commit + tag
directly to `main` as `github-actions[bot]`, using the default `GITHUB_TOKEN`.
The publish workflow pushes commits and/or tags to `main` as
`github-actions[bot]`, using the default `GITHUB_TOKEN`.

- **Stable releases** (`channel=latest`) commit the `package.json` version
bump back to `main` AND push the tag.
- **Dev releases** (`channel=dev`) push only the tag (pointing at an
orphan commit). `main` history stays clean.

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

This is a stopgap. The long-term plan is to adopt the same `kilo-maintainer`
GitHub App pattern used by the kilocode monorepo
Expand Down
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

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

Initial beta release.
Initial dev release.

### Added

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

[Unreleased]: https://github.com/Kilo-Org/openclaw-security-advisor/compare/v0.1.0-beta.1...HEAD
[0.1.0-beta.1]: https://github.com/Kilo-Org/openclaw-security-advisor/releases/tag/v0.1.0-beta.1
[Unreleased]: https://github.com/Kilo-Org/openclaw-security-advisor/compare/v0.1.0-dev.1...HEAD
[0.1.0-dev.1]: https://github.com/Kilo-Org/openclaw-security-advisor/releases/tag/v0.1.0-dev.1
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ openclaw gateway restart
That's it. On first use, the plugin will walk you through a one-time
device auth flow to connect your KiloCode account.

### Channels

The plugin ships on two npm dist-tags:

- `latest` (default) — public stable releases (`X.Y.Z`).
- `dev` — internal dogfood snapshots (`X.Y.Z-dev.N`). Install with:

```bash
openclaw plugins install @kilocode/openclaw-security-advisor@dev
```

---

## Usage
Expand Down
Loading
Loading