mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-28 06:31:11 +00:00
ci: split install smoke fast path
This commit is contained in:
parent
4ca5ef694f
commit
3a2f0e7a1a
7 changed files with 242 additions and 28 deletions
123
.github/workflows/install-smoke.yml
vendored
123
.github/workflows/install-smoke.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
branches: [main]
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft]
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -24,6 +26,8 @@ jobs:
|
|||
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 }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
|
@ -34,7 +38,7 @@ jobs:
|
|||
submodules: false
|
||||
|
||||
- name: Ensure preflight base commit
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule'
|
||||
uses: ./.github/actions/ensure-base-commit
|
||||
with:
|
||||
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
|
||||
|
|
@ -46,7 +50,7 @@ jobs:
|
|||
|
||||
- name: Detect changed smoke scope
|
||||
id: changed_scope
|
||||
if: github.event_name != 'workflow_dispatch' && steps.docs_scope.outputs.docs_only != 'true'
|
||||
if: github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' && steps.docs_scope.outputs.docs_only != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
|
@ -63,26 +67,125 @@ jobs:
|
|||
id: manifest
|
||||
env:
|
||||
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
|
||||
OPENCLAW_CI_FORCE_INSTALL_SMOKE: ${{ github.event_name == 'workflow_dispatch' && 'true' || 'false' }}
|
||||
OPENCLAW_CI_RUN_CHANGED_SMOKE: ${{ steps.changed_scope.outputs.run_changed_smoke || 'false' }}
|
||||
OPENCLAW_CI_FORCE_FULL_INSTALL_SMOKE: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'push') && 'true' || '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}"
|
||||
force_install_smoke="${OPENCLAW_CI_FORCE_INSTALL_SMOKE:-false}"
|
||||
run_changed_smoke="${OPENCLAW_CI_RUN_CHANGED_SMOKE:-false}"
|
||||
force_full_install_smoke="${OPENCLAW_CI_FORCE_FULL_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_install_smoke=false
|
||||
if [ "$force_install_smoke" = "true" ]; then
|
||||
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" ] && [ "$run_changed_smoke" = "true" ]; then
|
||||
elif [ "$docs_only" != "true" ] && [ "$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_fast_install_smoke" = "true" ]; then
|
||||
run_fast_install_smoke=true
|
||||
run_install_smoke=true
|
||||
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"
|
||||
} >> "$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
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
|
||||
# Blacksmith's builder owns the Docker layer cache; keep smoke builds off
|
||||
# explicit gha cache directives so local tags still load cleanly.
|
||||
- name: Build root Dockerfile smoke image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
OPENCLAW_DOCKER_APT_UPGRADE=0
|
||||
OPENCLAW_EXTENSIONS=matrix
|
||||
tags: |
|
||||
openclaw-dockerfile-smoke:local
|
||||
openclaw-ext-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'
|
||||
|
||||
- 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(\"; \"),
|
||||
);
|
||||
}
|
||||
"
|
||||
'
|
||||
|
||||
install-smoke:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_install_smoke == 'true'
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
|
|
@ -224,7 +327,7 @@ jobs:
|
|||
|
||||
docker-e2e-fast:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_install_smoke == 'true'
|
||||
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: 8
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -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 computes `run_install_smoke` from the narrower changed-smoke signal, so Docker/install smoke runs for install, packaging, container-relevant changes, bundled extension production changes, and the core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Test-only and docs-only edits do not reserve Docker workers. Its QR package smoke forces the Docker `pnpm install` layer to rerun while preserving the BuildKit pnpm store cache, so it still exercises installation without redownloading dependencies on every run. Its gateway-network e2e reuses the runtime image built earlier in the job, so it adds real container-to-container WebSocket coverage without adding another Docker build. 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. QR and installer Docker tests keep their own install-focused Dockerfiles. A separate `docker-e2e-fast` job runs the bounded bundled-plugin Docker profile under a 120-second command timeout: setup-entry dependency repair plus synthetic bundled-loader failure isolation. 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, 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.
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,16 @@ export type ChangedScope = {
|
|||
runControlUiI18n: boolean;
|
||||
};
|
||||
|
||||
export type InstallSmokeScope = {
|
||||
runFastInstallSmoke: boolean;
|
||||
runFullInstallSmoke: boolean;
|
||||
};
|
||||
|
||||
export function detectChangedScope(changedPaths: string[]): ChangedScope;
|
||||
export function detectInstallSmokeScope(changedPaths: string[]): InstallSmokeScope;
|
||||
export function listChangedPaths(base: string, head?: string): string[];
|
||||
export function writeGitHubOutput(scope: ChangedScope, outputPath?: string): void;
|
||||
export function writeGitHubOutput(
|
||||
scope: ChangedScope,
|
||||
outputPath?: string,
|
||||
installSmokeScope?: InstallSmokeScope,
|
||||
): void;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
|
|||
import { appendFileSync } from "node:fs";
|
||||
|
||||
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean; runSkillsPython: boolean; runChangedSmoke: boolean; runControlUiI18n: boolean }} ChangedScope */
|
||||
/** @typedef {{ runFastInstallSmoke: boolean; runFullInstallSmoke: boolean }} InstallSmokeScope */
|
||||
|
||||
const FULL_SCOPE = {
|
||||
runNode: true,
|
||||
|
|
@ -43,10 +44,11 @@ const CONTROL_UI_I18N_SCOPE_RE =
|
|||
/^(ui\/src\/i18n\/|scripts\/control-ui-i18n\.ts$|\.github\/workflows\/control-ui-locale-refresh\.yml$)/;
|
||||
const NATIVE_ONLY_RE =
|
||||
/^(apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/macos-mlx-tts\/|apps\/shared\/|Swabble\/|appcast\.xml$)/;
|
||||
const CHANGED_SMOKE_SCOPE_RE =
|
||||
/^(Dockerfile$|\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|scripts\/ci-changed-scope\.mjs$|scripts\/install\.sh$|scripts\/postinstall-bundled-plugins\.mjs$|scripts\/test-install-sh-docker\.sh$|scripts\/docker\/|scripts\/e2e\/(?:Dockerfile(?:\.qr-import)?|.*\.sh)$|src\/plugins\/bundled-runtime-deps\.ts$|extensions\/[^/]+\/package\.json$|\.github\/workflows\/install-smoke\.yml$|\.github\/actions\/setup-node-env\/action\.yml$)/;
|
||||
const CHANGED_SMOKE_RUNTIME_SCOPE_RE =
|
||||
/^(src\/(?:channels|gateway|plugin-sdk|plugins)\/|extensions\/)/;
|
||||
const FAST_INSTALL_SMOKE_SCOPE_RE =
|
||||
/^(Dockerfile$|\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|scripts\/ci-changed-scope\.mjs$|scripts\/postinstall-bundled-plugins\.mjs$|scripts\/e2e\/(?:Dockerfile(?:\.qr-import)?|gateway-network-docker\.sh|bundled-channel-runtime-deps-docker\.sh)$|src\/plugins\/bundled-runtime-deps\.ts$|extensions\/[^/]+\/(?:package\.json|openclaw\.plugin\.json)$|\.github\/workflows\/install-smoke\.yml$|\.github\/actions\/setup-node-env\/action\.yml$)/;
|
||||
const FULL_INSTALL_SMOKE_SCOPE_RE =
|
||||
/^(Dockerfile$|\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|scripts\/ci-changed-scope\.mjs$|scripts\/install\.sh$|scripts\/test-install-sh-docker\.sh$|scripts\/docker\/|scripts\/e2e\/(?:Dockerfile(?:\.qr-import)?|qr-import-docker\.sh|bun-global-install-smoke\.sh)$|\.github\/workflows\/install-smoke\.yml$|\.github\/actions\/setup-node-env\/action\.yml$)/;
|
||||
const FAST_INSTALL_SMOKE_RUNTIME_SCOPE_RE = /^src\/(?:channels|gateway|plugin-sdk|plugins)\//;
|
||||
|
||||
/**
|
||||
* @param {string[]} changedPaths
|
||||
|
|
@ -114,10 +116,7 @@ export function detectChangedScope(changedPaths) {
|
|||
runWindows = true;
|
||||
}
|
||||
|
||||
if (
|
||||
CHANGED_SMOKE_SCOPE_RE.test(path) ||
|
||||
(CHANGED_SMOKE_RUNTIME_SCOPE_RE.test(path) && !TEST_ONLY_PATH_RE.test(path))
|
||||
) {
|
||||
if (detectInstallSmokeScopeForPath(path).runFastInstallSmoke) {
|
||||
runChangedSmoke = true;
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +144,42 @@ export function detectChangedScope(changedPaths) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {InstallSmokeScope}
|
||||
*/
|
||||
function detectInstallSmokeScopeForPath(path) {
|
||||
const runFullInstallSmoke = FULL_INSTALL_SMOKE_SCOPE_RE.test(path);
|
||||
const runFastInstallSmoke =
|
||||
runFullInstallSmoke ||
|
||||
FAST_INSTALL_SMOKE_SCOPE_RE.test(path) ||
|
||||
(FAST_INSTALL_SMOKE_RUNTIME_SCOPE_RE.test(path) && !TEST_ONLY_PATH_RE.test(path));
|
||||
return { runFastInstallSmoke, runFullInstallSmoke };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} changedPaths
|
||||
* @returns {InstallSmokeScope}
|
||||
*/
|
||||
export function detectInstallSmokeScope(changedPaths) {
|
||||
if (!Array.isArray(changedPaths) || changedPaths.length === 0) {
|
||||
return { runFastInstallSmoke: true, runFullInstallSmoke: true };
|
||||
}
|
||||
|
||||
let runFastInstallSmoke = false;
|
||||
let runFullInstallSmoke = false;
|
||||
for (const rawPath of changedPaths) {
|
||||
const path = rawPath.trim();
|
||||
if (!path || DOCS_PATH_RE.test(path)) {
|
||||
continue;
|
||||
}
|
||||
const pathScope = detectInstallSmokeScopeForPath(path);
|
||||
runFastInstallSmoke ||= pathScope.runFastInstallSmoke;
|
||||
runFullInstallSmoke ||= pathScope.runFullInstallSmoke;
|
||||
}
|
||||
return { runFastInstallSmoke, runFullInstallSmoke };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} base
|
||||
* @param {string} [head]
|
||||
|
|
@ -167,8 +202,16 @@ export function listChangedPaths(base, head = "HEAD") {
|
|||
/**
|
||||
* @param {ChangedScope} scope
|
||||
* @param {string} [outputPath]
|
||||
* @param {InstallSmokeScope} [installSmokeScope]
|
||||
*/
|
||||
export function writeGitHubOutput(scope, outputPath = process.env.GITHUB_OUTPUT) {
|
||||
export function writeGitHubOutput(
|
||||
scope,
|
||||
outputPath = process.env.GITHUB_OUTPUT,
|
||||
installSmokeScope = {
|
||||
runFastInstallSmoke: scope.runChangedSmoke,
|
||||
runFullInstallSmoke: scope.runChangedSmoke,
|
||||
},
|
||||
) {
|
||||
if (!outputPath) {
|
||||
throw new Error("GITHUB_OUTPUT is required");
|
||||
}
|
||||
|
|
@ -178,6 +221,16 @@ export function writeGitHubOutput(scope, outputPath = process.env.GITHUB_OUTPUT)
|
|||
appendFileSync(outputPath, `run_windows=${scope.runWindows}\n`, "utf8");
|
||||
appendFileSync(outputPath, `run_skills_python=${scope.runSkillsPython}\n`, "utf8");
|
||||
appendFileSync(outputPath, `run_changed_smoke=${scope.runChangedSmoke}\n`, "utf8");
|
||||
appendFileSync(
|
||||
outputPath,
|
||||
`run_fast_install_smoke=${installSmokeScope.runFastInstallSmoke}\n`,
|
||||
"utf8",
|
||||
);
|
||||
appendFileSync(
|
||||
outputPath,
|
||||
`run_full_install_smoke=${installSmokeScope.runFullInstallSmoke}\n`,
|
||||
"utf8",
|
||||
);
|
||||
appendFileSync(outputPath, `run_control_ui_i18n=${scope.runControlUiI18n}\n`, "utf8");
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +264,11 @@ if (isDirectRun()) {
|
|||
writeGitHubOutput(EMPTY_SCOPE);
|
||||
process.exit(0);
|
||||
}
|
||||
writeGitHubOutput(detectChangedScope(changedPaths));
|
||||
writeGitHubOutput(
|
||||
detectChangedScope(changedPaths),
|
||||
process.env.GITHUB_OUTPUT,
|
||||
detectInstallSmokeScope(changedPaths),
|
||||
);
|
||||
} catch {
|
||||
writeGitHubOutput(FULL_SCOPE);
|
||||
}
|
||||
|
|
|
|||
4
src/infra/scripts-modules.d.ts
vendored
4
src/infra/scripts-modules.d.ts
vendored
|
|
@ -27,4 +27,8 @@ declare module "../../scripts/ci-changed-scope.mjs" {
|
|||
runChangedSmoke: boolean;
|
||||
runControlUiI18n: boolean;
|
||||
};
|
||||
export function detectInstallSmokeScope(paths: string[]): {
|
||||
runFastInstallSmoke: boolean;
|
||||
runFullInstallSmoke: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import path from "node:path";
|
|||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js";
|
||||
|
||||
const { detectChangedScope, listChangedPaths } =
|
||||
const { detectChangedScope, detectInstallSmokeScope, listChangedPaths } =
|
||||
(await import("../../scripts/ci-changed-scope.mjs")) as unknown as {
|
||||
detectChangedScope: (paths: string[]) => {
|
||||
runNode: boolean;
|
||||
|
|
@ -16,6 +16,10 @@ const { detectChangedScope, listChangedPaths } =
|
|||
runChangedSmoke: boolean;
|
||||
runControlUiI18n: boolean;
|
||||
};
|
||||
detectInstallSmokeScope: (paths: string[]) => {
|
||||
runFastInstallSmoke: boolean;
|
||||
runFullInstallSmoke: boolean;
|
||||
};
|
||||
listChangedPaths: (base: string, head?: string) => string[];
|
||||
};
|
||||
|
||||
|
|
@ -319,7 +323,7 @@ describe("detectChangedScope", () => {
|
|||
runAndroid: false,
|
||||
runWindows: false,
|
||||
runSkillsPython: false,
|
||||
runChangedSmoke: true,
|
||||
runChangedSmoke: false,
|
||||
runControlUiI18n: false,
|
||||
});
|
||||
expect(detectChangedScope(["scripts/postinstall-bundled-plugins.mjs"])).toEqual({
|
||||
|
|
@ -351,7 +355,7 @@ describe("detectChangedScope", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("runs changed-smoke for Docker-covered core and extension runtime surfaces", () => {
|
||||
it("runs changed-smoke for Docker-covered core runtime surfaces", () => {
|
||||
expect(detectChangedScope(["src/plugins/loader.ts"])).toEqual({
|
||||
runNode: true,
|
||||
runMacos: false,
|
||||
|
|
@ -394,11 +398,42 @@ describe("detectChangedScope", () => {
|
|||
runAndroid: false,
|
||||
runWindows: false,
|
||||
runSkillsPython: false,
|
||||
runChangedSmoke: true,
|
||||
runChangedSmoke: false,
|
||||
runControlUiI18n: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("splits install smoke into fast and full scopes", () => {
|
||||
expect(detectInstallSmokeScope([])).toEqual({
|
||||
runFastInstallSmoke: true,
|
||||
runFullInstallSmoke: true,
|
||||
});
|
||||
expect(detectInstallSmokeScope(["docs/ci.md"])).toEqual({
|
||||
runFastInstallSmoke: false,
|
||||
runFullInstallSmoke: false,
|
||||
});
|
||||
expect(detectInstallSmokeScope(["scripts/install.sh"])).toEqual({
|
||||
runFastInstallSmoke: true,
|
||||
runFullInstallSmoke: true,
|
||||
});
|
||||
expect(detectInstallSmokeScope(["Dockerfile"])).toEqual({
|
||||
runFastInstallSmoke: true,
|
||||
runFullInstallSmoke: true,
|
||||
});
|
||||
expect(detectInstallSmokeScope([bundledPluginFile("matrix", "package.json")])).toEqual({
|
||||
runFastInstallSmoke: true,
|
||||
runFullInstallSmoke: false,
|
||||
});
|
||||
expect(detectInstallSmokeScope(["src/plugins/loader.ts"])).toEqual({
|
||||
runFastInstallSmoke: true,
|
||||
runFullInstallSmoke: false,
|
||||
});
|
||||
expect(detectInstallSmokeScope([bundledPluginFile("matrix", "index.ts")])).toEqual({
|
||||
runFastInstallSmoke: false,
|
||||
runFullInstallSmoke: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps changed-smoke off for runtime-surface tests", () => {
|
||||
expect(detectChangedScope(["src/plugins/loader.test.ts"])).toEqual({
|
||||
runNode: true,
|
||||
|
|
@ -483,6 +518,8 @@ describe("detectChangedScope", () => {
|
|||
run_windows: "false",
|
||||
run_skills_python: "false",
|
||||
run_changed_smoke: "false",
|
||||
run_fast_install_smoke: "false",
|
||||
run_full_install_smoke: "false",
|
||||
run_control_ui_i18n: "false",
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -148,8 +148,11 @@ 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_INSTALL_SMOKE");
|
||||
expect(workflow).toContain('if [ "$force_install_smoke" = "true" ]; then');
|
||||
expect(workflow).toContain("OPENCLAW_CI_FORCE_FULL_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"');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue