ci: collapse preflight routing jobs

This commit is contained in:
Tak Hoffman
2026-03-25 19:05:54 -05:00
parent 30aaf830f2
commit 09c71133a3
5 changed files with 240 additions and 183 deletions

View File

@@ -12,7 +12,7 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
bun-manifest:
preflight:
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
@@ -43,11 +43,11 @@ jobs:
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false"
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false"
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}'
run: node scripts/ci-write-manifest-outputs.mjs
run: node scripts/ci-write-manifest-outputs.mjs --workflow ci-bun
build-bun-artifacts:
needs: [bun-manifest]
if: needs.bun-manifest.outputs.run_bun_checks == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_bun_checks == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -73,13 +73,13 @@ jobs:
bun-checks:
name: ${{ matrix.check_name }}
needs: [bun-manifest, build-bun-artifacts]
if: needs.bun-manifest.outputs.run_bun_checks == 'true'
needs: [preflight, build-bun-artifacts]
if: needs.preflight.outputs.run_bun_checks == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.bun-manifest.outputs.bun_checks_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.bun_checks_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6

View File

@@ -17,26 +17,43 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
# Scope: establish the fast global truth for this revision before the
# expensive platform and platform-specific lanes fan out.
# Detect docs-only changes to skip heavy jobs (test, build, Windows, macOS, Android).
# Keep this job focused on routing decisions so the rest of CI can fan out sooner.
# Fail-safe: if detection steps are skipped, downstream outputs fall back to
# conservative defaults that keep heavy lanes enabled.
scope:
# Preflight: establish routing truth and planner-owned matrices once, then let
# real work fan out from a single source of truth.
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
docs_only: ${{ steps.docs_scope.outputs.docs_only }}
docs_changed: ${{ steps.docs_scope.outputs.docs_changed }}
run_node: ${{ steps.changed_scope.outputs.run_node || 'false' }}
run_macos: ${{ steps.changed_scope.outputs.run_macos || 'false' }}
run_android: ${{ steps.changed_scope.outputs.run_android || 'false' }}
run_skills_python: ${{ steps.changed_scope.outputs.run_skills_python || 'false' }}
run_windows: ${{ steps.changed_scope.outputs.run_windows || 'false' }}
has_changed_extensions: ${{ steps.changed_extensions.outputs.has_changed_extensions || 'false' }}
changed_extensions_matrix: ${{ steps.changed_extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
docs_only: ${{ steps.manifest.outputs.docs_only }}
docs_changed: ${{ steps.manifest.outputs.docs_changed }}
run_node: ${{ steps.manifest.outputs.run_node }}
run_macos: ${{ steps.manifest.outputs.run_macos }}
run_android: ${{ steps.manifest.outputs.run_android }}
run_skills_python: ${{ steps.manifest.outputs.run_skills_python }}
run_windows: ${{ steps.manifest.outputs.run_windows }}
has_changed_extensions: ${{ steps.manifest.outputs.has_changed_extensions }}
changed_extensions_matrix: ${{ steps.manifest.outputs.changed_extensions_matrix }}
run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }}
run_release_check: ${{ steps.manifest.outputs.run_release_check }}
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
checks_fast_matrix: ${{ steps.manifest.outputs.checks_fast_matrix }}
run_checks: ${{ steps.manifest.outputs.run_checks }}
checks_matrix: ${{ steps.manifest.outputs.checks_matrix }}
run_extension_fast: ${{ steps.manifest.outputs.run_extension_fast }}
extension_fast_matrix: ${{ steps.manifest.outputs.extension_fast_matrix }}
run_check: ${{ steps.manifest.outputs.run_check }}
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_build_smoke: ${{ steps.manifest.outputs.run_build_smoke }}
run_check_docs: ${{ steps.manifest.outputs.run_check_docs }}
run_checks_windows: ${{ steps.manifest.outputs.run_checks_windows }}
checks_windows_matrix: ${{ steps.manifest.outputs.checks_windows_matrix }}
run_macos_node: ${{ steps.manifest.outputs.run_macos_node }}
macos_node_matrix: ${{ steps.manifest.outputs.macos_node_matrix }}
run_macos_swift_lint: ${{ steps.manifest.outputs.run_macos_swift_lint }}
run_macos_swift_build: ${{ steps.manifest.outputs.run_macos_swift_build }}
run_macos_swift_test: ${{ steps.manifest.outputs.run_macos_swift_test }}
run_android_job: ${{ steps.manifest.outputs.run_android_job }}
android_matrix: ${{ steps.manifest.outputs.android_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -56,8 +73,6 @@ jobs:
id: docs_scope
uses: ./.github/actions/detect-docs-changes
# Detect which heavy areas are touched so CI can skip unrelated expensive jobs.
# Fail-safe: if skipped, downstream lanes run.
- name: Detect changed scopes
id: changed_scope
if: steps.docs_scope.outputs.docs_only != 'true'
@@ -104,61 +119,19 @@ jobs:
appendFileSync(process.env.GITHUB_OUTPUT, `changed_extensions_matrix=${matrix}\n`, "utf8");
EOF
ci-manifest:
needs: [scope]
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }}
run_release_check: ${{ steps.manifest.outputs.run_release_check }}
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
checks_fast_matrix: ${{ steps.manifest.outputs.checks_fast_matrix }}
run_checks: ${{ steps.manifest.outputs.run_checks }}
checks_matrix: ${{ steps.manifest.outputs.checks_matrix }}
run_extension_fast: ${{ steps.manifest.outputs.run_extension_fast }}
extension_fast_matrix: ${{ steps.manifest.outputs.extension_fast_matrix }}
run_check: ${{ steps.manifest.outputs.run_check }}
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_build_smoke: ${{ steps.manifest.outputs.run_build_smoke }}
run_check_docs: ${{ steps.manifest.outputs.run_check_docs }}
run_skills_python: ${{ steps.manifest.outputs.run_skills_python }}
run_checks_windows: ${{ steps.manifest.outputs.run_checks_windows }}
checks_windows_matrix: ${{ steps.manifest.outputs.checks_windows_matrix }}
run_macos_node: ${{ steps.manifest.outputs.run_macos_node }}
macos_node_matrix: ${{ steps.manifest.outputs.macos_node_matrix }}
run_macos_swift_lint: ${{ steps.manifest.outputs.run_macos_swift_lint }}
run_macos_swift_build: ${{ steps.manifest.outputs.run_macos_swift_build }}
run_macos_swift_test: ${{ steps.manifest.outputs.run_macos_swift_test }}
run_android: ${{ steps.manifest.outputs.run_android }}
android_matrix: ${{ steps.manifest.outputs.android_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "false"
use-sticky-disk: "false"
- name: Build CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: ${{ needs.scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_CHANGED: ${{ needs.scope.outputs.docs_changed }}
OPENCLAW_CI_RUN_NODE: ${{ needs.scope.outputs.run_node }}
OPENCLAW_CI_RUN_MACOS: ${{ needs.scope.outputs.run_macos }}
OPENCLAW_CI_RUN_ANDROID: ${{ needs.scope.outputs.run_android }}
OPENCLAW_CI_RUN_WINDOWS: ${{ needs.scope.outputs.run_windows }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ needs.scope.outputs.run_skills_python }}
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: ${{ needs.scope.outputs.has_changed_extensions }}
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: ${{ needs.scope.outputs.changed_extensions_matrix }}
run: node scripts/ci-write-manifest-outputs.mjs
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_CHANGED: ${{ steps.docs_scope.outputs.docs_changed }}
OPENCLAW_CI_RUN_NODE: ${{ steps.changed_scope.outputs.run_node || 'false' }}
OPENCLAW_CI_RUN_MACOS: ${{ steps.changed_scope.outputs.run_macos || 'false' }}
OPENCLAW_CI_RUN_ANDROID: ${{ steps.changed_scope.outputs.run_android || 'false' }}
OPENCLAW_CI_RUN_WINDOWS: ${{ steps.changed_scope.outputs.run_windows || 'false' }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ steps.changed_scope.outputs.run_skills_python || 'false' }}
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: ${{ steps.changed_extensions.outputs.has_changed_extensions || 'false' }}
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: ${{ steps.changed_extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
run: node scripts/ci-write-manifest-outputs.mjs --workflow ci
# Run the fast security/SCM checks in parallel with scope detection so the
# main Node jobs do not have to wait for Python/pre-commit setup.
@@ -261,8 +234,8 @@ jobs:
# Keep this overlapping with the fast correctness lanes so green PRs get heavy
# test/build feedback sooner instead of waiting behind a full `check` pass.
build-artifacts:
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_build_artifacts == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_build_artifacts == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -308,8 +281,8 @@ jobs:
# Validate npm pack contents after build (only on push to main, not PRs).
release-check:
needs: [scope, ci-manifest, build-artifacts]
if: needs.ci-manifest.outputs.run_release_check == 'true'
needs: [preflight, build-artifacts]
if: needs.preflight.outputs.run_release_check == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -336,13 +309,13 @@ jobs:
checks-fast:
name: ${{ matrix.check_name }}
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_checks_fast == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_checks_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.checks_fast_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.checks_fast_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -361,13 +334,13 @@ jobs:
checks:
name: ${{ matrix.check_name }}
needs: [scope, ci-manifest, build-artifacts]
if: always() && needs.ci-manifest.outputs.run_checks == 'true' && needs.build-artifacts.result == 'success'
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_checks == 'true' && needs.build-artifacts.result == 'success'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.checks_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.checks_matrix) }}
steps:
- name: Skip compatibility lanes on pull requests
if: github.event_name == 'pull_request' && matrix.task == 'compat-node22'
@@ -428,13 +401,13 @@ jobs:
extension-fast:
name: "extension-fast"
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_extension_fast == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_extension_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.extension_fast_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.extension_fast_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -456,8 +429,8 @@ jobs:
# Types, lint, and format check.
check:
name: "check"
needs: [scope, ci-manifest]
if: always() && needs.ci-manifest.outputs.run_check == 'true'
needs: [preflight]
if: always() && needs.preflight.outputs.run_check == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -481,8 +454,8 @@ jobs:
check-additional:
name: "check-additional"
needs: [scope, ci-manifest]
if: always() && needs.ci-manifest.outputs.run_check_additional == 'true'
needs: [preflight]
if: always() && needs.preflight.outputs.run_check_additional == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -580,8 +553,8 @@ jobs:
build-smoke:
name: "build-smoke"
needs: [scope, ci-manifest, build-artifacts]
if: always() && needs.ci-manifest.outputs.run_build_smoke == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_build_smoke == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -622,8 +595,8 @@ jobs:
# Validate docs (format, lint, broken links) only when docs files changed.
check-docs:
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_check_docs == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_check_docs == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -643,8 +616,8 @@ jobs:
run: pnpm check:docs
skills-python:
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_skills_python == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_skills_python_job == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
@@ -672,8 +645,8 @@ jobs:
checks-windows:
name: ${{ matrix.check_name }}
needs: [scope, ci-manifest, build-artifacts]
if: always() && needs.ci-manifest.outputs.run_checks_windows == 'true' && needs.build-artifacts.result == 'success'
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_checks_windows == 'true' && needs.build-artifacts.result == 'success'
runs-on: blacksmith-32vcpu-windows-2025
timeout-minutes: 20
env:
@@ -686,7 +659,7 @@ jobs:
shell: bash
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.checks_windows_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.checks_windows_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -778,13 +751,13 @@ jobs:
macos-node:
name: ${{ matrix.check_name }}
needs: [scope, ci-manifest, build-artifacts]
if: always() && needs.ci-manifest.outputs.run_macos_node == 'true' && needs.build-artifacts.result == 'success'
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_macos_node == 'true' && needs.build-artifacts.result == 'success'
runs-on: macos-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.macos_node_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.macos_node_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -821,8 +794,8 @@ jobs:
macos-swift-lint:
name: "macos-swift-lint"
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_macos_swift_lint == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_macos_swift_lint == 'true'
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -853,8 +826,8 @@ jobs:
macos-swift-build:
name: "macos-swift-build"
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_macos_swift_build == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_macos_swift_build == 'true'
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -894,8 +867,8 @@ jobs:
macos-swift-test:
name: "macos-swift-test"
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_macos_swift_test == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_macos_swift_test == 'true'
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -935,13 +908,13 @@ jobs:
android:
name: ${{ matrix.check_name }}
needs: [scope, ci-manifest]
if: needs.ci-manifest.outputs.run_android == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_android_job == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.ci-manifest.outputs.android_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.android_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6

View File

@@ -15,49 +15,34 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
docs-scope:
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
docs_only: ${{ steps.manifest.outputs.docs_only }}
run_install_smoke: ${{ steps.manifest.outputs.run_install_smoke }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure docs-scope base commit
- name: Ensure preflight base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect docs-only changes
id: check
id: docs_scope
uses: ./.github/actions/detect-docs-changes
changed-smoke:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
run_changed_smoke: ${{ steps.scope.outputs.run_changed_smoke }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
- name: Ensure changed-smoke base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect changed smoke scope
id: scope
id: changed_scope
if: steps.docs_scope.outputs.docs_only != 'true'
shell: bash
run: |
set -euo pipefail
@@ -70,20 +55,8 @@ jobs:
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
ci-manifest:
needs: [docs-scope, changed-smoke]
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
run_install_smoke: ${{ steps.manifest.outputs.run_install_smoke }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
if: steps.docs_scope.outputs.docs_only != 'true'
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
@@ -93,7 +66,7 @@ jobs:
- name: Build install-smoke CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: ${{ needs.docs-scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_CHANGED: "false"
OPENCLAW_CI_RUN_NODE: "false"
OPENCLAW_CI_RUN_MACOS: "false"
@@ -102,12 +75,12 @@ jobs:
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false"
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false"
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}'
OPENCLAW_CI_RUN_CHANGED_SMOKE: ${{ needs.changed-smoke.outputs.run_changed_smoke || 'false' }}
run: node scripts/ci-write-manifest-outputs.mjs
OPENCLAW_CI_RUN_CHANGED_SMOKE: ${{ steps.changed_scope.outputs.run_changed_smoke || 'false' }}
run: node scripts/ci-write-manifest-outputs.mjs --workflow install-smoke
install-smoke:
needs: [docs-scope, changed-smoke, ci-manifest]
if: needs.ci-manifest.outputs.run_install_smoke == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
env:
DOCKER_BUILD_SUMMARY: "false"

View File

@@ -1,41 +1,78 @@
import { appendFileSync } from "node:fs";
import { buildCIExecutionManifest } from "./test-planner/planner.mjs";
const WORKFLOWS = new Set(["ci", "install-smoke", "ci-bun"]);
const parseArgs = (argv) => {
const parsed = {
workflow: "ci",
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--workflow") {
const nextValue = argv[index + 1] ?? "";
if (!WORKFLOWS.has(nextValue)) {
throw new Error(
`Unsupported --workflow value "${String(nextValue || "<missing>")}". Supported values: ci, install-smoke, ci-bun.`,
);
}
parsed.workflow = nextValue;
index += 1;
}
}
return parsed;
};
const outputPath = process.env.GITHUB_OUTPUT;
if (!outputPath) {
throw new Error("GITHUB_OUTPUT is required");
}
const { workflow } = parseArgs(process.argv.slice(2));
const manifest = buildCIExecutionManifest(undefined, { env: process.env });
const writeOutput = (name, value) => {
appendFileSync(outputPath, `${name}=${value}\n`, "utf8");
};
writeOutput("run_build_artifacts", String(manifest.jobs.buildArtifacts.enabled));
writeOutput("run_release_check", String(manifest.jobs.releaseCheck.enabled));
writeOutput("run_checks_fast", String(manifest.jobs.checksFast.enabled));
writeOutput("checks_fast_matrix", JSON.stringify(manifest.jobs.checksFast.matrix));
writeOutput("run_checks", String(manifest.jobs.checks.enabled));
writeOutput("checks_matrix", JSON.stringify(manifest.jobs.checks.matrix));
writeOutput("run_extension_fast", String(manifest.jobs.extensionFast.enabled));
writeOutput("extension_fast_matrix", JSON.stringify(manifest.jobs.extensionFast.matrix));
writeOutput("run_check", String(manifest.jobs.check.enabled));
writeOutput("run_check_additional", String(manifest.jobs.checkAdditional.enabled));
writeOutput("run_build_smoke", String(manifest.jobs.buildSmoke.enabled));
writeOutput("run_check_docs", String(manifest.jobs.checkDocs.enabled));
writeOutput("run_skills_python", String(manifest.jobs.skillsPython.enabled));
writeOutput("run_checks_windows", String(manifest.jobs.checksWindows.enabled));
writeOutput("checks_windows_matrix", JSON.stringify(manifest.jobs.checksWindows.matrix));
writeOutput("run_macos_node", String(manifest.jobs.macosNode.enabled));
writeOutput("macos_node_matrix", JSON.stringify(manifest.jobs.macosNode.matrix));
writeOutput("run_macos_swift_lint", String(manifest.jobs.macosSwiftLint.enabled));
writeOutput("run_macos_swift_build", String(manifest.jobs.macosSwiftBuild.enabled));
writeOutput("run_macos_swift_test", String(manifest.jobs.macosSwiftTest.enabled));
writeOutput("run_android", String(manifest.jobs.android.enabled));
writeOutput("android_matrix", JSON.stringify(manifest.jobs.android.matrix));
writeOutput("run_bun_checks", String(manifest.jobs.bunChecks.enabled));
writeOutput("bun_checks_matrix", JSON.stringify(manifest.jobs.bunChecks.matrix));
writeOutput("run_install_smoke", String(manifest.jobs.installSmoke.enabled));
writeOutput("required_check_names", JSON.stringify(manifest.requiredCheckNames));
if (workflow === "ci") {
writeOutput("docs_only", String(manifest.scope.docsOnly));
writeOutput("docs_changed", String(manifest.scope.docsChanged));
writeOutput("run_node", String(manifest.scope.runNode));
writeOutput("run_macos", String(manifest.scope.runMacos));
writeOutput("run_android", String(manifest.scope.runAndroid));
writeOutput("run_skills_python", String(manifest.scope.runSkillsPython));
writeOutput("run_windows", String(manifest.scope.runWindows));
writeOutput("has_changed_extensions", String(manifest.scope.hasChangedExtensions));
writeOutput("changed_extensions_matrix", JSON.stringify(manifest.scope.changedExtensionsMatrix));
writeOutput("run_build_artifacts", String(manifest.jobs.buildArtifacts.enabled));
writeOutput("run_release_check", String(manifest.jobs.releaseCheck.enabled));
writeOutput("run_checks_fast", String(manifest.jobs.checksFast.enabled));
writeOutput("checks_fast_matrix", JSON.stringify(manifest.jobs.checksFast.matrix));
writeOutput("run_checks", String(manifest.jobs.checks.enabled));
writeOutput("checks_matrix", JSON.stringify(manifest.jobs.checks.matrix));
writeOutput("run_extension_fast", String(manifest.jobs.extensionFast.enabled));
writeOutput("extension_fast_matrix", JSON.stringify(manifest.jobs.extensionFast.matrix));
writeOutput("run_check", String(manifest.jobs.check.enabled));
writeOutput("run_check_additional", String(manifest.jobs.checkAdditional.enabled));
writeOutput("run_build_smoke", String(manifest.jobs.buildSmoke.enabled));
writeOutput("run_check_docs", String(manifest.jobs.checkDocs.enabled));
writeOutput("run_skills_python_job", String(manifest.jobs.skillsPython.enabled));
writeOutput("run_checks_windows", String(manifest.jobs.checksWindows.enabled));
writeOutput("checks_windows_matrix", JSON.stringify(manifest.jobs.checksWindows.matrix));
writeOutput("run_macos_node", String(manifest.jobs.macosNode.enabled));
writeOutput("macos_node_matrix", JSON.stringify(manifest.jobs.macosNode.matrix));
writeOutput("run_macos_swift_lint", String(manifest.jobs.macosSwiftLint.enabled));
writeOutput("run_macos_swift_build", String(manifest.jobs.macosSwiftBuild.enabled));
writeOutput("run_macos_swift_test", String(manifest.jobs.macosSwiftTest.enabled));
writeOutput("run_android_job", String(manifest.jobs.android.enabled));
writeOutput("android_matrix", JSON.stringify(manifest.jobs.android.matrix));
writeOutput("required_check_names", JSON.stringify(manifest.requiredCheckNames));
} else if (workflow === "install-smoke") {
writeOutput("docs_only", String(manifest.scope.docsOnly));
writeOutput("run_install_smoke", String(manifest.jobs.installSmoke.enabled));
} else if (workflow === "ci-bun") {
writeOutput("run_bun_checks", String(manifest.jobs.bunChecks.enabled));
writeOutput("bun_checks_matrix", JSON.stringify(manifest.jobs.bunChecks.matrix));
}

View File

@@ -242,6 +242,80 @@ describe("scripts/test-parallel lane planning", () => {
expect(manifest.jobs.checksWindows.enabled).toBe(true);
});
it("writes CI workflow outputs in ci mode", () => {
const repoRoot = path.resolve(import.meta.dirname, "../..");
const outputPath = path.join(os.tmpdir(), `openclaw-ci-output-${Date.now()}.txt`);
execFileSync("node", ["scripts/ci-write-manifest-outputs.mjs", "--workflow", "ci"], {
cwd: repoRoot,
env: {
...clearPlannerShardEnv(process.env),
GITHUB_OUTPUT: outputPath,
GITHUB_EVENT_NAME: "pull_request",
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_DOCS_CHANGED: "false",
OPENCLAW_CI_RUN_NODE: "true",
OPENCLAW_CI_RUN_MACOS: "true",
OPENCLAW_CI_RUN_ANDROID: "true",
OPENCLAW_CI_RUN_WINDOWS: "true",
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false",
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false",
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}',
},
encoding: "utf8",
});
const outputs = fs.readFileSync(outputPath, "utf8");
expect(outputs).toContain("run_build_artifacts=true");
expect(outputs).toContain("run_checks_windows=true");
expect(outputs).toContain("run_macos_node=true");
expect(outputs).toContain("android_matrix=");
fs.rmSync(outputPath, { force: true });
});
it("writes install-smoke outputs in install-smoke mode", () => {
const repoRoot = path.resolve(import.meta.dirname, "../..");
const outputPath = path.join(os.tmpdir(), `openclaw-install-output-${Date.now()}.txt`);
execFileSync("node", ["scripts/ci-write-manifest-outputs.mjs", "--workflow", "install-smoke"], {
cwd: repoRoot,
env: {
...clearPlannerShardEnv(process.env),
GITHUB_OUTPUT: outputPath,
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_RUN_CHANGED_SMOKE: "true",
},
encoding: "utf8",
});
const outputs = fs.readFileSync(outputPath, "utf8");
expect(outputs).toContain("run_install_smoke=true");
expect(outputs).not.toContain("run_checks=");
fs.rmSync(outputPath, { force: true });
});
it("writes bun outputs in ci-bun mode", () => {
const repoRoot = path.resolve(import.meta.dirname, "../..");
const outputPath = path.join(os.tmpdir(), `openclaw-bun-output-${Date.now()}.txt`);
execFileSync("node", ["scripts/ci-write-manifest-outputs.mjs", "--workflow", "ci-bun"], {
cwd: repoRoot,
env: {
...clearPlannerShardEnv(process.env),
GITHUB_OUTPUT: outputPath,
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_RUN_NODE: "true",
},
encoding: "utf8",
});
const outputs = fs.readFileSync(outputPath, "utf8");
expect(outputs).toContain("run_bun_checks=true");
expect(outputs).toContain("bun_checks_matrix=");
expect(outputs).not.toContain("run_install_smoke=");
fs.rmSync(outputPath, { force: true });
});
it("passes through vitest --mode values that are not wrapper runtime overrides", () => {
const repoRoot = path.resolve(import.meta.dirname, "../..");
const output = execFileSync(