ci: keep install smoke off pull requests

This commit is contained in:
Peter Steinberger
2026-04-24 04:24:50 +01:00
parent b5779b992f
commit a42bd94b21
3 changed files with 16 additions and 70 deletions

View File

@@ -1,10 +1,6 @@
name: Install Smoke
on:
push:
branches: [main]
pull_request:
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft]
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
@@ -30,7 +26,7 @@ permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'workflow_dispatch' && format('{0}-manual-{1}', github.workflow, github.run_id) || github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.ref) }}
group: ${{ github.event_name == 'workflow_dispatch' && format('{0}-manual-{1}', github.workflow, github.run_id) || format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: true
env:
@@ -38,7 +34,6 @@ env:
jobs:
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: ubuntu-24.04
outputs:
docs_only: ${{ steps.manifest.outputs.docs_only }}
@@ -56,67 +51,19 @@ jobs:
persist-credentials: false
submodules: false
- name: Ensure preflight base commit
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 }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect docs-only changes
id: docs_scope
uses: ./.github/actions/detect-docs-changes
- name: Detect changed smoke scope
id: changed_scope
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
if [ "${{ github.event_name }}" = "push" ]; then
BASE="${{ github.event.before }}"
else
BASE="${{ github.event.pull_request.base.sha }}"
fi
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
- name: Build install-smoke CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
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') && '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
docs_only=false
run_fast_install_smoke=true
run_full_install_smoke=true
run_bun_global_install_smoke=false
run_install_smoke=false
if [ "$force_full_install_smoke" = "true" ]; then
run_fast_install_smoke=true
run_full_install_smoke=true
run_install_smoke=true
elif [ "$docs_only" != "true" ] && [ "$event_name" != "push" ] && [ "$run_changed_full_install_smoke" = "true" ]; then
run_fast_install_smoke=true
run_full_install_smoke=true
run_install_smoke=true
elif [ "$docs_only" != "true" ] && [ "$run_changed_full_install_smoke" = "true" ]; then
run_fast_install_smoke=true
run_install_smoke=true
elif [ "$docs_only" != "true" ] && [ "$run_changed_fast_install_smoke" = "true" ]; then
run_fast_install_smoke=true
run_install_smoke=true
fi
run_install_smoke=true
if [ "$event_name" = "schedule" ]; then
run_bun_global_install_smoke=true
elif [ "$event_name" = "workflow_dispatch" ] || [ "$event_name" = "workflow_call" ]; then
@@ -326,7 +273,6 @@ jobs:
provenance: false
- name: Build installer non-root image
if: github.event_name != 'pull_request'
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
with:
context: ./scripts/docker
@@ -356,8 +302,8 @@ jobs:
OPENCLAW_NO_ONBOARD: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_CLI: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1"
OPENCLAW_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }}
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}
OPENCLAW_INSTALL_NONROOT_SKIP_IMAGE_BUILD: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT: "0"
OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS: "1"
OPENCLAW_INSTALL_SMOKE_UPDATE_BASELINE: latest

View File

@@ -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 and installer Docker/update coverage for nightly scheduled runs, manual dispatches, workflow-call release checks, and pull requests that truly touch installer/package/Docker surfaces. `main` pushes, including merge commits, do not force the full path; when changed-scope logic would request full coverage on a push, the workflow keeps the fast Docker smoke and leaves the full install smoke to nightly or release validation. 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 and `main` pushes 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.
The separate `install-smoke` workflow is not a PR or `main` push gate. It runs once per day from its schedule, can be started manually, and is reused by release checks through `workflow_call`. Scheduled and release-call runs execute the full install smoke path: QR package import, root Dockerfile CLI smoke, gateway-network e2e, bundled extension build-arg smoke, installer Docker/update coverage, the bounded bundled-plugin Docker profile, and Bun global install image-provider smoke when enabled. Pull requests should use the main CI lanes and targeted local Docker proof instead of waiting on `install-smoke`. 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.

View File

@@ -143,7 +143,10 @@ describe("bun global install smoke", () => {
const workflow = readFileSync(INSTALL_SMOKE_WORKFLOW_PATH, "utf8");
const releaseChecks = readFileSync(RELEASE_CHECKS_WORKFLOW_PATH, "utf8");
expect(workflow).not.toContain("pull_request:");
expect(workflow).not.toContain("branches: [main]");
expect(workflow).toContain("workflow_call:");
expect(workflow).toContain('cron: "17 3 * * *"');
expect(workflow).toContain("run_bun_global_install_smoke:");
expect(workflow).toContain(
"install-bun: ${{ needs.preflight.outputs.run_bun_global_install_smoke }}",
@@ -157,20 +160,17 @@ describe("bun global install smoke", () => {
"OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE: openclaw-dockerfile-smoke:local",
);
expect(workflow).toContain("format('{0}-manual-{1}', github.workflow, github.run_id)");
expect(workflow).toContain("OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE");
expect(workflow).toContain(
"github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'workflow_call'",
);
expect(workflow).not.toContain(
"github.event_name == 'workflow_call' || github.event_name == 'push'",
);
expect(workflow).not.toContain("github.event_name == 'pull_request'");
expect(workflow).not.toContain("node scripts/ci-changed-scope.mjs");
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(
'[ "$event_name" != "push" ] && [ "$run_changed_full_install_smoke" = "true" ]',
);
expect(workflow).toContain("run_fast_install_smoke=true");
expect(workflow).toContain("run_full_install_smoke=true");
expect(workflow).toContain("run_install_smoke=true");
expect(workflow).toContain("install-smoke-fast:");
expect(workflow).toContain("run_fast_install_smoke");
expect(workflow).toContain("run_full_install_smoke");