fix(i18n): route native inventory checks narrowly

This commit is contained in:
Vincent Koc 2026-07-01 01:00:31 -07:00 committed by Vincent Koc
parent da6ee4f0dd
commit 9fd6ca9602
5 changed files with 55 additions and 6 deletions

View file

@ -95,6 +95,7 @@ jobs:
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_check_docs: ${{ steps.manifest.outputs.run_check_docs }}
run_control_ui_i18n: ${{ steps.manifest.outputs.run_control_ui_i18n }}
run_native_i18n: ${{ steps.manifest.outputs.run_native_i18n }}
run_checks_windows: ${{ steps.manifest.outputs.run_checks_windows }}
checks_windows_matrix: ${{ steps.manifest.outputs.checks_windows_matrix }}
run_macos_node: ${{ steps.manifest.outputs.run_macos_node }}
@ -213,6 +214,7 @@ jobs:
OPENCLAW_CI_RUN_NODE_FAST_CI_ROUTING: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_ci_routing || 'false' }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_skills_python || 'false' }}
OPENCLAW_CI_RUN_CONTROL_UI_I18N: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_control_ui_i18n || 'false' }}
OPENCLAW_CI_RUN_NATIVE_I18N: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_native_i18n || 'false' }}
OPENCLAW_CI_CHECKOUT_REVISION: ${{ steps.checkout_ref.outputs.sha }}
OPENCLAW_CI_REPOSITORY: ${{ github.repository }}
OPENCLAW_CI_EVENT_NAME: ${{ github.event_name }}
@ -280,6 +282,8 @@ jobs:
const runSkillsPython = parseBoolean(process.env.OPENCLAW_CI_RUN_SKILLS_PYTHON) && !docsOnly;
const runControlUiI18n =
parseBoolean(process.env.OPENCLAW_CI_RUN_CONTROL_UI_I18N) && !docsOnly;
const runNativeI18n =
parseBoolean(process.env.OPENCLAW_CI_RUN_NATIVE_I18N) && !docsOnly;
const checksFastCoreTasks = [];
if (runNodeFull) {
checksFastCoreTasks.push(
@ -346,6 +350,7 @@ jobs:
run_check_additional: runNodeFull,
run_check_docs: docsChanged && eventName !== "push",
run_control_ui_i18n: runControlUiI18n,
run_native_i18n: runNativeI18n,
run_skills_python_job: runSkillsPython,
run_checks_windows: runWindows,
checks_windows_matrix: createMatrix(
@ -810,7 +815,7 @@ jobs:
permissions:
contents: read
needs: [preflight]
if: ${{ !cancelled() && always() && (needs.preflight.outputs.run_macos == 'true' || needs.preflight.outputs.run_android == 'true' || needs.preflight.outputs.run_node == 'true') }}
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_native_i18n == 'true' }}
runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
timeout-minutes: 10
steps:

View file

@ -15,6 +15,7 @@ export type InstallSmokeScope = {
};
export function detectChangedScope(changedPaths: string[]): ChangedScope;
export function shouldRunNativeI18n(changedPaths: string[]): boolean;
export function detectInstallSmokeScope(changedPaths: string[]): InstallSmokeScope;
export function listChangedPaths(
base: string,
@ -26,4 +27,10 @@ export function writeGitHubOutput(
scope: ChangedScope,
outputPath?: string,
installSmokeScope?: InstallSmokeScope,
nodeFastScope?: {
runFastOnly: boolean;
runPluginContracts: boolean;
runCiRouting: boolean;
},
runNativeI18n?: boolean,
): void;

View file

@ -51,6 +51,8 @@ const TEST_ONLY_PATH_RE =
/(^test\/|\/test\/|\/tests\/|(?:^|\/)[^/]+\.(?:test|spec|test-utils|test-support|test-harness|e2e-harness)\.[cm]?[jt]sx?$)/;
const CONTROL_UI_I18N_SCOPE_RE =
/^(ui\/src\/i18n\/|scripts\/control-ui-i18n\.ts$|\.github\/workflows\/control-ui-locale-refresh\.yml$)/;
const NATIVE_I18N_SCOPE_RE =
/^(?:apps\/\.i18n\/|apps\/android\/app\/src\/main\/|apps\/ios\/|apps\/macos\/Sources\/|apps\/shared\/OpenClawKit\/Sources\/|scripts\/(?:android-app-i18n|apple-app-i18n|native-app-i18n)\.ts$|test\/scripts\/(?:android-app-i18n|apple-app-i18n|native-app-i18n)\.test\.ts$|\.github\/workflows\/(?:ci|native-locale-refresh)\.yml$)/;
const NATIVE_ONLY_RE =
/^(apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/macos-mlx-tts\/|apps\/shared\/|apps\/swabble\/|Swabble\/|appcast\.xml$)/;
const FAST_INSTALL_SMOKE_SCOPE_RE =
@ -165,6 +167,14 @@ export function detectChangedScope(changedPaths) {
};
}
export function shouldRunNativeI18n(changedPaths) {
return (
!Array.isArray(changedPaths) ||
changedPaths.length === 0 ||
changedPaths.some((path) => NATIVE_I18N_SCOPE_RE.test(path.trim()))
);
}
/**
* @param {string[]} changedPaths
* @returns {NodeFastScope}
@ -294,6 +304,7 @@ export function writeGitHubOutput(
runFullInstallSmoke: scope.runChangedSmoke,
},
nodeFastScope = { runFastOnly: false, runPluginContracts: false, runCiRouting: false },
runNativeI18n = true,
) {
if (!outputPath) {
throw new Error("GITHUB_OUTPUT is required");
@ -323,6 +334,7 @@ export function writeGitHubOutput(
"utf8",
);
appendFileSync(outputPath, `run_control_ui_i18n=${scope.runControlUiI18n}\n`, "utf8");
appendFileSync(outputPath, `run_native_i18n=${runNativeI18n}\n`, "utf8");
}
function isDirectRun() {
@ -369,7 +381,7 @@ if (isDirectRun()) {
args.mergeHeadFirstParent,
);
if (changedPaths.length === 0) {
writeGitHubOutput(EMPTY_SCOPE);
writeGitHubOutput(EMPTY_SCOPE, process.env.GITHUB_OUTPUT, undefined, undefined, false);
process.exit(0);
}
writeGitHubOutput(
@ -377,8 +389,9 @@ if (isDirectRun()) {
process.env.GITHUB_OUTPUT,
detectInstallSmokeScope(changedPaths),
detectNodeFastScope(changedPaths),
shouldRunNativeI18n(changedPaths),
);
} catch {
writeGitHubOutput(FULL_SCOPE);
writeGitHubOutput(FULL_SCOPE, process.env.GITHUB_OUTPUT, undefined, undefined, true);
}
}

View file

@ -12,6 +12,7 @@ const {
detectNodeFastScope,
listChangedPaths,
parseArgs,
shouldRunNativeI18n,
} = (await import("../../scripts/ci-changed-scope.mjs")) as unknown as {
detectChangedScope: (paths: string[]) => {
runNode: boolean;
@ -32,6 +33,7 @@ const {
runPluginContracts: boolean;
runCiRouting: boolean;
};
shouldRunNativeI18n: (paths: string[]) => boolean;
listChangedPaths: (
base: string,
head?: string,
@ -120,9 +122,7 @@ describe("parseArgs", () => {
it("rejects missing CI diff refs", () => {
expect(() => parseArgs(["--base", "--head", "HEAD"])).toThrow("--base requires a value");
expect(() => parseArgs(["--base", "-h", "--head", "HEAD"])).toThrow(
"--base requires a value",
);
expect(() => parseArgs(["--base", "-h", "--head", "HEAD"])).toThrow("--base requires a value");
expect(() => parseArgs(["--head"])).toThrow("--head requires a value");
expect(() => parseArgs(["--head", "-h"])).toThrow("--head requires a value");
expect(() => parseArgs(["--base", ""])).toThrow("--base requires a value");
@ -130,6 +130,27 @@ describe("parseArgs", () => {
});
describe("detectChangedScope", () => {
it("routes only native i18n-owned paths to the native inventory job", () => {
for (const changedPath of [
"apps/.i18n/native-source.json",
"apps/android/app/src/main/java/ai/openclaw/app/MainActivity.kt",
"apps/ios/Sources/RootTabs.swift",
"apps/macos/Sources/OpenClaw/Settings.swift",
"apps/shared/OpenClawKit/Sources/OpenClawKit/Client.swift",
"scripts/native-app-i18n.ts",
"scripts/android-app-i18n.ts",
"scripts/apple-app-i18n.ts",
"test/scripts/native-app-i18n.test.ts",
".github/workflows/native-locale-refresh.yml",
".github/workflows/ci.yml",
]) {
expect(shouldRunNativeI18n([changedPath]), changedPath).toBe(true);
}
expect(shouldRunNativeI18n(["src/config/defaults.ts"])).toBe(false);
expect(shouldRunNativeI18n(["scripts/install.sh"])).toBe(false);
});
it("fails safe when no paths are provided", () => {
expect(detectChangedScope([])).toEqual({
runNode: true,
@ -891,6 +912,7 @@ describe("detectChangedScope", () => {
run_fast_install_smoke: "false",
run_full_install_smoke: "false",
run_control_ui_i18n: "false",
run_native_i18n: "false",
});
});
});

View file

@ -343,6 +343,8 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
"${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_ios_build || 'false' }}",
OPENCLAW_CI_RUN_MACOS:
"${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_macos || 'false' }}",
OPENCLAW_CI_RUN_NATIVE_I18N:
"${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_native_i18n || 'false' }}",
OPENCLAW_CI_RUN_NODE:
"${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_node || 'false' }}",
OPENCLAW_CI_RUN_NODE_FAST_CI_ROUTING: