From 0f52bb8be5c0f3cdbfa0f98bd259e8d0425d8591 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:05:30 -0700 Subject: [PATCH] perf(ci): use four ARM test workers and remove repeated compilation (#23685) * ci: benchmark per-job Node compile caching on full unit shards * ci: measure unit shards with three and four workers * ci: benchmark localization extraction CLI patch * perf(build): reuse identical relay bundles across platforms * ci: compare Vitest 4 and 5 on complete ARM shards * perf(ci): upgrade localization extraction to skip irrelevant syntax walks * perf(ci): use all four ARM cores and remove benchmark workflows * ci: preserve failures while capturing unit source revision * fix(ci): preserve commented and escaped localization calls * ci: remove corrected localization benchmark harness --- .github/workflows/unit-tests.yml | 6 +- config/patches/i18next-cli@1.74.2.patch | 44 +++++++++++++ config/scripts/build-relay.mjs | 62 ++++++++++++------- ...calization-extraction-call-syntax.test.mjs | 56 +++++++++++++++++ .../scripts/relay-artifact-manifest.test.mjs | 16 ++++- package.json | 2 +- pnpm-lock.yaml | 21 ++++--- pnpm-workspace.yaml | 2 + 8 files changed, 172 insertions(+), 37 deletions(-) create mode 100644 config/patches/i18next-cli@1.74.2.patch create mode 100644 config/scripts/localization-extraction-call-syntax.test.mjs diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2691f4907bc..9ed96847677 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -83,9 +83,11 @@ jobs: ORCA_BALANCE_UNIT_SHARDS: '1' ORCA_BACKGROUND_LAUNCH: '1' run: | - export ORCA_SHARD_SOURCE_SHA="$(git rev-parse HEAD)" + ORCA_SHARD_SOURCE_SHA="$(git rev-parse HEAD)" + export ORCA_SHARD_SOURCE_SHA pnpm exec vitest run --config config/vitest.config.ts \ - --shard=${{ matrix.shard.index }}/${{ matrix.shard.count }} + --shard=${{ matrix.shard.index }}/${{ matrix.shard.count }} \ + ${{ inputs.runner == 'ubuntu-24.04-arm' && '--maxWorkers=4' || '' }} - name: Upload unit shard assignment if: always() diff --git a/config/patches/i18next-cli@1.74.2.patch b/config/patches/i18next-cli@1.74.2.patch new file mode 100644 index 00000000000..e4eb976d1ec --- /dev/null +++ b/config/patches/i18next-cli@1.74.2.patch @@ -0,0 +1,44 @@ +diff --git a/dist/cjs/extractor/core/extractor.js b/dist/cjs/extractor/core/extractor.js +index 6d61ade2471c20bf1a253e3784bfadcb01440545..c1b001e78a215f89a8d4da1756a87562a5a6e723 100644 +--- a/dist/cjs/extractor/core/extractor.js ++++ b/dist/cjs/extractor/core/extractor.js +@@ -143,6 +143,8 @@ const extractionSiteRegexes = new WeakMap(); + * @internal + */ + function mayContainExtractionSite(code, config) { ++ // Escaped identifiers need the parser; comments can separate a name from its call. ++ if (code.includes('\\u')) return true; + let re = extractionSiteRegexes.get(config); + if (!re) { + const calls = new Set(['t']); +@@ -160,7 +162,7 @@ function mayContainExtractionSite(code, config) { + for (const component of config.extract.transComponents || ['Trans']) + names.add(component.split('.').pop()); + const alt = (set) => [...set].map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); +- re = new RegExp(`(? [...set].map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); +- re = new RegExp(`(?('probe.generic', 'Generic')", + dollar: "$api.t /* member */ ('probe.dollar', 'Dollar')", + unicode: "transl\\u0061te('probe.unicode', 'Unicode')" +} + +describe.each([ + ['ES module', findKeys], + ['CommonJS', commonJsFindKeys] +])('localization extraction through %s', (_name, extractKeys) => { + it('keeps keys in calls containing comments or escaped identifiers', async () => { + const directory = mkdtempSync(join(tmpdir(), 'orca-extraction-syntax-')) + try { + for (const [name, source] of Object.entries(calls)) { + writeFileSync(join(directory, `${name}.ts`), source) + } + const errors = [] + const result = await extractKeys( + { + locales: ['en'], + extract: { + input: [join(directory, '*.ts').replaceAll('\\', '/')], + defaultNS: false, + functions: ['t', '*.t', 'translate'], + disablePlurals: true + } + }, + undefined, + errors + ) + expect(errors).toEqual([]) + expect([...result.allKeys.values()].map((entry) => entry.key).sort()).toEqual( + Object.keys(calls) + .map((name) => `probe.${name}`) + .sort() + ) + } finally { + rmSync(directory, { recursive: true, force: true }) + } + }) +}) diff --git a/config/scripts/relay-artifact-manifest.test.mjs b/config/scripts/relay-artifact-manifest.test.mjs index 7177b9821f8..5f66ee7136f 100644 --- a/config/scripts/relay-artifact-manifest.test.mjs +++ b/config/scripts/relay-artifact-manifest.test.mjs @@ -5,7 +5,15 @@ // and never forks. import { execFileSync } from 'node:child_process' import { createHash } from 'node:crypto' -import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs' +import { + existsSync, + mkdirSync, + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync +} from 'node:fs' import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -23,6 +31,12 @@ const projectDir = resolve(import.meta.dirname, '../..') const relayOutDir = mkdtempSync(join(tmpdir(), 'orca-relay-contract-')) beforeAll(() => { + // A repeated build must not copy stale companions into the other platform bundles. + for (const platform of RELAY_BUILD_PLATFORMS) { + const outDir = join(relayOutDir, platform) + mkdirSync(outDir, { recursive: true }) + writeFileSync(join(outDir, 'stale-companion.js'), 'throw new Error("stale")') + } execFileSync('node', [join(projectDir, 'config', 'scripts', 'build-relay.mjs')], { cwd: projectDir, stdio: 'pipe', diff --git a/package.json b/package.json index feec79fee07..0b41ed3980a 100644 --- a/package.json +++ b/package.json @@ -266,7 +266,7 @@ "happy-dom": "^20.11.8", "html-to-image": "^1.11.13", "husky": "^9.1.7", - "i18next-cli": "1.74.1", + "i18next-cli": "1.74.2", "katex": "^0.16.45", "lint-staged": "^16.4.0", "lowlight": "^3.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d82edc51bf..02422ea3f19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,6 +109,7 @@ overrides: monaco-editor>dompurify: 3.4.15 patchedDependencies: + i18next-cli@1.74.2: 7955b89d3aa229f477408608d331f78c85148a85a85913729f89fac65ad8b207 '@vscode/windows-process-tree@0.8.0': b4be93859cefb159949d9cf05f97fa91a221dff0f793a6f03d02d78ae5b32035 '@xterm/addon-image@0.10.0-beta.300': 5fc280019957573eefe8daa2969534d6090ddb29c6b5842b8f22e99dc1ce3146 '@xterm/addon-ligatures@0.11.0-beta.300': 47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920 @@ -395,8 +396,8 @@ importers: specifier: ^9.1.7 version: 9.1.7 i18next-cli: - specifier: 1.74.1 - version: 1.74.1(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2) + specifier: 1.74.2 + version: 1.74.2(patch_hash=7955b89d3aa229f477408608d331f78c85148a85a85913729f89fac65ad8b207)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2) katex: specifier: ^0.16.45 version: 0.16.45 @@ -5339,13 +5340,13 @@ packages: engines: {node: '>=18'} hasBin: true - i18next-cli@1.74.1: - resolution: {integrity: sha512-8OEbo4jfn4r4/QwieyAvoLWV/rJW2OR6SG09I12jA6kffyhUNsyrH6OaA4bumLIWkdBaGySbiFm0wj1k19N/jA==} + i18next-cli@1.74.2: + resolution: {integrity: sha512-y32hOiVNLs85f+TKCl4XqERjD0dFy274IOEXe7/Zyp+WMaQC9VhF33JLO0hQZZKK4PUmYwbRDXaSObeE3PbsmA==} engines: {node: '>=22'} hasBin: true - i18next-resources-for-ts@2.1.0: - resolution: {integrity: sha512-n5UexwEVt0OoIAhG2MWpSnAVJW1U8mQrQTmXyxc5DMAx+NLhcLZhSMJo/FnUsA5JQ3obTYqTgB7YIuZKWpDgow==} + i18next-resources-for-ts@2.1.1: + resolution: {integrity: sha512-Xhz8S8dtNIPawzeWdmssnsII1JYsbvPWOLmstJakNcgKSFLCneA+xN7qajj/zlk5nf9sx1KGDTx8RNZ73h68tw==} hasBin: true i18next@26.3.1: @@ -12377,7 +12378,7 @@ snapshots: husky@9.1.7: {} - i18next-cli@1.74.1(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2): + i18next-cli@1.74.2(patch_hash=7955b89d3aa229f477408608d331f78c85148a85a85913729f89fac65ad8b207)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2): dependencies: '@croct/json5-parser': 0.2.2 '@swc/core': 1.16.2 @@ -12386,7 +12387,7 @@ snapshots: execa: 10.0.1 glob: 13.0.6 i18next: 26.4.2(typescript@7.0.2) - i18next-resources-for-ts: 2.1.0 + i18next-resources-for-ts: 2.1.1 inquirer: 14.2.2(@types/node@25.9.5) jiti: 2.7.0 jsonc-parser: 3.3.1 @@ -12403,10 +12404,10 @@ snapshots: - react-native - typescript - i18next-resources-for-ts@2.1.0: + i18next-resources-for-ts@2.1.1: dependencies: '@babel/runtime': 7.29.7 - '@swc/core': 1.15.46 + '@swc/core': 1.16.2 chokidar: 5.0.0 yaml: 2.9.1 transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e8a845e1975..cbb3e10ad4c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,6 +17,7 @@ minimumReleaseAgeExclude: - pdfjs-dist@6.3.289 - zod@4.5.4 - '@shadcn/lint@0.1.0' + - i18next-cli@1.74.2 shamefullyHoist: true # Orca always launches the user's own resolved Claude CLI via @@ -68,3 +69,4 @@ patchedDependencies: '@xterm/xterm@6.1.0-beta.303': config/patches/@xterm__xterm@6.1.0-beta.303.patch lint-staged@16.4.0: config/patches/lint-staged@16.4.0.patch '@vscode/windows-process-tree@0.8.0': config/patches/@vscode__windows-process-tree@0.8.0.patch + i18next-cli@1.74.2: config/patches/i18next-cli@1.74.2.patch