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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ jobs:
run: |
node --test npm/clawscan/test/*.test.mjs
node --test scripts/build-npm-package.test.mjs
node --test scripts/release-notes.test.mjs
node scripts/build-npm-package.mjs --version v0.0.0 --pack --smoke

- name: Test runtime update branch push
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,14 @@ jobs:
publish:
name: Publish GitHub Release
runs-on: ubuntu-latest
needs: build
needs: [build, publish-npm]
if: github.event_name == 'push' || inputs.publish
steps:
- name: Checkout release notes
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.build.outputs.checkout_ref }}

- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
Expand All @@ -94,10 +99,12 @@ jobs:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.build.outputs.version }}
run: |
npm view "@openclaw/clawscan@${RELEASE_TAG#v}" --json > npm-metadata.json
node scripts/release-notes.mjs "$RELEASE_TAG" npm-metadata.json > release-notes.md
gh release create "$RELEASE_TAG" dist/* \
--repo "$GITHUB_REPOSITORY" \
--title "$RELEASE_TAG" \
--generate-notes
--notes-file release-notes.md

publish-npm:
name: Publish ClawScan npm package
Expand Down
16 changes: 11 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

## Unreleased

- Prevent large Hugging Face `Retry-After` values from overflowing into immediate retries while preserving server cooldowns and cancellation.
- Label worker-owned Docker containers with optional run and command IDs so supervisors can clean up after cancellation. Thanks @jesse-merhi (#55).
- Add `--platform` to build smaller npm tarballs for one supported operating system and architecture while retaining the default universal package.
- Preserve every scanner report when sanitized target, profile, or custom scanner names collide with each other or generated numeric suffixes.
## 0.2.0 - 2026-09-22

**Highlights:** Preserve scanner evidence, bound benchmark input memory, and identify worker-owned containers for cleanup.

- Preserve every scanner report when sanitized target, profile, or custom scanner names collide with each other or generated numeric suffixes (#54).
- Fix unbounded memory use when loading benchmark `--ids` from files or HTTP, including whitespace-padded IDs; document selection limits and preserve full-set JSONL support. Thanks @SebTardif (#47).
- Refresh bundled scanner tools and Go dependencies; source builds now require Go 1.27.1, and the Docker runtime uses Node.js 24 LTS.
- Label worker-owned Docker containers with optional run and command IDs so supervisors can clean up after cancellation. Thanks @jesse-merhi (#55).
- Prevent large Hugging Face `Retry-After` values from overflowing into immediate retries while preserving server cooldowns and cancellation (#58).
- Add `--platform` to build smaller npm tarballs for one supported operating system and architecture while retaining the default universal package. Thanks @vincentkoc (#56).
- Repair contributor links and add the missing scanner-adapter guide. Thanks @atarico for the documentation report (#57).
- Refresh bundled scanner tools and Go dependencies; source builds now require Go 1.27.1, and the Docker runtime uses Node.js 24 LTS (#59).
- Publish changelog-backed release notes with verified npm metadata and show live CI and release status badges.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ ClawScan is a composable security scanning harness for agent skills.

Run a suite of skill security scanners, pass the results to a judge harness, and compare against multiple skill security benchmarks.

[![CI](https://img.shields.io/badge/CI-passing-brightgreen)](https://github.com/openclaw/clawscan/actions/workflows/ci.yml?query=branch%3Amain)
[![Release](https://img.shields.io/badge/Release-passing-brightgreen)](https://github.com/openclaw/clawscan/actions/workflows/release.yml)
[![Latest release](https://img.shields.io/badge/latest%20release-unreleased-lightgrey)](https://github.com/openclaw/clawscan/releases)
[![CI](https://github.com/openclaw/clawscan/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/openclaw/clawscan/actions/workflows/ci.yml?query=branch%3Amain)
[![Release](https://github.com/openclaw/clawscan/actions/workflows/release.yml/badge.svg)](https://github.com/openclaw/clawscan/actions/workflows/release.yml)
[![Latest release](https://img.shields.io/github/v/release/openclaw/clawscan)](https://github.com/openclaw/clawscan/releases)


## Quick Start
Expand Down
33 changes: 33 additions & 0 deletions scripts/release-notes.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url";
import { normalizePackageVersion } from "./build-npm-package.mjs";

export function releaseNotes(changelog, tag, metadata) {
const version = normalizePackageVersion(tag);
const lines = changelog.split("\n");
const start = lines.findIndex((line) => line.startsWith(`## ${version} - `));
if (start === -1) throw new Error(`Missing changelog section for ${version}`);
const end = lines.findIndex((line, index) => index > start && line.startsWith("## "));
const body = lines.slice(start + 1, end === -1 ? undefined : end).join("\n").trim();
if (!body) throw new Error(`Empty changelog section for ${version}`);
if (metadata.name !== "@openclaw/clawscan" || metadata.version !== version) {
throw new Error("npm metadata does not match the release");
}
const tarball = metadata.dist?.tarball;
const integrity = metadata.dist?.integrity;
if (tarball !== `https://registry.npmjs.org/@openclaw/clawscan/-/clawscan-${version}.tgz` ||
!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(integrity ?? "")) {
throw new Error("npm metadata is missing a valid registry tarball or integrity");
}
return `${body}\n\n## Package\n\n` +
`[npm ${version}](https://www.npmjs.com/package/@openclaw/clawscan/v/${version}) · ` +
`[Registry tarball](${tarball})\n\nIntegrity: \`${integrity}\`\n`;
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
process.stdout.write(releaseNotes(
readFileSync("CHANGELOG.md", "utf8"),
process.argv[2],
JSON.parse(readFileSync(process.argv[3], "utf8")),
));
}
29 changes: 29 additions & 0 deletions scripts/release-notes.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import assert from "node:assert/strict";
import test from "node:test";
import { releaseNotes } from "./release-notes.mjs";

const metadata = {
name: "@openclaw/clawscan",
version: "0.2.0",
dist: {
tarball: "https://registry.npmjs.org/@openclaw/clawscan/-/clawscan-0.2.0.tgz",
integrity: "sha512-dGVzdA==",
},
};
const changelog = "# Changelog\n\n## Unreleased\n\n## 0.2.0 - 2026-09-22\n\n**Highlights:** Changes.\n\n- Fixed.\n\n## 0.1.8 - 2026-09-10\n\n- Older.\n";

test("publishes only the requested changelog section and verified package metadata", () => {
const notes = releaseNotes(changelog, "v0.2.0", metadata);
assert.ok(notes.startsWith("**Highlights:** Changes.\n\n- Fixed.\n\n## Package"));
assert.ok(!notes.includes("Older") && !notes.includes("Unreleased"));
assert.ok(notes.includes(metadata.dist.tarball));
assert.ok(notes.includes(metadata.dist.integrity));
assert.equal(releaseNotes(changelog.split("## 0.1.8")[0], "v0.2.0", metadata), notes);
});

test("refuses missing or empty notes and mismatched registry metadata", () => {
assert.throws(() => releaseNotes("## Unreleased", "v0.2.0", metadata), /Missing/);
assert.throws(() => releaseNotes("## 0.2.0 - 2026-09-22\n", "v0.2.0", metadata), /Empty/);
assert.throws(() => releaseNotes(changelog, "v0.2.0", { ...metadata, version: "0.1.8" }), /match/);
assert.throws(() => releaseNotes(changelog, "v0.2.0", { ...metadata, dist: {} }), /tarball/);
});
Loading