diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 628c33aa8684..7124d08563da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1883,6 +1883,9 @@ jobs: OPENCLAW_NODE_TEST_ENV_JSON: ${{ toJson(matrix.env) }} OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }} OPENCLAW_NODE_TEST_TARGETS_JSON: ${{ toJson(matrix.targets) }} + # Frozen targets can carry integration hooks whose cold setup exceeds + # the current 120-second default on shared release runners. + OPENCLAW_NODE_TEST_VITEST_ARGS_JSON: ${{ needs.preflight.outputs.compatibility_target == 'true' && '["--hookTimeout=300000"]' || '[]' }} OPENCLAW_VITEST_SHARD_NAME: ${{ matrix.shard_name }} OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS: "300000" OPENCLAW_VITEST_NO_OUTPUT_RETRY: "1" diff --git a/apps/android/app/src/main/res/values-ja/strings.xml b/apps/android/app/src/main/res/values-ja/strings.xml index a8b9a9d88416..93f54cc3e947 100644 --- a/apps/android/app/src/main/res/values-ja/strings.xml +++ b/apps/android/app/src/main/res/values-ja/strings.xml @@ -1050,7 +1050,7 @@ "未ペアリング" "Gatewayがこのスマートフォンを認識しました" "設定済みのモデルはありません" - "無効にする" + "無効化" "アプリの言語" "Gatewayをペアリング中" "保存済みの認証情報が無効です" diff --git a/scripts/ci-run-node-test-shard.mjs b/scripts/ci-run-node-test-shard.mjs index 7f6c0330f9c0..42ec776b91d5 100644 --- a/scripts/ci-run-node-test-shard.mjs +++ b/scripts/ci-run-node-test-shard.mjs @@ -17,6 +17,7 @@ const FS_MODULE_CACHE_PATH_ENV_KEY = "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH"; const FS_MODULE_CACHE_WRITER_ENV_KEY = "OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER"; const NODE_COMPILE_CACHE_PATH_ENV_KEY = "NODE_COMPILE_CACHE"; const NODE_COMPILE_CACHE_WRITER_ENV_KEY = "OPENCLAW_NODE_COMPILE_CACHE_WRITER"; +const VITEST_EXTRA_ARGS_ENV_KEY = "OPENCLAW_NODE_TEST_VITEST_ARGS_JSON"; const FS_MODULE_CACHE_MAX_BYTES = 2 * 1024 * 1024 * 1024; const NODE_COMPILE_CACHE_MAX_BYTES = 1024 * 1024 * 1024; const FS_MODULE_CACHE_PRUNE_TARGET_RATIO = 0.75; @@ -220,6 +221,7 @@ function runChild(args, childEnv, label) { export async function runShardPlans(plans, options = {}) { const baseEnv = options.env ?? process.env; + const vitestExtraArgs = parseJsonEnv(baseEnv, VITEST_EXTRA_ARGS_ENV_KEY, []); const concurrency = Math.max(1, options.concurrency ?? PLAN_CONCURRENCY); const runner = options.runChild ?? runChild; const scratchDir = options.scratchDir ?? mkdtempSync(join(tmpdir(), "openclaw-node-shard-")); @@ -233,12 +235,16 @@ export async function runShardPlans(plans, options = {}) { const index = nextIndex; nextIndex += 1; const entry = plans[index]; - const args = entry.kind === "target" ? [entry.target] : entry.plan.configs; - if (!Array.isArray(args) || args.length === 0) { + const targetArgs = entry.kind === "target" ? [entry.target] : entry.plan.configs; + if (!Array.isArray(targetArgs) || targetArgs.length === 0) { console.error(`Missing node test shard configs for ${entry.name}`); exitCode = exitCode || 1; return; } + const args = + Array.isArray(vitestExtraArgs) && vitestExtraArgs.length > 0 + ? [...targetArgs, "--", ...vitestExtraArgs] + : targetArgs; const childEnv = buildChildEnv(entry, baseEnv, scratchDir, index, { serial: concurrency === 1, cacheSlot, diff --git a/test/scripts/ci-run-node-test-shard.test.ts b/test/scripts/ci-run-node-test-shard.test.ts index c5dcd08e3b52..2b2bbd97950f 100644 --- a/test/scripts/ci-run-node-test-shard.test.ts +++ b/test/scripts/ci-run-node-test-shard.test.ts @@ -152,6 +152,30 @@ describe("scripts/ci-run-node-test-shard.mjs", () => { expect(new Set(seen.map((run) => run.cache)).size).toBe(3); }); + it("forwards trusted Vitest arguments after the target separator", async () => { + const scratchDir = makeScratchDir(); + const seen: string[][] = []; + const exitCode = await runShardPlans( + resolveShardPlans({ + OPENCLAW_NODE_TEST_CONFIGS_JSON: JSON.stringify(["test/vitest/vitest.unit.config.ts"]), + }), + { + concurrency: 1, + env: { + OPENCLAW_NODE_TEST_VITEST_ARGS_JSON: JSON.stringify(["--hookTimeout=300000"]), + }, + runChild: async (args: string[]) => { + seen.push(args); + return 0; + }, + scratchDir, + }, + ); + + expect(exitCode).toBe(0); + expect(seen).toEqual([["test/vitest/vitest.unit.config.ts", "--", "--hookTimeout=300000"]]); + }); + it("reuses isolated persistent cache slots across serial work", async () => { const scratchDir = makeScratchDir(); const persistentRoot = path.join(makeScratchDir(), "persistent"); diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 003919912a7f..6a10af4719de 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -3800,6 +3800,9 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(runStep.env.OPENCLAW_VITEST_NO_OUTPUT_RETRY).toBe("1"); expect(runStep.env.OPENCLAW_NODE_TEST_ENV_JSON).toBe("${{ toJson(matrix.env) }}"); expect(runStep.env.OPENCLAW_NODE_TEST_TARGETS_JSON).toBe("${{ toJson(matrix.targets) }}"); + expect(runStep.env.OPENCLAW_NODE_TEST_VITEST_ARGS_JSON).toBe( + "${{ needs.preflight.outputs.compatibility_target == 'true' && '[\"--hookTimeout=300000\"]' || '[]' }}", + ); expect(runStep.env.JOB_CONTEXT_JSON).toBe("${{ toJSON(job) }}"); // Shard execution policy lives in the unit-tested wrapper script. Frozen // release targets load that wrapper from the exact trusted workflow SHA. diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index 5b807716b49f..39c05f7afc93 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -2385,7 +2385,7 @@ describe("package artifact reuse", () => { }); expect(dockerAcceptanceJob.with).toMatchObject({ allow_frozen_target_scenario_omissions: - "${{ inputs.allow_frozen_target_scenario_omissions }}", + "${{ inputs.allow_frozen_target_scenario_omissions || false }}", }); expect(workflow).toContain( "live_repo_e2e_release_checks:\n name: Run repo/live E2E validation\n needs: [resolve_target]",