Files
openclaw/.github/workflows/install-smoke.yml
2026-05-02 01:38:09 +01:00

513 lines
20 KiB
YAML

name: Install Smoke
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
update_baseline_version:
description: Baseline openclaw version or dist-tag for installer update smoke
required: false
default: latest
type: string
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
update_baseline_version:
description: Baseline openclaw version or dist-tag for installer update smoke
required: false
default: latest
type: string
permissions:
contents: read
packages: write
concurrency:
group: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && format('{0}-{1}-{2}', github.workflow, github.event_name, github.run_id) || format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: ${{ github.event_name != 'workflow_call' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
preflight:
runs-on: ubuntu-24.04
outputs:
docs_only: ${{ steps.manifest.outputs.docs_only }}
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 }}
target_sha: ${{ steps.manifest.outputs.target_sha }}
dockerfile_image: ${{ steps.manifest.outputs.dockerfile_image }}
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: Build install-smoke CI manifest
id: manifest
env:
OPENCLAW_CI_EVENT_NAME: ${{ github.event_name }}
OPENCLAW_CI_WORKFLOW_BUN_GLOBAL_INSTALL_SMOKE: ${{ inputs.run_bun_global_install_smoke || 'false' }}
run: |
event_name="${OPENCLAW_CI_EVENT_NAME:-}"
workflow_bun_global_install_smoke="${OPENCLAW_CI_WORKFLOW_BUN_GLOBAL_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=true
target_sha="$(git rev-parse HEAD)"
owner="$(printf '%s' "${GITHUB_REPOSITORY_OWNER:-openclaw}" | tr '[:upper:]' '[:lower:]')"
dockerfile_image="ghcr.io/${owner}/openclaw-dockerfile-smoke:${target_sha}"
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"
echo "target_sha=$target_sha"
echo "dockerfile_image=$dockerfile_image"
} >> "$GITHUB_OUTPUT"
install-smoke-fast:
needs: [preflight]
if: needs.preflight.outputs.run_fast_install_smoke == 'true' && needs.preflight.outputs.run_full_install_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
with:
ref: ${{ inputs.ref || github.ref }}
- name: Set up Blacksmith Docker Builder
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
with:
max-cache-size-mb: 800000
# Keep release smoke builds bounded and log-producing. The Blacksmith
# build action can leave jobs in-progress without step logs when a remote
# builder stalls; an explicit buildx invocation fails closed instead.
- name: Build root Dockerfile smoke image
run: |
timeout 45m docker buildx build \
--progress=plain \
--load \
--build-arg OPENCLAW_EXTENSIONS=matrix \
-t openclaw-dockerfile-smoke:local \
-t openclaw-ext-smoke:local \
-f ./Dockerfile \
.
- name: Run root Dockerfile CLI smoke
run: |
docker run --rm --entrypoint sh openclaw-dockerfile-smoke:local -lc '
which openclaw &&
openclaw --version &&
node -e "
const fs = require(\"node:fs\");
const path = require(\"node:path\");
const pkg = require(\"/app/package.json\");
for (const [dep, rel] of Object.entries(pkg.pnpm?.patchedDependencies ?? {})) {
const absolute = path.join(\"/app\", rel);
if (!fs.existsSync(absolute)) {
throw new Error(`missing patch for ${dep}: ${rel}`);
}
}
"
'
- name: Run agents delete shared workspace Docker CLI smoke
env:
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE: openclaw-dockerfile-smoke:local
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD: "1"
run: bash scripts/e2e/agents-delete-shared-workspace-docker.sh
- name: Run Docker gateway network e2e
env:
OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE: openclaw-dockerfile-smoke:local
OPENCLAW_GATEWAY_NETWORK_E2E_SKIP_BUILD: "1"
run: bash scripts/e2e/gateway-network-docker.sh
- 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(\"; \"),
);
}
"
'
root_dockerfile_image:
needs: [preflight]
if: needs.preflight.outputs.run_full_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
image_ref: ${{ steps.image.outputs.image_ref }}
env:
DOCKER_BUILD_SUMMARY: "false"
DOCKER_BUILD_RECORD_UPLOAD: "false"
steps:
- name: Checkout CLI
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Log in to GHCR
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Check for existing root Dockerfile smoke image
id: existing
env:
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
run: |
set -euo pipefail
if timeout 180s docker pull "$IMAGE_REF"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Using existing root Dockerfile smoke image: \`$IMAGE_REF\`" >> "$GITHUB_STEP_SUMMARY"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "No existing root Dockerfile smoke image found for \`$IMAGE_REF\`; building it." >> "$GITHUB_STEP_SUMMARY"
fi
- name: Set up Blacksmith Docker Builder
if: steps.existing.outputs.exists != 'true'
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
with:
max-cache-size-mb: 800000
# Build once with the matrix extension and publish by target SHA. Use a
# direct buildx command so release jobs emit Docker progress and time out.
- name: Build and push root Dockerfile smoke image
if: steps.existing.outputs.exists != 'true'
env:
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
run: |
timeout 45m docker buildx build \
--progress=plain \
--push \
--build-arg OPENCLAW_EXTENSIONS=matrix \
-t "$IMAGE_REF" \
-f ./Dockerfile \
.
- name: Record root image output
id: image
env:
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
run: echo "image_ref=$IMAGE_REF" >> "$GITHUB_OUTPUT"
- name: Summarize root image
env:
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
TARGET_SHA: ${{ needs.preflight.outputs.target_sha }}
run: |
{
echo "## Root Dockerfile smoke image"
echo
echo "- Target SHA: \`${TARGET_SHA}\`"
echo "- Image: \`${IMAGE_REF}\`"
echo "- Reused existing image: \`${{ steps.existing.outputs.exists }}\`"
} >> "$GITHUB_STEP_SUMMARY"
qr_package_install_smoke:
needs: [preflight]
if: needs.preflight.outputs.run_full_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout CLI
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Run QR package install smoke
env:
OPENCLAW_QR_SMOKE_FORCE_INSTALL: "1"
run: bash scripts/e2e/qr-import-docker.sh
root_dockerfile_smokes:
needs: [preflight, root_dockerfile_image]
if: needs.preflight.outputs.run_full_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout CLI
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Log in to GHCR
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Pull root Dockerfile smoke image
env:
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
run: timeout 600s docker pull "$IMAGE_REF"
- name: Run root Dockerfile CLI smoke
env:
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
run: |
docker run --rm --entrypoint sh "$IMAGE_REF" -lc 'which openclaw && openclaw --version'
- name: Run agents delete shared workspace Docker CLI smoke
env:
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD: "1"
run: bash scripts/e2e/agents-delete-shared-workspace-docker.sh
- name: Run Docker gateway network e2e
env:
OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
OPENCLAW_GATEWAY_NETWORK_E2E_SKIP_BUILD: "1"
run: bash scripts/e2e/gateway-network-docker.sh
- name: Smoke test Dockerfile with matrix extension build arg
env:
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
run: |
docker run --rm --entrypoint sh "$IMAGE_REF" -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(\"; \"),
);
}
"
'
installer_smoke:
needs: [preflight, root_dockerfile_image]
if: needs.preflight.outputs.run_full_install_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
with:
ref: ${{ inputs.ref || github.ref }}
- name: Log in to GHCR
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Pull root Dockerfile smoke image
env:
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
run: timeout 600s docker pull "$IMAGE_REF"
- name: Set up Blacksmith Docker Builder
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
with:
max-cache-size-mb: 800000
- name: Build installer smoke image
run: |
timeout 20m docker buildx build \
--progress=plain \
--load \
-t openclaw-install-smoke:local \
-f ./scripts/docker/install-sh-smoke/Dockerfile \
./scripts/docker
- name: Build installer non-root image
run: |
timeout 20m docker buildx build \
--progress=plain \
--load \
-t openclaw-install-nonroot:local \
-f ./scripts/docker/install-sh-nonroot/Dockerfile \
./scripts/docker
- name: Setup Node environment for installer smoke
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "true"
- name: Run installer docker tests
env:
OPENCLAW_INSTALL_URL: https://openclaw.ai/install.sh
OPENCLAW_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh
OPENCLAW_NO_ONBOARD: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_CLI: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1"
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: ${{ inputs.update_baseline_version || 'latest' }}
OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
OPENCLAW_INSTALL_SMOKE_UPDATE_SKIP_LOCAL_BUILD: "1"
run: bash scripts/test-install-sh-docker.sh
bun_global_install_smoke:
needs: [preflight, root_dockerfile_image]
if: needs.preflight.outputs.run_full_install_smoke == 'true' && needs.preflight.outputs.run_bun_global_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout CLI
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Log in to GHCR
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Pull root Dockerfile smoke image
env:
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
run: timeout 600s docker pull "$IMAGE_REF"
- name: Setup Node environment for Bun smoke
uses: ./.github/actions/setup-node-env
with:
install-bun: "true"
install-deps: "true"
- name: Run Bun global install image-provider smoke
env:
OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD: "0"
run: bash scripts/e2e/bun-global-install-smoke.sh
docker-e2e-fast:
needs: [preflight]
if: needs.preflight.outputs.run_fast_install_smoke == 'true' || needs.preflight.outputs.run_full_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 12
env:
DOCKER_BUILD_SUMMARY: "false"
DOCKER_BUILD_RECORD_UPLOAD: "false"
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@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
with:
max-cache-size-mb: 800000
- name: Setup Node environment for package smoke
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "true"