diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml index bb39a2b6b5f..146b576a339 100644 --- a/.github/workflows/install-smoke.yml +++ b/.github/workflows/install-smoke.yml @@ -8,6 +8,23 @@ on: schedule: - cron: "17 3 * * *" workflow_dispatch: + inputs: + run_bun_global_install_smoke: + description: Run the Bun global install image-provider smoke + required: false + default: false + type: boolean + workflow_call: + inputs: + ref: + description: Git ref to validate + required: false + type: string + run_bun_global_install_smoke: + description: Run the Bun global install image-provider smoke + required: false + default: true + type: boolean permissions: contents: read @@ -28,17 +45,19 @@ jobs: run_install_smoke: ${{ steps.manifest.outputs.run_install_smoke }} run_fast_install_smoke: ${{ steps.manifest.outputs.run_fast_install_smoke }} run_full_install_smoke: ${{ steps.manifest.outputs.run_full_install_smoke }} + run_bun_global_install_smoke: ${{ steps.manifest.outputs.run_bun_global_install_smoke }} steps: - name: Checkout uses: actions/checkout@v6 with: + ref: ${{ inputs.ref || github.ref }} fetch-depth: 1 fetch-tags: false persist-credentials: false submodules: false - name: Ensure preflight base commit - if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' + if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' && github.event_name != 'workflow_call' uses: ./.github/actions/ensure-base-commit with: base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} @@ -50,7 +69,7 @@ jobs: - name: Detect changed smoke scope id: changed_scope - if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' && steps.docs_scope.outputs.docs_only != 'true' + if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' && github.event_name != 'workflow_call' && steps.docs_scope.outputs.docs_only != 'true' shell: bash run: | set -euo pipefail @@ -67,16 +86,21 @@ jobs: id: manifest env: OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }} - OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'push') && 'true' || 'false' }} + OPENCLAW_CI_EVENT_NAME: ${{ github.event_name }} + OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'workflow_call' || github.event_name == 'push') && 'true' || 'false' }} + OPENCLAW_CI_WORKFLOW_BUN_GLOBAL_INSTALL_SMOKE: ${{ inputs.run_bun_global_install_smoke || 'false' }} OPENCLAW_CI_RUN_FAST_INSTALL_SMOKE: ${{ steps.changed_scope.outputs.run_fast_install_smoke || steps.changed_scope.outputs.run_changed_smoke || 'false' }} OPENCLAW_CI_RUN_FULL_INSTALL_SMOKE: ${{ steps.changed_scope.outputs.run_full_install_smoke || 'false' }} run: | docs_only="${OPENCLAW_CI_DOCS_ONLY:-false}" + event_name="${OPENCLAW_CI_EVENT_NAME:-}" force_full_install_smoke="${OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE:-false}" + workflow_bun_global_install_smoke="${OPENCLAW_CI_WORKFLOW_BUN_GLOBAL_INSTALL_SMOKE:-false}" run_changed_fast_install_smoke="${OPENCLAW_CI_RUN_FAST_INSTALL_SMOKE:-false}" run_changed_full_install_smoke="${OPENCLAW_CI_RUN_FULL_INSTALL_SMOKE:-false}" run_fast_install_smoke=false run_full_install_smoke=false + run_bun_global_install_smoke=false run_install_smoke=false if [ "$force_full_install_smoke" = "true" ]; then run_fast_install_smoke=true @@ -90,11 +114,19 @@ jobs: run_fast_install_smoke=true run_install_smoke=true fi + if [ "$event_name" = "schedule" ]; then + run_bun_global_install_smoke=true + elif [ "$event_name" = "workflow_dispatch" ] || [ "$event_name" = "workflow_call" ]; then + if [ "$workflow_bun_global_install_smoke" = "true" ]; then + run_bun_global_install_smoke=true + fi + fi { echo "docs_only=$docs_only" echo "run_install_smoke=$run_install_smoke" echo "run_fast_install_smoke=$run_fast_install_smoke" echo "run_full_install_smoke=$run_full_install_smoke" + echo "run_bun_global_install_smoke=$run_bun_global_install_smoke" } >> "$GITHUB_OUTPUT" install-smoke-fast: @@ -107,6 +139,8 @@ jobs: steps: - name: Checkout CLI uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Blacksmith Docker Builder uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 @@ -193,6 +227,8 @@ jobs: steps: - name: Checkout CLI uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Blacksmith Docker Builder uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 @@ -298,12 +334,14 @@ jobs: provenance: false - name: Setup Node environment for local pack smoke + if: needs.preflight.outputs.run_bun_global_install_smoke == 'true' uses: ./.github/actions/setup-node-env with: install-bun: "true" install-deps: "true" - name: Run Bun global install image-provider smoke + if: needs.preflight.outputs.run_bun_global_install_smoke == 'true' env: OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE: openclaw-dockerfile-smoke:local OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD: "0" @@ -336,6 +374,8 @@ jobs: steps: - name: Checkout CLI uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Blacksmith Docker Builder uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml index bb39d1f46d1..9c452a4b048 100644 --- a/.github/workflows/openclaw-release-checks.yml +++ b/.github/workflows/openclaw-release-checks.yml @@ -123,9 +123,18 @@ jobs: echo "- Validated SHA: \`${RELEASE_SHA}\`" echo "- Cross-OS provider: \`${RELEASE_PROVIDER}\`" echo "- Cross-OS mode: \`${RELEASE_MODE}\`" - echo "- This run will execute cross-OS release validation, QA Lab parity, Matrix, and Telegram lanes, and the non-Parallels Docker/live/openwebui coverage from the CI migration plan." + echo "- This run will execute cross-OS release validation, install smoke, QA Lab parity, Matrix, and Telegram lanes, and the non-Parallels Docker/live/openwebui coverage from the CI migration plan." } >> "$GITHUB_STEP_SUMMARY" + install_smoke_release_checks: + needs: [resolve_target] + permissions: + contents: read + uses: ./.github/workflows/install-smoke.yml + with: + ref: ${{ needs.resolve_target.outputs.ref }} + run_bun_global_install_smoke: true + cross_os_release_checks: needs: [resolve_target] permissions: read-all diff --git a/docs/ci.md b/docs/ci.md index aaffa2ac448..163111e8187 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -91,7 +91,7 @@ Jobs are ordered so cheap checks fail before expensive ones run: Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests in `src/scripts/ci-changed-scope.test.ts`. CI workflow edits validate the Node CI graph plus workflow linting, but do not force Windows, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes. Windows Node checks are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes so they do not reserve a 16-vCPU Windows worker for coverage that is already exercised by the normal test shards. -The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It splits smoke coverage into `run_fast_install_smoke` and `run_full_install_smoke`. Pull requests run the fast path for Docker/package surfaces, bundled plugin package/manifest changes, and core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Source-only bundled plugin changes, test-only edits, and docs-only edits do not reserve Docker workers. The fast path builds the root Dockerfile image once, checks the CLI, runs the container gateway-network e2e, verifies a bundled extension build arg, and runs the bounded bundled-plugin Docker profile under a 120-second command timeout. The full path keeps QR package install, Bun global install, and installer Docker/update coverage for `main` pushes, nightly scheduled runs, manual dispatches, and true installer/package/Docker changes. QR and installer Docker tests keep their own install-focused Dockerfiles. Local `test:docker:all` prebuilds one shared live-test image and one shared `scripts/e2e/Dockerfile` built-app image, then runs the live/E2E smoke lanes in parallel with `OPENCLAW_SKIP_DOCKER_BUILD=1`; tune the default concurrency of 4 with `OPENCLAW_DOCKER_ALL_PARALLELISM`. The local aggregate stops scheduling new pooled lanes after the first failure by default, and each lane has a 120-minute timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`. Startup- or provider-sensitive lanes run exclusively after the parallel pool. The reusable live/E2E workflow mirrors the shared-image pattern by building and pushing one SHA-tagged GHCR Docker E2E image before the Docker matrix, then running the matrix with `OPENCLAW_SKIP_DOCKER_BUILD=1`. The scheduled live/E2E workflow runs the full release-path Docker suite daily. The full bundled update/channel matrix remains manual/full-suite because it performs repeated real npm update and doctor repair passes. +The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It splits smoke coverage into `run_fast_install_smoke` and `run_full_install_smoke`. Pull requests run the fast path for Docker/package surfaces, bundled plugin package/manifest changes, and core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Source-only bundled plugin changes, test-only edits, and docs-only edits do not reserve Docker workers. The fast path builds the root Dockerfile image once, checks the CLI, runs the container gateway-network e2e, verifies a bundled extension build arg, and runs the bounded bundled-plugin Docker profile under a 120-second command timeout. The full path keeps QR package install and installer Docker/update coverage for `main` pushes, nightly scheduled runs, manual dispatches, workflow-call release checks, and true installer/package/Docker changes. The slow Bun global install image-provider smoke is separately gated by `run_bun_global_install_smoke`; it runs on the nightly schedule and from the release checks workflow, and manual `install-smoke` dispatches can opt into it, but pull requests do not run it. QR and installer Docker tests keep their own install-focused Dockerfiles. Local `test:docker:all` prebuilds one shared live-test image and one shared `scripts/e2e/Dockerfile` built-app image, then runs the live/E2E smoke lanes in parallel with `OPENCLAW_SKIP_DOCKER_BUILD=1`; tune the default concurrency of 4 with `OPENCLAW_DOCKER_ALL_PARALLELISM`. The local aggregate stops scheduling new pooled lanes after the first failure by default, and each lane has a 120-minute timeout overrideable with `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`. Startup- or provider-sensitive lanes run exclusively after the parallel pool. The reusable live/E2E workflow mirrors the shared-image pattern by building and pushing one SHA-tagged GHCR Docker E2E image before the Docker matrix, then running the matrix with `OPENCLAW_SKIP_DOCKER_BUILD=1`. The scheduled live/E2E workflow runs the full release-path Docker suite daily. The full bundled update/channel matrix remains manual/full-suite because it performs repeated real npm update and doctor repair passes. Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local gate is stricter about architecture boundaries than the broad CI platform scope: core production changes run core prod typecheck plus core tests, core test-only changes run only core test typecheck/tests, extension production changes run extension prod typecheck plus extension tests, and extension test-only changes run only extension test typecheck/tests. Public Plugin SDK or plugin-contract changes expand to extension validation because extensions depend on those core contracts. Release metadata-only version bumps run targeted version/config/root-dependency checks. Unknown root/config changes fail safe to all lanes. diff --git a/test/scripts/test-install-sh-docker.test.ts b/test/scripts/test-install-sh-docker.test.ts index 105b4ceb7e0..5722bc86403 100644 --- a/test/scripts/test-install-sh-docker.test.ts +++ b/test/scripts/test-install-sh-docker.test.ts @@ -5,6 +5,7 @@ const SCRIPT_PATH = "scripts/test-install-sh-docker.sh"; const SMOKE_RUNNER_PATH = "scripts/docker/install-sh-smoke/run.sh"; const BUN_GLOBAL_SMOKE_PATH = "scripts/e2e/bun-global-install-smoke.sh"; const INSTALL_SMOKE_WORKFLOW_PATH = ".github/workflows/install-smoke.yml"; +const RELEASE_CHECKS_WORKFLOW_PATH = ".github/workflows/openclaw-release-checks.yml"; describe("test-install-sh-docker", () => { it("defaults local Apple Silicon smoke runs to native arm64 while keeping CI on amd64", () => { @@ -138,10 +139,16 @@ describe("bun global install smoke", () => { expect(script).toContain("OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE"); }); - it("runs from the install-smoke workflow with Bun enabled", () => { + it("gates workflow Bun install smoke to scheduled and release-check runs", () => { const workflow = readFileSync(INSTALL_SMOKE_WORKFLOW_PATH, "utf8"); + const releaseChecks = readFileSync(RELEASE_CHECKS_WORKFLOW_PATH, "utf8"); + expect(workflow).toContain("workflow_call:"); + expect(workflow).toContain("run_bun_global_install_smoke:"); expect(workflow).toContain('install-bun: "true"'); + expect(workflow).toContain( + "if: needs.preflight.outputs.run_bun_global_install_smoke == 'true'", + ); expect(workflow).toContain("Run Bun global install image-provider smoke"); expect(workflow).toContain("bash scripts/e2e/bun-global-install-smoke.sh"); expect(workflow).toContain( @@ -149,10 +156,16 @@ describe("bun global install smoke", () => { ); expect(workflow).toContain("format('{0}-manual-{1}', github.workflow, github.run_id)"); expect(workflow).toContain("OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE"); + expect(workflow).toContain("OPENCLAW_CI_WORKFLOW_BUN_GLOBAL_INSTALL_SMOKE"); + expect(workflow).toContain('if [ "$event_name" = "schedule" ]; then'); + expect(workflow).toContain('echo "run_bun_global_install_smoke=$run_bun_global_install_smoke"'); expect(workflow).toContain('if [ "$force_full_install_smoke" = "true" ]; then'); expect(workflow).toContain("install-smoke-fast:"); expect(workflow).toContain("run_fast_install_smoke"); expect(workflow).toContain("run_full_install_smoke"); expect(workflow).toContain('OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL: "1"'); + expect(releaseChecks).toContain("install_smoke_release_checks:"); + expect(releaseChecks).toContain("uses: ./.github/workflows/install-smoke.yml"); + expect(releaseChecks).toContain("run_bun_global_install_smoke: true"); }); });