@invarn/cibuild 2.3.8 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cli.cjs +54 -37
  2. package/dist/src/cli.js +8 -0
  3. package/dist/src/commands/fidelity-trim.d.ts +13 -0
  4. package/dist/src/commands/fidelity-trim.d.ts.map +1 -0
  5. package/dist/src/commands/fidelity-trim.js +21 -0
  6. package/dist/src/commands/fidelity-trim.test.d.ts +8 -0
  7. package/dist/src/commands/fidelity-trim.test.d.ts.map +1 -0
  8. package/dist/src/commands/fidelity-trim.test.js +79 -0
  9. package/dist/src/yaml/steps/render-post-processor.d.ts +52 -32
  10. package/dist/src/yaml/steps/render-post-processor.d.ts.map +1 -1
  11. package/dist/src/yaml/steps/render-post-processor.js +104 -205
  12. package/dist/src/yaml/steps/render-post-processor.test.d.ts +7 -10
  13. package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +1 -1
  14. package/dist/src/yaml/steps/render-post-processor.test.js +80 -183
  15. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +28 -0
  16. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +1 -0
  17. package/dist/src/yaml/steps/trim-align-corpus.test.js +383 -0
  18. package/dist/src/yaml/steps/trim-align.d.ts +85 -0
  19. package/dist/src/yaml/steps/trim-align.d.ts.map +1 -0
  20. package/dist/src/yaml/steps/trim-align.js +348 -0
  21. package/dist/src/yaml/steps/trim-align.test.d.ts +11 -0
  22. package/dist/src/yaml/steps/trim-align.test.d.ts.map +1 -0
  23. package/dist/src/yaml/steps/trim-align.test.js +258 -0
  24. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +1 -1
  25. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +1 -1
  26. package/dist/src/yaml/steps/ui-fidelity-preview-android.js +9 -0
  27. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +4 -3
  28. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +1 -1
  29. package/dist/src/yaml/steps/ui-fidelity-preview.js +9 -0
  30. package/dist/src/yaml/steps/ui-fidelity-preview.test.js +13 -3
  31. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +16 -5
  32. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +1 -1
  33. package/dist/src/yaml/steps/ui-fidelity-render-android.js +190 -26
  34. package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +165 -10
  35. package/dist/src/yaml/steps/ui-fidelity-render.d.ts +20 -4
  36. package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +1 -1
  37. package/dist/src/yaml/steps/ui-fidelity-render.js +186 -10
  38. package/dist/src/yaml/steps/ui-fidelity-render.test.js +180 -22
  39. package/package.json +3 -1
@@ -36,10 +36,11 @@ const FAKE_GRADLEW = [
36
36
  // Fake `swift`: only the script-interpreter branch the shared trim stage uses
37
37
  // (`swift <Trim.swift> <in> <out> <aligned> <ref>`). Writes trimmed + aligned
38
38
  // PNGs and emits a fixed TRIMDIMS line.
39
- const FAKE_SWIFT = [
39
+ // Fake `ci` covering the shared trim stage's `ci fidelity-trim` invocation.
40
+ const FAKE_CI = [
40
41
  '#!/bin/bash',
41
42
  'case "$1" in',
42
- ' *.swift)',
43
+ ' fidelity-trim)',
43
44
  ' in_png="$2"; out_png="$3"; aligned_png="$4"',
44
45
  " printf '\\x89PNG\\r\\n\\x1a\\n' > \"$out_png\"",
45
46
  ' printf \'trimmed:%s\' "$(basename "$in_png")" >> "$out_png"',
@@ -56,7 +57,7 @@ function fakeToolchain() {
56
57
  const bin = tempDir('ui-fidelity-android-fakebin-');
57
58
  for (const [name, body] of [
58
59
  ['java', FAKE_JAVA],
59
- ['swift', FAKE_SWIFT],
60
+ ['ci', FAKE_CI],
60
61
  ]) {
61
62
  const p = join(bin, name);
62
63
  writeFileSync(p, body + '\n');
@@ -1 +1 @@
1
- {"version":3,"file":"ui-fidelity-preview.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA4BH,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC/C;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,aAAa,EAAE,QAAQ,CAAC;IACxB,oEAAoE;IACpE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,wDAAwD;IACxD,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACzC,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,EAAE,EAAE,OAAO,CAAC;CACb;AAED,kEAAkE;AAClE,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACjD;AAuBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,gBAAgB,EACrB,YAAY,EAAE,MAAM,GACnB,6BAA6B,CAgB/B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CA8HzB"}
1
+ {"version":3,"file":"ui-fidelity-preview.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA6BH,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC/C;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,aAAa,EAAE,QAAQ,CAAC;IACxB,oEAAoE;IACpE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,wDAAwD;IACxD,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACzC,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,EAAE,EAAE,OAAO,CAAC;CACb;AAED,kEAAkE;AAClE,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACjD;AAuBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,gBAAgB,EACrB,YAAY,EAAE,MAAM,GACnB,6BAA6B,CAgB/B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CAqIzB"}
@@ -21,6 +21,7 @@ import { tmpdir } from 'node:os';
21
21
  import { basename, join, resolve } from 'node:path';
22
22
  import { DEFAULT_RENDER_SIZE, DEFAULT_SCALE, generateRenderScript, parseRenderSize, parseScale, } from './ui-fidelity-render.js';
23
23
  import { evaluateAspectRatio, formatAspectRatioWarning, } from './fidelity-aspect-guard.js';
24
+ import { resolveCliJsPath } from './render-post-processor.js';
24
25
  function runTar(args, pathPrepend) {
25
26
  const env = pathPrepend
26
27
  ? { ...process.env, PATH: pathPrepend + ':' + (process.env.PATH ?? '') }
@@ -143,6 +144,14 @@ export function renderUiFidelityPreview(options) {
143
144
  if (options.toolchainPath) {
144
145
  env.PATH = options.toolchainPath + ':' + (process.env.PATH ?? '');
145
146
  }
147
+ // Hand the script this installation's CLI entrypoint so its trim stage can
148
+ // spawn `fidelity-trim` without a ci on PATH (library callers like the
149
+ // Invarn CLI). Never override an explicitly-set entrypoint.
150
+ if (!env.CIBUILD_CLI_JS) {
151
+ const cliJs = resolveCliJsPath();
152
+ if (cliJs !== null)
153
+ env.CIBUILD_CLI_JS = cliJs;
154
+ }
146
155
  const run = spawnSync('node', ['-e', script], {
147
156
  cwd: workDir,
148
157
  env,
@@ -52,8 +52,16 @@ const FAKE_SWIFT = [
52
52
  ' out="${pos[1]}"',
53
53
  " printf '\\x89PNG\\r\\n\\x1a\\n' > \"$out\"; printf 'fake-render' >> \"$out\"",
54
54
  ' exit 0 ;;',
55
- ' *.swift)',
56
- ' out_png="$2"; aligned_png="$3"',
55
+ ' *) exit 0 ;;',
56
+ 'esac',
57
+ ].join('\n');
58
+ const FAKE_XCRUN = ['#!/bin/bash', 'exit 0'].join('\n');
59
+ // Fake `ci` covering the shared trim stage's `ci fidelity-trim` invocation.
60
+ const FAKE_CI = [
61
+ '#!/bin/bash',
62
+ 'case "$1" in',
63
+ ' fidelity-trim)',
64
+ ' out_png="$3"; aligned_png="$4"',
57
65
  " printf '\\x89PNG\\r\\n\\x1a\\n' > \"$out_png\"; printf 'trimmed' >> \"$out_png\"",
58
66
  ' if [ -n "$aligned_png" ]; then printf \'\\x89PNG\\r\\n\\x1a\\n\' > "$aligned_png"; printf \'aligned\' >> "$aligned_png"; fi',
59
67
  ' echo "TRIMDIMS 320 640 160 320 400 800"',
@@ -61,7 +69,6 @@ const FAKE_SWIFT = [
61
69
  ' *) exit 0 ;;',
62
70
  'esac',
63
71
  ].join('\n');
64
- const FAKE_XCRUN = ['#!/bin/bash', 'exit 0'].join('\n');
65
72
  function fakeToolchain() {
66
73
  const bin = tempDir('ui-fidelity-fakebin-');
67
74
  const swiftPath = join(bin, 'swift');
@@ -70,6 +77,9 @@ function fakeToolchain() {
70
77
  const xcrunPath = join(bin, 'xcrun');
71
78
  writeFileSync(xcrunPath, FAKE_XCRUN + '\n');
72
79
  chmodSync(xcrunPath, 0o755);
80
+ const ciPath = join(bin, 'ci');
81
+ writeFileSync(ciPath, FAKE_CI + '\n');
82
+ chmodSync(ciPath, 0o755);
73
83
  return bin;
74
84
  }
75
85
  function writeReference() {
@@ -76,12 +76,16 @@ export interface UiFidelityRenderAndroidInputs {
76
76
  */
77
77
  export declare const DEFAULT_GRADLE_TASK = "recordRoborazziDebug";
78
78
  /** Valid values for the package_source input. */
79
- export declare const PACKAGE_SOURCES: readonly ["repo", "inputs"];
79
+ export declare const PACKAGE_SOURCES: readonly ["repo", "inputs", "url"];
80
80
  export type PackageSource = (typeof PACKAGE_SOURCES)[number];
81
81
  /**
82
82
  * Default package source: the Gradle project ships as package.tar.gz in the
83
83
  * run-inputs directory (the agent-upload path). "repo" instead renders the
84
84
  * committed project at package_path, for a triggered (agent-less) run.
85
+ * "url" (fidelity-linux 03) downloads the same package.tar.gz from a signed
86
+ * package_url into the run-inputs directory and then proceeds byte-for-byte
87
+ * like inputs mode — the clone-free delivery for triggered runs, on macOS
88
+ * and Linux workers alike.
85
89
  */
86
90
  export declare const DEFAULT_PACKAGE_SOURCE: PackageSource;
87
91
  /** Valid values for the reference_source input. */
@@ -107,16 +111,23 @@ export interface AndroidRenderScriptConfig {
107
111
  scale: number;
108
112
  gradleTask: string;
109
113
  /**
110
- * Only present (and only ever "repo") when package_source was explicitly set
111
- * to "repo". When absent, the project ships as package.tar.gz and is
114
+ * Only present when package_source was explicitly set to a non-default mode
115
+ * ("repo" or "url"). When absent, the project ships as package.tar.gz and is
112
116
  * extracted/validated exactly as the default, byte-identical script.
113
117
  */
114
118
  packageSource?: PackageSource;
115
119
  /**
116
120
  * Repo-relative path to the committed Gradle project. Present (and required)
117
- * only with packageSource "repo"; ignored otherwise.
121
+ * with packageSource "repo" (the checkout dir to render) and "url" (the
122
+ * prefix committed reference paths are resolved under, inside the extracted
123
+ * package); ignored otherwise.
118
124
  */
119
125
  packagePath?: string;
126
+ /**
127
+ * Signed download URL for package.tar.gz. Present (and required) only with
128
+ * packageSource "url"; ignored otherwise.
129
+ */
130
+ packageUrl?: string;
120
131
  /**
121
132
  * Only present (and only ever "repo") when reference_source was explicitly set
122
133
  * to "repo". When absent, references are read from `.ci/inputs/<basename>` and
@@ -138,7 +149,7 @@ export declare function generateAndroidRenderScript(config: AndroidRenderScriptC
138
149
  * script performs the render at runtime on the macOS runner.
139
150
  */
140
151
  export declare class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
141
- getValidationRequirements(_inputs: UiFidelityRenderAndroidInputs, _env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
152
+ getValidationRequirements(inputs: UiFidelityRenderAndroidInputs, _env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
142
153
  execute(inputs: UiFidelityRenderAndroidInputs, _env: Record<string, string>, _config: CIConfig): Promise<StepDef>;
143
154
  }
144
155
  //# sourceMappingURL=ui-fidelity-render-android.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui-fidelity-render-android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render-android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAMpE,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,iDAAiD;AACjD,eAAO,MAAM,eAAe,6BAA8B,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAAwB,CAAC;AAE9D,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,6BAA8B,CAAC;AAC7D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAA0B,CAAC;AAElE,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,8EAA8E;AAC9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAkiCD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CAqBrF;AAED;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,gBAAgB;IACvE,yBAAyB,CACvB,OAAO,EAAE,6BAA6B,EACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAepB,OAAO,CACX,MAAM,EAAE,6BAA6B,EACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CA0EpB"}
1
+ {"version":3,"file":"ui-fidelity-render-android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render-android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAMpE,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,iDAAiD;AACjD,eAAO,MAAM,eAAe,oCAAqC,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAAwB,CAAC;AAE9D,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,6BAA8B,CAAC;AAC7D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAA0B,CAAC;AAElE,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,8EAA8E;AAC9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAurCD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CA0BrF;AAED;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,gBAAgB;IACvE,yBAAyB,CACvB,MAAM,EAAE,6BAA6B,EACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAyBpB,OAAO,CACX,MAAM,EAAE,6BAA6B,EACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CAiGpB"}
@@ -63,11 +63,15 @@ import { RENDER_SCRIPT_SOURCE_DIGEST_STAGE } from '../fidelity-input-digest.js';
63
63
  */
64
64
  export const DEFAULT_GRADLE_TASK = 'recordRoborazziDebug';
65
65
  /** Valid values for the package_source input. */
66
- export const PACKAGE_SOURCES = ['repo', 'inputs'];
66
+ export const PACKAGE_SOURCES = ['repo', 'inputs', 'url'];
67
67
  /**
68
68
  * Default package source: the Gradle project ships as package.tar.gz in the
69
69
  * run-inputs directory (the agent-upload path). "repo" instead renders the
70
70
  * committed project at package_path, for a triggered (agent-less) run.
71
+ * "url" (fidelity-linux 03) downloads the same package.tar.gz from a signed
72
+ * package_url into the run-inputs directory and then proceeds byte-for-byte
73
+ * like inputs mode — the clone-free delivery for triggered runs, on macOS
74
+ * and Linux workers alike.
71
75
  */
72
76
  export const DEFAULT_PACKAGE_SOURCE = 'inputs';
73
77
  /** Valid values for the reference_source input. */
@@ -500,20 +504,9 @@ function cleanupShippedProject() {
500
504
 
501
505
  // ---- render stage ----
502
506
  //
503
- // runSwift drives the shared CoreGraphics trim/align helper in script-
504
- // interpreter mode (the same helper iOS uses); it is available because the
505
- // render runs on the macOS runner fleet regardless of the JVM render engine.
506
-
507
- function runSwift(args) {
508
- var result = cp.spawnSync('swift', args, { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 });
509
- if (result.stdout) process.stdout.write(result.stdout);
510
- if (result.stderr) process.stderr.write(result.stderr);
511
- if (result.error) {
512
- result.status = result.status == null ? 127 : result.status;
513
- result.stderr = (result.stderr || '') + String(result.error.message || result.error);
514
- }
515
- return result;
516
- }
507
+ // Trim/align runs through the shared trim stage's runTrimHelper (the pure-JS
508
+ // helper inside the ci binary — the same bytes iOS uses), so this step has no
509
+ // platform-toolchain dependency and runs on macOS and Linux workers alike.
517
510
 
518
511
  // Runs the project's Roborazzi record task once, from the project root, so
519
512
  // Gradle resolves the task in whichever module declares it. Prefers the shipped
@@ -692,7 +685,7 @@ function renderScreens(renderable, projectRoot) {
692
685
  entry.source_digest = __fidSourceDigest(packageFiles, referenceBytes);
693
686
  }
694
687
  log('rendered ' + entry.screen + ' -> ' + relative);
695
- trimRenderedImage(entry, outputPath, workDir);
688
+ trimRenderedImage(entry, outputPath);
696
689
  // Signal A: the render emits a per-node layout dump into a roborazzi
697
690
  // output dir (named "<Screen>.layout.json" or "...render<Screen>.layout.json").
698
691
  // Locate it the same way as the PNG and derive structural facts; absent or
@@ -1125,6 +1118,145 @@ function buildAndroidReferenceRepoMain(base) {
1125
1118
  ' }\n' +
1126
1119
  ' var source = path.resolve(refPath);');
1127
1120
  }
1121
+ /**
1122
+ * Builds the runtime main for package_source "url" (fidelity-linux 03) by
1123
+ * patching the default (inputs) runtime text. Three departures from the
1124
+ * default, all forced by the same fact — a clone-free worker has no checkout:
1125
+ *
1126
+ * 1. The package is DOWNLOADED (curl, already a runner requirement in this
1127
+ * mode) to `.ci/inputs/package.tar.gz` and extracted BEFORE the per-screen
1128
+ * reference stage, because committed references travel inside it. A failed
1129
+ * download is one structured per-build error, never per-screen noise.
1130
+ * 2. Each screen's reference is the committed repo-relative path resolved
1131
+ * INSIDE the extracted package: the path must sit under packagePath, and
1132
+ * the remainder resolves against the extracted project root.
1133
+ * 3. The render stage reuses the already-extracted root (no second extract);
1134
+ * cleanup mirrors the inputs path. The result document records
1135
+ * package_source "url".
1136
+ *
1137
+ * ANDROID_RENDER_SCRIPT_MAIN itself is never modified, so omitting
1138
+ * package_source keeps the generated script byte-identical to the default.
1139
+ */
1140
+ function buildAndroidPackageUrlMain(base) {
1141
+ let main = base;
1142
+ // The result document advertises the url package source.
1143
+ main = replaceOnce(main, "package_source: 'inputs',", "package_source: 'url',");
1144
+ // Download + extract FIRST — the reference stage below reads from inside
1145
+ // the extracted package.
1146
+ main = replaceOnce(main, ' // 2. Per-screen validation + reference copies. Each screen gets its OWN copy\n' +
1147
+ ' // named by screen, even when two screens share a reference basename.\n' +
1148
+ ' currentEntries.forEach(function (entry) {', ' // 2a. Download + extract the shipped package FIRST (URL mode): committed\n' +
1149
+ ' // references live inside it, so the per-screen reference copies below\n' +
1150
+ ' // need the extracted project root. A failed download or an invalid\n' +
1151
+ ' // archive is ONE structured per-build error; per-screen reference\n' +
1152
+ ' // work then stops cleanly (no bogus REFERENCE_MISSING noise).\n' +
1153
+ ' var urlProjectRoot = null;\n' +
1154
+ ' (function () {\n' +
1155
+ ' try {\n' +
1156
+ ' fs.mkdirSync(inputsDir, { recursive: true });\n' +
1157
+ ' } catch (mkdirError) {\n' +
1158
+ ' // statSync below reports the real failure\n' +
1159
+ ' }\n' +
1160
+ ' var dest = path.join(inputsDir, PACKAGE_ARCHIVE_NAME);\n' +
1161
+ " log('downloading render package');\n" +
1162
+ ' var download = cp.spawnSync(\n' +
1163
+ " 'curl',\n" +
1164
+ " ['-fsSL', '--retry', '2', '--max-time', '300', '-o', dest, CONFIG.packageUrl],\n" +
1165
+ " { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 }\n" +
1166
+ ' );\n' +
1167
+ ' if (download.error) {\n' +
1168
+ ' download.status = download.status == null ? 127 : download.status;\n' +
1169
+ " download.stderr = (download.stderr || '') + String(download.error.message || download.error);\n" +
1170
+ ' }\n' +
1171
+ ' if (download.status !== 0) {\n' +
1172
+ ' setBuildError(\n' +
1173
+ " 'ANDROID_PACKAGE_DOWNLOAD_FAILED',\n" +
1174
+ " 'render package download failed (curl exit ' + download.status + '):\\n' + outputTail(download)\n" +
1175
+ ' );\n' +
1176
+ ' return;\n' +
1177
+ ' }\n' +
1178
+ ' urlProjectRoot = prepareShippedProject();\n' +
1179
+ ' })();\n' +
1180
+ '\n' +
1181
+ ' // 2. Per-screen validation + reference copies. Each screen gets its OWN copy\n' +
1182
+ ' // named by screen, even when two screens share a reference basename.\n' +
1183
+ ' currentEntries.forEach(function (entry) {');
1184
+ // References resolve INSIDE the extracted package: repo-relative path minus
1185
+ // the packagePath prefix, against the extracted project root.
1186
+ main = replaceOnce(main, 'var basename = params.screens[entry.screen];\n' +
1187
+ ' if (\n' +
1188
+ " typeof basename !== 'string' || basename === '' ||\n" +
1189
+ " basename === '.' || basename === '..' ||\n" +
1190
+ ' basename !== path.basename(basename)\n' +
1191
+ ' ) {\n' +
1192
+ ' setError(\n' +
1193
+ ' entry,\n' +
1194
+ " 'REFERENCE_MISSING',\n" +
1195
+ " 'reference for ' + entry.screen + ' must be a plain file basename inside ' +\n" +
1196
+ " inputsDir + ', got: ' + JSON.stringify(basename)\n" +
1197
+ ' );\n' +
1198
+ ' return;\n' +
1199
+ ' }\n' +
1200
+ ' var source = path.join(inputsDir, basename);', 'var refPath = params.screens[entry.screen];\n' +
1201
+ " if (typeof refPath !== 'string' || refPath === '') {\n" +
1202
+ ' setError(\n' +
1203
+ ' entry,\n' +
1204
+ " 'REFERENCE_MISSING',\n" +
1205
+ " 'reference for ' + entry.screen + ' must be a non-empty repo-relative path, got: ' +\n" +
1206
+ ' JSON.stringify(refPath)\n' +
1207
+ ' );\n' +
1208
+ ' return;\n' +
1209
+ ' }\n' +
1210
+ ' if (urlProjectRoot === null) {\n' +
1211
+ ' // Package unavailable: the per-build error above carries the cause.\n' +
1212
+ ' return;\n' +
1213
+ ' }\n' +
1214
+ " var packagePrefix = CONFIG.packagePath.replace(/^\\.\\//, '').replace(/\\/+$/, '');\n" +
1215
+ " var normalizedRef = refPath.replace(/^\\.\\//, '');\n" +
1216
+ " if (normalizedRef.indexOf(packagePrefix + '/') !== 0) {\n" +
1217
+ ' setError(\n' +
1218
+ ' entry,\n' +
1219
+ " 'REFERENCE_MISSING',\n" +
1220
+ " 'reference for ' + entry.screen + ' must live inside the shipped package (' +\n" +
1221
+ " packagePrefix + '), got: ' + JSON.stringify(refPath)\n" +
1222
+ ' );\n' +
1223
+ ' return;\n' +
1224
+ ' }\n' +
1225
+ ' var source = path.join(urlProjectRoot, normalizedRef.slice(packagePrefix.length + 1));');
1226
+ // Render from the package extracted in stage 2a — no second extract.
1227
+ main = replaceOnce(main, ' // 3. Extract + validate the shipped Gradle project, then render the\n' +
1228
+ ' // renderable screens. The project stage runs even when no screen is\n' +
1229
+ ' // renderable, so packaging mistakes are always reported.\n' +
1230
+ ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1231
+ ' try {\n' +
1232
+ ' var projectRoot = prepareShippedProject();\n' +
1233
+ ' if (projectRoot !== null) {\n' +
1234
+ ' if (renderable.length > 0) {\n' +
1235
+ ' renderScreens(renderable, projectRoot);\n' +
1236
+ ' } else if (currentEntries.length > 0) {\n' +
1237
+ " log('no renderable screens, skipping render');\n" +
1238
+ ' }\n' +
1239
+ ' }\n' +
1240
+ ' } finally {\n' +
1241
+ ' cleanupShippedProject();\n' +
1242
+ ' }', ' // 3. Render from the package downloaded + extracted in stage 2a. The\n' +
1243
+ ' // extraction already reported packaging mistakes as the per-build\n' +
1244
+ ' // error, so a null root here just skips the render.\n' +
1245
+ ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1246
+ ' try {\n' +
1247
+ ' if (urlProjectRoot !== null) {\n' +
1248
+ ' if (renderable.length > 0) {\n' +
1249
+ ' renderScreens(renderable, urlProjectRoot);\n' +
1250
+ ' } else if (currentEntries.length > 0) {\n' +
1251
+ " log('no renderable screens, skipping render');\n" +
1252
+ ' }\n' +
1253
+ ' }\n' +
1254
+ ' } finally {\n' +
1255
+ ' cleanupShippedProject();\n' +
1256
+ ' }');
1257
+ return main;
1258
+ }
1259
+ const ANDROID_RENDER_SCRIPT_MAIN_URL = buildAndroidPackageUrlMain(ANDROID_RENDER_SCRIPT_MAIN);
1128
1260
  /**
1129
1261
  * Generates the self-contained runtime node script for the step. Pure function
1130
1262
  * of its config — exported so tests can execute the script directly.
@@ -1134,10 +1266,15 @@ function buildAndroidReferenceRepoMain(base) {
1134
1266
  * runtime is used).
1135
1267
  */
1136
1268
  export function generateAndroidRenderScript(config) {
1137
- let main = config.packageSource === 'repo' ? ANDROID_RENDER_SCRIPT_MAIN_REPO : ANDROID_RENDER_SCRIPT_MAIN;
1269
+ let main = config.packageSource === 'repo'
1270
+ ? ANDROID_RENDER_SCRIPT_MAIN_REPO
1271
+ : config.packageSource === 'url'
1272
+ ? ANDROID_RENDER_SCRIPT_MAIN_URL
1273
+ : ANDROID_RENDER_SCRIPT_MAIN;
1138
1274
  // Reference-from-repo is an independent dimension layered on top of whichever
1139
1275
  // package base applies. Absent → byte-identical reference block (default
1140
- // basename behaviour).
1276
+ // basename behaviour). URL mode embeds its own reference sourcing and the
1277
+ // executor rejects the combination before this point.
1141
1278
  if (config.referenceSource === 'repo') {
1142
1279
  main = buildAndroidReferenceRepoMain(main);
1143
1280
  }
@@ -1159,11 +1296,19 @@ export function generateAndroidRenderScript(config) {
1159
1296
  * script performs the render at runtime on the macOS runner.
1160
1297
  */
1161
1298
  export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1162
- getValidationRequirements(_inputs, _env, _config) {
1163
- return [
1164
- this.requireCommand('swift', 'Swift toolchain used to run the CoreGraphics trim/align helper', 'Install Xcode (or the Swift toolchain) on the runner'),
1299
+ getValidationRequirements(inputs, _env, _config) {
1300
+ // No platform-toolchain requirement: trim/align is pure JS inside the ci
1301
+ // binary (fidelity-linux 06), so the step validates on macOS and Linux
1302
+ // runners alike.
1303
+ const requirements = [
1165
1304
  this.requireCommand('tar', 'Archive tool used to extract the shipped Gradle project', 'Install tar on the runner'),
1166
1305
  ];
1306
+ // URL mode (fidelity-linux 03) downloads the package at runtime; existing
1307
+ // inputs/repo runs gain no new requirement.
1308
+ if (this.getInput(inputs, 'package_source', undefined) === 'url') {
1309
+ requirements.push(this.requireCommand('curl', 'Downloader used to fetch the render package by signed URL', 'Install curl on the runner'));
1310
+ }
1311
+ return requirements;
1167
1312
  }
1168
1313
  async execute(inputs, _env, _config) {
1169
1314
  const stepName = 'ui-fidelity-render-android';
@@ -1174,9 +1319,11 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1174
1319
  'expected a Gradle task path (letters, digits, and : - _)');
1175
1320
  }
1176
1321
  // package_source: validate when provided, but only record (and patch the
1177
- // script for) the non-default "repo" so omitting it — or setting the default
1322
+ // script for) the non-default modes so omitting it — or setting the default
1178
1323
  // "inputs" — keeps the default archive-extract script byte-identical. In
1179
- // repo mode package_path is required (the committed project to render).
1324
+ // repo mode package_path is required (the committed project to render); in
1325
+ // url mode (fidelity-linux 03) package_url and package_path are required
1326
+ // (the signed download + the prefix committed reference paths sit under).
1180
1327
  const rawPackageSource = this.getInput(inputs, 'package_source', undefined);
1181
1328
  let packageSource;
1182
1329
  if (rawPackageSource !== undefined) {
@@ -1185,14 +1332,21 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1185
1332
  throw new Error(`Invalid package_source '${value}' for step '${stepName}': ` +
1186
1333
  `expected one of: ${PACKAGE_SOURCES.join(', ')}`);
1187
1334
  }
1188
- if (value === 'repo') {
1189
- packageSource = 'repo';
1335
+ if (value === 'repo' || value === 'url') {
1336
+ packageSource = value;
1190
1337
  }
1191
1338
  }
1192
1339
  let packagePath;
1193
- if (packageSource === 'repo') {
1340
+ if (packageSource === 'repo' || packageSource === 'url') {
1194
1341
  packagePath = String(this.getRequiredInput(inputs, 'package_path', stepName));
1195
1342
  }
1343
+ let packageUrl;
1344
+ if (packageSource === 'url') {
1345
+ packageUrl = String(this.getRequiredInput(inputs, 'package_url', stepName));
1346
+ if (!/^https?:\/\//.test(packageUrl)) {
1347
+ throw new Error(`Invalid package_url for step '${stepName}': expected an http(s) URL`);
1348
+ }
1349
+ }
1196
1350
  // reference_source: validate when provided, but only record (and patch the
1197
1351
  // script for) the non-default "repo" so omitting it — or setting the default
1198
1352
  // "inputs" — keeps the default basename reference block intact.
@@ -1204,6 +1358,13 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1204
1358
  throw new Error(`Invalid reference_source '${value}' for step '${stepName}': ` +
1205
1359
  `expected one of: ${REFERENCE_SOURCES.join(', ')}`);
1206
1360
  }
1361
+ // URL mode owns reference sourcing (inside the extracted package) —
1362
+ // an explicit reference_source alongside it is a contradiction.
1363
+ if (packageSource === 'url') {
1364
+ throw new Error(`Invalid reference_source for step '${stepName}': ` +
1365
+ "package_source 'url' resolves references inside the downloaded " +
1366
+ 'package; omit reference_source');
1367
+ }
1207
1368
  if (value === 'repo') {
1208
1369
  referenceSource = 'repo';
1209
1370
  }
@@ -1215,6 +1376,9 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1215
1376
  if (packagePath !== undefined) {
1216
1377
  config.packagePath = packagePath;
1217
1378
  }
1379
+ if (packageUrl !== undefined) {
1380
+ config.packageUrl = packageUrl;
1381
+ }
1218
1382
  if (referenceSource !== undefined) {
1219
1383
  config.referenceSource = referenceSource;
1220
1384
  }