mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-18 03:52:42 +08:00
Compare commits
203 Commits
fix/securi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d988851fe0 | ||
|
|
4005e03919 | ||
|
|
3be74caeae | ||
|
|
2c71e71833 | ||
|
|
abb6f04e0c | ||
|
|
3a570f1410 | ||
|
|
7019da8c7b | ||
|
|
5a15ea1b5c | ||
|
|
38988d5395 | ||
|
|
d371112c41 | ||
|
|
34be976c6d | ||
|
|
e54c56962b | ||
|
|
c41bc58cf6 | ||
|
|
8ce486a3be | ||
|
|
9e5bebb1a2 | ||
|
|
b35b1f2b7c | ||
|
|
aa498cfe11 | ||
|
|
27e56828ad | ||
|
|
d8f2f5c884 | ||
|
|
1ee2733b2f | ||
|
|
dbcbafc208 | ||
|
|
21125352d8 | ||
|
|
baa389ebed | ||
|
|
5556f19b8c | ||
|
|
59fb685884 | ||
|
|
3c1b346115 | ||
|
|
3952ac9585 | ||
|
|
f83693490b | ||
|
|
cf79735a65 | ||
|
|
1579d833d6 | ||
|
|
d4f11d3005 | ||
|
|
62563c2cfc | ||
|
|
a7f96847ce | ||
|
|
014c4ae103 | ||
|
|
c85bd45284 | ||
|
|
402c85b07a | ||
|
|
c56a4aad85 | ||
|
|
076aa93356 | ||
|
|
405df6f166 | ||
|
|
45d7167ea2 | ||
|
|
d1169c3dd0 | ||
|
|
4d6befe7cd | ||
|
|
b45f65f90a | ||
|
|
64afc856bc | ||
|
|
63df9f7b11 | ||
|
|
019fb52411 | ||
|
|
6f981c494a | ||
|
|
dd92ea1319 | ||
|
|
d2491412f5 | ||
|
|
2ea7ed6b5a | ||
|
|
05bbcabacf | ||
|
|
bc1af44e7c | ||
|
|
a77d0b0acc | ||
|
|
38e03ef4b6 | ||
|
|
f2f975112d | ||
|
|
63b0e45e56 | ||
|
|
2b00b39da9 | ||
|
|
6c84475a50 | ||
|
|
275e835aa1 | ||
|
|
9ffd4c9f01 | ||
|
|
16a5d3b51a | ||
|
|
606f8ec669 | ||
|
|
73df6d48af | ||
|
|
e7aa2a66f2 | ||
|
|
ec3f76b380 | ||
|
|
aaa73a5ba2 | ||
|
|
d98394a865 | ||
|
|
aa4978e9ab | ||
|
|
6802eca299 | ||
|
|
1914cc35bd | ||
|
|
40bd375ef3 | ||
|
|
2ab883a7b8 | ||
|
|
97ce204d97 | ||
|
|
7a74bb280d | ||
|
|
2195b446d4 | ||
|
|
f3f2d398f6 | ||
|
|
45f9086d29 | ||
|
|
5053ce248c | ||
|
|
47cad606f4 | ||
|
|
731dfcc5f9 | ||
|
|
2e27a37791 | ||
|
|
9d04064e73 | ||
|
|
c05acc7a14 | ||
|
|
4e2351dd4d | ||
|
|
8b8b13417e | ||
|
|
38723a531d | ||
|
|
0e46fd1081 | ||
|
|
e2292d18e2 | ||
|
|
023ce6e96c | ||
|
|
39250bbe65 | ||
|
|
fb6df23a89 | ||
|
|
b3a422d987 | ||
|
|
e3b2c1c30a | ||
|
|
02330f372c | ||
|
|
5645dd4d22 | ||
|
|
5a7857dc18 | ||
|
|
25bd8a7191 | ||
|
|
df87b40bec | ||
|
|
5d9c010628 | ||
|
|
03ca096e84 | ||
|
|
22ddf87d2c | ||
|
|
2147312aa2 | ||
|
|
9698070939 | ||
|
|
1c0b38f960 | ||
|
|
0842cb71eb | ||
|
|
392bd16a1d | ||
|
|
f3050ab614 | ||
|
|
6e798c02d8 | ||
|
|
911cd683d5 | ||
|
|
4637b65470 | ||
|
|
e2b6753b87 | ||
|
|
366ef93641 | ||
|
|
dc881a6a31 | ||
|
|
ea72a3382d | ||
|
|
19677bd4ef | ||
|
|
9c9c884526 | ||
|
|
120fd2f702 | ||
|
|
582c2d41b9 | ||
|
|
30955d3660 | ||
|
|
5370e73ee9 | ||
|
|
cf7850040e | ||
|
|
1380a9e094 | ||
|
|
5055f32ee3 | ||
|
|
1075f3819c | ||
|
|
c09ed1954f | ||
|
|
5372c7146b | ||
|
|
529150868c | ||
|
|
08e0b8cf6b | ||
|
|
5c34695491 | ||
|
|
f3ae525211 | ||
|
|
d47371d9c4 | ||
|
|
b962c53e78 | ||
|
|
4ae94d1d46 | ||
|
|
102c1f4ec7 | ||
|
|
71645bb8a3 | ||
|
|
db4bcd7d09 | ||
|
|
745b011632 | ||
|
|
a18cbcb7c6 | ||
|
|
2ca375fc1a | ||
|
|
22356395a2 | ||
|
|
759b7902ee | ||
|
|
f1c44e2d6d | ||
|
|
ffeccce5f9 | ||
|
|
d4fb49f3c4 | ||
|
|
f7178a74ef | ||
|
|
da67802baf | ||
|
|
5b46a11d2d | ||
|
|
5ee0f13a54 | ||
|
|
3f18ee4567 | ||
|
|
5b1ba437ba | ||
|
|
4132ce155e | ||
|
|
91bcc4cf2a | ||
|
|
a079d98eb4 | ||
|
|
85d5d94519 | ||
|
|
cb1e4356aa | ||
|
|
93e3bcef7a | ||
|
|
8decb546f7 | ||
|
|
e99a6d4c19 | ||
|
|
19c7731292 | ||
|
|
3c64a575dd | ||
|
|
81df1b239b | ||
|
|
80c47ecb99 | ||
|
|
41a0b8df36 | ||
|
|
122f29e5ea | ||
|
|
b6714bf109 | ||
|
|
df86f36a57 | ||
|
|
7279f43bbb | ||
|
|
f51b52ceca | ||
|
|
10da9ae248 | ||
|
|
49e95c5308 | ||
|
|
7de5bdca19 | ||
|
|
7a880bcf29 | ||
|
|
b86b891326 | ||
|
|
481fd10988 | ||
|
|
299d31c56e | ||
|
|
d6774e46e0 | ||
|
|
d491018a45 | ||
|
|
f3a1d1fcb0 | ||
|
|
6456d03868 | ||
|
|
e68db3a1b8 | ||
|
|
57b66b2ec8 | ||
|
|
90e72a67a3 | ||
|
|
6810c67f0c | ||
|
|
ba91eb7acf | ||
|
|
c12d921291 | ||
|
|
884a6a113c | ||
|
|
bed5bf339e | ||
|
|
d1923085e3 | ||
|
|
3b4808100d | ||
|
|
0455028a3c | ||
|
|
e0b1cb76e0 | ||
|
|
be4c541176 | ||
|
|
cbf6f0001b | ||
|
|
bda7581126 | ||
|
|
e349bdb949 | ||
|
|
768704e906 | ||
|
|
ba1403604d | ||
|
|
e939963784 | ||
|
|
5ff7242391 | ||
|
|
c25a4e6d0b | ||
|
|
4ea1b4fc4a | ||
|
|
3881cb3426 | ||
|
|
bfd11ee29f |
@@ -24,6 +24,25 @@ Use this with `$release-openclaw-maintainer` and `$openclaw-testing` when a rele
|
||||
fails, the parent cancels the remaining child matrix and prints the failed
|
||||
job summary. Inspect that first red job instead of waiting for unrelated
|
||||
matrix tails.
|
||||
- In a sparse worktree or Testbox source sync, first confirm `package.json`,
|
||||
`pnpm-lock.yaml`, and every source path the selected check reads. If any are
|
||||
absent, that checkout cannot validate a release dependency or Docker lane:
|
||||
stop and use the repo remote changed gate or a full task worktree. When the
|
||||
inputs are present and a release fix changes `package.json` or
|
||||
`pnpm-lock.yaml`, rebuild only the task-owned disposable box with
|
||||
`CI=true pnpm install --frozen-lockfile`, then run an explicit
|
||||
`require.resolve()` probe before Docker or focused tests. The CI flag permits
|
||||
pnpm to recreate a prewarmed modules directory without an interactive
|
||||
confirmation. Do not weaken the lockfile or label sparse-checkout failures
|
||||
as product/Docker failures.
|
||||
- If the candidate is rebased or its base SHA changes after warmup, stop the
|
||||
task-owned box and warm a fresh one before testing. Testbox source sync is
|
||||
relative to the warmed source tree; continuing can mix an old base file with
|
||||
a new candidate diff and produce false lockfile or Docker failures.
|
||||
- For a committed release candidate, warm the box with
|
||||
`blacksmith testbox warmup ... --ref <candidate-branch-or-sha>`. Do not rely
|
||||
on source sync to overlay committed branch changes onto the workflow's
|
||||
default ref.
|
||||
|
||||
## Preflight
|
||||
|
||||
@@ -57,7 +76,7 @@ gh workflow run openclaw-performance.yml \
|
||||
-f repeat=3 \
|
||||
-f deep_profile=false \
|
||||
-f live_openai_candidate=false \
|
||||
-f fail_on_regression=false
|
||||
-f fail_on_regression=true
|
||||
```
|
||||
|
||||
- Do not wait for full release validation to start this early perf signal.
|
||||
@@ -66,8 +85,9 @@ gh workflow run openclaw-performance.yml \
|
||||
- Call out any regression in the release proof. Treat a major regression as a
|
||||
release blocker until it is fixed, waived by the operator, or proven to be
|
||||
infrastructure noise.
|
||||
- Full Release Validation also records advisory product-performance evidence;
|
||||
the early standalone run is for overlap and faster regression discovery.
|
||||
- Full Release Validation records blocking product-performance evidence. The
|
||||
early standalone run is for overlap and faster regression discovery, but a
|
||||
regression or missing child run blocks the parent validation.
|
||||
|
||||
Prefer the trusted workflow on `main`, target the exact release SHA:
|
||||
|
||||
@@ -89,7 +109,7 @@ gh workflow run full-release-validation.yml \
|
||||
-f rerun_group=all
|
||||
```
|
||||
|
||||
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Use narrow `rerun_group` after focused fixes.
|
||||
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Stable and full profiles force the release soak; the beta profile may opt in with `run_release_soak=true`. Use narrow `rerun_group` after focused fixes.
|
||||
Publish with `openclaw-release-publish.yml` using `release_profile=from-validation`
|
||||
unless a maintainer intentionally wants to cross-check a specific profile; the
|
||||
publish workflow reads the effective profile from the full-validation manifest.
|
||||
@@ -125,6 +145,19 @@ Stop watchers before ending the turn or switching strategy.
|
||||
Anthropic API-key lane.
|
||||
5. For live-cache failures, inspect whether it is missing/invalid key, empty text, provider refusal, timeout, or baseline miss. Do not weaken release gates without clear provider evidence.
|
||||
6. Fix narrowly, run local/changed proof, commit, push, rerun the smallest matching group.
|
||||
7. If a required PR CI run is capacity-stalled with queued jobs and no active
|
||||
jobs, do not cancel unrelated work or accept a generic manual dispatch.
|
||||
From the PR head branch, dispatch the explicit exact-SHA fallback:
|
||||
`gh workflow run ci.yml --repo openclaw/openclaw --ref <pr-head-branch> -f
|
||||
target_ref=<full-pr-sha> -f include_android=true -f release_gate=true`.
|
||||
It runs on GitHub-hosted runners and is accepted only when its run title is
|
||||
`CI release gate <full-pr-sha>`. Record the stalled Blacksmith run and the
|
||||
fallback run in release evidence.
|
||||
If `Blacksmith Build Artifacts Testbox` is the only remaining required gate
|
||||
and remains queued without a runner, that completed exact fallback may cover
|
||||
it because CI's `build-artifacts` job already builds, packages, and smoke
|
||||
tests the artifacts. Do not use this coverage after the artifact workflow
|
||||
starts or completes non-successfully.
|
||||
|
||||
## Evidence
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ Use this skill for release and publish-time workflow. Load `$release-private` if
|
||||
- This skill should be sufficient to drive the normal release flow end-to-end.
|
||||
- Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy.
|
||||
- Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself.
|
||||
- Do not edit the root `README.md` as release prep, release closeout, or a
|
||||
substitute for release notes. Package-root README validation is a hard
|
||||
packaging gate, but a release only changes README content when an actual
|
||||
user-facing documentation contract changed.
|
||||
- Normal release work happens on a branch cut from `main`, not directly on
|
||||
`main`. Use `release/YYYY.M.PATCH` for the branch name.
|
||||
- If the operator asks for a release without saying stable/full, default to
|
||||
@@ -76,6 +80,44 @@ Use this skill for release and publish-time workflow. Load `$release-private` if
|
||||
or clawgrit reports. Report regressions explicitly. A major regression is a
|
||||
release blocker unless the operator waives it or the data clearly proves
|
||||
infrastructure noise.
|
||||
- Heal CI before tagging or publishing. The exact candidate SHA must have green
|
||||
`Full Release Validation`, including the root Dockerfile/install-smoke path.
|
||||
Treat a red Docker, package, or release workflow lane as a release-branch
|
||||
defect until the smallest correct fix is landed and proven; do not waive it
|
||||
because npm preflight or another sibling lane passed.
|
||||
- Keep the canonical `scripts/pr` runner authoritative for prepare and merge
|
||||
artifacts. A release-gate policy change may use focused candidate tests and
|
||||
exact-SHA hosted CI for proof, but never route `prepare-*` or `merge-*`
|
||||
through PR-controlled scripts or synthesize prepare artifacts to bootstrap
|
||||
the change. If the current canonical gate cannot validate the new policy,
|
||||
stop for explicit maintainer direction rather than weakening that boundary.
|
||||
- In maintainer Testbox mode, use `OPENCLAW_TESTBOX=1 scripts/pr prepare-run
|
||||
<PR>` only after the exact PR head has passed `CI` and every scheduled
|
||||
hosted gate. For a workflow change, that means `Blacksmith Testbox`,
|
||||
`Blacksmith ARM Testbox`, `Blacksmith Build Artifacts Testbox`, and
|
||||
`Workflow Sanity`; only gates GitHub actually scheduled for that exact head
|
||||
are required. This preserves the canonical prepare artifacts while avoiding
|
||||
a redundant broad local suite. A
|
||||
literal `CHANGELOG.md`-only head gets a clean diff check instead because
|
||||
those workflows intentionally do not dispatch. Documentation and README
|
||||
changes still require CI. If `merge-run` requires a mainline sync, run
|
||||
`OPENCLAW_TESTBOX=1 scripts/pr prepare-sync-head <PR>`, wait for those hosted
|
||||
gates on the newly pushed SHA, then run `prepare-run` again.
|
||||
- If an exact PR-head CI run has no active jobs because Blacksmith capacity is
|
||||
stalled, a maintainer may dispatch the explicit GitHub-hosted fallback from
|
||||
the PR head branch:
|
||||
`gh workflow run ci.yml --repo openclaw/openclaw --ref <pr-head-branch> -f
|
||||
target_ref=<full-pr-sha> -f include_android=true -f release_gate=true`.
|
||||
Use it only for an observed provider queue stall, never for failed CI or as a
|
||||
routine shortcut. The run must be named `CI release gate <full-pr-sha>` and
|
||||
pass on that exact SHA; the native hosted-gate verifier rejects generic manual
|
||||
CI runs. If `Blacksmith Build Artifacts Testbox` is the only remaining
|
||||
required gate and it is still queued without a runner, the same completed
|
||||
fallback CI may cover it because its `build-artifacts` job builds, packages,
|
||||
and smoke tests those artifacts. The verifier records that coverage. Never
|
||||
use this coverage when the artifact workflow has started, failed, been
|
||||
cancelled, or been skipped. Then rerun `OPENCLAW_TESTBOX=1 scripts/pr
|
||||
prepare-run <PR>`.
|
||||
- Generate the changelog before every beta, beta rerun, stable release, or
|
||||
stable rerun, before version/tag preparation. Use
|
||||
`$openclaw-changelog-update` for the rewrite. Do not continue release prep if
|
||||
@@ -119,6 +161,14 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
`OPENCLAW_TESTBOX=1 pnpm check:changed`. Push, then verify `origin/main`
|
||||
contains the shipped version and changelog before calling the stable release
|
||||
done.
|
||||
6. Keep repository variables `RELEASE_ROLLBACK_DRILL_ID` and
|
||||
`RELEASE_ROLLBACK_DRILL_DATE` current after each private rollback drill.
|
||||
`openclaw-stable-main-closeout.yml` starts from the `main` push carrying the
|
||||
shipped version, changelog, and appcast after stable publication, then binds
|
||||
immutable evidence to the published tag. Do not declare stable complete
|
||||
until it writes the immutable closeout manifest to the GitHub release. The
|
||||
drill must be within 90 days; manual dispatch is only for repair/replay, and
|
||||
private rollback commands remain in the maintainer-only runbook.
|
||||
|
||||
## Handle versions and release files consistently
|
||||
|
||||
|
||||
@@ -29,11 +29,17 @@ publish skill; use `$release-openclaw-maintainer` before changing release state.
|
||||
- Confirm release body has npm, CI, plugin npm, ClawHub, mac/appcast evidence
|
||||
links when expected.
|
||||
- Confirm assets expected for stable mac releases are uploaded: zip, dmg,
|
||||
dSYM, dependency evidence when present.
|
||||
dSYM, dependency evidence, immutable full-validation manifest,
|
||||
postpublish evidence, and stable-main closeout manifest.
|
||||
- Download each immutable evidence asset and its `.sha256` companion, then
|
||||
verify the checksum before trusting the release record.
|
||||
2. Root npm:
|
||||
- `npm view openclaw@<VERSION> version dist-tags.latest dist.tarball dist.integrity time.<VERSION> --json`
|
||||
- `latest` must equal `<VERSION>` for stable.
|
||||
- Record tarball, integrity, publish time.
|
||||
- Confirm the release postpublish evidence records
|
||||
`npmRegistrySignaturesVerified: true` and
|
||||
`npmProvenanceAttestationMatched: true`.
|
||||
3. Plugin publish set:
|
||||
- Get exact tag metadata from GitHub, not the local checkout when dirty:
|
||||
download `https://api.github.com/repos/openclaw/openclaw/tarball/v<VERSION>`
|
||||
@@ -57,6 +63,9 @@ publish skill; use `$release-openclaw-maintainer` before changing release state.
|
||||
Full Release Validation, OpenClaw Release Checks, OpenClaw NPM Release,
|
||||
Plugin NPM Release, Plugin ClawHub Release, mac preflight/validation/publish
|
||||
when stable mac assets are expected.
|
||||
- For stable, verify `OpenClaw Stable Main Closeout` succeeded and its
|
||||
manifest records the matching release tag, current rollback drill, stable
|
||||
soak, and blocking performance evidence.
|
||||
- Summarize only relevant successful/failed jobs; ignore routine skipped
|
||||
optional lanes unless the release body promised them.
|
||||
6. Published package smoke:
|
||||
|
||||
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -12,9 +12,14 @@
|
||||
/.github/workflows/codeql-android-critical-security.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/dependency-guard.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/security-sensitive-guard.yml @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-guard-workflow.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-guard-script.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/security-sensitive-guard-workflow.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/security-sensitive-guard-script.test.ts @openclaw/openclaw-secops
|
||||
/scripts/github/dependency-guard.mjs @openclaw/openclaw-secops
|
||||
/scripts/github/security-sensitive-guard.mjs @openclaw/openclaw-secops
|
||||
/.gitignore @openclaw/openclaw-secops
|
||||
/package-lock.json @openclaw/openclaw-secops
|
||||
/npm-shrinkwrap.json @openclaw/openclaw-secops
|
||||
/extensions/*/package-lock.json @openclaw/openclaw-secops
|
||||
|
||||
2
.github/actions/docker-e2e-plan/action.yml
vendored
2
.github/actions/docker-e2e-plan/action.yml
vendored
@@ -113,7 +113,7 @@ runs:
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package-artifact-name }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
2
.github/actions/setup-node-env/action.yml
vendored
2
.github/actions/setup-node-env/action.yml
vendored
@@ -139,7 +139,7 @@ runs:
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ steps.setup-pnpm.outputs.store-path }}
|
||||
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}
|
||||
|
||||
@@ -92,7 +92,7 @@ runs:
|
||||
- name: Restore pnpm store cache
|
||||
id: pnpm-store-cache
|
||||
if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.path }}
|
||||
key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-${{ hashFiles(inputs.lockfile-path) }}
|
||||
|
||||
8
.github/workflows/auto-response.yml
vendored
8
.github/workflows/auto-response.yml
vendored
@@ -25,24 +25,24 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
persist-credentials: false
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
if: steps.app-token.outcome == 'failure'
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- name: Run Barnacle auto-response
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
|
||||
- name: Restore dist build cache
|
||||
id: dist-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
.artifacts/build-all-cache/
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
|
||||
- name: Save dist build cache
|
||||
if: steps.dist-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
.artifacts/build-all-cache/
|
||||
|
||||
58
.github/workflows/ci.yml
vendored
58
.github/workflows/ci.yml
vendored
@@ -13,6 +13,11 @@ on:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
release_gate:
|
||||
description: Run an exact-SHA maintainer release-gate fallback when PR CI is capacity-stalled.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
@@ -26,6 +31,8 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
run-name: ${{ github.event_name == 'workflow_dispatch' && inputs.release_gate && format('CI release gate {0}', inputs.target_ref) || 'CI' }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.event_name == 'workflow_dispatch' && format('{0}-manual-v1-{1}', github.workflow, github.run_id) || (github.event_name == 'pull_request' && format('{0}-v7-{1}', github.workflow, github.event.pull_request.number) || (github.repository == 'openclaw/openclaw' && format('{0}-v7-{1}', github.workflow, github.ref) || format('{0}-v7-{1}-{2}', github.workflow, github.ref, github.sha))) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.repository == 'openclaw/openclaw' && github.ref == 'refs/heads/main') }}
|
||||
@@ -75,6 +82,23 @@ jobs:
|
||||
run_android_job: ${{ steps.manifest.outputs.run_android_job }}
|
||||
android_matrix: ${{ steps.manifest.outputs.android_matrix }}
|
||||
steps:
|
||||
- name: Validate release-gate dispatch
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.release_gate
|
||||
env:
|
||||
TARGET_REF: ${{ inputs.target_ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! "$TARGET_REF" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "release_gate requires target_ref to be a full commit SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$GITHUB_SHA" != "$TARGET_REF" ]]; then
|
||||
echo "release_gate must run from the branch at target_ref" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
@@ -159,7 +183,7 @@ jobs:
|
||||
OPENCLAW_CI_DOCS_CHANGED: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.docs_scope.outputs.docs_changed }}
|
||||
OPENCLAW_CI_RUN_NODE: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_node || 'false' }}
|
||||
OPENCLAW_CI_RUN_MACOS: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_macos || 'false' }}
|
||||
OPENCLAW_CI_RUN_ANDROID: ${{ github.event_name == 'workflow_dispatch' && inputs.include_android && 'true' || steps.changed_scope.outputs.run_android || 'false' }}
|
||||
OPENCLAW_CI_RUN_ANDROID: ${{ github.event_name == 'workflow_dispatch' && (inputs.release_gate || inputs.include_android) && 'true' || steps.changed_scope.outputs.run_android || 'false' }}
|
||||
OPENCLAW_CI_RUN_WINDOWS: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_windows || 'false' }}
|
||||
OPENCLAW_CI_RUN_NODE_FAST_ONLY: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_only || 'false' }}
|
||||
OPENCLAW_CI_RUN_NODE_FAST_PLUGIN_CONTRACTS: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_plugin_contracts || 'false' }}
|
||||
@@ -598,7 +622,7 @@ jobs:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Restore build-all step cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .artifacts/build-all-cache
|
||||
key: ${{ runner.os }}-build-all-v3-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'npm-shrinkwrap.json', 'packages/plugin-sdk/package.json', 'packages/llm-core/package.json', 'packages/model-catalog-core/package.json', 'packages/memory-host-sdk/package.json', 'scripts/build-all.mjs', 'scripts/write-plugin-sdk-entry-dts.ts', 'scripts/lib/plugin-sdk-entries.mjs', 'tsconfig.json', 'tsconfig.plugin-sdk.dts.json', 'src/plugin-sdk/**', 'packages/llm-core/src/**', 'packages/model-catalog-core/src/**', 'packages/memory-host-sdk/src/**', 'src/types/**', 'src/video-generation/dashscope-compatible.ts', 'src/video-generation/types.ts', 'scripts/copy-export-html-templates.ts', 'scripts/lib/copy-assets.ts', 'src/auto-reply/reply/export-html/**') }}
|
||||
@@ -607,7 +631,7 @@ jobs:
|
||||
|
||||
- name: Restore dist build cache
|
||||
id: dist_build_cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
dist/
|
||||
@@ -630,14 +654,14 @@ jobs:
|
||||
run: tar --posix -cf dist-runtime-build.tar.zst --use-compress-program zstdmt dist dist-runtime
|
||||
|
||||
- name: Upload built runtime artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: dist-runtime-build
|
||||
path: dist-runtime-build.tar.zst
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload bundled plugin asset artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: bundled-plugin-assets
|
||||
path: |
|
||||
@@ -668,7 +692,7 @@ jobs:
|
||||
|
||||
- name: Upload startup memory report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: startup-memory
|
||||
path: .artifacts/startup-memory/
|
||||
@@ -757,7 +781,7 @@ jobs:
|
||||
|
||||
- name: Save dist build cache
|
||||
if: steps.dist_build_cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: |
|
||||
@@ -769,7 +793,7 @@ jobs:
|
||||
|
||||
- name: Upload gateway watch regression artifacts
|
||||
if: always() && needs.preflight.outputs.run_check_additional == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: gateway-watch-regression
|
||||
path: .local/gateway-watch-regression/
|
||||
@@ -1339,7 +1363,7 @@ jobs:
|
||||
|
||||
- name: Upload deadcode reports
|
||||
if: ${{ always() && matrix.task == 'dependencies' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: deadcode-reports
|
||||
path: .artifacts/deadcode
|
||||
@@ -1428,7 +1452,7 @@ jobs:
|
||||
- name: Cache extension package boundary artifacts
|
||||
id: extension-package-boundary-cache
|
||||
if: matrix.group == 'extension-package-boundary'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
dist/plugin-sdk
|
||||
@@ -1696,7 +1720,7 @@ jobs:
|
||||
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
@@ -1965,7 +1989,7 @@ jobs:
|
||||
echo "key=$toolchain_key" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cache SwiftPM
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ~/Library/Caches/org.swift.swiftpm
|
||||
key: ${{ runner.os }}-swiftpm-${{ hashFiles('apps/macos/Package.resolved') }}
|
||||
@@ -1974,7 +1998,7 @@ jobs:
|
||||
|
||||
- name: Cache Swift build directory
|
||||
id: swift-build-cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: apps/macos/.build
|
||||
key: ${{ runner.os }}-swift-build-v2-${{ steps.swift-toolchain.outputs.key }}-${{ hashFiles('apps/macos/Package.swift', 'apps/macos/Package.resolved', 'apps/macos/Sources/**', 'apps/macos/Tests/**', 'apps/shared/OpenClawKit/Package.swift', 'apps/shared/OpenClawKit/Sources/**', 'apps/swabble/Package.swift', 'apps/swabble/Sources/**') }}
|
||||
@@ -2105,7 +2129,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
|
||||
with:
|
||||
distribution: temurin
|
||||
# Keep sdkmanager on the stable JDK path for Linux CI runners.
|
||||
@@ -2117,7 +2141,7 @@ jobs:
|
||||
apps/android/gradle/libs.versions.toml
|
||||
|
||||
- name: Cache Android SDK
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ~/.android-sdk
|
||||
key: ${{ runner.os }}-android-sdk-v1-cmdline-14742923-platform-37.0-build-tools-36.0.0
|
||||
@@ -2204,7 +2228,7 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout timing summary helper
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || needs.preflight.outputs.checkout_revision || github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -2220,7 +2244,7 @@ jobs:
|
||||
cat ci-timings-summary.txt >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload CI timing summary
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ci-timings-summary
|
||||
path: ci-timings-summary.txt
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
locales_json: ${{ steps.plan.outputs.locales_json }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
name: Refresh ${{ matrix.locale }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
submodules: false
|
||||
|
||||
10
.github/workflows/crabbox-hydrate.yml
vendored
10
.github/workflows/crabbox-hydrate.yml
vendored
@@ -45,12 +45,12 @@ jobs:
|
||||
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
@@ -328,12 +328,12 @@ jobs:
|
||||
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
@@ -561,7 +561,7 @@ jobs:
|
||||
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
|
||||
10
.github/workflows/docker-release.yml
vendored
10
.github/workflows/docker-release.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout selected tag
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
browser_digest: ${{ steps.build-browser.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }}
|
||||
fetch-depth: 0
|
||||
@@ -293,7 +293,7 @@ jobs:
|
||||
browser_digest: ${{ steps.build-browser.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }}
|
||||
fetch-depth: 0
|
||||
@@ -500,7 +500,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }}
|
||||
fetch-depth: 0
|
||||
@@ -595,7 +595,7 @@ jobs:
|
||||
packages: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
2
.github/workflows/docs-agent.yml
vendored
2
.github/workflows/docs-agent.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
6
.github/workflows/docs-sync-publish.yml
vendored
6
.github/workflows/docs-sync-publish.yml
vendored
@@ -25,13 +25,13 @@ jobs:
|
||||
|
||||
- name: Checkout source repo
|
||||
if: env.OPENCLAW_DOCS_SYNC_TOKEN != ''
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout ClawHub docs source
|
||||
if: env.OPENCLAW_DOCS_SYNC_TOKEN != ''
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: openclaw/clawhub
|
||||
path: clawhub-source
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
- name: Setup Node
|
||||
if: env.OPENCLAW_DOCS_SYNC_TOKEN != ''
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "24.x"
|
||||
|
||||
|
||||
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Checkout ClawHub docs source
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: openclaw/clawhub
|
||||
path: clawhub-source
|
||||
|
||||
3
.github/workflows/duplicate-after-merge.yml
vendored
3
.github/workflows/duplicate-after-merge.yml
vendored
@@ -35,8 +35,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Close confirmed duplicates
|
||||
env:
|
||||
APPLY: ${{ inputs.apply }}
|
||||
|
||||
78
.github/workflows/full-release-validation.yml
vendored
78
.github/workflows/full-release-validation.yml
vendored
@@ -36,7 +36,7 @@ on:
|
||||
- stable
|
||||
- full
|
||||
run_release_soak:
|
||||
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for release_profile=full
|
||||
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for stable and full release profiles
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
sha: ${{ steps.resolve.outputs.sha }}
|
||||
steps:
|
||||
- name: Checkout trusted workflow helper
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
path: workflow
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
PACKAGE_ACCEPTANCE_PACKAGE_SPEC: ${{ inputs.package_acceptance_package_spec }}
|
||||
CODEX_PLUGIN_SPEC: ${{ inputs.codex_plugin_spec }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'full' }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'stable' || inputs.release_profile == 'full' }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
CROSS_OS_SUITE_FILTER: ${{ inputs.cross_os_suite_filter }}
|
||||
@@ -234,7 +234,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout target SHA
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -537,7 +537,7 @@ jobs:
|
||||
PROVIDER: ${{ inputs.provider }}
|
||||
MODE: ${{ inputs.mode }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'full' }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'stable' || inputs.release_profile == 'full' }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
CROSS_OS_SUITE_FILTER: ${{ inputs.cross_os_suite_filter }}
|
||||
@@ -780,7 +780,7 @@ jobs:
|
||||
source_sha: ${{ steps.package.outputs.source_sha }}
|
||||
steps:
|
||||
- name: Checkout trusted workflow ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ github.ref_name }}
|
||||
@@ -826,7 +826,7 @@ jobs:
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload release package artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-package-under-test
|
||||
path: |
|
||||
@@ -1017,9 +1017,12 @@ jobs:
|
||||
echo "- Repeat: \`3\`"
|
||||
echo "- Deep profile: \`false\`"
|
||||
echo "- Live OpenAI candidate: \`false\`"
|
||||
echo "- Release impact: advisory"
|
||||
echo "- Release impact: blocking"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
dispatch_id="full-release-validation-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
|
||||
dispatch_run_name="OpenClaw Performance ${dispatch_id}"
|
||||
|
||||
dispatch_output="$(gh_with_retry workflow run openclaw-performance.yml \
|
||||
--ref "$CHILD_WORKFLOW_REF" \
|
||||
-f target_ref="$TARGET_SHA" \
|
||||
@@ -1027,17 +1030,27 @@ jobs:
|
||||
-f repeat=3 \
|
||||
-f deep_profile=false \
|
||||
-f live_openai_candidate=false \
|
||||
-f fail_on_regression=false)"
|
||||
-f fail_on_regression=true \
|
||||
-f dispatch_id="$dispatch_id")"
|
||||
printf '%s\n' "$dispatch_output"
|
||||
run_id="$(
|
||||
printf '%s\n' "$dispatch_output" |
|
||||
sed -nE 's#.*actions/runs/([0-9]+).*#\1#p' |
|
||||
tail -n 1
|
||||
)"
|
||||
|
||||
run_id=""
|
||||
for _ in $(seq 1 60); do
|
||||
run_id="$(
|
||||
DISPATCH_RUN_NAME="$dispatch_run_name" gh_with_retry api -X GET "repos/${GITHUB_REPOSITORY}/actions/workflows/openclaw-performance.yml/runs" \
|
||||
-F event=workflow_dispatch \
|
||||
-F per_page=100 \
|
||||
--jq '.workflow_runs | map(select(.display_title == env.DISPATCH_RUN_NAME)) | sort_by(.created_at) | reverse | .[0].id // empty'
|
||||
)"
|
||||
if [[ -n "$run_id" ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
if [[ -z "$run_id" ]]; then
|
||||
echo "::warning::gh workflow run openclaw-performance.yml did not return an Actions run URL; refusing to guess from recent workflow_dispatch runs."
|
||||
exit 0
|
||||
echo "::error::Could not find dispatched run for ${dispatch_run_name}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Dispatched openclaw-performance.yml: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
|
||||
@@ -1072,8 +1085,9 @@ jobs:
|
||||
echo "url=${url}" >> "$GITHUB_OUTPUT"
|
||||
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$conclusion" != "success" ]]; then
|
||||
echo "::warning::OpenClaw Performance is advisory and ended with ${conclusion}: ${url}"
|
||||
echo "::error::OpenClaw Performance ended with ${conclusion}: ${url}"
|
||||
gh_with_retry run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
summary:
|
||||
@@ -1364,6 +1378,7 @@ jobs:
|
||||
normal_ci_required=0
|
||||
plugin_prerelease_required=0
|
||||
release_checks_required=0
|
||||
performance_required=0
|
||||
if [[ "$RERUN_GROUP" == "all" && "$DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT" != "success" ]]; then
|
||||
echo "::error::Docker runtime-assets preflight ended with ${DOCKER_RUNTIME_ASSETS_PREFLIGHT_RESULT}."
|
||||
failed=1
|
||||
@@ -1371,6 +1386,7 @@ jobs:
|
||||
normal_ci_required=1
|
||||
plugin_prerelease_required=1
|
||||
release_checks_required=1
|
||||
performance_required=1
|
||||
else
|
||||
case "$RERUN_GROUP" in
|
||||
ci)
|
||||
@@ -1382,6 +1398,9 @@ jobs:
|
||||
release-checks|install-smoke|cross-os|live-e2e|package|qa|qa-parity|qa-live)
|
||||
release_checks_required=1
|
||||
;;
|
||||
performance)
|
||||
performance_required=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -1415,6 +1434,12 @@ jobs:
|
||||
check_child "npm_telegram" "$NPM_TELEGRAM_RUN_ID" 1 || failed=1
|
||||
fi
|
||||
|
||||
if [[ "$PERFORMANCE_RESULT" == "skipped" && -z "${PERFORMANCE_RUN_ID// }" ]]; then
|
||||
check_child "product_performance" "" "$performance_required" || failed=1
|
||||
else
|
||||
check_child "product_performance" "$PERFORMANCE_RUN_ID" "$performance_required" || failed=1
|
||||
fi
|
||||
|
||||
summarize_child_timing "normal_ci" "$NORMAL_CI_RUN_ID"
|
||||
summarize_child_timing "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID"
|
||||
summarize_child_timing "release_checks" "$RELEASE_CHECKS_RUN_ID"
|
||||
@@ -1426,6 +1451,7 @@ jobs:
|
||||
summarize_failed_child "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID"
|
||||
summarize_failed_child "release_checks" "$RELEASE_CHECKS_RUN_ID"
|
||||
summarize_failed_child "npm_telegram" "$NPM_TELEGRAM_RUN_ID"
|
||||
summarize_failed_child "product_performance" "$PERFORMANCE_RUN_ID"
|
||||
fi
|
||||
|
||||
exit "$failed"
|
||||
@@ -1512,12 +1538,13 @@ jobs:
|
||||
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'full' }}
|
||||
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'stable' || inputs.release_profile == 'full' }}
|
||||
NORMAL_CI_RUN_ID: ${{ needs.normal_ci.outputs.run_id }}
|
||||
PLUGIN_PRERELEASE_RUN_ID: ${{ needs.plugin_prerelease.outputs.run_id }}
|
||||
RELEASE_CHECKS_RUN_ID: ${{ needs.release_checks.outputs.run_id }}
|
||||
NPM_TELEGRAM_RUN_ID: ${{ needs.npm_telegram.outputs.run_id }}
|
||||
PERFORMANCE_RUN_ID: ${{ needs.performance.outputs.run_id }}
|
||||
PERFORMANCE_CONCLUSION: ${{ needs.performance.outputs.conclusion }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
manifest_dir="${RUNNER_TEMP}/full-release-validation"
|
||||
@@ -1537,8 +1564,9 @@ jobs:
|
||||
--arg releaseChecksRunId "$RELEASE_CHECKS_RUN_ID" \
|
||||
--arg npmTelegramRunId "$NPM_TELEGRAM_RUN_ID" \
|
||||
--arg performanceRunId "$PERFORMANCE_RUN_ID" \
|
||||
--arg performanceConclusion "$PERFORMANCE_CONCLUSION" \
|
||||
'{
|
||||
version: 1,
|
||||
version: 2,
|
||||
workflowName: $workflowName,
|
||||
runId: $runId,
|
||||
runAttempt: $runAttempt,
|
||||
@@ -1548,18 +1576,26 @@ jobs:
|
||||
releaseProfile: $releaseProfile,
|
||||
rerunGroup: $rerunGroup,
|
||||
runReleaseSoak: $runReleaseSoak,
|
||||
controls: {
|
||||
stableSoakRequired: ($releaseProfile == "stable" or $releaseProfile == "full"),
|
||||
performanceBlocking: true
|
||||
},
|
||||
childRuns: {
|
||||
normalCi: $normalCiRunId,
|
||||
pluginPrerelease: $pluginPrereleaseRunId,
|
||||
releaseChecks: $releaseChecksRunId,
|
||||
npmTelegram: $npmTelegramRunId,
|
||||
productPerformance: $performanceRunId
|
||||
productPerformance: {
|
||||
runId: $performanceRunId,
|
||||
conclusion: $performanceConclusion,
|
||||
blocking: true
|
||||
}
|
||||
}
|
||||
}' > "${manifest_dir}/full-release-validation-manifest.json"
|
||||
|
||||
- name: Upload release validation manifest
|
||||
if: ${{ success() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: full-release-validation-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/full-release-validation
|
||||
|
||||
16
.github/workflows/install-smoke.yml
vendored
16
.github/workflows/install-smoke.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
dockerfile_image: ${{ steps.manifest.outputs.dockerfile_image }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -217,7 +217,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -289,7 +289,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -305,7 +305,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -411,7 +411,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -499,7 +499,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
@@ -538,7 +538,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
2
.github/workflows/ios-periphery-comment.yml
vendored
2
.github/workflows/ios-periphery-comment.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
github.event.workflow_run.name == 'iOS Periphery Dead Code'
|
||||
steps:
|
||||
- name: Upsert Periphery PR comment
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require("node:fs");
|
||||
|
||||
6
.github/workflows/ios-periphery.yml
vendored
6
.github/workflows/ios-periphery.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect changed paths
|
||||
id: scope
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
if (context.eventName === "workflow_dispatch") {
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
@@ -216,7 +216,7 @@ jobs:
|
||||
|
||||
- name: Upload Periphery report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ios-periphery-dead-code-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/ios-periphery
|
||||
|
||||
28
.github/workflows/labeler.yml
vendored
28
.github/workflows/labeler.yml
vendored
@@ -32,25 +32,25 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
if: steps.app-token.outcome == 'failure'
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- uses: actions/labeler@v6
|
||||
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
sync-labels: true
|
||||
- name: Apply PR size label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
labels: [targetSizeLabel],
|
||||
});
|
||||
- name: Apply maintainer or trusted-contributor label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -210,7 +210,7 @@ jobs:
|
||||
// });
|
||||
// }
|
||||
- name: Apply beta-blocker title label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -263,7 +263,7 @@ jobs:
|
||||
});
|
||||
}
|
||||
- name: Apply too-many-prs label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -466,20 +466,20 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
if: steps.app-token.outcome == 'failure'
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- name: Backfill PR labels
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -765,20 +765,20 @@ jobs:
|
||||
issues: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
if: steps.app-token.outcome == 'failure'
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- name: Apply maintainer or trusted-contributor label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
@@ -849,7 +849,7 @@ jobs:
|
||||
// });
|
||||
// }
|
||||
- name: Apply beta-blocker title label
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
script: |
|
||||
|
||||
@@ -26,8 +26,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
|
||||
2
.github/workflows/macos-release.yml
vendored
2
.github/workflows/macos-release.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout selected tag
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
MAINTAINER_COMMAND_REACTIONS: ${{ vars.MAINTAINER_COMMAND_REACTIONS || '/autoclose,/clawsweeper autoclose,/clawsweeper automerge,/merge,/land,/landpr' }}
|
||||
steps:
|
||||
- name: React to maintainer slash command
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const comment = context.payload.comment;
|
||||
|
||||
8
.github/workflows/mantis-discord-smoke.yml
vendored
8
.github/workflows/mantis-discord-smoke.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const allowed = new Set(["admin", "maintain", "write"]);
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ inputs.ref }}
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
|
||||
- name: Upload Mantis artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-discord-smoke-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: .artifacts/qa-e2e/mantis/
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const allowed = new Set(["admin", "maintain", "write"]);
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
steps:
|
||||
- name: Resolve refs and target PR
|
||||
id: resolve
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const defaultBaseline = "0bf06e953fdda290799fc9fb9244a8f67fdae593";
|
||||
@@ -179,7 +179,7 @@ jobs:
|
||||
candidate_revision: ${{ steps.validate.outputs.candidate_revision }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -245,7 +245,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -260,7 +260,7 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Setup Go for Crabbox CLI
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
with:
|
||||
go-version: "1.26.x"
|
||||
cache: false
|
||||
@@ -535,7 +535,7 @@ jobs:
|
||||
- name: Upload Mantis status reaction artifacts
|
||||
id: upload_artifact
|
||||
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-discord-status-reactions-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_mantis.outputs.output_dir }}
|
||||
@@ -545,7 +545,7 @@ jobs:
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
@@ -590,7 +590,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Remove workflow eyes reaction
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const allowed = new Set(["admin", "maintain", "write"]);
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
steps:
|
||||
- name: Resolve refs and target PR
|
||||
id: resolve
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const defaultBaseline = "synthetic-reverted-thread-filepath-fix";
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
candidate_revision: ${{ steps.validate.outputs.candidate_revision }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
output_dir: ${{ steps.run_mantis.outputs.output_dir }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -250,7 +250,7 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Setup Go for Crabbox CLI
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
with:
|
||||
go-version: "1.26.x"
|
||||
cache: false
|
||||
@@ -543,7 +543,7 @@ jobs:
|
||||
- name: Upload Mantis thread attachment artifacts
|
||||
id: upload_artifact
|
||||
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-discord-thread-attachment-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_mantis.outputs.output_dir }}
|
||||
@@ -553,7 +553,7 @@ jobs:
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
@@ -612,7 +612,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Remove workflow eyes reaction
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
14
.github/workflows/mantis-slack-desktop-smoke.yml
vendored
14
.github/workflows/mantis-slack-desktop-smoke.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const allowed = new Set(["admin", "maintain", "write"]);
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
candidate_revision: ${{ steps.validate.outputs.candidate_revision }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -165,7 +165,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Cache Mantis candidate pnpm store
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
~/.local/share/pnpm/store
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
mantis-slack-pnpm-${{ runner.os }}-${{ env.NODE_VERSION }}-
|
||||
|
||||
- name: Setup Go for Crabbox CLI
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
with:
|
||||
go-version: "1.26.x"
|
||||
cache: false
|
||||
@@ -453,7 +453,7 @@ jobs:
|
||||
- name: Upload Mantis Slack desktop artifacts
|
||||
id: upload_artifact
|
||||
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-slack-desktop-smoke-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_mantis.outputs.output_dir }}
|
||||
@@ -463,7 +463,7 @@ jobs:
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
if: ${{ always() && inputs.pr_number != '' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
if (context.eventName === "pull_request_target") {
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
steps:
|
||||
- name: Resolve refs and target PR
|
||||
id: resolve
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const eventName = context.eventName;
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
candidate_trust: ${{ steps.validate.outputs.candidate_trust }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: main
|
||||
persist-credentials: false
|
||||
@@ -350,7 +350,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -362,7 +362,7 @@ jobs:
|
||||
install-bun: "true"
|
||||
|
||||
- name: Setup Go for Crabbox CLI
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
with:
|
||||
go-version: "1.26.x"
|
||||
cache: false
|
||||
@@ -551,7 +551,7 @@ jobs:
|
||||
- name: Upload Mantis Telegram desktop artifacts
|
||||
id: upload_artifact
|
||||
if: ${{ always() && steps.inspect.outputs.output_dir != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-telegram-desktop-proof-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.inspect.outputs.output_dir }}
|
||||
@@ -561,7 +561,7 @@ jobs:
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
@@ -620,7 +620,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -663,7 +663,7 @@ jobs:
|
||||
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
@@ -709,7 +709,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Remove workflow eyes reaction
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
18
.github/workflows/mantis-telegram-live.yml
vendored
18
.github/workflows/mantis-telegram-live.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const allowed = new Set(["admin", "maintain", "write"]);
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
steps:
|
||||
- name: Resolve refs and target PR
|
||||
id: resolve
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const eventName = context.eventName;
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
candidate_revision: ${{ steps.validate.outputs.candidate_revision }}
|
||||
steps:
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -312,7 +312,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Checkout harness ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -327,7 +327,7 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Cache Mantis candidate pnpm store
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
~/.local/share/pnpm/store
|
||||
@@ -337,7 +337,7 @@ jobs:
|
||||
mantis-telegram-pnpm-${{ runner.os }}-${{ env.NODE_VERSION }}-
|
||||
|
||||
- name: Setup Go for Crabbox CLI
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
with:
|
||||
go-version: "1.26.x"
|
||||
cache: false
|
||||
@@ -501,7 +501,7 @@ jobs:
|
||||
- name: Upload Mantis Telegram artifacts
|
||||
id: upload_artifact
|
||||
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mantis-telegram-live-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_mantis.outputs.output_dir }}
|
||||
@@ -511,7 +511,7 @@ jobs:
|
||||
- name: Create Mantis GitHub App token
|
||||
id: mantis_app_token
|
||||
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
with:
|
||||
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
||||
@@ -572,7 +572,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Remove workflow eyes reaction
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
8
.github/workflows/npm-telegram-beta-e2e.yml
vendored
8
.github/workflows/npm-telegram-beta-e2e.yml
vendored
@@ -120,7 +120,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout dispatch ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.harness_ref || github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -190,14 +190,14 @@ jobs:
|
||||
|
||||
- name: Download package-under-test artifact
|
||||
if: inputs.package_artifact_name != '' && inputs.package_artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name }}
|
||||
path: .artifacts/telegram-package-under-test
|
||||
|
||||
- name: Download package-under-test artifact from release run
|
||||
if: inputs.package_artifact_name != '' && inputs.package_artifact_run_id != ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name }}
|
||||
path: .artifacts/telegram-package-under-test
|
||||
@@ -268,7 +268,7 @@ jobs:
|
||||
|
||||
- name: Upload npm Telegram E2E artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: npm-telegram-beta-e2e-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: .artifacts/qa-e2e/
|
||||
|
||||
@@ -332,7 +332,7 @@ jobs:
|
||||
esac
|
||||
|
||||
- name: Checkout workflow repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: ${{ env.OPENCLAW_REPOSITORY }}
|
||||
ref: ${{ steps.workflow_ref.outputs.value }}
|
||||
@@ -342,7 +342,7 @@ jobs:
|
||||
|
||||
- name: Checkout public source ref
|
||||
if: inputs.candidate_artifact_name == ''
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: ${{ env.OPENCLAW_REPOSITORY }}
|
||||
ref: ${{ inputs.ref }}
|
||||
@@ -352,7 +352,7 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
@@ -379,14 +379,14 @@ jobs:
|
||||
|
||||
- name: Download current-run candidate artifact
|
||||
if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.candidate_artifact_name }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
|
||||
|
||||
- name: Download previous-run candidate artifact
|
||||
if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id != ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.candidate_artifact_name }}
|
||||
run-id: ${{ inputs.candidate_artifact_run_id }}
|
||||
@@ -510,7 +510,7 @@ jobs:
|
||||
NODE
|
||||
|
||||
- name: Upload candidate artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package/${{ steps.candidate_metadata.outputs.file_name }}
|
||||
@@ -518,7 +518,7 @@ jobs:
|
||||
|
||||
- name: Upload baseline artifact
|
||||
if: ${{ inputs.mode != 'fresh' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/baseline/${{ steps.baseline_metadata.outputs.file_name }}
|
||||
@@ -558,7 +558,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout workflow repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: ${{ env.OPENCLAW_REPOSITORY }}
|
||||
ref: ${{ needs.prepare.outputs.workflow_ref }}
|
||||
@@ -567,7 +567,7 @@ jobs:
|
||||
persist-credentials: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
@@ -582,14 +582,14 @@ jobs:
|
||||
- name: Download candidate artifact
|
||||
id: download_candidate
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate
|
||||
|
||||
- name: Retry candidate artifact download
|
||||
if: ${{ steps.download_candidate.outcome == 'failure' }}
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate
|
||||
@@ -598,14 +598,14 @@ jobs:
|
||||
if: ${{ matrix.suite == 'packaged-upgrade' }}
|
||||
id: download_baseline
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline
|
||||
|
||||
- name: Retry baseline artifact download
|
||||
if: ${{ matrix.suite == 'packaged-upgrade' && steps.download_baseline.outcome == 'failure' }}
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline
|
||||
@@ -684,7 +684,7 @@ jobs:
|
||||
|
||||
- name: Upload release-check artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-cross-os-release-checks-${{ matrix.artifact_name }}-${{ matrix.suite }}-${{ github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/${{ matrix.artifact_name }}-${{ matrix.suite }}
|
||||
|
||||
@@ -329,7 +329,7 @@ jobs:
|
||||
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
|
||||
steps:
|
||||
- name: Checkout workflow repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -493,7 +493,7 @@ jobs:
|
||||
live_models_omitted_json: ${{ steps.plan.outputs.live_models_omitted_json }}
|
||||
steps:
|
||||
- name: Checkout trusted release harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.sha }}
|
||||
@@ -523,7 +523,7 @@ jobs:
|
||||
OPENCLAW_LIVE_TEST: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
@@ -570,7 +570,7 @@ jobs:
|
||||
OPENCLAW_VITEST_MAX_WORKERS: "2"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
@@ -614,7 +614,7 @@ jobs:
|
||||
OPENCLAW_VITEST_MAX_WORKERS: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
@@ -740,7 +740,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
@@ -748,7 +748,7 @@ jobs:
|
||||
|
||||
- name: Checkout trusted release harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.sha }}
|
||||
@@ -801,7 +801,7 @@ jobs:
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
@@ -894,7 +894,7 @@ jobs:
|
||||
|
||||
- name: Upload Docker E2E chunk artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: docker-e2e-${{ matrix.chunk_id }}
|
||||
path: .artifacts/docker-tests/
|
||||
@@ -910,7 +910,7 @@ jobs:
|
||||
groups_json: ${{ steps.groups.outputs.groups_json }}
|
||||
steps:
|
||||
- name: Checkout trusted release harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.sha }}
|
||||
@@ -1002,14 +1002,14 @@ jobs:
|
||||
DOCKER_E2E_LANES: ${{ matrix.group.docker_lanes }}
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted release harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.sha }}
|
||||
@@ -1062,7 +1062,7 @@ jobs:
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
@@ -1154,7 +1154,7 @@ jobs:
|
||||
|
||||
- name: Upload targeted Docker E2E artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: docker-e2e-${{ steps.plan.outputs.artifact_suffix }}
|
||||
path: .artifacts/docker-tests/
|
||||
@@ -1179,13 +1179,13 @@ jobs:
|
||||
OPENCLAW_SKIP_DOCKER_BUILD: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted release harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -1229,7 +1229,7 @@ jobs:
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
@@ -1281,7 +1281,7 @@ jobs:
|
||||
|
||||
- name: Upload Open WebUI Docker E2E artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: docker-e2e-openwebui
|
||||
path: .artifacts/docker-tests/
|
||||
@@ -1312,13 +1312,13 @@ jobs:
|
||||
OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }}
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted release harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -1364,14 +1364,14 @@ jobs:
|
||||
|
||||
- name: Download current-run OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name != '' && inputs.package_artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Download previous-run OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_run_id != ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
@@ -1421,7 +1421,7 @@ jobs:
|
||||
|
||||
- name: Upload OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1' && (inputs.package_artifact_name == '' || inputs.package_artifact_run_id != '')
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
|
||||
path: .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
@@ -1581,7 +1581,7 @@ jobs:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
@@ -1693,14 +1693,14 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted live Docker harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -1815,13 +1815,13 @@ jobs:
|
||||
OPENCLAW_VITEST_MAX_WORKERS: "2"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted live Docker harness
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -2187,14 +2187,14 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-anthropic' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-anthropic-')) || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-opencode-go' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-opencode-go-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted live shard harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-anthropic' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-anthropic-')) || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-opencode-go' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-opencode-go-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -2409,14 +2409,14 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'live-gateway-advisory-docker' && startsWith(matrix.suite_id, 'live-gateway-advisory-docker-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted live shard harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'live-gateway-advisory-docker' && startsWith(matrix.suite_id, 'live-gateway-advisory-docker-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -2623,14 +2623,14 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-extensions-media-video' && startsWith(matrix.suite_id, 'native-live-extensions-media-video-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout trusted live shard harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-extensions-media-video' && startsWith(matrix.suite_id, 'native-live-extensions-media-video-')))
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
28
.github/workflows/openclaw-npm-release.yml
vendored
28
.github/workflows/openclaw-npm-release.yml
vendored
@@ -87,7 +87,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
@@ -354,7 +354,7 @@ jobs:
|
||||
node --import tsx scripts/openclaw-npm-prepublish-verify.ts "$TARBALL_PATH" "$PACKAGE_VERSION"
|
||||
|
||||
- name: Upload dependency release evidence
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-release-dependency-evidence-${{ inputs.tag }}
|
||||
path: ${{ steps.dependency_evidence.outputs.dir }}
|
||||
@@ -362,14 +362,14 @@ jobs:
|
||||
|
||||
- name: Upload dependency release evidence tag alias
|
||||
if: ${{ steps.packed_tarball.outputs.release_tag != inputs.tag }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-release-dependency-evidence-${{ steps.packed_tarball.outputs.release_tag }}
|
||||
path: ${{ steps.dependency_evidence.outputs.dir }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload prepared npm publish bundle
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-npm-preflight-${{ inputs.tag }}
|
||||
path: ${{ steps.packed_tarball.outputs.dir }}
|
||||
@@ -377,7 +377,7 @@ jobs:
|
||||
|
||||
- name: Upload prepared npm publish bundle tag alias
|
||||
if: ${{ steps.packed_tarball.outputs.release_tag != inputs.tag }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-npm-preflight-${{ steps.packed_tarball.outputs.release_tag }}
|
||||
path: ${{ steps.packed_tarball.outputs.dir }}
|
||||
@@ -391,7 +391,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -492,7 +492,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
@@ -611,7 +611,7 @@ jobs:
|
||||
|
||||
- name: Download full release validation manifest
|
||||
if: ${{ inputs.full_release_validation_run_id != '' }}
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: full-release-validation-${{ inputs.full_release_validation_run_id }}
|
||||
path: full-release-validation
|
||||
@@ -677,6 +677,8 @@ jobs:
|
||||
|
||||
- name: Verify full release validation target
|
||||
if: ${{ inputs.full_release_validation_run_id != '' }}
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXPECTED_RELEASE_SHA="$(git rev-parse HEAD)"
|
||||
@@ -689,6 +691,8 @@ jobs:
|
||||
WORKFLOW_NAME="$(jq -r '.workflowName // ""' "$MANIFEST_FILE")"
|
||||
TARGET_SHA="$(jq -r '.targetSha // ""' "$MANIFEST_FILE")"
|
||||
RERUN_GROUP="$(jq -r '.rerunGroup // ""' "$MANIFEST_FILE")"
|
||||
RUN_RELEASE_SOAK="$(jq -r '.runReleaseSoak // ""' "$MANIFEST_FILE")"
|
||||
PERFORMANCE_BLOCKING="$(jq -r '.controls.performanceBlocking // false' "$MANIFEST_FILE")"
|
||||
if [[ "$WORKFLOW_NAME" != "Full Release Validation" ]]; then
|
||||
echo "Full release validation manifest workflow mismatch: $WORKFLOW_NAME" >&2
|
||||
exit 1
|
||||
@@ -701,6 +705,14 @@ jobs:
|
||||
echo "Full release validation must run rerun_group=all before npm publish; got $RERUN_GROUP" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$PERFORMANCE_BLOCKING" != "true" ]]; then
|
||||
echo "Full release validation manifest does not record blocking product performance evidence." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$RELEASE_TAG" != *"-alpha."* && "$RELEASE_TAG" != *"-beta."* && "$RUN_RELEASE_SOAK" != "true" ]]; then
|
||||
echo "Stable releases require Full Release Validation with runReleaseSoak=true." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Resolve publish tarball
|
||||
id: publish_tarball
|
||||
|
||||
13
.github/workflows/openclaw-performance.yml
vendored
13
.github/workflows/openclaw-performance.yml
vendored
@@ -1,5 +1,7 @@
|
||||
name: OpenClaw Performance
|
||||
|
||||
run-name: ${{ inputs.dispatch_id != '' && format('OpenClaw Performance {0}', inputs.dispatch_id) || 'OpenClaw Performance' }}
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "11 5 * * *"
|
||||
@@ -45,6 +47,11 @@ on:
|
||||
required: false
|
||||
default: b63b6f9e20efb23641df00487e982230d81a90ac
|
||||
type: string
|
||||
dispatch_id:
|
||||
description: Optional parent workflow dispatch identifier
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -145,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Checkout OpenClaw
|
||||
if: steps.lane.outputs.run == 'true'
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.target_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
@@ -153,7 +160,7 @@ jobs:
|
||||
|
||||
- name: Checkout performance workflow helpers
|
||||
if: steps.lane.outputs.run == 'true'
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: .artifacts/performance-workflow
|
||||
@@ -556,7 +563,7 @@ jobs:
|
||||
|
||||
- name: Upload Kova artifacts
|
||||
if: ${{ always() && steps.lane.outputs.run == 'true' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-performance-${{ matrix.lane }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
72
.github/workflows/openclaw-release-checks.yml
vendored
72
.github/workflows/openclaw-release-checks.yml
vendored
@@ -40,7 +40,7 @@ on:
|
||||
- stable
|
||||
- full
|
||||
run_release_soak:
|
||||
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for release_profile=full
|
||||
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for release_profile=stable and full
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout trusted workflow helper
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref_name }}
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
|
||||
- name: Checkout selected ref for reachability fallback
|
||||
if: steps.fast_ref.outputs.fallback == 'true'
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ inputs.ref }}
|
||||
@@ -330,7 +330,7 @@ jobs:
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
if [[ "$release_profile" == "full" ]]; then
|
||||
if [[ "$release_profile" == "stable" || "$release_profile" == "full" ]]; then
|
||||
run_release_soak=true
|
||||
fi
|
||||
codex_plugin_spec="$RELEASE_CODEX_PLUGIN_SPEC_INPUT"
|
||||
@@ -507,7 +507,7 @@ jobs:
|
||||
source_sha: ${{ steps.package.outputs.source_sha }}
|
||||
steps:
|
||||
- name: Checkout trusted workflow ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ github.ref_name }}
|
||||
@@ -559,7 +559,7 @@ jobs:
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload release package artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-package-under-test
|
||||
path: |
|
||||
@@ -798,7 +798,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -849,7 +849,7 @@ jobs:
|
||||
- name: Upload parity lane artifacts
|
||||
id: upload_parity_lane_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-parity-${{ matrix.lane }}-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -895,7 +895,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-parity-${{ matrix.lane }}-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_lab_parity_lane_release_checks-${{ matrix.lane }}.env
|
||||
@@ -917,7 +917,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -930,7 +930,7 @@ jobs:
|
||||
install-bun: "true"
|
||||
|
||||
- name: Download parity lane artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
pattern: release-qa-parity-*-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -955,7 +955,7 @@ jobs:
|
||||
- name: Upload parity artifacts
|
||||
id: upload_parity_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -999,7 +999,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-parity-report-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_lab_parity_report_release_checks.env
|
||||
@@ -1028,7 +1028,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1127,7 +1127,7 @@ jobs:
|
||||
- name: Upload runtime parity artifacts
|
||||
id: upload_runtime_parity_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-runtime-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1171,7 +1171,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-runtime-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_lab_runtime_parity_release_checks.env
|
||||
@@ -1192,7 +1192,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1205,7 +1205,7 @@ jobs:
|
||||
install-bun: "true"
|
||||
|
||||
- name: Download runtime parity status
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: release-check-status-qa-runtime-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/
|
||||
@@ -1226,7 +1226,7 @@ jobs:
|
||||
|
||||
- name: Download runtime parity artifacts
|
||||
if: steps.verify_runtime_parity_status.outputs.ready == 'true'
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: release-qa-runtime-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1243,7 +1243,7 @@ jobs:
|
||||
|
||||
- name: Upload runtime tool coverage artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-runtime-tool-coverage-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/runtime-parity-standard-report/
|
||||
@@ -1266,7 +1266,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1323,7 +1323,7 @@ jobs:
|
||||
- name: Upload Matrix QA artifacts
|
||||
id: upload_matrix_qa_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-live-matrix-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1367,7 +1367,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-live-matrix-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_live_matrix_release_checks.env
|
||||
@@ -1390,7 +1390,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1463,7 +1463,7 @@ jobs:
|
||||
- name: Upload Telegram QA artifacts
|
||||
id: upload_telegram_qa_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-live-telegram-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1507,7 +1507,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-live-telegram-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_live_telegram_release_checks.env
|
||||
@@ -1530,7 +1530,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1603,7 +1603,7 @@ jobs:
|
||||
- name: Upload Discord QA artifacts
|
||||
id: upload_discord_qa_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-live-discord-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1647,7 +1647,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-live-discord-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_live_discord_release_checks.env
|
||||
@@ -1673,7 +1673,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1746,7 +1746,7 @@ jobs:
|
||||
- name: Upload WhatsApp QA artifacts
|
||||
id: upload_whatsapp_qa_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-live-whatsapp-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1790,7 +1790,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-live-whatsapp-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_live_whatsapp_release_checks.env
|
||||
@@ -1813,7 +1813,7 @@ jobs:
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
@@ -1886,7 +1886,7 @@ jobs:
|
||||
- name: Upload Slack QA artifacts
|
||||
id: upload_slack_qa_artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-live-slack-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -1930,7 +1930,7 @@ jobs:
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-live-slack-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_live_slack_release_checks.env
|
||||
@@ -1964,7 +1964,7 @@ jobs:
|
||||
- name: Download advisory status artifacts
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
pattern: release-check-status-*
|
||||
path: .artifacts/release-check-status
|
||||
|
||||
105
.github/workflows/openclaw-release-publish.yml
vendored
105
.github/workflows/openclaw-release-publish.yml
vendored
@@ -290,7 +290,7 @@ jobs:
|
||||
|
||||
- name: Download full release validation manifest
|
||||
if: ${{ inputs.publish_openclaw_npm }}
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: full-release-validation-${{ inputs.full_release_validation_run_id }}
|
||||
path: ${{ runner.temp }}/full-release-validation-manifest
|
||||
@@ -299,7 +299,7 @@ jobs:
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Checkout release tag
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
fetch-depth: 0
|
||||
@@ -359,6 +359,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
FULL_RELEASE_VALIDATION_RUN_ID: ${{ inputs.full_release_validation_run_id }}
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
EXPECTED_SHA: ${{ steps.ref.outputs.sha }}
|
||||
EXPECTED_RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
EXPECTED_WORKFLOW_BRANCH: ${{ github.ref_name }}
|
||||
@@ -377,6 +378,8 @@ jobs:
|
||||
target_sha="$(jq -r '.targetSha // ""' "$manifest")"
|
||||
release_profile="$(jq -r '.releaseProfile // ""' "$manifest")"
|
||||
rerun_group="$(jq -r '.rerunGroup // ""' "$manifest")"
|
||||
run_release_soak="$(jq -r '.runReleaseSoak // ""' "$manifest")"
|
||||
performance_blocking="$(jq -r '.controls.performanceBlocking // false' "$manifest")"
|
||||
if [[ "$workflow_name" != "Full Release Validation" ]]; then
|
||||
echo "Full release validation manifest workflow mismatch: $workflow_name" >&2
|
||||
exit 1
|
||||
@@ -393,6 +396,14 @@ jobs:
|
||||
echo "Full release validation must run rerun_group=all before npm publish; got $rerun_group" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$performance_blocking" != "true" ]]; then
|
||||
echo "Full release validation manifest does not record blocking product performance evidence." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$RELEASE_TAG" != *"-alpha."* && "$RELEASE_TAG" != *"-beta."* && "$run_release_soak" != "true" ]]; then
|
||||
echo "Stable releases require Full Release Validation with runReleaseSoak=true." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "release_profile=$release_profile" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate release tag is reachable from a trusted release branch
|
||||
@@ -455,12 +466,22 @@ jobs:
|
||||
environment: npm-release
|
||||
steps:
|
||||
- name: Checkout release SHA
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_release_target.outputs.sha }}
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download full release validation manifest
|
||||
if: ${{ inputs.publish_openclaw_npm }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: full-release-validation-${{ inputs.full_release_validation_run_id }}
|
||||
path: ${{ runner.temp }}/full-release-validation-manifest
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.full_release_validation_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
@@ -484,6 +505,7 @@ jobs:
|
||||
WINDOWS_NODE_TAG: ${{ inputs.windows_node_tag }}
|
||||
WINDOWS_NODE_INSTALLER_DIGESTS: ${{ needs.resolve_release_target.outputs.windows_node_installer_digests }}
|
||||
POSTPUBLISH_EVIDENCE_DIR: ${{ runner.temp }}/openclaw-release-postpublish-evidence
|
||||
FULL_RELEASE_VALIDATION_MANIFEST_DIR: ${{ runner.temp }}/full-release-validation-manifest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -1060,13 +1082,75 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${download_dir}" && zip -qr "${asset_path}" dependency-evidence)
|
||||
gh release upload "${RELEASE_TAG}" "${asset_path}#${asset_name}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--clobber
|
||||
(
|
||||
cd "${download_dir}"
|
||||
find dependency-evidence -type f -print | LC_ALL=C sort | zip -X -q "${asset_path}" -@
|
||||
)
|
||||
attach_or_verify_release_asset "${asset_path}" "${asset_name}"
|
||||
echo "- Dependency evidence asset: \`${asset_name}\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
}
|
||||
|
||||
attach_or_verify_release_asset() {
|
||||
local source_path="$1"
|
||||
local asset_name="$2"
|
||||
local existing_dir="${RUNNER_TEMP}/openclaw-release-existing-assets/${asset_name}"
|
||||
local existing_path="${existing_dir}/${asset_name}"
|
||||
|
||||
if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets |
|
||||
jq -e --arg name "${asset_name}" 'any(.assets[]?; .name == $name)' >/dev/null; then
|
||||
rm -rf "${existing_dir}"
|
||||
mkdir -p "${existing_dir}"
|
||||
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "${asset_name}" --dir "${existing_dir}"
|
||||
cmp --silent "${source_path}" "${existing_path}" || {
|
||||
echo "Existing release evidence asset ${asset_name} differs from this release run." >&2
|
||||
exit 1
|
||||
}
|
||||
return
|
||||
fi
|
||||
|
||||
gh release upload "${RELEASE_TAG}" "${source_path}#${asset_name}" --repo "${GITHUB_REPOSITORY}"
|
||||
}
|
||||
|
||||
upload_release_evidence_assets() {
|
||||
local release_version manifest_path evidence_path manifest_asset evidence_asset
|
||||
release_version="${RELEASE_TAG#v}"
|
||||
manifest_path="${FULL_RELEASE_VALIDATION_MANIFEST_DIR}/full-release-validation-manifest.json"
|
||||
evidence_path="${POSTPUBLISH_EVIDENCE_DIR}/release-postpublish-evidence.json"
|
||||
manifest_asset="openclaw-${release_version}-release-manifest.json"
|
||||
evidence_asset="openclaw-${release_version}-postpublish-evidence.json"
|
||||
|
||||
if [[ ! -f "${manifest_path}" ]]; then
|
||||
echo "Full release validation manifest is missing from ${FULL_RELEASE_VALIDATION_MANIFEST_DIR}." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${evidence_path}" ]]; then
|
||||
echo "Postpublish release evidence is missing from ${POSTPUBLISH_EVIDENCE_DIR}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "${manifest_path}" "${RUNNER_TEMP}/${manifest_asset}"
|
||||
cp "${evidence_path}" "${RUNNER_TEMP}/${evidence_asset}"
|
||||
(
|
||||
cd "${RUNNER_TEMP}"
|
||||
sha256sum "${manifest_asset}" > "${manifest_asset}.sha256"
|
||||
sha256sum "${evidence_asset}" > "${evidence_asset}.sha256"
|
||||
)
|
||||
|
||||
attach_or_verify_release_asset "${RUNNER_TEMP}/${manifest_asset}" "${manifest_asset}"
|
||||
attach_or_verify_release_asset \
|
||||
"${RUNNER_TEMP}/${manifest_asset}.sha256" \
|
||||
"${manifest_asset}.sha256"
|
||||
attach_or_verify_release_asset "${RUNNER_TEMP}/${evidence_asset}" "${evidence_asset}"
|
||||
attach_or_verify_release_asset \
|
||||
"${RUNNER_TEMP}/${evidence_asset}.sha256" \
|
||||
"${evidence_asset}.sha256"
|
||||
{
|
||||
echo "- Immutable release manifest: \`${manifest_asset}\`"
|
||||
echo "- Immutable postpublish evidence: \`${evidence_asset}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
}
|
||||
|
||||
verify_published_release() {
|
||||
local release_version evidence_path skip_clawhub clawhub_runtime_state_path
|
||||
local -a verify_args
|
||||
@@ -1105,6 +1189,10 @@ jobs:
|
||||
fi
|
||||
|
||||
pnpm "${verify_args[@]}"
|
||||
jq --arg release_publish_run_id "$GITHUB_RUN_ID" \
|
||||
'.releasePublishRunId = $release_publish_run_id' \
|
||||
"${evidence_path}" > "${evidence_path}.next"
|
||||
mv "${evidence_path}.next" "${evidence_path}"
|
||||
{
|
||||
echo "- Postpublish verification: passed"
|
||||
echo "- Postpublish evidence: \`${evidence_path}\`"
|
||||
@@ -1382,6 +1470,7 @@ jobs:
|
||||
fi
|
||||
create_or_update_github_release
|
||||
upload_dependency_evidence_release_asset
|
||||
upload_release_evidence_assets
|
||||
if ! promote_windows_release_assets; then
|
||||
failed=1
|
||||
fi
|
||||
@@ -1398,7 +1487,7 @@ jobs:
|
||||
|
||||
- name: Upload postpublish evidence
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-release-postpublish-evidence-${{ inputs.tag }}
|
||||
path: ${{ runner.temp }}/openclaw-release-postpublish-evidence
|
||||
|
||||
384
.github/workflows/openclaw-stable-main-closeout.yml
vendored
Normal file
384
.github/workflows/openclaw-stable-main-closeout.yml
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
name: OpenClaw Stable Main Closeout
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Stable OpenClaw tag to replay or repair, for example v2026.6.8 or v2026.6.8-2
|
||||
required: false
|
||||
type: string
|
||||
rollback_drill_id:
|
||||
description: Opaque identifier for the current private rollback drill record
|
||||
required: false
|
||||
type: string
|
||||
rollback_drill_date:
|
||||
description: UTC date of the private rollback drill in YYYY-MM-DD form; must be within 90 days
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: openclaw-stable-main-closeout
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
resolve:
|
||||
name: Resolve stable release closeout inputs
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
full_release_validation_run_id: ${{ steps.inputs.outputs.full_release_validation_run_id }}
|
||||
release_publish_run_id: ${{ steps.inputs.outputs.release_publish_run_id }}
|
||||
rollback_drill_date: ${{ steps.inputs.outputs.rollback_drill_date }}
|
||||
rollback_drill_id: ${{ steps.inputs.outputs.rollback_drill_id }}
|
||||
evidence_tag: ${{ steps.inputs.outputs.evidence_tag }}
|
||||
fallback_correction: ${{ steps.inputs.outputs.fallback_correction }}
|
||||
main_ref: ${{ steps.inputs.outputs.main_ref }}
|
||||
repair_partial_closeout: ${{ steps.inputs.outputs.repair_partial_closeout }}
|
||||
should_closeout: ${{ steps.inputs.outputs.should_closeout }}
|
||||
tag: ${{ steps.inputs.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout pushed main
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Resolve published stable release evidence
|
||||
id: inputs
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
MANUAL_TAG: ${{ inputs.tag }}
|
||||
ROLLBACK_DRILL_DATE: ${{ inputs.rollback_drill_date || vars.RELEASE_ROLLBACK_DRILL_DATE }}
|
||||
ROLLBACK_DRILL_ID: ${{ inputs.rollback_drill_id || vars.RELEASE_ROLLBACK_DRILL_ID }}
|
||||
TRIGGER_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "$EVENT_NAME" == "push" ]]; then
|
||||
main_ref="$TRIGGER_SHA"
|
||||
tag="$(gh release list --repo "$GITHUB_REPOSITORY" --exclude-drafts --limit 100 \
|
||||
--json tagName,isPrerelease,publishedAt \
|
||||
--jq '[.[] | select(.isPrerelease | not) | select(.tagName | test("^v[0-9]{4}\\.[0-9]+\\.[0-9]+(-[0-9]+)?$"))] | sort_by(.publishedAt) | last | .tagName // empty')"
|
||||
if [[ -z "$tag" ]]; then
|
||||
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
tag="$MANUAL_TAG"
|
||||
fi
|
||||
if [[ ! "$tag" =~ ^v[0-9]{4}\.[0-9]+\.[0-9]+(-[0-9]+)?$ ]]; then
|
||||
if [[ "$EVENT_NAME" == "push" ]]; then
|
||||
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "Stable main closeout accepts only a stable vYYYY.M.PATCH or vYYYY.M.PATCH-N tag, got $tag." >&2
|
||||
exit 1
|
||||
fi
|
||||
release_asset_version="${tag#v}"
|
||||
release_package_version="$release_asset_version"
|
||||
fallback_package_version="$release_asset_version"
|
||||
if [[ "$release_package_version" =~ ^(.+)-[0-9]+$ ]]; then
|
||||
fallback_package_version="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
tag_package_version="$(gh api "repos/$GITHUB_REPOSITORY/contents/package.json?ref=$tag" \
|
||||
--jq '.content' | tr -d '\n' | base64 --decode | jq -r '.version // empty')"
|
||||
fallback_correction=false
|
||||
evidence_source_tag="$tag"
|
||||
if [[ "$release_package_version" != "$fallback_package_version" &&
|
||||
"$tag_package_version" == "$fallback_package_version" ]]; then
|
||||
fallback_correction=true
|
||||
evidence_source_tag="v$fallback_package_version"
|
||||
elif [[ "$tag_package_version" != "$release_package_version" ]]; then
|
||||
echo "Stable closeout requires $tag package.json to match $release_package_version, or the legacy fallback package version $fallback_package_version." >&2
|
||||
exit 1
|
||||
fi
|
||||
evidence_version="${evidence_source_tag#v}"
|
||||
evidence_asset="openclaw-${evidence_version}-postpublish-evidence.json"
|
||||
evidence_checksum_asset="${evidence_asset}.sha256"
|
||||
closeout_asset="openclaw-${release_asset_version}-stable-main-closeout.json"
|
||||
closeout_checksum_asset="${closeout_asset}.sha256"
|
||||
closeout_dir="$RUNNER_TEMP/release-closeout-evidence"
|
||||
mkdir -p "$closeout_dir"
|
||||
gh release download "$tag" --repo "$GITHUB_REPOSITORY" \
|
||||
--pattern "$closeout_asset" --pattern "$closeout_checksum_asset" --dir "$closeout_dir" || true
|
||||
closeout_json_path="$closeout_dir/$closeout_asset"
|
||||
closeout_checksum_path="$closeout_dir/$closeout_checksum_asset"
|
||||
repair_partial_closeout=false
|
||||
existing_closeout_full_release_validation_run_id=""
|
||||
existing_closeout_release_publish_run_id=""
|
||||
if [[ -f "$closeout_json_path" && -f "$closeout_checksum_path" ]]; then
|
||||
expected_closeout_digest="$(awk 'NF { print $1; exit }' "$closeout_checksum_path")"
|
||||
actual_closeout_digest="$(sha256sum "$closeout_json_path" | awk '{print $1}')"
|
||||
if [[ ! "$expected_closeout_digest" =~ ^[0-9a-f]{64}$ ||
|
||||
"$expected_closeout_digest" != "$actual_closeout_digest" ]]; then
|
||||
echo "Stable closeout evidence for $tag has an invalid checksum; refusing to repair it." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ -f "$closeout_checksum_path" && ! -f "$closeout_json_path" ]]; then
|
||||
echo "Stable closeout evidence for $tag has a checksum without its manifest; refusing to repair it." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -f "$closeout_json_path" ]]; then
|
||||
existing_closeout_tag="$(jq -r '.releaseTag // empty' "$closeout_json_path")"
|
||||
existing_closeout_version="$(jq -r '.releaseVersion // empty' "$closeout_json_path")"
|
||||
existing_closeout_release_tag_sha="$(jq -r '.releaseTagSha // empty' "$closeout_json_path")"
|
||||
existing_closeout_main_ref="$(jq -r '.mainSha // empty' "$closeout_json_path")"
|
||||
existing_closeout_full_release_validation_run_id="$(jq -r '.fullReleaseValidationRunId // empty' "$closeout_json_path")"
|
||||
existing_closeout_release_publish_run_id="$(jq -r '.releasePublishRunId // empty' "$closeout_json_path")"
|
||||
existing_closeout_rollback_drill_id="$(jq -r '.rollbackDrill.id // empty' "$closeout_json_path")"
|
||||
existing_closeout_rollback_drill_date="$(jq -r '.rollbackDrill.date // empty' "$closeout_json_path")"
|
||||
if [[ "$existing_closeout_tag" != "$tag" ||
|
||||
"$existing_closeout_version" != "$tag_package_version" ||
|
||||
! "$existing_closeout_release_tag_sha" =~ ^[0-9a-f]{40}$ ||
|
||||
! "$existing_closeout_main_ref" =~ ^[0-9a-f]{40}$ ||
|
||||
-z "$existing_closeout_full_release_validation_run_id" ||
|
||||
-z "$existing_closeout_release_publish_run_id" ||
|
||||
-z "$existing_closeout_rollback_drill_id" ||
|
||||
! "$existing_closeout_rollback_drill_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
echo "Stable closeout manifest for $tag is incomplete; refusing to repair it." >&2
|
||||
exit 1
|
||||
fi
|
||||
main_ref="$existing_closeout_main_ref"
|
||||
ROLLBACK_DRILL_ID="$existing_closeout_rollback_drill_id"
|
||||
ROLLBACK_DRILL_DATE="$existing_closeout_rollback_drill_date"
|
||||
repair_partial_closeout=true
|
||||
elif [[ "$EVENT_NAME" == "push" ]]; then
|
||||
main_version="$(jq -r '.version // empty' package.json)"
|
||||
if [[ "$main_version" != "$release_package_version" &&
|
||||
"$main_version" != "$fallback_package_version" ]]; then
|
||||
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
main_ref="main"
|
||||
fi
|
||||
evidence_dir="$RUNNER_TEMP/release-postpublish-evidence"
|
||||
mkdir -p "$evidence_dir"
|
||||
if ! gh release download "$evidence_source_tag" --repo "$GITHUB_REPOSITORY" \
|
||||
--pattern "$evidence_asset" --pattern "$evidence_checksum_asset" --dir "$evidence_dir"; then
|
||||
if [[ "$EVENT_NAME" == "push" ]]; then
|
||||
echo "Stable closeout skipped: $evidence_source_tag predates immutable postpublish evidence." >&2
|
||||
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "Stable closeout is required for $tag, but immutable postpublish evidence from $evidence_source_tag is missing." >&2
|
||||
exit 1
|
||||
fi
|
||||
evidence_path="$evidence_dir/$evidence_asset"
|
||||
if ! (
|
||||
cd "$evidence_dir"
|
||||
sha256sum --strict --status -c "$evidence_checksum_asset"
|
||||
); then
|
||||
echo "Postpublish evidence checksum failed for $tag." >&2
|
||||
exit 1
|
||||
fi
|
||||
evidence_release_tag="$(jq -r '.releaseTag // empty' "$evidence_path")"
|
||||
full_release_validation_run_id="$(jq -r '[.workflowRuns[]? | select(.label == "Full Release Validation") | .id] | if length == 1 then .[0] else empty end' "$evidence_path")"
|
||||
release_publish_run_id="$(jq -r '.releasePublishRunId // empty' "$evidence_path")"
|
||||
if [[ "$evidence_release_tag" != "$evidence_source_tag" || -z "$full_release_validation_run_id" || -z "$release_publish_run_id" ]]; then
|
||||
echo "Stable closeout is required for $tag, but postpublish evidence does not bind $evidence_source_tag to exactly one Full Release Validation run and its Publish run." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -n "$existing_closeout_full_release_validation_run_id" &&
|
||||
( "$existing_closeout_full_release_validation_run_id" != "$full_release_validation_run_id" ||
|
||||
"$existing_closeout_release_publish_run_id" != "$release_publish_run_id" ) ]]; then
|
||||
echo "Stable closeout manifest for $tag does not match immutable postpublish evidence; refusing to accept it." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$ROLLBACK_DRILL_ID" || -z "$ROLLBACK_DRILL_DATE" ]]; then
|
||||
echo "Stable closeout requires repository variables RELEASE_ROLLBACK_DRILL_ID and RELEASE_ROLLBACK_DRILL_DATE, or explicit manual overrides." >&2
|
||||
exit 1
|
||||
fi
|
||||
{
|
||||
echo "full_release_validation_run_id=$full_release_validation_run_id"
|
||||
echo "release_publish_run_id=$release_publish_run_id"
|
||||
echo "rollback_drill_date=$ROLLBACK_DRILL_DATE"
|
||||
echo "rollback_drill_id=$ROLLBACK_DRILL_ID"
|
||||
echo "evidence_tag=$evidence_source_tag"
|
||||
echo "fallback_correction=$fallback_correction"
|
||||
echo "main_ref=$main_ref"
|
||||
echo "repair_partial_closeout=$repair_partial_closeout"
|
||||
echo "should_closeout=true"
|
||||
echo "tag=$tag"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
verify:
|
||||
name: Verify stable main closeout
|
||||
needs: resolve
|
||||
if: ${{ needs.resolve.outputs.should_closeout == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout resolved main state
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.resolve.outputs.main_ref }}
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Checkout shipped release tag
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ needs.resolve.outputs.tag }}
|
||||
path: release-tag
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Checkout fallback evidence tag
|
||||
if: ${{ needs.resolve.outputs.fallback_correction == 'true' }}
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: refs/tags/${{ needs.resolve.outputs.evidence_tag }}
|
||||
path: evidence-tag
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Bind fallback correction to the published package source
|
||||
if: ${{ needs.resolve.outputs.fallback_correction == 'true' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
correction_sha="$(git -C "$GITHUB_WORKSPACE/release-tag" rev-parse HEAD)"
|
||||
evidence_sha="$(git -C "$GITHUB_WORKSPACE/evidence-tag" rev-parse HEAD)"
|
||||
if [[ "$correction_sha" != "$evidence_sha" ]]; then
|
||||
echo "Fallback correction ${{ needs.resolve.outputs.tag }} must point to the same source commit as ${{ needs.resolve.outputs.evidence_tag }} to reuse immutable package evidence." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify release workflow evidence
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
FULL_RELEASE_VALIDATION_RUN_ID: ${{ needs.resolve.outputs.full_release_validation_run_id }}
|
||||
RELEASE_PUBLISH_RUN_ID: ${{ needs.resolve.outputs.release_publish_run_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh run view "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
||||
--json workflowName,event,status,conclusion \
|
||||
> "$RUNNER_TEMP/full-release-validation-run.json"
|
||||
node --input-type=module - "$RUNNER_TEMP/full-release-validation-run.json" <<'NODE'
|
||||
import { readFileSync } from "node:fs";
|
||||
const run = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
||||
for (const [key, expected] of [
|
||||
["workflowName", "Full Release Validation"],
|
||||
["event", "workflow_dispatch"],
|
||||
["status", "completed"],
|
||||
["conclusion", "success"],
|
||||
]) {
|
||||
if (run[key] !== expected) {
|
||||
throw new Error(`Full Release Validation must have ${key}=${expected}, got ${run[key] ?? "<missing>"}.`);
|
||||
}
|
||||
}
|
||||
NODE
|
||||
gh run view "$RELEASE_PUBLISH_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
||||
--json workflowName,event,status,conclusion \
|
||||
> "$RUNNER_TEMP/release-publish-run.json"
|
||||
node --input-type=module - "$RUNNER_TEMP/release-publish-run.json" <<'NODE'
|
||||
import { readFileSync } from "node:fs";
|
||||
const run = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
||||
for (const [key, expected] of [
|
||||
["workflowName", "OpenClaw Release Publish"],
|
||||
["event", "workflow_dispatch"],
|
||||
["status", "completed"],
|
||||
["conclusion", "success"],
|
||||
]) {
|
||||
if (run[key] !== expected) {
|
||||
throw new Error(`OpenClaw Release Publish must have ${key}=${expected}, got ${run[key] ?? "<missing>"}.`);
|
||||
}
|
||||
}
|
||||
NODE
|
||||
|
||||
manifest_dir="$RUNNER_TEMP/full-release-validation-manifest"
|
||||
rm -rf "$manifest_dir"
|
||||
mkdir -p "$manifest_dir"
|
||||
gh run download "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
|
||||
--name "full-release-validation-${FULL_RELEASE_VALIDATION_RUN_ID}" \
|
||||
--dir "$manifest_dir"
|
||||
tag_sha="$(git -C "$GITHUB_WORKSPACE/release-tag" rev-parse HEAD)"
|
||||
jq -e --arg tag_sha "$tag_sha" '
|
||||
.workflowName == "Full Release Validation" and
|
||||
.targetSha == $tag_sha and
|
||||
.rerunGroup == "all" and
|
||||
.runReleaseSoak == "true" and
|
||||
.controls.performanceBlocking == true and
|
||||
.childRuns.productPerformance.conclusion == "success"
|
||||
' "$manifest_dir/full-release-validation-manifest.json" >/dev/null || {
|
||||
echo "Full Release Validation manifest does not contain the required stable release controls." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Verify stable state and write closeout manifest
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ needs.resolve.outputs.tag }}
|
||||
FULL_RELEASE_VALIDATION_RUN_ID: ${{ needs.resolve.outputs.full_release_validation_run_id }}
|
||||
RELEASE_PUBLISH_RUN_ID: ${{ needs.resolve.outputs.release_publish_run_id }}
|
||||
ROLLBACK_DRILL_ID: ${{ needs.resolve.outputs.rollback_drill_id }}
|
||||
ROLLBACK_DRILL_DATE: ${{ needs.resolve.outputs.rollback_drill_date }}
|
||||
REPAIR_PARTIAL_CLOSEOUT: ${{ needs.resolve.outputs.repair_partial_closeout }}
|
||||
CLOSEOUT_DIR: ${{ runner.temp }}/openclaw-stable-main-closeout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$CLOSEOUT_DIR"
|
||||
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
|
||||
--json tagName,isDraft,isPrerelease,assets \
|
||||
> "$CLOSEOUT_DIR/github-release.json"
|
||||
node scripts/verify-stable-main-closeout.mjs \
|
||||
--tag "$RELEASE_TAG" \
|
||||
--main-dir "$GITHUB_WORKSPACE" \
|
||||
--tag-dir "$GITHUB_WORKSPACE/release-tag" \
|
||||
--release-json "$CLOSEOUT_DIR/github-release.json" \
|
||||
--full-release-validation-run-id "$FULL_RELEASE_VALIDATION_RUN_ID" \
|
||||
--release-publish-run-id "$RELEASE_PUBLISH_RUN_ID" \
|
||||
--rollback-drill-id "$ROLLBACK_DRILL_ID" \
|
||||
--rollback-drill-date "$ROLLBACK_DRILL_DATE" \
|
||||
--allow-stale-rollback-drill "$REPAIR_PARTIAL_CLOSEOUT" \
|
||||
--output "$CLOSEOUT_DIR/stable-main-closeout.json"
|
||||
release_version="${RELEASE_TAG#v}"
|
||||
sha256sum "$CLOSEOUT_DIR/stable-main-closeout.json" | awk -v asset="openclaw-${release_version}-stable-main-closeout.json" \
|
||||
'{print $1 " " asset}' \
|
||||
> "$CLOSEOUT_DIR/stable-main-closeout.json.sha256"
|
||||
|
||||
- name: Attach immutable closeout evidence
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ needs.resolve.outputs.tag }}
|
||||
CLOSEOUT_DIR: ${{ runner.temp }}/openclaw-stable-main-closeout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
release_version="${RELEASE_TAG#v}"
|
||||
attach_or_verify() {
|
||||
local source_path="$1"
|
||||
local asset_name="$2"
|
||||
local existing_dir="$CLOSEOUT_DIR/existing-${asset_name}"
|
||||
mkdir -p "$existing_dir"
|
||||
if gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
|
||||
--pattern "$asset_name" --dir "$existing_dir"; then
|
||||
cmp --silent "$source_path" "$existing_dir/$asset_name" || {
|
||||
echo "Existing release asset $asset_name differs from closeout evidence." >&2
|
||||
exit 1
|
||||
}
|
||||
return
|
||||
fi
|
||||
gh release upload "$RELEASE_TAG" "$source_path#$asset_name" --repo "$GITHUB_REPOSITORY"
|
||||
}
|
||||
attach_or_verify \
|
||||
"$CLOSEOUT_DIR/stable-main-closeout.json" \
|
||||
"openclaw-${release_version}-stable-main-closeout.json"
|
||||
attach_or_verify \
|
||||
"$CLOSEOUT_DIR/stable-main-closeout.json.sha256" \
|
||||
"openclaw-${release_version}-stable-main-closeout.json.sha256"
|
||||
|
||||
- name: Upload closeout workflow evidence
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openclaw-stable-main-closeout-${{ needs.resolve.outputs.tag }}
|
||||
path: ${{ runner.temp }}/openclaw-stable-main-closeout
|
||||
if-no-files-found: error
|
||||
6
.github/workflows/opengrep-precise-full.yml
vendored
6
.github/workflows/opengrep-precise-full.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
scripts/run-opengrep.sh --sarif --error
|
||||
|
||||
- name: Upload SARIF to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v4.36.2
|
||||
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
|
||||
# Only upload if the scan actually produced a SARIF file.
|
||||
if: always() && hashFiles('.opengrep-out/precise.sarif') != ''
|
||||
with:
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
- name: Upload SARIF as workflow artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: opengrep-full-sarif
|
||||
path: .opengrep-out/precise.sarif
|
||||
|
||||
6
.github/workflows/opengrep-precise.yml
vendored
6
.github/workflows/opengrep-precise.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 2
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
scripts/run-opengrep.sh --changed --sarif --error
|
||||
|
||||
- name: Upload SARIF to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v4.36.2
|
||||
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
|
||||
# Only upload if the scan actually produced a SARIF file.
|
||||
if: always() && hashFiles('.opengrep-out/precise.sarif') != ''
|
||||
with:
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
|
||||
- name: Upload SARIF as workflow artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: opengrep-pr-diff-sarif
|
||||
path: .opengrep-out/precise.sarif
|
||||
|
||||
10
.github/workflows/package-acceptance.yml
vendored
10
.github/workflows/package-acceptance.yml
vendored
@@ -325,7 +325,7 @@ jobs:
|
||||
telegram_mode: ${{ steps.profile.outputs.telegram_mode }}
|
||||
steps:
|
||||
- name: Checkout package workflow ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.workflow_ref }}
|
||||
fetch-depth: 0
|
||||
@@ -339,7 +339,7 @@ jobs:
|
||||
|
||||
- name: Download current-run package artifact input
|
||||
if: inputs.source == 'artifact' && inputs.artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.artifact_name }}
|
||||
path: .artifacts/package-candidate-input
|
||||
@@ -492,7 +492,7 @@ jobs:
|
||||
node scripts/resolve-upgrade-survivor-baselines.mjs "${args[@]}" >/dev/null
|
||||
|
||||
- name: Upload package-under-test artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ env.PACKAGE_ARTIFACT_NAME }}
|
||||
path: |
|
||||
@@ -541,13 +541,13 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout package workflow ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.workflow_ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download package-under-test artifact
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ needs.resolve_package.outputs.package_artifact_name }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
6
.github/workflows/plugin-clawhub-new.yml
vendored
6
.github/workflows/plugin-clawhub-new.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
matrix: ${{ steps.plan.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref }}
|
||||
@@ -229,7 +229,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -303,7 +303,7 @@ jobs:
|
||||
plugin: ${{ fromJson(needs.resolve_bootstrap_plan.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
8
.github/workflows/plugin-clawhub-release.yml
vendored
8
.github/workflows/plugin-clawhub-release.yml
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
missing_trusted_publisher_matrix: ${{ steps.plan.outputs.missing_trusted_publisher_matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref }}
|
||||
@@ -275,7 +275,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -315,7 +315,7 @@ jobs:
|
||||
plugin: ${{ fromJson(needs.preview_plugins_clawhub.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref }}
|
||||
@@ -364,7 +364,7 @@ jobs:
|
||||
run: bash scripts/plugin-clawhub-publish.sh --pack "${PACKAGE_DIR}"
|
||||
|
||||
- name: Upload ClawHub package artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ matrix.plugin.artifactName }}
|
||||
path: ${{ runner.temp }}/clawhub-package-artifact/*.tgz
|
||||
|
||||
8
.github/workflows/plugin-npm-release.yml
vendored
8
.github/workflows/plugin-npm-release.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
matrix: ${{ steps.plan.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
||||
@@ -185,7 +185,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
plugin: ${{ fromJson(needs.preview_plugins_npm.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }}
|
||||
@@ -257,7 +257,7 @@ jobs:
|
||||
plugin: ${{ fromJson(needs.preview_plugins_npm.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }}
|
||||
|
||||
12
.github/workflows/plugin-prerelease.yml
vendored
12
.github/workflows/plugin-prerelease.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
plugin_prerelease_docker_lanes: ${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}
|
||||
steps:
|
||||
- name: Checkout target
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.target_ref }}
|
||||
fetch-depth: 1
|
||||
@@ -216,7 +216,7 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_static_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
@@ -252,7 +252,7 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_node_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
@@ -325,7 +325,7 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_extension_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
@@ -357,7 +357,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
@@ -519,7 +519,7 @@ jobs:
|
||||
|
||||
- name: Upload plugin inspector advisory artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: plugin-inspector-advisory
|
||||
path: .artifacts/plugin-inspector/**
|
||||
|
||||
36
.github/workflows/qa-live-transports-convex.yml
vendored
36
.github/workflows/qa-live-transports-convex.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
steps:
|
||||
- name: Require maintainer-level repository access
|
||||
id: permission
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
if (context.eventName === "schedule") {
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ""
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
|
||||
- name: Upload parity artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-parity-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: .artifacts/qa-e2e/
|
||||
@@ -241,7 +241,7 @@ jobs:
|
||||
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -310,7 +310,7 @@ jobs:
|
||||
|
||||
- name: Upload live runtime token-efficiency artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-runtime-token-efficiency-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -326,7 +326,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -386,7 +386,7 @@ jobs:
|
||||
|
||||
- name: Upload Matrix QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-matrix-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -411,7 +411,7 @@ jobs:
|
||||
- e2ee-cli
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -470,7 +470,7 @@ jobs:
|
||||
|
||||
- name: Upload Matrix QA shard artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-matrix-${{ matrix.profile }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -485,7 +485,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -564,7 +564,7 @@ jobs:
|
||||
|
||||
- name: Upload Telegram QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-telegram-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -579,7 +579,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -658,7 +658,7 @@ jobs:
|
||||
|
||||
- name: Upload Discord QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-discord-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -676,7 +676,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -755,7 +755,7 @@ jobs:
|
||||
|
||||
- name: Upload WhatsApp QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-whatsapp-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
@@ -770,7 +770,7 @@ jobs:
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
@@ -850,7 +850,7 @@ jobs:
|
||||
|
||||
- name: Upload Slack QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: qa-live-slack-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ steps.run_lane.outputs.output_dir }}
|
||||
|
||||
6
.github/workflows/real-behavior-proof.yml
vendored
6
.github/workflows/real-behavior-proof.yml
vendored
@@ -22,11 +22,11 @@ jobs:
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
persist-credentials: false
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
permission-issues: read
|
||||
permission-members: read
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
if: steps.app-token.outcome == 'failure'
|
||||
continue-on-error: true
|
||||
|
||||
2
.github/workflows/sandbox-common-smoke.yml
vendored
2
.github/workflows/sandbox-common-smoke.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
|
||||
114
.github/workflows/security-sensitive-guard.yml
vendored
Normal file
114
.github/workflows/security-sensitive-guard.yml
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
name: Security Sensitive Guard
|
||||
|
||||
on:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] checks trusted base script only; never checks out PR head
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
env:
|
||||
# Temporary rollout bridge for PRs opened before this workflow's script landed.
|
||||
# Remove once the pre-rollout PR set has drained.
|
||||
OPENCLAW_SECURITY_SENSITIVE_GUARD_ROLLOUT_SHA: 5d9c010628ea4de3492a12e32f9be5b8c5dfa9ed
|
||||
|
||||
concurrency:
|
||||
group: security-sensitive-guard-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
security-sensitive-guard-detect:
|
||||
if: ${{ !github.event.pull_request.draft }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check security-sensitive guard rollout eligibility
|
||||
id: rollout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
status="$(
|
||||
gh api \
|
||||
"repos/${GITHUB_REPOSITORY}/compare/${OPENCLAW_SECURITY_SENSITIVE_GUARD_ROLLOUT_SHA}...${PR_BASE_SHA}" \
|
||||
--jq '.status'
|
||||
)"
|
||||
case "$status" in
|
||||
ahead|identical)
|
||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
||||
;;
|
||||
behind|diverged)
|
||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::Skipping security-sensitive guard for a PR base that predates rollout commit ${OPENCLAW_SECURITY_SENSITIVE_GUARD_ROLLOUT_SHA}."
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected compare status for security-sensitive guard rollout: $status" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Check out trusted base workflow scripts
|
||||
if: steps.rollout.outputs.ready == 'true'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
ref: ${{ github.workflow_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Detect security-sensitive changes
|
||||
if: steps.rollout.outputs.ready == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
OPENCLAW_SECURITY_APPROVERS: vincentkoc,steipete,joshavant
|
||||
OPENCLAW_SECURITY_SENSITIVE_GUARD_MODE: detect
|
||||
OPENCLAW_SECURITY_TEAM_SLUG: openclaw-secops
|
||||
run: node scripts/github/security-sensitive-guard.mjs
|
||||
|
||||
security-sensitive-guard:
|
||||
if: ${{ !github.event.pull_request.draft && always() }}
|
||||
needs:
|
||||
- security-sensitive-guard-detect
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check security-sensitive guard rollout eligibility
|
||||
id: rollout
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
status="$(
|
||||
gh api \
|
||||
"repos/${GITHUB_REPOSITORY}/compare/${OPENCLAW_SECURITY_SENSITIVE_GUARD_ROLLOUT_SHA}...${PR_BASE_SHA}" \
|
||||
--jq '.status'
|
||||
)"
|
||||
case "$status" in
|
||||
ahead|identical)
|
||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
||||
;;
|
||||
behind|diverged)
|
||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::Skipping security-sensitive guard for a PR base that predates rollout commit ${OPENCLAW_SECURITY_SENSITIVE_GUARD_ROLLOUT_SHA}."
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected compare status for security-sensitive guard rollout: $status" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Check out trusted base workflow scripts
|
||||
if: steps.rollout.outputs.ready == 'true'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
ref: ${{ github.workflow_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Enforce security-sensitive guard
|
||||
if: steps.rollout.outputs.ready == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
OPENCLAW_SECURITY_APPROVERS: vincentkoc,steipete,joshavant
|
||||
OPENCLAW_SECURITY_SENSITIVE_GUARD_MODE: enforce
|
||||
OPENCLAW_SECURITY_TEAM_SLUG: openclaw-secops
|
||||
run: node scripts/github/security-sensitive-guard.mjs
|
||||
26
.github/workflows/stale.yml
vendored
26
.github/workflows/stale.yml
vendored
@@ -44,13 +44,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token-fallback
|
||||
continue-on-error: true
|
||||
with:
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
- name: Mark stale unassigned issues and pull requests (primary)
|
||||
id: stale-primary
|
||||
continue-on-error: true
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 14
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
- name: Mark stale assigned issues (primary)
|
||||
id: assigned-issue-stale-primary
|
||||
continue-on-error: true
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 30
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
- name: Mark stale assigned pull requests (primary)
|
||||
id: assigned-stale-primary
|
||||
continue-on-error: true
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: -1
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
- name: Check stale state cache
|
||||
id: stale-state
|
||||
if: always()
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token-fallback.outputs.token || steps.app-token.outputs.token }}
|
||||
script: |
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
}
|
||||
- name: Mark stale unassigned issues and pull requests (fallback)
|
||||
if: (steps.stale-primary.outcome == 'failure' || steps.stale-state.outputs.has_state == 'true') && steps.app-token-fallback.outputs.token != ''
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 14
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
That channel is the escape hatch for high-quality PRs that get auto-closed.
|
||||
- name: Mark stale assigned issues (fallback)
|
||||
if: (steps.assigned-issue-stale-primary.outcome == 'failure' || steps.stale-state.outputs.has_state == 'true') && steps.app-token-fallback.outputs.token != ''
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 30
|
||||
@@ -218,7 +218,7 @@ jobs:
|
||||
close-issue-reason: not_planned
|
||||
- name: Mark stale assigned pull requests (fallback)
|
||||
if: (steps.assigned-stale-primary.outcome == 'failure' || steps.stale-state.outputs.has_state == 'true') && steps.app-token-fallback.outputs.token != ''
|
||||
uses: actions/stale@v10
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: -1
|
||||
@@ -247,13 +247,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- name: Backfill stale closures
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
env:
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
INCLUDE_ISSUES: ${{ inputs.include_issues }}
|
||||
@@ -494,13 +494,13 @@ jobs:
|
||||
issues: write
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
||||
id: app-token
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Lock closed issues after 48h of no comments
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
|
||||
4
.github/workflows/test-performance-agent.yml
vendored
4
.github/workflows/test-performance-agent.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
timeout-minutes: 240
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
@@ -271,7 +271,7 @@ jobs:
|
||||
|
||||
- name: Upload test performance artifacts
|
||||
if: steps.gate.outputs.run_agent == 'true' && always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: test-performance-agent-${{ github.run_id }}
|
||||
path: .artifacts/test-perf/
|
||||
|
||||
3
.github/workflows/tui-pty.yml
vendored
3
.github/workflows/tui-pty.yml
vendored
@@ -32,8 +32,7 @@ jobs:
|
||||
OPENCLAW_TUI_PTY_INCLUDE_LOCAL: "1"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
|
||||
24
.github/workflows/website-installer-sync.yml
vendored
24
.github/workflows/website-installer-sync.yml
vendored
@@ -37,8 +37,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Install ShellCheck
|
||||
run: sudo apt-get update -y && sudo apt-get install -y shellcheck
|
||||
|
||||
@@ -71,8 +70,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: install.sh in Docker
|
||||
run: |
|
||||
timeout --kill-after=30s 20m docker run --rm \
|
||||
@@ -93,10 +91,9 @@ jobs:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
@@ -115,10 +112,9 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
@@ -141,13 +137,13 @@ jobs:
|
||||
|
||||
- name: Checkout OpenClaw
|
||||
if: env.OPENCLAW_GH_TOKEN != ''
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
path: openclaw
|
||||
|
||||
- name: Checkout openclaw.ai
|
||||
if: env.OPENCLAW_GH_TOKEN != ''
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
repository: openclaw/openclaw.ai
|
||||
token: ${{ env.OPENCLAW_GH_TOKEN }}
|
||||
@@ -175,13 +171,13 @@ jobs:
|
||||
|
||||
- name: Setup Bun
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
uses: oven-sh/setup-bun@v2
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
2
.github/workflows/windows-testbox-probe.yml
vendored
2
.github/workflows/windows-testbox-probe.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
shell: pwsh
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
ref: ${{ inputs.target_ref || github.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
2
.github/workflows/workflow-sanity.yml
vendored
2
.github/workflows/workflow-sanity.yml
vendored
@@ -115,7 +115,7 @@ jobs:
|
||||
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -77,6 +77,7 @@ extensions/canvas/src/host/a2ui/*.map
|
||||
|
||||
# fastlane (iOS)
|
||||
apps/ios/fastlane/README.md
|
||||
apps/android/fastlane/README.md
|
||||
apps/ios/fastlane/report.xml
|
||||
apps/ios/fastlane/Preview.html
|
||||
apps/ios/fastlane/screenshots/
|
||||
|
||||
@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Channels and delivery: preserve account-scoped DM channel send policy, intentional rich-message line breaks in Telegram and status output, rich Telegram final replies, rich Telegram tables and lists, Telegram thread-create CLI remapping, Feishu dynamic-agent routes after persisted binding reuse, Slack outbound `message_sent` hooks, contributed message-tool schema optionality, same-channel generated media completions, and channel chunking around surrogate pairs and Infinity limits. (#92788, #93164, #92679, #89421, #89943, #42837, #92814, #91137, #91246, #92735) Thanks @yetval, @obviyus, @spacegeologist, @rishitamrakar, @liuhao1024, @lundog, @TurboTheTurtle, and @yhterrance.
|
||||
- Gemini CLI: use the selected OpenClaw OAuth/API-key auth profile in an isolated Gemini CLI runtime home, preventing ambient Google machine credentials from overriding the chosen profile. (#88748) Thanks @jason-allen-oneal and @shakkernerd.
|
||||
- Feishu: fetch quoted/replied message content before the empty-message guard so a mention-only reply that quotes a message with meaningful content is no longer dropped. (#90192) Thanks @bladin.
|
||||
- Discord: give generated auto-thread titles a 60-second timeout and 4,096-token reasoning-model output budget, clamped to the selected model output cap. (#64734) Thanks @hanamizuki.
|
||||
- Agent, cron, and Gateway runtime: mark active main sessions before restart shutdown aborts, pause yielded subagent runs whose terminal also signals abort, clamp trusted subagent thinking overrides through provider/model fallback, preserve yielded media completions, deliver channel message-tool final replies through auto-reply while hiding internal delivery hints, restore reset archive fallback reads when active async transcripts are missing, de-duplicate main-session heartbeat events, expose session identity in runtime prompts, reject unknown OpenAI agent selectors, keep generated media completions, slash-command block replies, and trajectory export commands in WebChat, and require admin privileges for HTTP session/model override surfaces. (#91357, #92631, #92412, #92146, #92879, #91287, #92468, #92510, #91246, #92651, #92646) Thanks @ooiuuii, @openperf, @IWhatsskill, @masatohoshino, @CadanHu, @ZengWen-DT, @zhangguiping-xydt, and @TurboTheTurtle.
|
||||
- Providers and model replay: preserve storeless OpenAI Responses replay compatibility, recover invalid OpenAI reasoning signatures and genericized Anthropic thinking-signature replay errors, route OAuth image defaults through Codex for eligible OpenAI profiles, avoid eager tool streaming for Claude 4.5 in Copilot, quarantine unreadable and post-hook OpenAI/Anthropic-family tool schemas without broadening allowed tool choices, deliver explicit thinking-off requests to LM Studio binary-thinking models, honor profile auth for SecretRef model entries, bound model browsing, strip provider prefixes where runtimes need bare IDs, and surface nested embedding fetch failures. (#90706, #92941, #92201, #92916, #92824, #75393, #92908, #92921, #92928, #92002, #90686, #92247, #92627, #91218, #92628) Thanks @snowzlm, @mmyzwl, @CarlCapital, @bek91, @Kailigithub, @vincentkoc, @rohitjavvadi, @samson910022, @nxmxbbd, @liuhao1024, @bymle, and @mushuiyu886.
|
||||
|
||||
@@ -110,7 +110,7 @@ For coordinated change sets that genuinely need more than 20 PRs, join the **#cl
|
||||
- Keep PRs takeover-ready: open them from a branch maintainers can push to. For fork PRs, leave GitHub's **Allow edits by maintainers** option enabled so maintainers can finish urgent fixes, changelog entries, or merge prep when needed. If GitHub shows **Allow edits and access to secrets by maintainers**, enable it only when that workflow/secrets access is acceptable and say so in the PR.
|
||||
- Do not edit `CHANGELOG.md` in contributor PRs. Maintainers or ClawSweeper add the changelog entry when landing user-facing changes.
|
||||
- Run tests: `pnpm build && pnpm check && pnpm test`
|
||||
- For iterative local commits, `scripts/committer --fast "message" <files...>` passes `FAST_COMMIT=1` through to the pre-commit hook so it skips the repo-wide `pnpm check`. Only use it when you've already run equivalent targeted validation for the touched surface.
|
||||
- For iterative local commits, `scripts/committer --fast "message" <files...>` skips commit hooks. Only use it when you've already run equivalent targeted validation for the touched surface.
|
||||
- For extension/plugin changes, run the fast local lane first:
|
||||
- `pnpm test:extension <extension-name>`
|
||||
- `pnpm test:extension --list` to see valid extension ids
|
||||
|
||||
11
apps/android/CHANGELOG.md
Normal file
11
apps/android/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# OpenClaw Android Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
Maintenance update for the current OpenClaw Android release.
|
||||
|
||||
## 2026.6.2 - 2026-06-02
|
||||
|
||||
OpenClaw is now available on Android.
|
||||
|
||||
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
|
||||
14
apps/android/Config/ReleaseSigning.json
Normal file
14
apps/android/Config/ReleaseSigning.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"signingRepo": "git@github.com:openclaw/apps-signing.git",
|
||||
"signingBranch": "main",
|
||||
"assetPath": "android/openclaw",
|
||||
"uploadKeystoreEncryptedFile": "upload-keystore.jks.enc",
|
||||
"gradlePropertiesEncryptedFile": "gradle.properties.enc",
|
||||
"materializedRoot": "apps/android/build/release-signing",
|
||||
"gradlePropertyNames": [
|
||||
"OPENCLAW_ANDROID_STORE_FILE",
|
||||
"OPENCLAW_ANDROID_STORE_PASSWORD",
|
||||
"OPENCLAW_ANDROID_KEY_ALIAS",
|
||||
"OPENCLAW_ANDROID_KEY_PASSWORD"
|
||||
]
|
||||
}
|
||||
@@ -53,6 +53,16 @@ pnpm android:version:pin -- --from-gateway
|
||||
pnpm android:version:pin -- --version 2026.6.5 --version-code 2026060501
|
||||
```
|
||||
|
||||
Release-owner signing sync:
|
||||
|
||||
```bash
|
||||
pnpm android:release:signing:plan
|
||||
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull
|
||||
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:check
|
||||
```
|
||||
|
||||
The signing sync pulls encrypted Android upload-key assets from the shared `apps-signing` repo and materializes decrypted files under `apps/android/build/release-signing/`.
|
||||
|
||||
Generate raw Google Play screenshots:
|
||||
|
||||
```bash
|
||||
@@ -64,7 +74,7 @@ pnpm android:screenshots
|
||||
- Play build: `openclaw-<version>-play-release.aab`
|
||||
- Third-party build: `openclaw-<version>-third-party-release.apk`
|
||||
|
||||
`pnpm android:bundle:release` is an alias for the same archive helper.
|
||||
`pnpm android:bundle:release` is an alias for the same Fastlane archive lane.
|
||||
|
||||
See `apps/android/VERSIONING.md` and `apps/android/fastlane/SETUP.md` for the release workflow.
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ Android release builds use pinned app metadata instead of auto-bumping `build.gr
|
||||
- `version` is the Play `versionName` and uses CalVer: `YYYY.M.D`.
|
||||
- `versionCode` uses `YYYYMMDDNN`, where `NN` is a two-digit build number for that pinned app version.
|
||||
- `apps/android/Config/Version.properties` is generated from `version.json` and read by Gradle.
|
||||
- `apps/android/CHANGELOG.md` is the Android-only changelog and release-note source.
|
||||
- `apps/android/fastlane/metadata/android/en-US/release_notes.txt` is generated from the changelog.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -23,16 +25,41 @@ pnpm android:version:check
|
||||
pnpm android:version:sync
|
||||
pnpm android:version:pin -- --from-gateway
|
||||
pnpm android:version:pin -- --version 2026.6.5 --version-code 2026060501
|
||||
pnpm android:release:signing:plan
|
||||
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull
|
||||
pnpm android:release:preflight
|
||||
```
|
||||
|
||||
## Release-note resolution order
|
||||
|
||||
When generating `apps/android/fastlane/metadata/android/en-US/release_notes.txt`, the tooling reads the first available changelog section in this order:
|
||||
|
||||
1. exact pinned version, for example `## 2026.6.2`
|
||||
2. `## Unreleased`
|
||||
|
||||
Recommended workflow:
|
||||
|
||||
- while iterating on a Play internal testing train, keep pending notes under `## Unreleased`
|
||||
- before the production release, move or copy the final notes under `## <pinned version>` and run sync again
|
||||
|
||||
## Release Workflow
|
||||
|
||||
1. Pin Android to the intended release version.
|
||||
2. Run `pnpm android:version:sync`.
|
||||
3. Update `apps/android/fastlane/metadata/android/en-US/release_notes.txt`.
|
||||
4. Run `pnpm android:screenshots` to refresh raw Google Play screenshots.
|
||||
5. Run `pnpm android:release:archive` to produce the signed Play AAB and third-party APK.
|
||||
6. Run `pnpm android:release:upload` to upload metadata, screenshots, and the Play AAB to Google Play internal testing.
|
||||
7. Promote to production manually in Google Play Console.
|
||||
3. Update `apps/android/CHANGELOG.md`, then run `pnpm android:version:sync` again if needed.
|
||||
4. Run `MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull` to materialize encrypted Android signing assets from `apps-signing`.
|
||||
5. Run `pnpm android:release:preflight` to validate Play auth, signing, synced versioning, and release notes.
|
||||
6. Run `pnpm android:screenshots` to refresh raw Google Play screenshots.
|
||||
7. Run `pnpm android:release:archive` to produce the signed Play AAB and third-party APK.
|
||||
8. Run `pnpm android:release:upload` to upload metadata, screenshots, and the Play AAB to Google Play internal testing.
|
||||
9. Promote to production manually in Google Play Console.
|
||||
|
||||
The third-party flavor is archived as a signed APK for non-Play distribution. It is not uploaded by the Play release lane.
|
||||
|
||||
## Signing model
|
||||
|
||||
`apps/android/Config/ReleaseSigning.json` pins the Android signing assets in the shared private `apps-signing` repo. The Android pipeline uses the same `MATCH_PASSWORD` release-owner secret as iOS, but the Android files are managed by `scripts/android-release-signing.mjs` instead of Fastlane `match`.
|
||||
|
||||
`sync:pull` decrypts the Play upload keystore and Gradle signing properties into `apps/android/build/release-signing/`. That directory is gitignored, and Fastlane exports the materialized values as Gradle project properties for the current release command.
|
||||
|
||||
If `MATCH_PASSWORD` is not set, the existing manual Gradle-property signing path still works: provide `OPENCLAW_ANDROID_STORE_FILE`, `OPENCLAW_ANDROID_STORE_PASSWORD`, `OPENCLAW_ANDROID_KEY_ALIAS`, and `OPENCLAW_ANDROID_KEY_PASSWORD` through your local Gradle user properties before running release tasks.
|
||||
|
||||
@@ -1114,7 +1114,7 @@ internal fun gatewayRecoveryUiState(
|
||||
ready: Boolean,
|
||||
statusText: String,
|
||||
connectSettling: Boolean,
|
||||
nodeCapabilityApprovalState: GatewayNodeApprovalState = GatewayNodeApprovalState.Loading,
|
||||
nodeCapabilityApprovalState: GatewayNodeApprovalState,
|
||||
gatewayConnectionProblem: GatewayConnectionProblem? = null,
|
||||
): GatewayRecoveryUiState =
|
||||
when {
|
||||
|
||||
@@ -112,6 +112,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Gateway error: pairing required; approval in progress",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -124,6 +125,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = true,
|
||||
statusText = "Gateway error: pairing required",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -162,6 +164,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Connecting…",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
gatewayConnectionProblem =
|
||||
GatewayConnectionProblem(
|
||||
code = "PAIRING_REQUIRED",
|
||||
@@ -184,6 +187,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Connecting…",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
gatewayConnectionProblem =
|
||||
GatewayConnectionProblem(
|
||||
code = "PAIRING_REQUIRED",
|
||||
@@ -206,6 +210,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Offline",
|
||||
connectSettling = true,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -218,6 +223,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Connected (node offline)",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -230,6 +236,7 @@ class OnboardingFlowLogicTest {
|
||||
ready = false,
|
||||
statusText = "Gateway error: connection refused",
|
||||
connectSettling = false,
|
||||
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ default_platform(:android)
|
||||
DEFAULT_PLAY_PACKAGE_NAME = "ai.openclaw.app"
|
||||
DEFAULT_PLAY_TRACK = "internal"
|
||||
DEFAULT_PLAY_RELEASE_STATUS = "completed"
|
||||
ANDROID_RELEASE_SIGNING_GRADLE_PROPERTIES = [
|
||||
"OPENCLAW_ANDROID_STORE_FILE",
|
||||
"OPENCLAW_ANDROID_STORE_PASSWORD",
|
||||
"OPENCLAW_ANDROID_KEY_ALIAS",
|
||||
"OPENCLAW_ANDROID_KEY_PASSWORD"
|
||||
].freeze
|
||||
|
||||
def load_env_file(path)
|
||||
return unless File.exist?(path)
|
||||
@@ -36,6 +42,14 @@ def repo_root
|
||||
File.expand_path("../..", android_root)
|
||||
end
|
||||
|
||||
def android_release_signing_script
|
||||
File.join(repo_root, "scripts", "android-release-signing.mjs")
|
||||
end
|
||||
|
||||
def android_release_signing_materialized_properties_path
|
||||
File.join(android_root, "build", "release-signing", "gradle.properties")
|
||||
end
|
||||
|
||||
def shell_join(args)
|
||||
args.shelljoin
|
||||
end
|
||||
@@ -136,17 +150,22 @@ def android_release_notes_path
|
||||
File.join(__dir__, "metadata", "android", "en-US", "release_notes.txt")
|
||||
end
|
||||
|
||||
def validate_android_release_notes!
|
||||
release_notes_path = android_release_notes_path
|
||||
UI.user_error!("Missing Android release notes at #{release_notes_path}. Run `pnpm android:version:sync`.") unless File.exist?(release_notes_path)
|
||||
UI.user_error!("Android release notes at #{release_notes_path} are empty.") unless env_present?(File.read(release_notes_path))
|
||||
end
|
||||
|
||||
def android_changelog_path(version_code)
|
||||
File.join(__dir__, "metadata", "android", "en-US", "changelogs", "#{version_code}.txt")
|
||||
end
|
||||
|
||||
def sync_android_changelog!(version_code)
|
||||
release_notes_path = android_release_notes_path
|
||||
UI.user_error!("Missing Android release notes at #{release_notes_path}.") unless File.exist?(release_notes_path)
|
||||
validate_android_release_notes!
|
||||
|
||||
changelog_path = android_changelog_path(version_code)
|
||||
FileUtils.mkdir_p(File.dirname(changelog_path))
|
||||
File.write(changelog_path, File.read(release_notes_path))
|
||||
File.write(changelog_path, File.read(android_release_notes_path))
|
||||
changelog_path
|
||||
end
|
||||
|
||||
@@ -178,6 +197,69 @@ def capture_android_screenshots!
|
||||
sh(shell_join(["bash", File.join(repo_root, "scripts", "android-screenshots.sh")]))
|
||||
end
|
||||
|
||||
def read_android_release_signing_properties!(path)
|
||||
UI.user_error!("Missing materialized Android release signing properties at #{path}.") unless File.exist?(path)
|
||||
|
||||
properties = {}
|
||||
File.foreach(path) do |line|
|
||||
stripped = line.strip
|
||||
next if stripped.empty? || stripped.start_with?("#")
|
||||
|
||||
key, value = stripped.split("=", 2)
|
||||
next if key.nil? || key.empty? || value.nil?
|
||||
|
||||
properties[key] = value.strip
|
||||
end
|
||||
|
||||
missing = ANDROID_RELEASE_SIGNING_GRADLE_PROPERTIES.reject { |key| env_present?(properties[key]) }
|
||||
UI.user_error!("Materialized Android release signing properties are missing: #{missing.join(', ')}.") unless missing.empty?
|
||||
|
||||
properties
|
||||
end
|
||||
|
||||
def export_android_release_signing_properties!(path)
|
||||
read_android_release_signing_properties!(path).each do |key, value|
|
||||
ENV["ORG_GRADLE_PROJECT_#{key}"] = value
|
||||
end
|
||||
end
|
||||
|
||||
def sync_android_release_signing!
|
||||
sh(shell_join(["node", android_release_signing_script, "--mode", "sync-pull"]))
|
||||
export_android_release_signing_properties!(android_release_signing_materialized_properties_path)
|
||||
end
|
||||
|
||||
def prepare_android_release_signing!
|
||||
if env_present?(ENV["MATCH_PASSWORD"])
|
||||
sync_android_release_signing!
|
||||
elsif File.exist?(android_release_signing_materialized_properties_path)
|
||||
export_android_release_signing_properties!(android_release_signing_materialized_properties_path)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_android_release_signing!
|
||||
Dir.chdir(android_root) do
|
||||
sh(shell_join(["./gradlew", ":app:bundlePlayRelease", "--dry-run"]))
|
||||
end
|
||||
end
|
||||
|
||||
def print_android_release_plan!(version_metadata)
|
||||
UI.message("Android Play release plan:")
|
||||
UI.message(" package: #{play_package_name}")
|
||||
UI.message(" track: #{play_track}")
|
||||
UI.message(" release_status: #{play_release_status}")
|
||||
UI.message(" validate_only: #{play_validate_only?}")
|
||||
UI.message(" versionName: #{version_metadata.fetch(:version)}")
|
||||
UI.message(" versionCode: #{version_metadata.fetch(:version_code)}")
|
||||
end
|
||||
|
||||
def validate_android_release_preflight!(version_metadata)
|
||||
validate_play_auth!
|
||||
prepare_android_release_signing!
|
||||
validate_android_release_signing!
|
||||
validate_android_release_notes!
|
||||
print_android_release_plan!(version_metadata)
|
||||
end
|
||||
|
||||
def upload_play_store_metadata!(version_metadata)
|
||||
validate_android_screenshots!
|
||||
sync_android_changelog!(version_metadata.fetch(:version_code))
|
||||
@@ -230,6 +312,38 @@ platform :android do
|
||||
UI.success("Google Play API credentials are valid.")
|
||||
end
|
||||
|
||||
desc "Print the Android release signing plan"
|
||||
lane :signing_plan do
|
||||
sh(shell_join(["node", android_release_signing_script, "--mode", "plan"]))
|
||||
end
|
||||
|
||||
desc "Pull encrypted Android release signing assets and validate Gradle release signing"
|
||||
lane :signing_check do
|
||||
sync_android_release_signing!
|
||||
validate_android_release_signing!
|
||||
UI.success("Android release signing assets are available locally.")
|
||||
end
|
||||
|
||||
desc "Pull encrypted Android release signing assets from the shared signing repo"
|
||||
lane :signing_sync_pull do
|
||||
sync_android_release_signing!
|
||||
UI.success("Pulled Android release signing assets.")
|
||||
end
|
||||
|
||||
desc "Create or refresh encrypted Android release signing assets in the shared signing repo"
|
||||
lane :signing_sync_push do
|
||||
sh(shell_join(["node", android_release_signing_script, "--mode", "sync-push"]))
|
||||
UI.success("Pushed Android release signing assets.")
|
||||
end
|
||||
|
||||
desc "Validate Android Play release auth, signing, versioning, and release notes"
|
||||
lane :release_preflight do
|
||||
sync_android_versioning!
|
||||
version_metadata = read_android_version_metadata
|
||||
validate_android_release_preflight!(version_metadata)
|
||||
UI.success("Android Play release preflight passed for #{version_metadata[:version]} (#{version_metadata[:version_code]}).")
|
||||
end
|
||||
|
||||
desc "Upload Google Play metadata, changelog, and optional screenshots"
|
||||
lane :metadata do
|
||||
sync_android_versioning!
|
||||
@@ -242,6 +356,7 @@ platform :android do
|
||||
desc "Build signed Android release artifacts locally without uploading"
|
||||
lane :play_store_archive do
|
||||
sync_android_versioning!
|
||||
prepare_android_release_signing!
|
||||
build_release_artifacts!
|
||||
end
|
||||
|
||||
@@ -260,9 +375,9 @@ platform :android do
|
||||
|
||||
desc "Upload Android metadata, archive release artifacts, then upload the Play AAB"
|
||||
lane :release_upload do
|
||||
auth_check
|
||||
sync_android_versioning!
|
||||
version_metadata = read_android_version_metadata
|
||||
validate_android_release_preflight!(version_metadata)
|
||||
screenshots
|
||||
ENV["SUPPLY_UPLOAD_METADATA"] = "1"
|
||||
ENV["SUPPLY_UPLOAD_SCREENSHOTS"] = "1"
|
||||
|
||||
@@ -20,6 +20,35 @@ Optional app targeting:
|
||||
GOOGLE_PLAY_PACKAGE_NAME=ai.openclaw.app
|
||||
```
|
||||
|
||||
Android release signing uses the same private `apps-signing` repository and `MATCH_PASSWORD` secret as iOS, but with Android-specific encrypted assets. Pull the shared upload key before release validation:
|
||||
|
||||
```bash
|
||||
pnpm android:release:signing:plan
|
||||
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull
|
||||
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:check
|
||||
```
|
||||
|
||||
The pull command materializes decrypted signing files under `apps/android/build/release-signing/`, which is gitignored. Later Fastlane release commands reload those materialized values and export them to Gradle for the current process.
|
||||
|
||||
For the first setup or rotation, provide the Play upload keystore and a local signing properties file, then push encrypted assets to `apps-signing`:
|
||||
|
||||
```bash
|
||||
MATCH_PASSWORD=<signing repo password> \
|
||||
OPENCLAW_ANDROID_UPLOAD_KEYSTORE=<path-to-upload-keystore.jks> \
|
||||
OPENCLAW_ANDROID_SIGNING_PROPERTIES=<path-to-android-signing.properties> \
|
||||
pnpm android:release:signing:sync:push
|
||||
```
|
||||
|
||||
The source signing properties file must contain:
|
||||
|
||||
```properties
|
||||
OPENCLAW_ANDROID_STORE_PASSWORD=<store-password>
|
||||
OPENCLAW_ANDROID_KEY_ALIAS=<upload-key-alias>
|
||||
OPENCLAW_ANDROID_KEY_PASSWORD=<key-password>
|
||||
```
|
||||
|
||||
Store the Google Play upload key, not the irreplaceable app signing key, when Play App Signing is enabled.
|
||||
|
||||
Validate auth:
|
||||
|
||||
```bash
|
||||
@@ -56,12 +85,19 @@ Release rules:
|
||||
|
||||
- `apps/android/version.json` is the pinned Android release version source.
|
||||
- `apps/android/Config/Version.properties` is generated from that source and read by Gradle.
|
||||
- `apps/android/CHANGELOG.md` is the Android-only changelog and release-note source.
|
||||
- `apps/android/fastlane/metadata/android/en-US/release_notes.txt` is generated from that changelog by `pnpm android:version:sync`.
|
||||
- `apps/android/Config/ReleaseSigning.json` pins the encrypted Android signing assets in the shared signing repo.
|
||||
- `MATCH_PASSWORD` enables Fastlane to pull encrypted Android signing assets into `apps/android/build/release-signing/` before release validation or archive builds.
|
||||
- Supported pinned Android versions use CalVer: `YYYY.M.D`.
|
||||
- `versionCode` uses `YYYYMMDDNN`, where `NN` is a two-digit build number for the pinned version.
|
||||
- `pnpm android:version:pin -- --from-gateway` promotes the current root gateway version into the pinned Android release version.
|
||||
- `pnpm android:version:pin -- --version 2026.6.5 --version-code 2026060502` increments another build on the same Android release train.
|
||||
- `pnpm android:version:sync` updates generated version artifacts.
|
||||
- `pnpm android:version:check` validates checked-in Android version artifacts.
|
||||
- `pnpm android:release:preflight` validates Google Play auth, Android release signing, synced versioning, release notes, and prints the package/track/version/versionCode that will be uploaded.
|
||||
- `pnpm android:release:signing:sync:pull` pulls encrypted Android signing assets from `apps-signing`.
|
||||
- `pnpm android:release:signing:sync:push` creates or refreshes encrypted Android signing assets in `apps-signing`.
|
||||
- `pnpm android:screenshots` builds and installs the Play debug app, launches deterministic screenshot scenes, and captures raw PNGs.
|
||||
- `pnpm android:release:archive` builds the signed Play AAB and third-party APK into `apps/android/build/release-artifacts/`.
|
||||
- `pnpm android:release:upload` uploads the Play AAB to the configured Google Play track. The default track is `internal`.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
e2a646aa93124c089fcfed3c3ef982c88d1fdd2170fcdec274446f3d02f20d2b plugin-sdk-api-baseline.json
|
||||
f1762c7b4bbaea4a3ce47ab943daaa6ca3dbc58322cc5d39688da66b3d483a2d plugin-sdk-api-baseline.jsonl
|
||||
c84eab270f19d11a807ce71e783d35ee95a7620295dbffcca7fff31dacfcc882 plugin-sdk-api-baseline.json
|
||||
55656396a5f1941af61603402c43e23e0ffc90003e7efa7c1857c4541a0f1bb4 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -1175,8 +1175,24 @@
|
||||
"source": "Control UI",
|
||||
"target": "Control UI"
|
||||
},
|
||||
{
|
||||
"source": "Models CLI",
|
||||
"target": "模型 CLI"
|
||||
},
|
||||
{
|
||||
"source": "Z.AI (GLM)",
|
||||
"target": "Z.AI (GLM)"
|
||||
},
|
||||
{
|
||||
"source": "Cohere",
|
||||
"target": "Cohere"
|
||||
},
|
||||
{
|
||||
"source": "Cohere plugin",
|
||||
"target": "Cohere 插件"
|
||||
},
|
||||
{
|
||||
"source": "cohere",
|
||||
"target": "cohere"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -189,7 +189,7 @@ Every lane uploads GitHub artifacts. When `CLAWGRIT_REPORTS_TOKEN` is configured
|
||||
|
||||
## Full Release Validation
|
||||
|
||||
`Full Release Validation` is the manual umbrella workflow for "run everything before release." It accepts a branch, tag, or full commit SHA, dispatches the manual `CI` workflow with that target, dispatches `Plugin Prerelease` for release-only plugin/package/static/Docker proof, and dispatches `OpenClaw Release Checks` for install smoke, package acceptance, cross-OS package checks, QA Lab parity, Matrix, and Telegram lanes. Stable/default runs keep exhaustive live/E2E and Docker release-path coverage behind `run_release_soak=true`; `release_profile=full` forces that soak coverage on so broad advisory validation remains broad. With `rerun_group=all` and `release_profile=full`, it also runs `NPM Telegram Beta E2E` against the `release-package-under-test` artifact from release checks. After publishing, pass `release_package_spec` to reuse the shipped npm package across release checks, Package Acceptance, Docker, cross-OS, and Telegram without rebuilding. Use `npm_telegram_package_spec` only when Telegram must prove a different package. The Codex plugin live package lane uses the same selected state by default: published `release_package_spec=openclaw@<tag>` derives `codex_plugin_spec=npm:@openclaw/codex@<tag>`, while SHA/artifact runs pack `extensions/codex` from the selected ref. Set `codex_plugin_spec` explicitly for custom plugin sources such as `npm:`, `npm-pack:`, or `git:` specs.
|
||||
`Full Release Validation` is the manual umbrella workflow for "run everything before release." It accepts a branch, tag, or full commit SHA, dispatches the manual `CI` workflow with that target, dispatches `Plugin Prerelease` for release-only plugin/package/static/Docker proof, and dispatches `OpenClaw Release Checks` for install smoke, package acceptance, cross-OS package checks, QA Lab parity, Matrix, and Telegram lanes. Stable and full profiles always include exhaustive live/E2E and Docker release-path soak coverage; the beta profile can opt in with `run_release_soak=true`. With `rerun_group=all` and `release_profile=full`, it also runs `NPM Telegram Beta E2E` against the `release-package-under-test` artifact from release checks. After publishing, pass `release_package_spec` to reuse the shipped npm package across release checks, Package Acceptance, Docker, cross-OS, and Telegram without rebuilding. Use `npm_telegram_package_spec` only when Telegram must prove a different package. The Codex plugin live package lane uses the same selected state by default: published `release_package_spec=openclaw@<tag>` derives `codex_plugin_spec=npm:@openclaw/codex@<tag>`, while SHA/artifact runs pack `extensions/codex` from the selected ref. Set `codex_plugin_spec` explicitly for custom plugin sources such as `npm:`, `npm-pack:`, or `git:` specs.
|
||||
|
||||
See [Full release validation](/reference/full-release-validation) for the
|
||||
stage matrix, exact workflow job names, profile differences, artifacts, and
|
||||
@@ -232,9 +232,9 @@ different SHA.
|
||||
|
||||
`release_profile` controls live/provider breadth passed into release checks. The
|
||||
manual release workflows default to `stable`; use `full` only when you
|
||||
intentionally want the broad advisory provider/media matrix. `run_release_soak`
|
||||
controls whether stable/default release checks run the exhaustive live/E2E and
|
||||
Docker release-path soak; `full` forces soak on.
|
||||
intentionally want the broad advisory provider/media matrix. Stable and full
|
||||
release checks always run the exhaustive live/E2E and Docker release-path soak;
|
||||
the beta profile can opt in with `run_release_soak=true`.
|
||||
|
||||
- `minimum` keeps the fastest OpenAI/core release-critical lanes.
|
||||
- `stable` adds the stable provider/backend set.
|
||||
|
||||
@@ -231,7 +231,7 @@ Retention and pruning are controlled in config:
|
||||
## Migrating older jobs
|
||||
|
||||
<Note>
|
||||
If you have cron jobs from before the current delivery and store format, run `openclaw doctor --fix`. Doctor normalizes legacy cron fields (`jobId`, `schedule.cron`, top-level delivery fields including legacy `threadId`, payload `provider` delivery aliases) and migrates `notify: true` webhook fallback jobs from `cron.webhook` to explicit webhook delivery. Jobs that already announce to a chat keep that delivery and get a completion webhook destination.
|
||||
If you have cron jobs from before the current delivery and store format, run `openclaw doctor --fix`. Doctor normalizes legacy cron fields (`jobId`, `schedule.cron`, top-level delivery fields including legacy `threadId`, payload `provider` delivery aliases) and migrates `notify: true` webhook fallback jobs from `cron.webhook` to explicit webhook delivery. Jobs that already announce to a chat keep that delivery and get a completion webhook destination. When `cron.webhook` is unset, the inert top-level `notify` marker is removed for jobs with no migration target (the existing delivery is preserved unchanged), so `doctor --fix` no longer keeps re-warning about them.
|
||||
</Note>
|
||||
|
||||
## Common edits
|
||||
|
||||
@@ -11,13 +11,17 @@ sidebarTitle: "MCP"
|
||||
`openclaw mcp` has two jobs:
|
||||
|
||||
- run OpenClaw as an MCP server with `openclaw mcp serve`
|
||||
- manage OpenClaw-owned outbound MCP server definitions with `list`, `show`, `status`, `doctor`, `probe`, `add`, `set`, `configure`, `tools`, `login`, `logout`, `reload`, and `unset`
|
||||
- manage OpenClaw-managed outbound MCP server definitions with `list`, `show`, `status`, `doctor`, `probe`, `add`, `set`, `configure`, `tools`, `login`, `logout`, `reload`, and `unset`
|
||||
|
||||
In other words:
|
||||
|
||||
- `serve` is OpenClaw acting as an MCP server
|
||||
- the other subcommands are OpenClaw acting as an MCP client-side registry for MCP servers its runtimes may consume later
|
||||
|
||||
<Note>
|
||||
`list`, `show`, `set`, and `unset` only read and write OpenClaw-managed `mcp.servers` entries in OpenClaw config. They do not include mcporter servers from `config/mcporter.json`; use `mcporter list` for that registry.
|
||||
</Note>
|
||||
|
||||
Use [`openclaw acp`](/cli/acp) when OpenClaw should host a coding harness session itself and route that runtime through ACP.
|
||||
|
||||
## Choose the right MCP path
|
||||
@@ -368,7 +372,7 @@ For broader testing context, see [Testing](/help/testing).
|
||||
This is the `openclaw mcp list`, `show`, `status`, `doctor`, `probe`, `add`, `set`,
|
||||
`configure`, `tools`, `login`, `logout`, `reload`, and `unset` path.
|
||||
|
||||
These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP server definitions under `mcp.servers` in OpenClaw config.
|
||||
These commands do not expose OpenClaw over MCP. They manage OpenClaw-managed MCP server definitions under `mcp.servers` in OpenClaw config. They do not read mcporter servers from `config/mcporter.json`.
|
||||
|
||||
Those saved definitions are for runtimes that OpenClaw launches or configures later, such as embedded OpenClaw and other runtime adapters. OpenClaw stores the definitions centrally so those runtimes do not need to keep their own duplicate MCP server lists.
|
||||
|
||||
|
||||
@@ -107,6 +107,10 @@ Notes:
|
||||
in the shared managed skills directory when combined with `--global`.
|
||||
- `verify <slug>` prints ClawHub's `clawhub.skill.verify.v1` JSON envelope by
|
||||
default. There is no `--json` flag because JSON is already the default.
|
||||
- When ClawHub returns server-resolved source provenance, verify JSON also
|
||||
includes a commit-pinned `openclaw.verifiedSourceUrl`. Unavailable or
|
||||
self-declared source URLs stay only in the raw provenance envelope and are not
|
||||
promoted.
|
||||
- `verify` uses `.clawhub/origin.json` for installed ClawHub skills, so it
|
||||
verifies the installed version against the registry it came from. `--version`
|
||||
and `--tag` override the version selector but keep that installed registry
|
||||
|
||||
@@ -296,6 +296,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
|
||||
| --------------------------------------- | -------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
|
||||
| BytePlus | `byteplus` / `byteplus-plan` | `BYTEPLUS_API_KEY` | `byteplus-plan/ark-code-latest` |
|
||||
| Cerebras | `cerebras` | `CEREBRAS_API_KEY` | `cerebras/zai-glm-4.7` |
|
||||
| Cohere | `cohere` | `COHERE_API_KEY` | `cohere/command-a-03-2025` |
|
||||
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | `CLOUDFLARE_AI_GATEWAY_API_KEY` | - |
|
||||
| DeepInfra | `deepinfra` | `DEEPINFRA_API_KEY` | `deepinfra/deepseek-ai/DeepSeek-V4-Flash` |
|
||||
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek/deepseek-v4-flash` |
|
||||
|
||||
@@ -1417,6 +1417,7 @@
|
||||
"providers/azure-speech",
|
||||
"providers/cerebras",
|
||||
"providers/chutes",
|
||||
"providers/cohere",
|
||||
"providers/claude-max-api-proxy",
|
||||
"providers/cloudflare-ai-gateway",
|
||||
"providers/comfy",
|
||||
|
||||
@@ -373,11 +373,11 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
- top-level payload fields (`message`, `model`, `thinking`, ...) → `payload`
|
||||
- top-level delivery fields (`deliver`, `channel`, `to`, `provider`, ...) → `delivery`
|
||||
- payload `provider` delivery aliases → explicit `delivery.channel`
|
||||
- legacy `notify: true` webhook fallback jobs → explicit webhook delivery from `cron.webhook`; announce jobs keep their chat delivery and get `delivery.completionDestination`
|
||||
- legacy `notify: true` webhook fallback jobs → explicit webhook delivery from `cron.webhook` when set; announce jobs keep their chat delivery and get `delivery.completionDestination`. When `cron.webhook` is unset, the inert top-level `notify` marker is removed for no-target jobs (existing delivery, including announce, is preserved) since runtime delivery never reads it
|
||||
|
||||
The Gateway also sanitizes malformed cron rows at load time so valid jobs keep running. Raw malformed rows are copied to `jobs-quarantine.json` next to the active store before they are removed from `jobs.json`; doctor reports quarantined rows so you can review or repair them manually.
|
||||
|
||||
Doctor and Gateway startup use the same `notify: true` migration before the scheduler runs. If `cron.webhook` is missing, doctor warns and leaves the legacy notify marker for manual repair.
|
||||
Gateway startup normalizes the runtime projection and ignores the top-level `notify` marker, but leaves the persisted cron config for doctor repair. When `cron.webhook` is unset, doctor removes the inert marker for jobs with no migration target (`delivery.mode` none/absent, an unusable webhook target, or existing announce/chat delivery), leaving the existing delivery untouched, so repeated `doctor --fix` runs no longer re-warn about the same job. If `cron.webhook` is set but not a valid HTTP(S) URL, doctor still warns and leaves the marker so you can fix the URL.
|
||||
|
||||
On Linux, doctor also warns when the user's crontab still invokes legacy `~/.openclaw/bin/ensure-whatsapp.sh`. That host-local script is not maintained by current OpenClaw and can write false `Gateway inactive` messages to `~/.openclaw/logs/whatsapp-health.log` when cron cannot reach the systemd user bus. Remove the stale crontab entry with `crontab -e`; use `openclaw channels status --probe`, `openclaw doctor`, and `openclaw gateway status` for current health checks.
|
||||
|
||||
|
||||
@@ -335,6 +335,8 @@ the config fields that accept SecretRefs.
|
||||
- `BWS_ACCESS_TOKEN` available to the Gateway service.
|
||||
- `PATH` passed to the resolver, or `BWS_BIN` set to the absolute `bws`
|
||||
binary path.
|
||||
- `BWS_SERVER_URL` must be set in the environment when using a self-hosted
|
||||
Bitwarden instance.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -343,7 +345,7 @@ the config fields that accept SecretRefs.
|
||||
bws: {
|
||||
source: "exec",
|
||||
command: "/usr/local/bin/openclaw-bws-resolver.mjs",
|
||||
passEnv: ["BWS_ACCESS_TOKEN", "PATH", "BWS_BIN"],
|
||||
passEnv: ["BWS_ACCESS_TOKEN", "BWS_SERVER_URL", "PATH", "BWS_BIN"],
|
||||
jsonOnly: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -46,6 +46,29 @@ Docker is **optional**. Use it only if you want a containerized gateway or to va
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Airgapped rerun">
|
||||
On offline hosts, transfer and load the image first:
|
||||
|
||||
```bash
|
||||
docker load -i openclaw-image.tar
|
||||
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
|
||||
./scripts/docker/setup.sh --offline
|
||||
```
|
||||
|
||||
`--offline` verifies that `OPENCLAW_IMAGE` already exists locally, disables
|
||||
implicit Compose pulls and builds, then runs the normal setup flow such as
|
||||
`.env` synchronization, permission fixes, onboarding, gateway config sync,
|
||||
and Compose startup.
|
||||
|
||||
If `OPENCLAW_SANDBOX=1`, offline setup also checks the configured default
|
||||
and active per-agent sandbox images on the daemon behind
|
||||
`OPENCLAW_DOCKER_SOCKET`. Docker-backed browser images must also carry the
|
||||
current OpenClaw browser contract label. When a required image is missing or
|
||||
incompatible, setup exits without changing sandbox configuration instead of
|
||||
reporting success with an unusable sandbox.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Complete onboarding">
|
||||
The setup script runs onboarding automatically. It will:
|
||||
|
||||
|
||||
@@ -103,8 +103,46 @@ Supported `appServer` fields:
|
||||
| `approvalsReviewer` | `"user"` or an allowed guardian reviewer | Use `"auto_review"` to let Codex review native approval prompts when allowed. |
|
||||
| `defaultWorkspaceDir` | current process directory | Workspace used by `/codex bind` when `--cwd` is omitted. |
|
||||
| `serviceTier` | unset | Optional Codex app-server service tier. `"priority"` enables fast-mode routing, `"flex"` requests flex processing, and `null` clears the override. Legacy `"fast"` is accepted as `"priority"`. |
|
||||
| `networkProxy` | disabled | Opt into Codex permissions-profile networking for app-server commands. OpenClaw defines the selected `permissions.<profile>.network` config and selects it with `default_permissions` instead of sending `sandbox`. |
|
||||
| `experimental.sandboxExecServer` | `false` | Preview opt-in that registers an OpenClaw sandbox-backed Codex environment with Codex app-server 0.132.0 or newer so native Codex execution can run inside the active OpenClaw sandbox. |
|
||||
|
||||
`appServer.networkProxy` is explicit because it changes the Codex sandbox
|
||||
contract. When enabled, OpenClaw also sets `features.network_proxy.enabled` and
|
||||
`default_permissions` in the Codex thread config so the generated permission
|
||||
profile can start Codex managed networking. By default, OpenClaw generates a
|
||||
collision-resistant `openclaw-network-<fingerprint>` profile name from the
|
||||
profile body; use `profileName` only when a stable local name is required.
|
||||
|
||||
```js
|
||||
export default {
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: {
|
||||
config: {
|
||||
appServer: {
|
||||
sandbox: "workspace-write",
|
||||
networkProxy: {
|
||||
enabled: true,
|
||||
domains: {
|
||||
"api.openai.com": "allow",
|
||||
"blocked.example.com": "deny",
|
||||
},
|
||||
allowUpstreamProxy: true,
|
||||
proxyUrl: "http://127.0.0.1:3128",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
If the normal app-server runtime would be `danger-full-access`, enabling
|
||||
`networkProxy` uses workspace-style filesystem access for the generated
|
||||
permission profile. Codex managed network enforcement is sandboxed networking,
|
||||
so a full-access profile would not protect outbound traffic.
|
||||
|
||||
The plugin blocks older or unversioned app-server handshakes. Codex app-server
|
||||
must report stable version `0.125.0` or newer.
|
||||
|
||||
|
||||
@@ -561,8 +561,52 @@ Supported `appServer` fields:
|
||||
| `sandbox` | `"danger-full-access"` or an allowed guardian sandbox | Native Codex sandbox mode sent to thread start/resume. Guardian defaults prefer `"workspace-write"` when allowed, otherwise `"read-only"`. When an OpenClaw sandbox is active, `danger-full-access` turns use Codex `workspace-write` with network access derived from the OpenClaw sandbox egress setting. |
|
||||
| `approvalsReviewer` | `"user"` or an allowed guardian reviewer | Use `"auto_review"` to let Codex review native approval prompts when allowed, otherwise `guardian_subagent` or `user`. `guardian_subagent` remains a legacy alias. |
|
||||
| `serviceTier` | unset | Optional Codex app-server service tier. `"priority"` enables fast-mode routing, `"flex"` requests flex processing, `null` clears the override, and legacy `"fast"` is accepted as `"priority"`. |
|
||||
| `networkProxy` | disabled | Opt into Codex permissions-profile networking for app-server commands. OpenClaw defines the selected `permissions.<profile>.network` config and selects it with `default_permissions` instead of sending `sandbox`. |
|
||||
| `experimental.sandboxExecServer` | `false` | Preview opt-in that registers an OpenClaw sandbox-backed Codex environment with Codex app-server 0.132.0 or newer so native Codex execution can run inside the active OpenClaw sandbox. |
|
||||
|
||||
`appServer.networkProxy` is explicit because it changes the Codex sandbox
|
||||
contract. When enabled, OpenClaw also sets `features.network_proxy.enabled` and
|
||||
`default_permissions` in the Codex thread config so the generated permission
|
||||
profile can start Codex managed networking. By default, OpenClaw generates a
|
||||
collision-resistant `openclaw-network-<fingerprint>` profile name from the
|
||||
profile body; use `profileName` only when a stable local name is required.
|
||||
|
||||
```js
|
||||
export default {
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: {
|
||||
config: {
|
||||
appServer: {
|
||||
sandbox: "workspace-write",
|
||||
networkProxy: {
|
||||
enabled: true,
|
||||
domains: {
|
||||
"api.openai.com": "allow",
|
||||
"blocked.example.com": "deny",
|
||||
},
|
||||
unixSockets: {
|
||||
"/tmp/proxy.sock": "allow",
|
||||
"/tmp/blocked.sock": "none",
|
||||
},
|
||||
allowUpstreamProxy: true,
|
||||
proxyUrl: "http://127.0.0.1:3128",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
If the normal app-server runtime would be `danger-full-access`, enabling
|
||||
`networkProxy` uses workspace-style filesystem access for the generated
|
||||
permission profile. Codex managed network enforcement is sandboxed networking,
|
||||
so a full-access profile would not protect outbound traffic.
|
||||
Domain entries use `allow` or `deny`; Unix socket entries use Codex's
|
||||
`allow` or `none` values.
|
||||
|
||||
OpenClaw-owned dynamic tool calls are bounded independently from
|
||||
`appServer.requestTimeoutMs`: Codex `item/tool/call` requests use a 90 second
|
||||
OpenClaw watchdog by default. A positive per-call `timeoutMs` argument extends
|
||||
|
||||
@@ -51,7 +51,7 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
## Core npm package
|
||||
|
||||
90 plugins
|
||||
91 plugins
|
||||
|
||||
- **[admin-http-rpc](/plugins/reference/admin-http-rpc)** (`@openclaw/admin-http-rpc`) - included in OpenClaw. OpenClaw admin HTTP RPC endpoint.
|
||||
|
||||
@@ -81,6 +81,8 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
- **[codex-supervisor](/plugins/reference/codex-supervisor)** (`@openclaw/codex-supervisor`) - included in OpenClaw. Supervise Codex app-server sessions from OpenClaw.
|
||||
|
||||
- **[cohere](/plugins/reference/cohere)** (`@openclaw/cohere-provider`) - included in OpenClaw. Adds Cohere model provider support to OpenClaw.
|
||||
|
||||
- **[comfy](/plugins/reference/comfy)** (`@openclaw/comfy-provider`) - included in OpenClaw. Adds ComfyUI model provider support to OpenClaw.
|
||||
|
||||
- **[copilot-proxy](/plugins/reference/copilot-proxy)** (`@openclaw/copilot-proxy`) - included in OpenClaw. Adds Copilot Proxy model provider support to OpenClaw.
|
||||
|
||||
@@ -15,5 +15,5 @@ This page is generated from `extensions/*/package.json` and
|
||||
pnpm plugins:inventory:gen
|
||||
```
|
||||
|
||||
Use [Plugin inventory](/plugins/plugin-inventory) to browse all 127
|
||||
Use [Plugin inventory](/plugins/plugin-inventory) to browse all 128
|
||||
generated plugin reference pages by distribution, package, and description.
|
||||
|
||||
23
docs/plugins/reference/cohere.md
Normal file
23
docs/plugins/reference/cohere.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
summary: "Adds Cohere model provider support to OpenClaw."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the cohere plugin
|
||||
title: "Cohere plugin"
|
||||
---
|
||||
|
||||
# Cohere plugin
|
||||
|
||||
Adds Cohere model provider support to OpenClaw.
|
||||
|
||||
## Distribution
|
||||
|
||||
- Package: `@openclaw/cohere-provider`
|
||||
- Install route: included in OpenClaw
|
||||
|
||||
## Surface
|
||||
|
||||
providers: cohere
|
||||
|
||||
## Related docs
|
||||
|
||||
- [cohere](/providers/cohere)
|
||||
63
docs/providers/cohere.md
Normal file
63
docs/providers/cohere.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
summary: "Cohere setup (auth + model selection)"
|
||||
title: "Cohere"
|
||||
read_when:
|
||||
- You want to use Cohere with OpenClaw
|
||||
- You need the Cohere API key env var or CLI auth choice
|
||||
---
|
||||
|
||||
[Cohere](https://cohere.com) provides OpenAI-compatible inference through its Compatibility API. OpenClaw includes a bundled Cohere provider plugin with the Command A model catalog.
|
||||
|
||||
| Property | Value |
|
||||
| --------------- | ---------------------------------------- |
|
||||
| Provider id | `cohere` |
|
||||
| Plugin | bundled, `enabledByDefault: true` |
|
||||
| Auth env var | `COHERE_API_KEY` |
|
||||
| Onboarding flag | `--auth-choice cohere-api-key` |
|
||||
| Direct CLI flag | `--cohere-api-key <key>` |
|
||||
| API | OpenAI-compatible (`openai-completions`) |
|
||||
| Base URL | `https://api.cohere.ai/compatibility/v1` |
|
||||
| Default model | `cohere/command-a-03-2025` |
|
||||
|
||||
## Get started
|
||||
|
||||
1. Create a Cohere API key.
|
||||
2. Run onboarding:
|
||||
|
||||
```bash
|
||||
openclaw onboard --non-interactive \
|
||||
--auth-choice cohere-api-key \
|
||||
--cohere-api-key "$COHERE_API_KEY"
|
||||
```
|
||||
|
||||
3. Confirm the catalog is available:
|
||||
|
||||
```bash
|
||||
openclaw models list --provider cohere
|
||||
```
|
||||
|
||||
The default model is set only when no primary model is already configured.
|
||||
|
||||
## Environment-only setup
|
||||
|
||||
Make `COHERE_API_KEY` available to the Gateway process, then select the bundled model:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "cohere/command-a-03-2025" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
If the Gateway runs as a daemon or in Docker, configure `COHERE_API_KEY` for that service. Exporting it only in an interactive shell does not make it available to an already-running Gateway.
|
||||
</Note>
|
||||
|
||||
## Related
|
||||
|
||||
- [Model providers](/concepts/model-providers)
|
||||
- [Models CLI](/cli/models)
|
||||
- [Provider directory](/providers)
|
||||
@@ -33,6 +33,7 @@ Looking for chat channel docs (WhatsApp/Telegram/Discord/Slack/Mattermost (plugi
|
||||
- [BytePlus (International)](/concepts/model-providers#byteplus-international)
|
||||
- [Cerebras](/providers/cerebras)
|
||||
- [Chutes](/providers/chutes)
|
||||
- [Cohere](/providers/cohere)
|
||||
- [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway)
|
||||
- [ComfyUI](/providers/comfy)
|
||||
- [DeepSeek](/providers/deepseek)
|
||||
|
||||
@@ -27,6 +27,7 @@ model as `provider/model`.
|
||||
- [Anthropic (API + Claude CLI)](/providers/anthropic)
|
||||
- [BytePlus (International)](/concepts/model-providers#byteplus-international)
|
||||
- [Chutes](/providers/chutes)
|
||||
- [Cohere](/providers/cohere)
|
||||
- [ComfyUI](/providers/comfy)
|
||||
- [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway)
|
||||
- [DeepInfra](/providers/deepinfra)
|
||||
|
||||
@@ -1097,11 +1097,10 @@ sessionId})`; create, branch, continue, list, and fork flows live in their
|
||||
legacy `jobs.json`, `jobs-state.json`, and `runs/*.jsonl` files and removes
|
||||
the imported sources. Plugin target writebacks update matching `cron_jobs`
|
||||
rows instead of loading and replacing the whole cron store.
|
||||
- Doctor and Gateway startup translate legacy `notify: true` webhook fallback
|
||||
into explicit SQLite delivery before the scheduler runs. Jobs that already
|
||||
announce to a chat keep that delivery and receive a webhook
|
||||
`completionDestination`; jobs without `cron.webhook` are reported for manual
|
||||
repair.
|
||||
- Gateway startup ignores legacy `notify: true` markers in the runtime
|
||||
projection. Doctor translates them into explicit SQLite delivery when
|
||||
`cron.webhook` is valid, removes inert markers when it is unset, and preserves
|
||||
them with a warning when the configured webhook is invalid.
|
||||
- Outbound and session delivery queues now store queue status, entry kind,
|
||||
session key, channel, target, account id, retry count, last attempt/error,
|
||||
recovery state, and platform-send markers as typed columns in the shared
|
||||
|
||||
@@ -118,8 +118,8 @@ the maintainer-only release runbook.
|
||||
`CHANGELOG.md` section. Stable releases published to npm `latest` become the
|
||||
GitHub latest release; stable maintenance releases kept on npm `beta` are
|
||||
created with GitHub `latest=false`. The workflow also uploads the preflight
|
||||
dependency evidence to the GitHub release as
|
||||
`openclaw-<version>-dependency-evidence.zip` for post-release incident
|
||||
dependency evidence, the full-validation manifest, and postpublish registry
|
||||
verification evidence to the GitHub release for post-release incident
|
||||
response. The publish workflow prints child run IDs immediately, auto-approves
|
||||
release environment gates the workflow token is allowed to approve, summarizes
|
||||
failed child jobs with log tails, closes out the GitHub release and dependency
|
||||
@@ -178,6 +178,27 @@ release state.
|
||||
`OPENCLAW_TESTBOX=1 pnpm check:changed`. Push, then verify `origin/main`
|
||||
contains the shipped version and changelog before calling the stable release
|
||||
done.
|
||||
6. Keep the repository variables `RELEASE_ROLLBACK_DRILL_ID` and
|
||||
`RELEASE_ROLLBACK_DRILL_DATE` current after each private rollback drill.
|
||||
`OpenClaw Stable Main Closeout` starts from the `main` push that carries the
|
||||
shipped version, changelog, and appcast after stable publication. It reads
|
||||
immutable postpublish evidence to bind the shipped tag to its Full Release
|
||||
Validation and Publish runs, then verifies the stable main state, release,
|
||||
mandatory stable soak, and blocking performance evidence. It attaches an
|
||||
immutable closeout manifest and checksum to the GitHub release. The automatic
|
||||
push trigger skips legacy releases that predate immutable postpublish
|
||||
evidence; it never treats that skip as a completed closeout. A complete
|
||||
closeout requires both assets and a matching checksum. A partial manifest
|
||||
replays its recorded `main` SHA and rollback drill to regenerate identical
|
||||
bytes, then attaches the missing checksum; an invalid pair, or a checksum
|
||||
without a manifest, stays blocking. A missing or more-than-90-day-old drill
|
||||
record blocks a new evidence-backed closeout; private recovery commands
|
||||
remain in the maintainer-only runbook. Use manual dispatch only to repair or
|
||||
replay an evidence-backed stable closeout.
|
||||
A legacy fallback correction tag may reuse base-package evidence only when
|
||||
the correction tag resolves to the same source commit as the base stable tag.
|
||||
A correction with different source must publish and verify its own package
|
||||
evidence.
|
||||
|
||||
## Release preflight
|
||||
|
||||
@@ -205,9 +226,9 @@ release state.
|
||||
kick off all pre-release test boxes from one entrypoint. It accepts a branch,
|
||||
tag, or full commit SHA, dispatches manual `CI`, and dispatches
|
||||
`OpenClaw Release Checks` for install smoke, package acceptance, cross-OS
|
||||
package checks, QA Lab parity, Matrix, and Telegram lanes. Stable/default runs
|
||||
keep exhaustive live/E2E and Docker release-path soak behind
|
||||
`run_release_soak=true`; `release_profile=full` forces soak on. With
|
||||
package checks, QA Lab parity, Matrix, and Telegram lanes. Stable and full
|
||||
runs always include exhaustive live/E2E and Docker release-path soak;
|
||||
`run_release_soak=true` is retained for an explicit beta soak. With
|
||||
`release_profile=full` and `rerun_group=all`, it also runs package Telegram
|
||||
E2E against the `release-package-under-test` artifact from release checks.
|
||||
Provide `release_package_spec` after publishing a beta to reuse the shipped
|
||||
@@ -472,13 +493,12 @@ Use `release_profile` to select live/provider breadth:
|
||||
- `stable`: minimum plus stable provider/backend coverage for release approval
|
||||
- `full`: stable plus broad advisory provider/media coverage
|
||||
|
||||
Use `run_release_soak=true` with `stable` when the release-blocking lanes are
|
||||
green and you want the exhaustive live/E2E, Docker release-path, and
|
||||
bounded published upgrade-survivor sweep before promotion. That sweep covers
|
||||
Stable and full validation always run the exhaustive live/E2E, Docker
|
||||
release-path, and bounded published upgrade-survivor sweep before promotion.
|
||||
Use `run_release_soak=true` to request that same sweep for a beta. That sweep covers
|
||||
the latest four stable packages plus pinned `2026.4.23` and `2026.5.2`
|
||||
baselines plus older `2026.4.15` coverage, with duplicate baselines removed and
|
||||
each baseline sharded into its own Docker runner job. `full` implies
|
||||
`run_release_soak=true`.
|
||||
each baseline sharded into its own Docker runner job.
|
||||
|
||||
`OpenClaw Release Checks` uses the trusted workflow ref to resolve the target
|
||||
ref once as `release-package-under-test` and reuses that artifact in cross-OS,
|
||||
@@ -669,7 +689,7 @@ prepared release package artifact, `suite_profile=custom`,
|
||||
configured-auth update restart, live ClawHub skill install, stale plugin dependency cleanup, offline plugin
|
||||
fixtures, plugin update, and Telegram package QA against the same resolved
|
||||
tarball. Blocking release checks use the default latest published package
|
||||
baseline; `run_release_soak=true` or
|
||||
baseline; the beta profile with `run_release_soak=true`, `release_profile=stable`, or
|
||||
`release_profile=full` expands to every stable npm-published baseline from
|
||||
`2026.4.23` through `latest` plus reported-issue fixtures. Use
|
||||
Package Acceptance with `source=npm` for an already shipped candidate,
|
||||
@@ -835,8 +855,8 @@ package cannot ship without every publishable official plugin, including
|
||||
require the resolved commit to be reachable from an OpenClaw branch or
|
||||
release tag.
|
||||
- `run_release_soak`: opt into exhaustive live/E2E, Docker release-path, and
|
||||
all-since upgrade-survivor soak on stable/default release checks. It is forced
|
||||
on by `release_profile=full`.
|
||||
all-since upgrade-survivor soak for beta release checks. It is forced on by
|
||||
`release_profile=stable` and `release_profile=full`.
|
||||
|
||||
Rules:
|
||||
|
||||
|
||||
@@ -675,9 +675,10 @@ is disabled, uninstalled, or rolled back:
|
||||
clearCodeModeNamespacesForPlugin(pluginId);
|
||||
```
|
||||
|
||||
Use `unregisterCodeModeNamespace(namespaceId)` only when removing one known
|
||||
namespace. Tests can call `clearCodeModeNamespacesForTest()` to avoid leaking
|
||||
registrations across cases.
|
||||
Code-mode cleanup is plugin-owned; clear the plugin's namespace registrations
|
||||
when its lifecycle ends instead of keeping per-namespace teardown handles. Tests
|
||||
can call `clearCodeModeNamespacesForTest()` to avoid leaking registrations
|
||||
across cases.
|
||||
|
||||
### Test checklist
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ Child workflows use the trusted workflow ref for the harness and the input
|
||||
`ref` for the candidate under test. That keeps new validation logic available
|
||||
when validating an older release branch or tag.
|
||||
|
||||
By default, `release_profile=stable` runs the release-blocking lanes and skips
|
||||
the exhaustive live/Docker soak. Pass `run_release_soak=true` to include the
|
||||
soak lanes on a stable run. `release_profile=full` always enables soak lanes so
|
||||
the broad advisory profile never drops coverage silently.
|
||||
`release_profile=stable` and `release_profile=full` always run the exhaustive
|
||||
live/Docker soak. Pass `run_release_soak=true` to include the same soak lanes
|
||||
with the beta profile. Stable publication rejects a validation manifest without this
|
||||
soak and blocking product-performance evidence.
|
||||
|
||||
Package Acceptance normally builds the candidate tarball from the resolved
|
||||
`ref`, including full-SHA runs dispatched with `pnpm ci:full-release`. After a
|
||||
@@ -47,15 +47,15 @@ that plugin, then runs Codex CLI preflight and same-session OpenAI agent turns.
|
||||
|
||||
## Top-level stages
|
||||
|
||||
| Stage | Details |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Target resolution | **Job:** `Resolve target ref`<br />**Child workflow:** none<br />**Proves:** resolves the release branch, tag, or full commit SHA and records selected inputs.<br />**Rerun:** rerun the umbrella if this fails. |
|
||||
| Vitest and normal CI | **Job:** `Run normal full CI`<br />**Child workflow:** `CI`<br />**Proves:** manual full CI graph against the target ref, including Linux Node lanes, bundled plugin shards, plugin and channel contract shards, Node 22 compatibility, `check-*`, `check-additional-*`, built-artifact smoke checks, docs checks, Python skills, Windows, macOS, Control UI i18n, and Android via the umbrella.<br />**Rerun:** `rerun_group=ci`. |
|
||||
| Plugin prerelease | **Job:** `Run plugin prerelease validation`<br />**Child workflow:** `Plugin Prerelease`<br />**Proves:** release-only plugin static checks, agentic plugin coverage, full extension batch shards, plugin prerelease Docker lanes, and a non-blocking `plugin-inspector-advisory` artifact for compatibility triage.<br />**Rerun:** `rerun_group=plugin-prerelease`. |
|
||||
| Release checks | **Job:** `Run release/live/Docker/QA validation`<br />**Child workflow:** `OpenClaw Release Checks`<br />**Proves:** install smoke, cross-OS package checks, Package Acceptance, QA Lab parity, live Matrix, and live Telegram. With `run_release_soak=true` or `release_profile=full`, also runs exhaustive live/E2E suites and Docker release-path chunks.<br />**Rerun:** `rerun_group=release-checks` or a narrower release-checks handle. |
|
||||
| Package artifact | **Job:** `Prepare release package artifact`<br />**Child workflow:** none<br />**Proves:** creates the parent `release-package-under-test` tarball early enough for package-facing checks that do not need to wait for `OpenClaw Release Checks`.<br />**Rerun:** rerun the umbrella or provide `release_package_spec` for published-package reruns. |
|
||||
| Package Telegram | **Job:** `Run package Telegram E2E`<br />**Child workflow:** `NPM Telegram Beta E2E`<br />**Proves:** parent-artifact-backed Telegram package proof for `rerun_group=all` with `release_profile=full`, or published-package Telegram proof when `release_package_spec` or `npm_telegram_package_spec` is set.<br />**Rerun:** `rerun_group=npm-telegram` with `release_package_spec` or `npm_telegram_package_spec`. |
|
||||
| Umbrella verifier | **Job:** `Verify full validation`<br />**Child workflow:** none<br />**Proves:** re-checks recorded child run conclusions and appends slowest-job tables from child workflows.<br />**Rerun:** rerun only this job after rerunning a failed child to green. |
|
||||
| Stage | Details |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Target resolution | **Job:** `Resolve target ref`<br />**Child workflow:** none<br />**Proves:** resolves the release branch, tag, or full commit SHA and records selected inputs.<br />**Rerun:** rerun the umbrella if this fails. |
|
||||
| Vitest and normal CI | **Job:** `Run normal full CI`<br />**Child workflow:** `CI`<br />**Proves:** manual full CI graph against the target ref, including Linux Node lanes, bundled plugin shards, plugin and channel contract shards, Node 22 compatibility, `check-*`, `check-additional-*`, built-artifact smoke checks, docs checks, Python skills, Windows, macOS, Control UI i18n, and Android via the umbrella.<br />**Rerun:** `rerun_group=ci`. |
|
||||
| Plugin prerelease | **Job:** `Run plugin prerelease validation`<br />**Child workflow:** `Plugin Prerelease`<br />**Proves:** release-only plugin static checks, agentic plugin coverage, full extension batch shards, plugin prerelease Docker lanes, and a non-blocking `plugin-inspector-advisory` artifact for compatibility triage.<br />**Rerun:** `rerun_group=plugin-prerelease`. |
|
||||
| Release checks | **Job:** `Run release/live/Docker/QA validation`<br />**Child workflow:** `OpenClaw Release Checks`<br />**Proves:** install smoke, cross-OS package checks, Package Acceptance, QA Lab parity, live Matrix, and live Telegram. Stable and full profiles also run exhaustive live/E2E suites and Docker release-path chunks; beta can opt in with `run_release_soak=true`.<br />**Rerun:** `rerun_group=release-checks` or a narrower release-checks handle. |
|
||||
| Package artifact | **Job:** `Prepare release package artifact`<br />**Child workflow:** none<br />**Proves:** creates the parent `release-package-under-test` tarball early enough for package-facing checks that do not need to wait for `OpenClaw Release Checks`.<br />**Rerun:** rerun the umbrella or provide `release_package_spec` for published-package reruns. |
|
||||
| Package Telegram | **Job:** `Run package Telegram E2E`<br />**Child workflow:** `NPM Telegram Beta E2E`<br />**Proves:** parent-artifact-backed Telegram package proof for `rerun_group=all` with `release_profile=full`, or published-package Telegram proof when `release_package_spec` or `npm_telegram_package_spec` is set.<br />**Rerun:** `rerun_group=npm-telegram` with `release_package_spec` or `npm_telegram_package_spec`. |
|
||||
| Umbrella verifier | **Job:** `Verify full validation`<br />**Child workflow:** none<br />**Proves:** re-checks recorded child run conclusions and appends slowest-job tables from child workflows.<br />**Rerun:** rerun only this job after rerunning a failed child to green. |
|
||||
|
||||
For `ref=main` and `rerun_group=all`, a newer umbrella supersedes an older one.
|
||||
When the parent is cancelled, its monitor cancels any child workflow it already
|
||||
@@ -105,11 +105,11 @@ commands with package artifact and image reuse inputs when available.
|
||||
|
||||
`release_profile` mostly controls live/provider breadth inside release checks.
|
||||
It does not remove normal full CI, Plugin Prerelease, install smoke, package
|
||||
acceptance, or QA Lab. For `stable`, exhaustive repo/live E2E and Docker
|
||||
release-path chunks are soak coverage and run when `run_release_soak=true`.
|
||||
`full` forces soak coverage on and also makes the umbrella run package Telegram
|
||||
E2E against the parent release package artifact when `rerun_group=all`, so a full
|
||||
pre-publish candidate does not silently skip that Telegram package lane.
|
||||
acceptance, or QA Lab. Stable and full profiles always run exhaustive repo/live
|
||||
E2E and Docker release-path soak coverage. The beta profile can opt in with
|
||||
`run_release_soak=true`. The full profile also makes the umbrella run package
|
||||
Telegram E2E against the parent release package artifact when `rerun_group=all`,
|
||||
so a full pre-publish candidate does not silently skip that Telegram package lane.
|
||||
|
||||
| Profile | Intended use | Included live/provider coverage |
|
||||
| --------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -190,4 +190,24 @@ describe("ClickClack gateway", () => {
|
||||
abort.abort();
|
||||
await run;
|
||||
});
|
||||
|
||||
it("clears running status when backlog polling fails", async () => {
|
||||
mocks.client.events.mockRejectedValue(new Error("clickclack unavailable"));
|
||||
const abort = new AbortController();
|
||||
const ctx = createGatewayContext(abort.signal);
|
||||
|
||||
await expect(startClickClackGatewayAccount(ctx)).rejects.toThrow("clickclack unavailable");
|
||||
|
||||
expect(ctx.setStatus).toHaveBeenCalledWith({
|
||||
accountId: "default",
|
||||
running: true,
|
||||
configured: true,
|
||||
enabled: true,
|
||||
baseUrl: "https://clickclack.example",
|
||||
});
|
||||
expect(ctx.setStatus).toHaveBeenLastCalledWith({
|
||||
accountId: "default",
|
||||
running: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,62 +146,67 @@ export async function startClickClackGatewayAccount(
|
||||
});
|
||||
let afterCursor = "";
|
||||
let initialized = false;
|
||||
while (!ctx.abortSignal.aborted) {
|
||||
const backlog = await client.events(workspaceId, afterCursor);
|
||||
if (!initialized) {
|
||||
// First pass establishes the cursor without replaying historical backlog
|
||||
// into fresh gateway sessions.
|
||||
for (const event of backlog) {
|
||||
afterCursor = event.cursor || afterCursor;
|
||||
}
|
||||
initialized = true;
|
||||
} else {
|
||||
for (const event of backlog) {
|
||||
afterCursor = event.cursor || afterCursor;
|
||||
await processEvent({
|
||||
account,
|
||||
config: ctx.cfg,
|
||||
client,
|
||||
event,
|
||||
botUserId: account.botUserId,
|
||||
});
|
||||
}
|
||||
}
|
||||
const socket = client.websocket(workspaceId, afterCursor);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const abort = () => {
|
||||
socket.close();
|
||||
resolve();
|
||||
};
|
||||
ctx.abortSignal.addEventListener("abort", abort, { once: true });
|
||||
socket.on("message", (data) => {
|
||||
void (async () => {
|
||||
const event = parseSocketEvent(data);
|
||||
if (!event) {
|
||||
ctx.log?.warn?.(`[${account.accountId}] skipped malformed ClickClack websocket event`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
while (!ctx.abortSignal.aborted) {
|
||||
const backlog = await client.events(workspaceId, afterCursor);
|
||||
if (!initialized) {
|
||||
// First pass establishes the cursor without replaying historical backlog
|
||||
// into fresh gateway sessions.
|
||||
for (const event of backlog) {
|
||||
afterCursor = event.cursor || afterCursor;
|
||||
}
|
||||
initialized = true;
|
||||
} else {
|
||||
for (const event of backlog) {
|
||||
afterCursor = event.cursor || afterCursor;
|
||||
await processEvent({
|
||||
account,
|
||||
config: ctx.cfg,
|
||||
client,
|
||||
event,
|
||||
botUserId: account.botUserId ?? "",
|
||||
botUserId: account.botUserId,
|
||||
});
|
||||
})().catch(reject);
|
||||
});
|
||||
socket.on("close", () => {
|
||||
ctx.abortSignal.removeEventListener("abort", abort);
|
||||
resolve();
|
||||
});
|
||||
socket.on("error", reject);
|
||||
});
|
||||
if (!ctx.abortSignal.aborted) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, account.reconnectMs);
|
||||
}
|
||||
}
|
||||
const socket = client.websocket(workspaceId, afterCursor);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const abort = () => {
|
||||
socket.close();
|
||||
resolve();
|
||||
};
|
||||
ctx.abortSignal.addEventListener("abort", abort, { once: true });
|
||||
socket.on("message", (data) => {
|
||||
void (async () => {
|
||||
const event = parseSocketEvent(data);
|
||||
if (!event) {
|
||||
ctx.log?.warn?.(
|
||||
`[${account.accountId}] skipped malformed ClickClack websocket event`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
afterCursor = event.cursor || afterCursor;
|
||||
await processEvent({
|
||||
account,
|
||||
config: ctx.cfg,
|
||||
client,
|
||||
event,
|
||||
botUserId: account.botUserId ?? "",
|
||||
});
|
||||
})().catch(reject);
|
||||
});
|
||||
socket.on("close", () => {
|
||||
ctx.abortSignal.removeEventListener("abort", abort);
|
||||
resolve();
|
||||
});
|
||||
socket.on("error", reject);
|
||||
});
|
||||
if (!ctx.abortSignal.aborted) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, account.reconnectMs);
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ctx.setStatus({ accountId: account.accountId, running: false });
|
||||
}
|
||||
ctx.setStatus({ accountId: account.accountId, running: false });
|
||||
}
|
||||
|
||||
@@ -193,6 +193,47 @@
|
||||
"enum": ["user", "auto_review", "guardian_subagent"]
|
||||
},
|
||||
"serviceTier": { "type": ["string", "null"] },
|
||||
"networkProxy": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"profileName": { "type": "string" },
|
||||
"baseProfile": {
|
||||
"type": "string",
|
||||
"enum": ["read-only", "workspace"]
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": ["limited", "full"]
|
||||
},
|
||||
"domains": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"enum": ["allow", "deny"]
|
||||
}
|
||||
},
|
||||
"unixSockets": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"enum": ["allow", "none"]
|
||||
}
|
||||
},
|
||||
"proxyUrl": { "type": "string" },
|
||||
"socksUrl": { "type": "string" },
|
||||
"enableSocks5": { "type": "boolean" },
|
||||
"enableSocks5Udp": { "type": "boolean" },
|
||||
"allowUpstreamProxy": { "type": "boolean" },
|
||||
"allowLocalBinding": { "type": "boolean" },
|
||||
"dangerouslyAllowNonLoopbackProxy": { "type": "boolean" },
|
||||
"dangerouslyAllowAllUnixSockets": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"defaultWorkspaceDir": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -385,6 +426,81 @@
|
||||
"help": "Optional Codex app-server service tier. Use priority, flex, or null. Legacy fast is accepted as priority.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy": {
|
||||
"label": "Network Proxy",
|
||||
"help": "Enable Codex permissions-profile networking for app-server commands.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.enabled": {
|
||||
"label": "Network Proxy Enabled",
|
||||
"help": "When enabled, OpenClaw defines a Codex permissions profile and selects it with default_permissions instead of sandbox fields.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.profileName": {
|
||||
"label": "Network Proxy Profile",
|
||||
"help": "Optional stable Codex permissions profile name. Leave unset to use a generated openclaw-network fingerprint name.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.baseProfile": {
|
||||
"label": "Network Proxy Base",
|
||||
"help": "Filesystem access used by the generated profile. Defaults to read-only for read-only sandboxes and workspace otherwise.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.domains": {
|
||||
"label": "Network Domains",
|
||||
"help": "Domain allow and deny rules for Codex sandboxed networking.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.unixSockets": {
|
||||
"label": "Unix Sockets",
|
||||
"help": "Unix socket allow and none rules for Codex sandboxed networking.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.proxyUrl": {
|
||||
"label": "HTTP Proxy URL",
|
||||
"help": "HTTP listener URL used by Codex sandboxed networking.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.socksUrl": {
|
||||
"label": "SOCKS Proxy URL",
|
||||
"help": "SOCKS listener URL used by Codex sandboxed networking.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.enableSocks5": {
|
||||
"label": "Enable SOCKS5",
|
||||
"help": "Expose SOCKS5 support for the generated Codex permissions profile.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.enableSocks5Udp": {
|
||||
"label": "Enable SOCKS5 UDP",
|
||||
"help": "Allow UDP over the SOCKS5 listener when SOCKS5 is enabled.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.allowUpstreamProxy": {
|
||||
"label": "Allow Upstream Proxy",
|
||||
"help": "Allow Codex sandboxed networking to chain through inherited HTTP(S)_PROXY or ALL_PROXY settings.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.allowLocalBinding": {
|
||||
"label": "Allow Local Binding",
|
||||
"help": "Permit broader local and private-network access through Codex sandboxed networking.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.mode": {
|
||||
"label": "Network Mode",
|
||||
"help": "Codex sandboxed networking mode for subprocess traffic.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.dangerouslyAllowNonLoopbackProxy": {
|
||||
"label": "Allow Non-Loopback Proxy",
|
||||
"help": "Permit non-loopback bind addresses for Codex sandboxed networking listeners.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.networkProxy.dangerouslyAllowAllUnixSockets": {
|
||||
"label": "Allow All Unix Sockets",
|
||||
"help": "Bypass Codex's Unix socket allowlist for tightly controlled environments.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.defaultWorkspaceDir": {
|
||||
"label": "Default Workspace",
|
||||
"help": "Workspace used by /codex bind when --cwd is omitted.",
|
||||
|
||||
@@ -10,7 +10,10 @@ import { AUTH_PROFILE_RUNTIME_CONTRACT } from "openclaw/plugin-sdk/agent-runtime
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { CodexAppServerClientFactory } from "./client-factory.js";
|
||||
import { runCodexAppServerAttempt as runCodexAppServerAttemptImpl } from "./run-attempt.js";
|
||||
import { readCodexAppServerBinding, writeCodexAppServerBinding } from "./session-binding.js";
|
||||
import {
|
||||
readCodexAppServerBinding,
|
||||
writeCodexAppServerBinding as writeRawCodexAppServerBinding,
|
||||
} from "./session-binding.js";
|
||||
import { createCodexTestModel } from "./test-support.js";
|
||||
|
||||
let codexAppServerClientFactoryForTest: CodexAppServerClientFactory | undefined;
|
||||
@@ -58,6 +61,25 @@ function createParams(sessionFile: string, workspaceDir: string): EmbeddedRunAtt
|
||||
} as EmbeddedRunAttemptParams;
|
||||
}
|
||||
|
||||
const DISABLED_CODEX_WEB_SEARCH_THREAD_CONFIG_FINGERPRINT = JSON.stringify({
|
||||
"features.standalone_web_search": false,
|
||||
web_search: "disabled",
|
||||
});
|
||||
|
||||
function writeCodexAppServerBinding(
|
||||
...args: Parameters<typeof writeRawCodexAppServerBinding>
|
||||
) {
|
||||
const [sessionFile, binding, lookup] = args;
|
||||
return writeRawCodexAppServerBinding(
|
||||
sessionFile,
|
||||
{
|
||||
webSearchThreadConfigFingerprint: DISABLED_CODEX_WEB_SEARCH_THREAD_CONFIG_FINGERPRINT,
|
||||
...binding,
|
||||
},
|
||||
lookup,
|
||||
);
|
||||
}
|
||||
|
||||
function threadStartResult(threadId = "thread-auth-contract") {
|
||||
return {
|
||||
thread: {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
CODEX_PLUGINS_CONFIG_KEYS,
|
||||
canUseCodexModelBackedApprovalsReviewerForModel,
|
||||
codexAppServerStartOptionsKey,
|
||||
fingerprintCodexAppServerNetworkProxyConfigPatch,
|
||||
readCodexPluginConfig,
|
||||
resolveCodexAppServerRuntimeOptions,
|
||||
resolveCodexComputerUseConfig,
|
||||
@@ -83,6 +84,21 @@ describe("Codex app-server config", () => {
|
||||
sandbox: "danger-full-access",
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldAutoApproveCodexAppServerApprovals({
|
||||
approvalPolicy: "never",
|
||||
sandbox: "danger-full-access",
|
||||
networkProxy: {
|
||||
profileName: "openclaw-network",
|
||||
configFingerprint: "network-proxy-v1",
|
||||
configPatch: {
|
||||
"features.network_proxy.enabled": true,
|
||||
default_permissions: "openclaw-network",
|
||||
permissions: {},
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("parses typed plugin config before falling back to environment knobs", () => {
|
||||
@@ -125,6 +141,102 @@ describe("Codex app-server config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("builds Codex permissions-profile config for app-server network proxy", () => {
|
||||
const runtime = resolveRuntimeForTest({
|
||||
pluginConfig: {
|
||||
appServer: {
|
||||
sandbox: "workspace-write",
|
||||
networkProxy: {
|
||||
enabled: true,
|
||||
profileName: "mock-proxy",
|
||||
mode: "limited",
|
||||
domains: {
|
||||
" api.openai.com ": "allow",
|
||||
"blocked.example.com": "deny",
|
||||
},
|
||||
unixSockets: {
|
||||
" /tmp/mock-proxy.sock ": "allow",
|
||||
"/tmp/blocked.sock": "none",
|
||||
},
|
||||
proxyUrl: "http://127.0.0.1:3128",
|
||||
socksUrl: "socks5h://127.0.0.1:8081",
|
||||
enableSocks5: true,
|
||||
enableSocks5Udp: false,
|
||||
allowUpstreamProxy: true,
|
||||
allowLocalBinding: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const networkProxy = runtime.networkProxy;
|
||||
if (!networkProxy) {
|
||||
throw new Error("Expected network proxy runtime config");
|
||||
}
|
||||
expect(networkProxy).toEqual({
|
||||
profileName: "mock-proxy",
|
||||
configFingerprint: expect.any(String),
|
||||
configPatch: {
|
||||
"features.network_proxy.enabled": true,
|
||||
default_permissions: "mock-proxy",
|
||||
permissions: {
|
||||
"mock-proxy": {
|
||||
filesystem: {
|
||||
":minimal": "read",
|
||||
":project_roots": {
|
||||
".": "write",
|
||||
},
|
||||
},
|
||||
network: {
|
||||
enabled: true,
|
||||
mode: "limited",
|
||||
domains: {
|
||||
"api.openai.com": "allow",
|
||||
"blocked.example.com": "deny",
|
||||
},
|
||||
unix_sockets: {
|
||||
"/tmp/mock-proxy.sock": "allow",
|
||||
"/tmp/blocked.sock": "none",
|
||||
},
|
||||
proxy_url: "http://127.0.0.1:3128",
|
||||
socks_url: "socks5h://127.0.0.1:8081",
|
||||
enable_socks5: true,
|
||||
enable_socks5_udp: false,
|
||||
allow_upstream_proxy: true,
|
||||
allow_local_binding: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(networkProxy.configFingerprint).toBe(
|
||||
fingerprintCodexAppServerNetworkProxyConfigPatch(networkProxy.configPatch),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses read-only filesystem rules for read-only network proxy profiles", () => {
|
||||
const runtime = resolveRuntimeForTest({
|
||||
pluginConfig: {
|
||||
appServer: {
|
||||
sandbox: "read-only",
|
||||
networkProxy: {
|
||||
enabled: true,
|
||||
domains: { "example.com": "allow" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const profileName = runtime.networkProxy?.profileName;
|
||||
const permissions = runtime.networkProxy?.configPatch.permissions as Record<
|
||||
string,
|
||||
{ filesystem: { ":project_roots": { ".": string } } }
|
||||
>;
|
||||
|
||||
expect(profileName).toMatch(/^openclaw-network-[a-f0-9]{16}$/u);
|
||||
expect(runtime.networkProxy?.configPatch.default_permissions).toBe(profileName);
|
||||
expect(permissions[profileName ?? ""]?.filesystem[":project_roots"]["."]).toBe("read");
|
||||
});
|
||||
|
||||
it("clamps oversized app-server timer config", () => {
|
||||
const runtime = resolveRuntimeForTest({
|
||||
pluginConfig: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Codex helper module supports config behavior.
|
||||
import { createHmac, randomBytes } from "node:crypto";
|
||||
import { createHash, createHmac, randomBytes } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { hostname as readHostName } from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -16,7 +16,7 @@ import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
||||
import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { detectWindowsSpawnCommandInlineArgs } from "openclaw/plugin-sdk/windows-spawn";
|
||||
import { z } from "zod";
|
||||
import type { CodexSandboxPolicy, CodexServiceTier } from "./protocol.js";
|
||||
import type { CodexSandboxPolicy, CodexServiceTier, JsonObject, JsonValue } from "./protocol.js";
|
||||
|
||||
const START_OPTIONS_KEY_SECRET_SYMBOL = Symbol.for("openclaw.codexAppServerStartOptionsKeySecret");
|
||||
const START_OPTIONS_KEY_SECRET = getStartOptionsKeySecret();
|
||||
@@ -111,6 +111,34 @@ export type CodexAppServerExperimentalConfig = {
|
||||
sandboxExecServer?: boolean;
|
||||
};
|
||||
|
||||
export type CodexAppServerNetworkProxyDomainPermission = "allow" | "deny";
|
||||
export type CodexAppServerNetworkProxyUnixSocketPermission = "allow" | "none";
|
||||
export type CodexAppServerNetworkProxyBaseProfile = "read-only" | "workspace";
|
||||
export type CodexAppServerNetworkProxyMode = "limited" | "full";
|
||||
|
||||
export type CodexAppServerNetworkProxyConfig = {
|
||||
enabled?: boolean;
|
||||
profileName?: string;
|
||||
baseProfile?: CodexAppServerNetworkProxyBaseProfile;
|
||||
mode?: CodexAppServerNetworkProxyMode;
|
||||
domains?: Record<string, CodexAppServerNetworkProxyDomainPermission>;
|
||||
unixSockets?: Record<string, CodexAppServerNetworkProxyUnixSocketPermission>;
|
||||
proxyUrl?: string;
|
||||
socksUrl?: string;
|
||||
enableSocks5?: boolean;
|
||||
enableSocks5Udp?: boolean;
|
||||
allowUpstreamProxy?: boolean;
|
||||
allowLocalBinding?: boolean;
|
||||
dangerouslyAllowNonLoopbackProxy?: boolean;
|
||||
dangerouslyAllowAllUnixSockets?: boolean;
|
||||
};
|
||||
|
||||
export type ResolvedCodexAppServerNetworkProxyConfig = {
|
||||
profileName: string;
|
||||
configFingerprint: string;
|
||||
configPatch: JsonObject;
|
||||
};
|
||||
|
||||
export type ResolvedCodexPluginPolicy = {
|
||||
configKey: string;
|
||||
marketplaceName: typeof CODEX_PLUGINS_MARKETPLACE_NAME;
|
||||
@@ -151,6 +179,7 @@ export type CodexAppServerRuntimeOptions = {
|
||||
sandbox: CodexAppServerSandboxMode;
|
||||
approvalsReviewer: CodexAppServerApprovalsReviewer;
|
||||
serviceTier?: CodexServiceTier;
|
||||
networkProxy?: ResolvedCodexAppServerNetworkProxyConfig;
|
||||
};
|
||||
|
||||
export type CodexModelBackedReviewerContext = {
|
||||
@@ -188,15 +217,20 @@ export type CodexPluginConfig = {
|
||||
sandbox?: CodexAppServerSandboxMode;
|
||||
approvalsReviewer?: CodexAppServerApprovalsReviewer;
|
||||
serviceTier?: CodexServiceTier | null;
|
||||
networkProxy?: CodexAppServerNetworkProxyConfig;
|
||||
defaultWorkspaceDir?: string;
|
||||
experimental?: CodexAppServerExperimentalConfig;
|
||||
};
|
||||
};
|
||||
|
||||
export function shouldAutoApproveCodexAppServerApprovals(
|
||||
appServer: Pick<CodexAppServerRuntimeOptions, "approvalPolicy" | "sandbox">,
|
||||
appServer: Pick<CodexAppServerRuntimeOptions, "approvalPolicy" | "networkProxy" | "sandbox">,
|
||||
): boolean {
|
||||
return appServer.approvalPolicy === "never" && appServer.sandbox === "danger-full-access";
|
||||
return (
|
||||
appServer.networkProxy === undefined &&
|
||||
appServer.approvalPolicy === "never" &&
|
||||
appServer.sandbox === "danger-full-access"
|
||||
);
|
||||
}
|
||||
|
||||
export const CODEX_APP_SERVER_CONFIG_KEYS = [
|
||||
@@ -216,6 +250,7 @@ export const CODEX_APP_SERVER_CONFIG_KEYS = [
|
||||
"sandbox",
|
||||
"approvalsReviewer",
|
||||
"serviceTier",
|
||||
"networkProxy",
|
||||
"defaultWorkspaceDir",
|
||||
"experimental",
|
||||
] as const;
|
||||
@@ -249,6 +284,7 @@ export const CODEX_PLUGIN_ENTRY_CONFIG_KEYS = [
|
||||
const DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME = "computer-use";
|
||||
const DEFAULT_CODEX_COMPUTER_USE_MCP_SERVER_NAME = "computer-use";
|
||||
const DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS = 60_000;
|
||||
const DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX = "openclaw-network";
|
||||
|
||||
const codexAppServerTransportSchema = z.enum(["stdio", "websocket"]);
|
||||
const codexAppServerPolicyModeSchema = z.enum(["yolo", "guardian"]);
|
||||
@@ -273,6 +309,26 @@ const codexAppServerExperimentalSchema = z
|
||||
sandboxExecServer: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
const codexAppServerNetworkProxyDomainPermissionSchema = z.enum(["allow", "deny"]);
|
||||
const codexAppServerNetworkProxyUnixSocketPermissionSchema = z.enum(["allow", "none"]);
|
||||
const codexAppServerNetworkProxySchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
profileName: z.string().trim().min(1).optional(),
|
||||
baseProfile: z.enum(["read-only", "workspace"]).optional(),
|
||||
mode: z.enum(["limited", "full"]).optional(),
|
||||
domains: z.record(z.string(), codexAppServerNetworkProxyDomainPermissionSchema).optional(),
|
||||
unixSockets: z.record(z.string(), codexAppServerNetworkProxyUnixSocketPermissionSchema).optional(),
|
||||
proxyUrl: z.string().trim().min(1).optional(),
|
||||
socksUrl: z.string().trim().min(1).optional(),
|
||||
enableSocks5: z.boolean().optional(),
|
||||
enableSocks5Udp: z.boolean().optional(),
|
||||
allowUpstreamProxy: z.boolean().optional(),
|
||||
allowLocalBinding: z.boolean().optional(),
|
||||
dangerouslyAllowNonLoopbackProxy: z.boolean().optional(),
|
||||
dangerouslyAllowAllUnixSockets: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const codexPluginEntryConfigSchema = z
|
||||
.object({
|
||||
@@ -334,6 +390,7 @@ const codexPluginConfigSchema = z
|
||||
sandbox: codexAppServerSandboxSchema.optional(),
|
||||
approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
|
||||
serviceTier: codexAppServerServiceTierSchema,
|
||||
networkProxy: codexAppServerNetworkProxySchema.optional(),
|
||||
defaultWorkspaceDir: z.string().optional(),
|
||||
experimental: codexAppServerExperimentalSchema.optional(),
|
||||
})
|
||||
@@ -549,6 +606,11 @@ export function resolveCodexAppServerRuntimeOptions(
|
||||
? normalizedPolicyMode
|
||||
: (explicitPolicyMode ?? normalizedPolicyMode ?? defaultPolicy?.mode ?? "yolo");
|
||||
const serviceTier = normalizeCodexServiceTier(config.serviceTier);
|
||||
const resolvedSandbox =
|
||||
forcedPolicy?.sandbox ??
|
||||
configuredSandbox ??
|
||||
defaultPolicy?.sandbox ??
|
||||
(policyMode === "guardian" ? "workspace-write" : "danger-full-access");
|
||||
if (transport === "websocket" && !url) {
|
||||
throw new Error(
|
||||
"plugins.entries.codex.config.appServer.url is required when appServer.transport is websocket",
|
||||
@@ -597,17 +659,14 @@ export function resolveCodexAppServerRuntimeOptions(
|
||||
: {}),
|
||||
approvalPolicy: forcedPolicy?.approvalPolicy ?? approvalPolicy,
|
||||
approvalPolicySource,
|
||||
sandbox:
|
||||
forcedPolicy?.sandbox ??
|
||||
configuredSandbox ??
|
||||
defaultPolicy?.sandbox ??
|
||||
(policyMode === "guardian" ? "workspace-write" : "danger-full-access"),
|
||||
sandbox: resolvedSandbox,
|
||||
approvalsReviewer:
|
||||
forcedPolicy?.approvalsReviewer ??
|
||||
explicitApprovalsReviewer ??
|
||||
defaultPolicy?.approvalsReviewer ??
|
||||
(policyMode === "guardian" ? "auto_review" : "user"),
|
||||
...(serviceTier ? { serviceTier } : {}),
|
||||
...resolveCodexAppServerNetworkProxy(config.networkProxy, resolvedSandbox),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -821,6 +880,104 @@ export function codexSandboxPolicyForTurn(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveCodexAppServerNetworkProxy(
|
||||
config: CodexAppServerNetworkProxyConfig | undefined,
|
||||
sandbox: CodexAppServerSandboxMode,
|
||||
): { networkProxy?: ResolvedCodexAppServerNetworkProxyConfig } {
|
||||
if (config?.enabled !== true) {
|
||||
return {};
|
||||
}
|
||||
const fileSystemMode =
|
||||
config.baseProfile === "read-only" || (!config.baseProfile && sandbox === "read-only")
|
||||
? "read"
|
||||
: "write";
|
||||
const networkConfig = removeUndefinedJsonFields({
|
||||
enabled: true,
|
||||
mode: config.mode,
|
||||
domains: normalizeNetworkProxyPermissionMap(config.domains),
|
||||
unix_sockets: normalizeNetworkProxyPermissionMap(config.unixSockets),
|
||||
proxy_url: readNonEmptyString(config.proxyUrl),
|
||||
socks_url: readNonEmptyString(config.socksUrl),
|
||||
enable_socks5: config.enableSocks5,
|
||||
enable_socks5_udp: config.enableSocks5Udp,
|
||||
allow_upstream_proxy: config.allowUpstreamProxy,
|
||||
allow_local_binding: config.allowLocalBinding,
|
||||
dangerously_allow_non_loopback_proxy: config.dangerouslyAllowNonLoopbackProxy,
|
||||
dangerously_allow_all_unix_sockets: config.dangerouslyAllowAllUnixSockets,
|
||||
});
|
||||
const profile = {
|
||||
filesystem: {
|
||||
":minimal": "read",
|
||||
":project_roots": {
|
||||
".": fileSystemMode,
|
||||
},
|
||||
},
|
||||
network: networkConfig,
|
||||
};
|
||||
const profileName = resolveNetworkProxyPermissionProfileName(config, profile);
|
||||
const configPatch: JsonObject = {
|
||||
"features.network_proxy.enabled": true,
|
||||
default_permissions: profileName,
|
||||
permissions: {
|
||||
[profileName]: profile,
|
||||
},
|
||||
};
|
||||
return {
|
||||
networkProxy: {
|
||||
profileName,
|
||||
configFingerprint: fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch),
|
||||
configPatch,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolveNetworkProxyPermissionProfileName(
|
||||
config: CodexAppServerNetworkProxyConfig,
|
||||
profile: JsonObject,
|
||||
): string {
|
||||
const explicitProfileName = readNonEmptyString(config.profileName);
|
||||
if (explicitProfileName) {
|
||||
return explicitProfileName;
|
||||
}
|
||||
const suffix = createHash("sha256")
|
||||
.update(stableStringifyJson({ version: 1, profile }))
|
||||
.digest("hex")
|
||||
.slice(0, 16);
|
||||
return `${DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX}-${suffix}`;
|
||||
}
|
||||
|
||||
export function fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch: JsonObject): string {
|
||||
return createHash("sha256").update(stableStringifyJson(configPatch)).digest("hex");
|
||||
}
|
||||
|
||||
function normalizeNetworkProxyPermissionMap<TPermission extends string>(
|
||||
value: Record<string, TPermission> | undefined,
|
||||
): Record<string, TPermission> | undefined {
|
||||
const entries = Object.entries(value ?? {})
|
||||
.map(([key, permission]) => [key.trim(), permission] as const)
|
||||
.filter(([key]) => key.length > 0);
|
||||
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
||||
}
|
||||
|
||||
function removeUndefinedJsonFields(value: Record<string, JsonValue | undefined>): JsonObject {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).filter((entry): entry is [string, JsonValue] => entry[1] !== undefined),
|
||||
);
|
||||
}
|
||||
|
||||
function stableStringifyJson(value: JsonValue): string {
|
||||
if (Array.isArray(value)) {
|
||||
return `[${value.map((item) => stableStringifyJson(item)).join(",")}]`;
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
return `{${Object.entries(value)
|
||||
.toSorted(([left], [right]) => left.localeCompare(right))
|
||||
.map(([key, item]) => `${JSON.stringify(key)}:${stableStringifyJson(item)}`)
|
||||
.join(",")}}`;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
export function withMcpElicitationsApprovalPolicy(
|
||||
policy: CodexAppServerEffectiveApprovalPolicy,
|
||||
): CodexAppServerEffectiveApprovalPolicy {
|
||||
|
||||
@@ -1313,6 +1313,28 @@ describe("Codex app-server dynamic tool build", () => {
|
||||
expect(shouldForceMessageTool(params)).toBe(false);
|
||||
});
|
||||
|
||||
it("can retain message in the registered schema when disabled for the current turn", async () => {
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const params = createParams(path.join(tempDir, "session.jsonl"), workspaceDir);
|
||||
params.disableTools = false;
|
||||
params.disableMessageTool = true;
|
||||
params.sourceReplyDeliveryMode = "message_tool_only";
|
||||
params.toolsAllow = [];
|
||||
params.runtimePlan = createCodexRuntimePlanFixture();
|
||||
setOpenClawCodingToolsFactoryForTests((options) =>
|
||||
options?.disableMessageTool ? [] : [createRuntimeDynamicTool("message")],
|
||||
);
|
||||
|
||||
const availableTools = await buildDynamicToolsForTest(params, workspaceDir);
|
||||
const registeredTools = await buildDynamicToolsForTest(params, workspaceDir, {
|
||||
ignoreDisableMessageTool: true,
|
||||
ignoreRuntimePlan: true,
|
||||
});
|
||||
|
||||
expect(availableTools.map((tool) => tool.name)).not.toContain("message");
|
||||
expect(registeredTools.map((tool) => tool.name)).toContain("message");
|
||||
});
|
||||
|
||||
it("passes the live run session key to Codex dynamic tools when sandbox policy uses another key", () => {
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const params = createParams(path.join(tempDir, "session.jsonl"), workspaceDir);
|
||||
|
||||
@@ -76,6 +76,7 @@ export type DynamicToolBuildParams = {
|
||||
pluginConfig: CodexPluginConfig;
|
||||
profilerEnabled?: boolean;
|
||||
forceHeartbeatTool?: boolean;
|
||||
ignoreDisableMessageTool?: boolean;
|
||||
ignoreRuntimePlan?: boolean;
|
||||
onYieldDetected: () => void;
|
||||
onCodexAppServerEvent?: (event: CodexDynamicToolBuildEvent) => void;
|
||||
@@ -203,6 +204,9 @@ export function formatCodexDynamicToolBuildStageSummary(
|
||||
/** Builds, filters, and normalizes Codex-compatible runtime tools for a single turn. */
|
||||
export async function buildDynamicTools(input: DynamicToolBuildParams) {
|
||||
const { params } = input;
|
||||
const messagePolicyParams = input.ignoreDisableMessageTool
|
||||
? { ...params, disableMessageTool: false }
|
||||
: params;
|
||||
if (params.disableTools) {
|
||||
input.onWebSearchPolicyResolved?.(false);
|
||||
return [];
|
||||
@@ -295,8 +299,8 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
|
||||
requireExplicitMessageTarget:
|
||||
params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
|
||||
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
||||
disableMessageTool: params.disableMessageTool,
|
||||
forceMessageTool: shouldForceMessageTool(params),
|
||||
disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
|
||||
forceMessageTool: shouldForceMessageTool(messagePolicyParams),
|
||||
enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
|
||||
forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
|
||||
onYield: (message) => {
|
||||
@@ -375,7 +379,7 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
|
||||
transientWebSearchRestriction &&
|
||||
webSearchPolicy.persistentAllowed),
|
||||
);
|
||||
const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, params);
|
||||
const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams);
|
||||
const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, toolsAllow);
|
||||
toolBuildStages.mark("allowlist-filter");
|
||||
const normalizedTools = normalizeAgentRuntimeTools({
|
||||
|
||||
@@ -161,7 +161,7 @@ describe("OpenClaw-owned tool runtime contract — Codex app-server adapter", ()
|
||||
expectRecordFields(eventRecord, {
|
||||
toolName: "exec",
|
||||
toolCallId: "call-middleware",
|
||||
args: { command: "status" },
|
||||
args: mergedParams,
|
||||
});
|
||||
expectRecordFields(requireRecord(eventRecord.result, "tool_result middleware result"), {
|
||||
content: [{ type: "text", text: "raw output" }],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user