Compare commits

..

1 Commits

Author SHA1 Message Date
Vincent Koc
94672cf1f5 fix(ci): normalize GitHub CLI output 2026-06-17 15:48:05 +08:00
641 changed files with 5112 additions and 30610 deletions

View File

@@ -24,25 +24,6 @@ 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
@@ -76,7 +57,7 @@ gh workflow run openclaw-performance.yml \
-f repeat=3 \
-f deep_profile=false \
-f live_openai_candidate=false \
-f fail_on_regression=true
-f fail_on_regression=false
```
- Do not wait for full release validation to start this early perf signal.
@@ -85,9 +66,8 @@ 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 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.
- Full Release Validation also records advisory product-performance evidence;
the early standalone run is for overlap and faster regression discovery.
Prefer the trusted workflow on `main`, target the exact release SHA:
@@ -109,7 +89,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. 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.
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. 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.
@@ -145,19 +125,6 @@ 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

View File

@@ -17,10 +17,6 @@ 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
@@ -80,44 +76,6 @@ 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
@@ -161,14 +119,6 @@ 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

View File

@@ -29,17 +29,11 @@ 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, 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.
dSYM, dependency evidence when present.
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>`
@@ -63,9 +57,6 @@ 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
View File

@@ -12,14 +12,9 @@
/.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

View File

@@ -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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package-artifact-name }}
path: .artifacts/docker-e2e-package

View File

@@ -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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/save@v5
with:
path: ${{ steps.setup-pnpm.outputs.store-path }}
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}

View File

@@ -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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/restore@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) }}

View File

@@ -25,24 +25,24 @@ jobs:
pull-requests: write
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |

View File

@@ -140,7 +140,7 @@ jobs:
- name: Restore dist build cache
id: dist-cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/restore@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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/save@v5
with:
path: |
.artifacts/build-all-cache/

View File

@@ -13,11 +13,6 @@ 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:
@@ -31,8 +26,6 @@ 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') }}
@@ -82,23 +75,6 @@ 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 }}
@@ -183,7 +159,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.release_gate || inputs.include_android) && 'true' || steps.changed_scope.outputs.run_android || 'false' }}
OPENCLAW_CI_RUN_ANDROID: ${{ github.event_name == 'workflow_dispatch' && 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' }}
@@ -622,7 +598,7 @@ jobs:
install-bun: "false"
- name: Restore build-all step cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@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/**') }}
@@ -631,7 +607,7 @@ jobs:
- name: Restore dist build cache
id: dist_build_cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/restore@v5
with:
path: |
dist/
@@ -654,14 +630,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: bundled-plugin-assets
path: |
@@ -692,7 +668,7 @@ jobs:
- name: Upload startup memory report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: startup-memory
path: .artifacts/startup-memory/
@@ -781,7 +757,7 @@ jobs:
- name: Save dist build cache
if: steps.dist_build_cache.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache/save@v5
continue-on-error: true
with:
path: |
@@ -793,7 +769,7 @@ jobs:
- name: Upload gateway watch regression artifacts
if: always() && needs.preflight.outputs.run_check_additional == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: gateway-watch-regression
path: .local/gateway-watch-regression/
@@ -1363,7 +1339,7 @@ jobs:
- name: Upload deadcode reports
if: ${{ always() && matrix.task == 'dependencies' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: deadcode-reports
path: .artifacts/deadcode
@@ -1452,7 +1428,7 @@ jobs:
- name: Cache extension package boundary artifacts
id: extension-package-boundary-cache
if: matrix.group == 'extension-package-boundary'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@v5
with:
path: |
dist/plugin-sdk
@@ -1720,7 +1696,7 @@ jobs:
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
uses: actions/setup-python@v6
with:
python-version: "3.12"
@@ -1989,7 +1965,7 @@ jobs:
echo "key=$toolchain_key" >> "$GITHUB_OUTPUT"
- name: Cache SwiftPM
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@v5
with:
path: ~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-swiftpm-${{ hashFiles('apps/macos/Package.resolved') }}
@@ -1998,7 +1974,7 @@ jobs:
- name: Cache Swift build directory
id: swift-build-cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@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/**') }}
@@ -2129,7 +2105,7 @@ jobs:
exit 1
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5
uses: actions/setup-java@v5
with:
distribution: temurin
# Keep sdkmanager on the stable JDK path for Linux CI runners.
@@ -2141,7 +2117,7 @@ jobs:
apps/android/gradle/libs.versions.toml
- name: Cache Android SDK
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@v5
with:
path: ~/.android-sdk
key: ${{ runner.os }}-android-sdk-v1-cmdline-14742923-platform-37.0-build-tools-36.0.0
@@ -2228,7 +2204,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout timing summary helper
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || needs.preflight.outputs.checkout_revision || github.sha }}
fetch-depth: 1
@@ -2244,7 +2220,7 @@ jobs:
cat ci-timings-summary.txt >> "$GITHUB_STEP_SUMMARY"
- name: Upload CI timing summary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: ci-timings-summary
path: ci-timings-summary.txt

View File

@@ -35,7 +35,7 @@ jobs:
locales_json: ${{ steps.plan.outputs.locales_json }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
@@ -112,7 +112,7 @@ jobs:
name: Refresh ${{ matrix.locale }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: true
submodules: false

View File

@@ -45,12 +45,12 @@ jobs:
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
timeout-minutes: 120
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}

View File

@@ -49,7 +49,7 @@ jobs:
fi
- name: Checkout selected tag
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
fetch-depth: 1

View File

@@ -33,7 +33,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0

View File

@@ -25,13 +25,13 @@ jobs:
- name: Checkout source repo
if: env.OPENCLAW_DOCS_SYNC_TOKEN != ''
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Checkout ClawHub docs source
if: env.OPENCLAW_DOCS_SYNC_TOKEN != ''
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@v6
with:
node-version: "24.x"

View File

@@ -24,7 +24,7 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
@@ -37,7 +37,7 @@ jobs:
install-bun: "false"
- name: Checkout ClawHub docs source
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
repository: openclaw/clawhub
path: clawhub-source

View File

@@ -35,7 +35,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Close confirmed duplicates
env:
APPLY: ${{ inputs.apply }}

View File

@@ -36,7 +36,7 @@ on:
- stable
- full
run_release_soak:
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for stable and full release profiles
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for release_profile=full
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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 == 'stable' || inputs.release_profile == 'full' }}
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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 == 'stable' || inputs.release_profile == 'full' }}
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: release-package-under-test
path: |
@@ -1017,12 +1017,9 @@ jobs:
echo "- Repeat: \`3\`"
echo "- Deep profile: \`false\`"
echo "- Live OpenAI candidate: \`false\`"
echo "- Release impact: blocking"
echo "- Release impact: advisory"
} >> "$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" \
@@ -1030,27 +1027,17 @@ jobs:
-f repeat=3 \
-f deep_profile=false \
-f live_openai_candidate=false \
-f fail_on_regression=true \
-f dispatch_id="$dispatch_id")"
-f fail_on_regression=false)"
printf '%s\n' "$dispatch_output"
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
run_id="$(
printf '%s\n' "$dispatch_output" |
sed -nE 's#.*actions/runs/([0-9]+).*#\1#p' |
tail -n 1
)"
if [[ -z "$run_id" ]]; then
echo "::error::Could not find dispatched run for ${dispatch_run_name}." >&2
exit 1
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
fi
echo "Dispatched openclaw-performance.yml: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
@@ -1085,9 +1072,8 @@ jobs:
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
if [[ "$conclusion" != "success" ]]; then
echo "::error::OpenClaw Performance ended with ${conclusion}: ${url}"
echo "::warning::OpenClaw Performance is advisory and 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:
@@ -1378,7 +1364,6 @@ 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
@@ -1386,7 +1371,6 @@ jobs:
normal_ci_required=1
plugin_prerelease_required=1
release_checks_required=1
performance_required=1
else
case "$RERUN_GROUP" in
ci)
@@ -1398,9 +1382,6 @@ 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
@@ -1434,12 +1415,6 @@ 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"
@@ -1451,7 +1426,6 @@ 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"
@@ -1538,13 +1512,12 @@ 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 == 'stable' || inputs.release_profile == 'full' }}
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || 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"
@@ -1564,9 +1537,8 @@ jobs:
--arg releaseChecksRunId "$RELEASE_CHECKS_RUN_ID" \
--arg npmTelegramRunId "$NPM_TELEGRAM_RUN_ID" \
--arg performanceRunId "$PERFORMANCE_RUN_ID" \
--arg performanceConclusion "$PERFORMANCE_CONCLUSION" \
'{
version: 2,
version: 1,
workflowName: $workflowName,
runId: $runId,
runAttempt: $runAttempt,
@@ -1576,26 +1548,18 @@ 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: {
runId: $performanceRunId,
conclusion: $performanceConclusion,
blocking: true
}
productPerformance: $performanceRunId
}
}' > "${manifest_dir}/full-release-validation-manifest.json"
- name: Upload release validation manifest
if: ${{ success() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: full-release-validation-${{ github.run_id }}
path: ${{ runner.temp }}/full-release-validation

View File

@@ -56,7 +56,7 @@ jobs:
dockerfile_image: ${{ steps.manifest.outputs.dockerfile_image }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
persist-credentials: false

View File

@@ -24,7 +24,7 @@ jobs:
github.event.workflow_run.name == 'iOS Periphery Dead Code'
steps:
- name: Upsert Periphery PR comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const fs = require("node:fs");

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Detect changed paths
id: scope
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
if (context.eventName === "workflow_dispatch") {
@@ -65,7 +65,7 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
@@ -216,7 +216,7 @@ jobs:
- name: Upload Periphery report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: ios-periphery-dead-code-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/ios-periphery

View File

@@ -32,25 +32,25 @@ jobs:
pull-requests: write
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@f27b608878404679385c85cfa523b85ccb86e213 # v6
- uses: actions/labeler@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |

View File

@@ -26,7 +26,8 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Login to GHCR
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:

View File

@@ -43,7 +43,7 @@ jobs:
fi
- name: Checkout selected tag
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0

View File

@@ -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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const comment = context.payload.comment;

View File

@@ -37,7 +37,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: mantis-discord-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/mantis/

View File

@@ -56,7 +56,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
uses: actions/setup-go@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View File

@@ -56,7 +56,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
uses: actions/setup-go@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View File

@@ -81,7 +81,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
uses: actions/setup-go@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}

View File

@@ -79,7 +79,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: main
persist-credentials: false
@@ -350,7 +350,7 @@ jobs:
done
- name: Checkout harness ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
uses: actions/setup-go@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View File

@@ -68,7 +68,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
@@ -312,7 +312,7 @@ jobs:
done
- name: Checkout harness ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
uses: actions/setup-go@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View File

@@ -120,7 +120,7 @@ jobs:
DOCKER_BUILD_RECORD_UPLOAD: "false"
steps:
- name: Checkout dispatch ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: npm-telegram-beta-e2e-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/

View File

@@ -332,7 +332,7 @@ jobs:
esac
- name: Checkout workflow repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
repository: ${{ env.OPENCLAW_REPOSITORY }}
ref: ${{ inputs.ref }}
@@ -352,7 +352,7 @@ jobs:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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 }}

View File

@@ -329,7 +329,7 @@ jobs:
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
steps:
- name: Checkout workflow repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Checkout trusted release harness
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Checkout trusted release harness
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Checkout trusted release harness
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Checkout trusted live Docker harness
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
fetch-depth: 1

View File

@@ -87,7 +87,7 @@ jobs:
exit 1
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -492,7 +492,7 @@ jobs:
fi
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@v8
with:
name: full-release-validation-${{ inputs.full_release_validation_run_id }}
path: full-release-validation
@@ -677,8 +677,6 @@ 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)"
@@ -691,8 +689,6 @@ 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
@@ -705,14 +701,6 @@ 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

View File

@@ -1,7 +1,5 @@
name: OpenClaw Performance
run-name: ${{ inputs.dispatch_id != '' && format('OpenClaw Performance {0}', inputs.dispatch_id) || 'OpenClaw Performance' }}
on:
schedule:
- cron: "11 5 * * *"
@@ -47,11 +45,6 @@ on:
required: false
default: b63b6f9e20efb23641df00487e982230d81a90ac
type: string
dispatch_id:
description: Optional parent workflow dispatch identifier
required: false
default: ""
type: string
permissions:
contents: read
@@ -152,7 +145,7 @@ jobs:
- name: Checkout OpenClaw
if: steps.lane.outputs.run == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ inputs.target_ref || github.ref }}
fetch-depth: 1
@@ -160,7 +153,7 @@ jobs:
- name: Checkout performance workflow helpers
if: steps.lane.outputs.run == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
path: .artifacts/performance-workflow
@@ -563,7 +556,7 @@ jobs:
- name: Upload Kova artifacts
if: ${{ always() && steps.lane.outputs.run == 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: openclaw-performance-${{ matrix.lane }}-${{ github.run_id }}-${{ github.run_attempt }}
path: |

View File

@@ -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=stable and full
description: Run exhaustive live/Docker and upgrade-survivor soak lanes; forced on for release_profile=full
required: false
default: false
type: boolean
@@ -152,7 +152,7 @@ jobs:
fi
- name: Checkout trusted workflow helper
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ inputs.ref }}
@@ -330,7 +330,7 @@ jobs:
exit 1
;;
esac
if [[ "$release_profile" == "stable" || "$release_profile" == "full" ]]; then
if [[ "$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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@v8
with:
pattern: release-check-status-*
path: .artifacts/release-check-status

View File

@@ -290,7 +290,7 @@ jobs:
- name: Download full release validation manifest
if: ${{ inputs.publish_openclaw_npm }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
@@ -359,7 +359,6 @@ 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 }}
@@ -378,8 +377,6 @@ 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
@@ -396,14 +393,6 @@ 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
@@ -466,22 +455,12 @@ jobs:
environment: npm-release
steps:
- name: Checkout release SHA
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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:
@@ -505,7 +484,6 @@ 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
@@ -1082,75 +1060,13 @@ jobs:
exit 1
fi
(
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}"
(cd "${download_dir}" && zip -qr "${asset_path}" dependency-evidence)
gh release upload "${RELEASE_TAG}" "${asset_path}#${asset_name}" \
--repo "${GITHUB_REPOSITORY}" \
--clobber
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
@@ -1189,10 +1105,6 @@ 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}\`"
@@ -1470,7 +1382,6 @@ 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
@@ -1487,7 +1398,7 @@ jobs:
- name: Upload postpublish evidence
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: openclaw-release-postpublish-evidence-${{ inputs.tag }}
path: ${{ runner.temp }}/openclaw-release-postpublish-evidence

View File

@@ -1,384 +0,0 @@
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

View File

@@ -25,7 +25,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
uses: github/codeql-action/upload-sarif@v4.36.2
# 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: opengrep-full-sarif
path: .opengrep-out/precise.sarif

View File

@@ -41,7 +41,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
uses: github/codeql-action/upload-sarif@v4.36.2
# 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: opengrep-pr-diff-sarif
path: .opengrep-out/precise.sarif

View File

@@ -325,7 +325,7 @@ jobs:
telegram_mode: ${{ steps.profile.outputs.telegram_mode }}
steps:
- name: Checkout package workflow ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ inputs.workflow_ref }}
fetch-depth: 1
- name: Download package-under-test artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
uses: actions/download-artifact@v8
with:
name: ${{ needs.resolve_package.outputs.package_artifact_name }}
path: .artifacts/docker-e2e-package

View File

@@ -48,7 +48,7 @@ jobs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ github.ref }}
@@ -229,7 +229,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -303,7 +303,7 @@ jobs:
plugin: ${{ fromJson(needs.resolve_bootstrap_plan.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ github.ref }}

View File

@@ -63,7 +63,7 @@ jobs:
missing_trusted_publisher_matrix: ${{ steps.plan.outputs.missing_trusted_publisher_matrix }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ github.ref }}
@@ -275,7 +275,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -315,7 +315,7 @@ jobs:
plugin: ${{ fromJson(needs.preview_plugins_clawhub.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.plugin.artifactName }}
path: ${{ runner.temp }}/clawhub-package-artifact/*.tgz

View File

@@ -57,7 +57,7 @@ jobs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -224,7 +224,7 @@ jobs:
plugin: ${{ fromJson(needs.preview_plugins_npm.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }}

View File

@@ -47,7 +47,7 @@ jobs:
plugin_prerelease_docker_lanes: ${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}
steps:
- name: Checkout target
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: plugin-inspector-advisory
path: .artifacts/plugin-inspector/**

View File

@@ -65,7 +65,7 @@ jobs:
steps:
- name: Require maintainer-level repository access
id: permission
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: qa-live-slack-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}

View File

@@ -22,11 +22,11 @@ jobs:
pull-requests: read
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@v3
id: app-token-fallback
if: steps.app-token.outcome == 'failure'
continue-on-error: true

View File

@@ -30,7 +30,7 @@ jobs:
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
submodules: false

View File

@@ -1,114 +0,0 @@
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

View File

@@ -44,13 +44,13 @@ jobs:
pull-requests: write
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10
uses: actions/stale@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@v3
id: app-token
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Backfill stale closures
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
- uses: actions/create-github-app-token@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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |

View File

@@ -35,7 +35,7 @@ jobs:
timeout-minutes: 240
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@v7
with:
name: test-performance-agent-${{ github.run_id }}
path: .artifacts/test-perf/

View File

@@ -32,7 +32,8 @@ jobs:
OPENCLAW_TUI_PTY_INCLUDE_LOCAL: "1"
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:

View File

@@ -37,7 +37,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Install ShellCheck
run: sudo apt-get update -y && sudo apt-get install -y shellcheck
@@ -70,7 +71,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: install.sh in Docker
run: |
timeout --kill-after=30s 20m docker run --rm \
@@ -91,9 +93,10 @@ jobs:
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@v6
with:
node-version: 24
@@ -112,9 +115,10 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@v6
with:
node-version: 24
@@ -137,13 +141,13 @@ jobs:
- name: Checkout OpenClaw
if: env.OPENCLAW_GH_TOKEN != ''
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
path: openclaw
- name: Checkout openclaw.ai
if: env.OPENCLAW_GH_TOKEN != ''
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
repository: openclaw/openclaw.ai
token: ${{ env.OPENCLAW_GH_TOKEN }}
@@ -171,13 +175,13 @@ jobs:
- name: Setup Bun
if: steps.changes.outputs.changed == 'true'
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Setup Node.js
if: steps.changes.outputs.changed == 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: actions/setup-node@v6
with:
node-version: "24"

View File

@@ -120,7 +120,7 @@ jobs:
chmod 600 ~/.ssh/authorized_keys
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false

View File

@@ -54,7 +54,7 @@ jobs:
shell: pwsh
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
uses: actions/checkout@v6
with:
ref: ${{ inputs.target_ref || github.ref }}
persist-credentials: false

View File

@@ -115,7 +115,7 @@ jobs:
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
uses: actions/setup-python@v6
with:
python-version: "3.12"

1
.gitignore vendored
View File

@@ -77,7 +77,6 @@ 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/

View File

@@ -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...>` skips commit hooks. Only use it when you've already run equivalent targeted validation for the touched surface.
- 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 extension/plugin changes, run the fast local lane first:
- `pnpm test:extension <extension-name>`
- `pnpm test:extension --list` to see valid extension ids

View File

@@ -1,11 +0,0 @@
# 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.

View File

@@ -1,14 +0,0 @@
{
"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"
]
}

View File

@@ -53,16 +53,6 @@ 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
@@ -74,7 +64,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 Fastlane archive lane.
`pnpm android:bundle:release` is an alias for the same archive helper.
See `apps/android/VERSIONING.md` and `apps/android/fastlane/SETUP.md` for the release workflow.

View File

@@ -8,8 +8,6 @@ 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:
@@ -25,41 +23,16 @@ 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/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.
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.
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.

View File

@@ -1114,7 +1114,7 @@ internal fun gatewayRecoveryUiState(
ready: Boolean,
statusText: String,
connectSettling: Boolean,
nodeCapabilityApprovalState: GatewayNodeApprovalState,
nodeCapabilityApprovalState: GatewayNodeApprovalState = GatewayNodeApprovalState.Loading,
gatewayConnectionProblem: GatewayConnectionProblem? = null,
): GatewayRecoveryUiState =
when {

View File

@@ -112,7 +112,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Gateway error: pairing required; approval in progress",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
),
)
}
@@ -125,7 +124,6 @@ class OnboardingFlowLogicTest {
ready = true,
statusText = "Gateway error: pairing required",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
),
)
}
@@ -164,7 +162,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Connecting…",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
gatewayConnectionProblem =
GatewayConnectionProblem(
code = "PAIRING_REQUIRED",
@@ -187,7 +184,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Connecting…",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
gatewayConnectionProblem =
GatewayConnectionProblem(
code = "PAIRING_REQUIRED",
@@ -210,7 +206,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Offline",
connectSettling = true,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
),
)
}
@@ -223,7 +218,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Connected (node offline)",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
),
)
}
@@ -236,7 +230,6 @@ class OnboardingFlowLogicTest {
ready = false,
statusText = "Gateway error: connection refused",
connectSettling = false,
nodeCapabilityApprovalState = GatewayNodeApprovalState.Approved,
),
)
}

View File

@@ -9,12 +9,6 @@ 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)
@@ -42,14 +36,6 @@ 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
@@ -150,22 +136,17 @@ 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)
validate_android_release_notes!
release_notes_path = android_release_notes_path
UI.user_error!("Missing Android release notes at #{release_notes_path}.") unless File.exist?(release_notes_path)
changelog_path = android_changelog_path(version_code)
FileUtils.mkdir_p(File.dirname(changelog_path))
File.write(changelog_path, File.read(android_release_notes_path))
File.write(changelog_path, File.read(release_notes_path))
changelog_path
end
@@ -197,69 +178,6 @@ 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))
@@ -312,38 +230,6 @@ 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!
@@ -356,7 +242,6 @@ 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
@@ -375,9 +260,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"

View File

@@ -20,35 +20,6 @@ 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
@@ -85,19 +56,12 @@ 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`.

View File

@@ -1,2 +1,2 @@
c84eab270f19d11a807ce71e783d35ee95a7620295dbffcca7fff31dacfcc882 plugin-sdk-api-baseline.json
55656396a5f1941af61603402c43e23e0ffc90003e7efa7c1857c4541a0f1bb4 plugin-sdk-api-baseline.jsonl
e2a646aa93124c089fcfed3c3ef982c88d1fdd2170fcdec274446f3d02f20d2b plugin-sdk-api-baseline.json
f1762c7b4bbaea4a3ce47ab943daaa6ca3dbc58322cc5d39688da66b3d483a2d plugin-sdk-api-baseline.jsonl

View File

@@ -1175,24 +1175,8 @@
"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"
}
]

View File

@@ -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 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.
`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.
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. 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`.
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.
- `minimum` keeps the fastest OpenAI/core release-critical lanes.
- `stable` adds the stable provider/backend set.

View File

@@ -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. 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.
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.
</Note>
## Common edits

View File

@@ -11,17 +11,13 @@ sidebarTitle: "MCP"
`openclaw mcp` has two jobs:
- run OpenClaw as an MCP server with `openclaw mcp serve`
- manage OpenClaw-managed outbound MCP server definitions with `list`, `show`, `status`, `doctor`, `probe`, `add`, `set`, `configure`, `tools`, `login`, `logout`, `reload`, and `unset`
- manage OpenClaw-owned 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
@@ -372,7 +368,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-managed MCP server definitions under `mcp.servers` in OpenClaw config. They do not read mcporter servers from `config/mcporter.json`.
These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP server definitions under `mcp.servers` in OpenClaw config.
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.

View File

@@ -107,10 +107,6 @@ 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

View File

@@ -296,7 +296,6 @@ 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` |

View File

@@ -1417,7 +1417,6 @@
"providers/azure-speech",
"providers/cerebras",
"providers/chutes",
"providers/cohere",
"providers/claude-max-api-proxy",
"providers/cloudflare-ai-gateway",
"providers/comfy",

View File

@@ -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` 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
- legacy `notify: true` webhook fallback jobs → explicit webhook delivery from `cron.webhook`; announce jobs keep their chat delivery and get `delivery.completionDestination`
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.
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.
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.
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.

View File

@@ -335,8 +335,6 @@ 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
{
@@ -345,7 +343,7 @@ the config fields that accept SecretRefs.
bws: {
source: "exec",
command: "/usr/local/bin/openclaw-bws-resolver.mjs",
passEnv: ["BWS_ACCESS_TOKEN", "BWS_SERVER_URL", "PATH", "BWS_BIN"],
passEnv: ["BWS_ACCESS_TOKEN", "PATH", "BWS_BIN"],
jsonOnly: true,
},
},

View File

@@ -46,29 +46,6 @@ 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:

View File

@@ -103,46 +103,8 @@ 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.

View File

@@ -561,52 +561,8 @@ 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

View File

@@ -51,7 +51,7 @@ Each entry lists the package, distribution route, and description.
## Core npm package
91 plugins
90 plugins
- **[admin-http-rpc](/plugins/reference/admin-http-rpc)** (`@openclaw/admin-http-rpc`) - included in OpenClaw. OpenClaw admin HTTP RPC endpoint.
@@ -81,8 +81,6 @@ 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.

View File

@@ -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 128
Use [Plugin inventory](/plugins/plugin-inventory) to browse all 127
generated plugin reference pages by distribution, package, and description.

View File

@@ -1,23 +0,0 @@
---
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)

View File

@@ -1,63 +0,0 @@
---
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)

View File

@@ -33,7 +33,6 @@ 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)

View File

@@ -27,7 +27,6 @@ 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)

View File

@@ -1097,10 +1097,11 @@ 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.
- 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.
- 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.
- 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

View File

@@ -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, the full-validation manifest, and postpublish registry
verification evidence to the GitHub release for post-release incident
dependency evidence to the GitHub release as
`openclaw-<version>-dependency-evidence.zip` 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,27 +178,6 @@ 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
@@ -226,9 +205,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 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
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
`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
@@ -493,12 +472,13 @@ 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
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
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
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.
each baseline sharded into its own Docker runner job. `full` implies
`run_release_soak=true`.
`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,
@@ -689,7 +669,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; the beta profile with `run_release_soak=true`, `release_profile=stable`, or
baseline; `run_release_soak=true` 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,
@@ -855,8 +835,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 for beta release checks. It is forced on by
`release_profile=stable` and `release_profile=full`.
all-since upgrade-survivor soak on stable/default release checks. It is forced
on by `release_profile=full`.
Rules:

View File

@@ -675,10 +675,9 @@ is disabled, uninstalled, or rolled back:
clearCodeModeNamespacesForPlugin(pluginId);
```
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.
Use `unregisterCodeModeNamespace(namespaceId)` only when removing one known
namespace. Tests can call `clearCodeModeNamespacesForTest()` to avoid leaking
registrations across cases.
### Test checklist

View File

@@ -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.
`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.
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.
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. 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. |
| 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. |
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. 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.
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.
| Profile | Intended use | Included live/provider coverage |
| --------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -190,24 +190,4 @@ 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,
});
});
});

View File

@@ -146,67 +146,62 @@ export async function startClickClackGatewayAccount(
});
let afterCursor = "";
let initialized = false;
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) {
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;
}
afterCursor = event.cursor || afterCursor;
await processEvent({
account,
config: ctx.cfg,
client,
event,
botUserId: account.botUserId,
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;
}
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);
})().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);
});
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 });
}

View File

@@ -193,47 +193,6 @@
"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"
},
@@ -426,81 +385,6 @@
"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.",

View File

@@ -10,10 +10,7 @@ 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 as writeRawCodexAppServerBinding,
} from "./session-binding.js";
import { readCodexAppServerBinding, writeCodexAppServerBinding } from "./session-binding.js";
import { createCodexTestModel } from "./test-support.js";
let codexAppServerClientFactoryForTest: CodexAppServerClientFactory | undefined;
@@ -61,25 +58,6 @@ 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: {

View File

@@ -10,7 +10,6 @@ import {
CODEX_PLUGINS_CONFIG_KEYS,
canUseCodexModelBackedApprovalsReviewerForModel,
codexAppServerStartOptionsKey,
fingerprintCodexAppServerNetworkProxyConfigPatch,
readCodexPluginConfig,
resolveCodexAppServerRuntimeOptions,
resolveCodexComputerUseConfig,
@@ -84,21 +83,6 @@ 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", () => {
@@ -141,102 +125,6 @@ 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: {

View File

@@ -1,5 +1,5 @@
// Codex helper module supports config behavior.
import { createHash, createHmac, randomBytes } from "node:crypto";
import { 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, JsonObject, JsonValue } from "./protocol.js";
import type { CodexSandboxPolicy, CodexServiceTier } from "./protocol.js";
const START_OPTIONS_KEY_SECRET_SYMBOL = Symbol.for("openclaw.codexAppServerStartOptionsKeySecret");
const START_OPTIONS_KEY_SECRET = getStartOptionsKeySecret();
@@ -111,34 +111,6 @@ 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;
@@ -179,7 +151,6 @@ export type CodexAppServerRuntimeOptions = {
sandbox: CodexAppServerSandboxMode;
approvalsReviewer: CodexAppServerApprovalsReviewer;
serviceTier?: CodexServiceTier;
networkProxy?: ResolvedCodexAppServerNetworkProxyConfig;
};
export type CodexModelBackedReviewerContext = {
@@ -217,20 +188,15 @@ export type CodexPluginConfig = {
sandbox?: CodexAppServerSandboxMode;
approvalsReviewer?: CodexAppServerApprovalsReviewer;
serviceTier?: CodexServiceTier | null;
networkProxy?: CodexAppServerNetworkProxyConfig;
defaultWorkspaceDir?: string;
experimental?: CodexAppServerExperimentalConfig;
};
};
export function shouldAutoApproveCodexAppServerApprovals(
appServer: Pick<CodexAppServerRuntimeOptions, "approvalPolicy" | "networkProxy" | "sandbox">,
appServer: Pick<CodexAppServerRuntimeOptions, "approvalPolicy" | "sandbox">,
): boolean {
return (
appServer.networkProxy === undefined &&
appServer.approvalPolicy === "never" &&
appServer.sandbox === "danger-full-access"
);
return appServer.approvalPolicy === "never" && appServer.sandbox === "danger-full-access";
}
export const CODEX_APP_SERVER_CONFIG_KEYS = [
@@ -250,7 +216,6 @@ export const CODEX_APP_SERVER_CONFIG_KEYS = [
"sandbox",
"approvalsReviewer",
"serviceTier",
"networkProxy",
"defaultWorkspaceDir",
"experimental",
] as const;
@@ -284,7 +249,6 @@ 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"]);
@@ -309,26 +273,6 @@ 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({
@@ -390,7 +334,6 @@ const codexPluginConfigSchema = z
sandbox: codexAppServerSandboxSchema.optional(),
approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
serviceTier: codexAppServerServiceTierSchema,
networkProxy: codexAppServerNetworkProxySchema.optional(),
defaultWorkspaceDir: z.string().optional(),
experimental: codexAppServerExperimentalSchema.optional(),
})
@@ -606,11 +549,6 @@ 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",
@@ -659,14 +597,17 @@ export function resolveCodexAppServerRuntimeOptions(
: {}),
approvalPolicy: forcedPolicy?.approvalPolicy ?? approvalPolicy,
approvalPolicySource,
sandbox: resolvedSandbox,
sandbox:
forcedPolicy?.sandbox ??
configuredSandbox ??
defaultPolicy?.sandbox ??
(policyMode === "guardian" ? "workspace-write" : "danger-full-access"),
approvalsReviewer:
forcedPolicy?.approvalsReviewer ??
explicitApprovalsReviewer ??
defaultPolicy?.approvalsReviewer ??
(policyMode === "guardian" ? "auto_review" : "user"),
...(serviceTier ? { serviceTier } : {}),
...resolveCodexAppServerNetworkProxy(config.networkProxy, resolvedSandbox),
};
}
@@ -880,104 +821,6 @@ 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 {

View File

@@ -1313,28 +1313,6 @@ 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);

View File

@@ -76,7 +76,6 @@ export type DynamicToolBuildParams = {
pluginConfig: CodexPluginConfig;
profilerEnabled?: boolean;
forceHeartbeatTool?: boolean;
ignoreDisableMessageTool?: boolean;
ignoreRuntimePlan?: boolean;
onYieldDetected: () => void;
onCodexAppServerEvent?: (event: CodexDynamicToolBuildEvent) => void;
@@ -204,9 +203,6 @@ 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 [];
@@ -299,8 +295,8 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
requireExplicitMessageTarget:
params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
forceMessageTool: shouldForceMessageTool(messagePolicyParams),
disableMessageTool: params.disableMessageTool,
forceMessageTool: shouldForceMessageTool(params),
enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
onYield: (message) => {
@@ -379,7 +375,7 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
transientWebSearchRestriction &&
webSearchPolicy.persistentAllowed),
);
const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams);
const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, params);
const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, toolsAllow);
toolBuildStages.mark("allowlist-filter");
const normalizedTools = normalizeAgentRuntimeTools({

View File

@@ -161,7 +161,7 @@ describe("OpenClaw-owned tool runtime contract — Codex app-server adapter", ()
expectRecordFields(eventRecord, {
toolName: "exec",
toolCallId: "call-middleware",
args: mergedParams,
args: { command: "status" },
});
expectRecordFields(requireRecord(eventRecord.result, "tool_result middleware result"), {
content: [{ type: "text", text: "raw output" }],

View File

@@ -1055,6 +1055,14 @@
"description": "Usually the first user message in the thread, if available.",
"type": "string"
},
"recencyAt": {
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"sessionId": {
"description": "Session id shared by threads that belong to the same session tree.",
"type": "string"

Some files were not shown because too many files have changed in this diff Show More