Skip to content

Commit 637d2bd

Browse files
committed
chore(repo): add release and security automation
1 parent 84600e1 commit 637d2bd

5 files changed

Lines changed: 264 additions & 0 deletions

File tree

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
/.crabbox.yaml @openclaw/openclaw-secops
1616

1717
# Package integrity surfaces.
18+
/CHANGELOG.md @openclaw/openclaw-secops
1819
/package.json @openclaw/openclaw-secops
1920
/pnpm-lock.yaml @openclaw/openclaw-secops
21+
/scripts/ @openclaw/openclaw-secops
22+
/docs/release-prep.md @openclaw/openclaw-secops
2023

2124
# Provider execution, prompt construction, config, and patch workflow surfaces.
2225
/src/exec.ts @openclaw/openclaw-secops

.github/workflows/release.yml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
workflow_dispatch:
8+
inputs:
9+
tag_name:
10+
description: "Existing release tag to publish, for example v0.5.1"
11+
required: true
12+
type: string
13+
14+
permissions: {}
15+
16+
jobs:
17+
release:
18+
name: Publish npm and GitHub release
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 20
21+
permissions:
22+
contents: write
23+
id-token: write
24+
steps:
25+
- name: Resolve release tag
26+
id: release
27+
env:
28+
INPUT_TAG: ${{ inputs.tag_name }}
29+
run: |
30+
set -euo pipefail
31+
tag="${INPUT_TAG:-${GITHUB_REF_NAME}}"
32+
if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]]; then
33+
echo "Invalid release tag: $tag" >&2
34+
exit 1
35+
fi
36+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
37+
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
38+
39+
- name: Check out release tag
40+
uses: actions/checkout@v6
41+
with:
42+
fetch-depth: 0
43+
persist-credentials: false
44+
ref: ${{ steps.release.outputs.tag }}
45+
46+
- name: Verify release metadata
47+
env:
48+
RELEASE_TAG: ${{ steps.release.outputs.tag }}
49+
RELEASE_VERSION: ${{ steps.release.outputs.version }}
50+
run: |
51+
set -euo pipefail
52+
package_version="$(node -p 'require("./package.json").version')"
53+
if [[ "$package_version" != "$RELEASE_VERSION" ]]; then
54+
echo "$RELEASE_TAG does not match package.json version $package_version" >&2
55+
exit 1
56+
fi
57+
if ! grep -Eq "^## $RELEASE_VERSION - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md; then
58+
echo "CHANGELOG.md does not contain a dated $RELEASE_VERSION section" >&2
59+
exit 1
60+
fi
61+
62+
- name: Set up pnpm
63+
uses: pnpm/action-setup@v6
64+
with:
65+
version: 11.1.2
66+
67+
- name: Set up Node
68+
uses: actions/setup-node@v6
69+
with:
70+
node-version: 24
71+
cache: pnpm
72+
registry-url: https://registry.npmjs.org
73+
74+
- name: Install OIDC-capable npm
75+
run: npm install --global npm@11.16.0
76+
77+
- name: Install dependencies
78+
run: pnpm install --frozen-lockfile
79+
80+
- name: Validate release
81+
run: |
82+
pnpm typecheck
83+
pnpm lint
84+
pnpm format:check
85+
pnpm test
86+
pnpm build
87+
pnpm pack:smoke
88+
89+
- name: Publish npm package with trusted publishing
90+
run: npm publish --access public --provenance
91+
92+
- name: Build release notes
93+
env:
94+
RELEASE_VERSION: ${{ steps.release.outputs.version }}
95+
run: |
96+
set -euo pipefail
97+
awk -v version="$RELEASE_VERSION" '
98+
$0 ~ "^## " version " - " { capture = 1; next }
99+
capture && /^## / { exit }
100+
capture { print }
101+
' CHANGELOG.md > release-notes.md
102+
printf '\n## Package\n\n- npm: https://www.npmjs.com/package/clawpatch/v/%s\n' "$RELEASE_VERSION" >> release-notes.md
103+
104+
- name: Publish GitHub release
105+
env:
106+
GH_TOKEN: ${{ github.token }}
107+
RELEASE_TAG: ${{ steps.release.outputs.tag }}
108+
run: |
109+
set -euo pipefail
110+
flags=(--title "$RELEASE_TAG" --notes-file release-notes.md --verify-tag)
111+
if [[ "$RELEASE_TAG" =~ (alpha|beta|rc) ]]; then
112+
flags+=(--prerelease)
113+
else
114+
flags+=(--latest)
115+
fi
116+
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
117+
gh release edit "$RELEASE_TAG" "${flags[@]}" --draft=false
118+
else
119+
gh release create "$RELEASE_TAG" "${flags[@]}"
120+
fi

.github/workflows/secret-scan.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: "Security Gate: Secret Scanning"
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions: {}
10+
11+
jobs:
12+
trufflehog:
13+
name: Scan for verified secrets
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
steps:
18+
- name: Check out
19+
uses: actions/checkout@v6
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Resolve scan range
24+
id: scan-range
25+
env:
26+
EVENT_NAME: ${{ github.event_name }}
27+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
28+
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
29+
PUSH_BASE_SHA: ${{ github.event.before }}
30+
PUSH_HEAD_SHA: ${{ github.sha }}
31+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
32+
run: |
33+
set -euo pipefail
34+
zero_sha="0000000000000000000000000000000000000000"
35+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
36+
base="$PR_BASE_SHA"
37+
head="$PR_HEAD_SHA"
38+
else
39+
base="$PUSH_BASE_SHA"
40+
head="$PUSH_HEAD_SHA"
41+
if [[ -z "$base" || "$base" == "$zero_sha" ]]; then
42+
base="origin/$DEFAULT_BRANCH"
43+
fi
44+
fi
45+
echo "base=$base" >> "$GITHUB_OUTPUT"
46+
echo "head=$head" >> "$GITHUB_OUTPUT"
47+
48+
- name: Scan with TruffleHog
49+
uses: trufflesecurity/trufflehog@v3.95.3
50+
with:
51+
path: ./
52+
base: ${{ steps.scan-range.outputs.base }}
53+
head: ${{ steps.scan-range.outputs.head }}
54+
extra_args: --only-verified --debug

.github/workflows/stale.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Stale
2+
3+
on:
4+
schedule:
5+
- cron: "37 4 * * *"
6+
workflow_dispatch:
7+
8+
permissions: {}
9+
10+
jobs:
11+
stale:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
issues: write
15+
pull-requests: write
16+
steps:
17+
- name: Mark stale unassigned issues and pull requests
18+
uses: actions/stale@v10
19+
with:
20+
days-before-issue-stale: 14
21+
days-before-issue-close: 7
22+
days-before-pr-stale: 14
23+
days-before-pr-close: 7
24+
stale-issue-label: stale
25+
stale-pr-label: stale
26+
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
27+
exempt-pr-labels: maintainer,no-stale
28+
operations-per-run: 1000
29+
ascending: true
30+
exempt-all-assignees: true
31+
remove-stale-when-updated: true
32+
stale-issue-message: |
33+
This issue has been automatically marked as stale due to inactivity.
34+
Please add updated clawpatch details or it will be closed.
35+
stale-pr-message: |
36+
This pull request has been automatically marked as stale due to inactivity.
37+
Please update it or it will be closed.
38+
close-issue-message: |
39+
Closing due to inactivity.
40+
If this still affects clawpatch, open a new issue with current reproduction details.
41+
close-issue-reason: not_planned
42+
close-pr-message: |
43+
Closing due to inactivity.
44+
If this PR should be revived, reopen it with current context and validation.
45+
46+
- name: Mark stale assigned issues
47+
uses: actions/stale@v10
48+
with:
49+
days-before-issue-stale: 30
50+
days-before-issue-close: 10
51+
days-before-pr-stale: -1
52+
days-before-pr-close: -1
53+
stale-issue-label: stale
54+
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
55+
operations-per-run: 1000
56+
ascending: true
57+
include-only-assigned: true
58+
remove-stale-when-updated: true
59+
stale-issue-message: |
60+
This assigned issue has been automatically marked as stale after 30 days of inactivity.
61+
Please add an update or it will be closed.
62+
close-issue-message: |
63+
Closing due to inactivity.
64+
If this still affects clawpatch, reopen or file a new issue with current evidence.
65+
close-issue-reason: not_planned
66+
67+
- name: Mark stale assigned pull requests
68+
uses: actions/stale@v10
69+
with:
70+
days-before-issue-stale: -1
71+
days-before-issue-close: -1
72+
days-before-pr-stale: 27
73+
days-before-pr-close: 7
74+
stale-pr-label: stale
75+
exempt-pr-labels: maintainer,no-stale
76+
operations-per-run: 1000
77+
ascending: true
78+
include-only-assigned: true
79+
ignore-pr-updates: true
80+
remove-stale-when-updated: true
81+
stale-pr-message: |
82+
This assigned pull request has been automatically marked as stale after being open for 27 days.
83+
Please add an update or it will be closed.
84+
close-pr-message: |
85+
Closing due to inactivity.
86+
If this PR should be revived, reopen it with current context and validation.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 0.5.1 - 2026-06-10
44

5+
- Added npm trusted publishing through GitHub Actions OIDC, plus secops ownership, verified-secret scanning, and stale issue and pull request automation.
56
- Added opt-in npm registry verification that drops only matching single-package, whole-title-and-reasoning public-npm publication claims when the exact version is confirmed published, thanks @coletebou.
67
- Fixed revalidation to include linked patch attempts, validation results, feature context, and current relevant files so repaired findings can move out of `uncertain`.
78
- Added `clawpatch review --feature-list <path>` for reviewing an explicit ordered, de-duplicated set of feature IDs, thanks @camwest.

0 commit comments

Comments
 (0)