name: Install Smoke on: push: branches: [main] pull_request: types: [opened, reopened, synchronize, ready_for_review, converted_to_draft] workflow_dispatch: concurrency: group: install-smoke-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" jobs: docs-scope: 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 }} steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 1 fetch-tags: false - name: Ensure docs-scope 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 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 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 install-smoke: needs: [docs-scope, changed-smoke] if: (github.event_name != 'pull_request' || !github.event.pull_request.draft) && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-smoke.outputs.run_changed_smoke == 'true' runs-on: blacksmith-16vcpu-ubuntu-2404 env: DOCKER_BUILD_SUMMARY: "false" DOCKER_BUILD_RECORD_UPLOAD: "false" steps: - name: Checkout CLI uses: actions/checkout@v6 - name: Set up Docker Builder uses: docker/setup-buildx-action@v4 # Blacksmith can fall back to the local docker driver, which rejects gha # cache export/import. Keep smoke builds driver-agnostic. - name: Build root Dockerfile smoke image uses: useblacksmith/build-push-action@v2 with: context: . file: ./Dockerfile build-args: | OPENCLAW_DOCKER_APT_UPGRADE=0 tags: openclaw-dockerfile-smoke:local load: true push: false provenance: false - name: Run root Dockerfile CLI smoke run: | docker run --rm --entrypoint sh openclaw-dockerfile-smoke:local -lc 'which openclaw && openclaw --version' # This smoke validates that the build-arg path preinstalls the matrix # runtime deps declared by the plugin and that matrix discovery stays # healthy in the final runtime image. - name: Build extension Dockerfile smoke image uses: useblacksmith/build-push-action@v2 with: context: . file: ./Dockerfile build-args: | OPENCLAW_DOCKER_APT_UPGRADE=0 OPENCLAW_EXTENSIONS=matrix tags: openclaw-ext-smoke:local load: true push: false provenance: false - name: Smoke test Dockerfile with matrix extension build arg run: | docker run --rm --entrypoint sh openclaw-ext-smoke:local -lc ' which openclaw && openclaw --version && node -e " const Module = require(\"node:module\"); const matrixPackage = require(\"/app/extensions/matrix/package.json\"); const requireFromMatrix = Module.createRequire(\"/app/extensions/matrix/package.json\"); const runtimeDeps = Object.keys(matrixPackage.dependencies ?? {}); if (runtimeDeps.length === 0) { throw new Error( \"matrix package has no declared runtime dependencies; smoke cannot validate install mirroring\", ); } for (const dep of runtimeDeps) { requireFromMatrix.resolve(dep); } const { spawnSync } = require(\"node:child_process\"); const run = spawnSync(\"openclaw\", [\"plugins\", \"list\", \"--json\"], { encoding: \"utf8\" }); if (run.status !== 0) { process.stderr.write(run.stderr || run.stdout || \"plugins list failed\\n\"); process.exit(run.status ?? 1); } const parsed = JSON.parse(run.stdout); const matrix = (parsed.plugins || []).find((entry) => entry.id === \"matrix\"); if (!matrix) { throw new Error(\"matrix plugin missing from bundled plugin list\"); } const matrixDiag = (parsed.diagnostics || []).filter( (diag) => typeof diag.source === \"string\" && diag.source.includes(\"/extensions/matrix\") && typeof diag.message === \"string\" && diag.message.includes(\"extension entry escapes package directory\"), ); if (matrixDiag.length > 0) { throw new Error( \"unexpected matrix diagnostics: \" + matrixDiag.map((diag) => diag.message).join(\"; \"), ); } " ' - name: Build installer smoke image uses: useblacksmith/build-push-action@v2 with: context: ./scripts/docker file: ./scripts/docker/install-sh-smoke/Dockerfile tags: openclaw-install-smoke:local load: true push: false provenance: false - name: Build installer non-root image if: github.event_name != 'pull_request' uses: useblacksmith/build-push-action@v2 with: context: ./scripts/docker file: ./scripts/docker/install-sh-nonroot/Dockerfile tags: openclaw-install-nonroot:local load: true push: false provenance: false - name: Run installer docker tests env: CLAWDBOT_INSTALL_URL: https://openclaw.ai/install.sh CLAWDBOT_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh CLAWDBOT_NO_ONBOARD: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1" CLAWDBOT_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }} CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }} CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS: "1" run: bash scripts/test-install-sh-docker.sh