@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.
- package/dist/cli.cjs +54 -37
- package/dist/src/cli.js +8 -0
- package/dist/src/commands/fidelity-trim.d.ts +13 -0
- package/dist/src/commands/fidelity-trim.d.ts.map +1 -0
- package/dist/src/commands/fidelity-trim.js +21 -0
- package/dist/src/commands/fidelity-trim.test.d.ts +8 -0
- package/dist/src/commands/fidelity-trim.test.d.ts.map +1 -0
- package/dist/src/commands/fidelity-trim.test.js +79 -0
- package/dist/src/yaml/steps/render-post-processor.d.ts +52 -32
- package/dist/src/yaml/steps/render-post-processor.d.ts.map +1 -1
- package/dist/src/yaml/steps/render-post-processor.js +104 -205
- package/dist/src/yaml/steps/render-post-processor.test.d.ts +7 -10
- package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +1 -1
- package/dist/src/yaml/steps/render-post-processor.test.js +80 -183
- package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +28 -0
- package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/trim-align-corpus.test.js +383 -0
- package/dist/src/yaml/steps/trim-align.d.ts +85 -0
- package/dist/src/yaml/steps/trim-align.d.ts.map +1 -0
- package/dist/src/yaml/steps/trim-align.js +348 -0
- package/dist/src/yaml/steps/trim-align.test.d.ts +11 -0
- package/dist/src/yaml/steps/trim-align.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/trim-align.test.js +258 -0
- package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +1 -1
- package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +1 -1
- package/dist/src/yaml/steps/ui-fidelity-preview-android.js +9 -0
- package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +4 -3
- package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +1 -1
- package/dist/src/yaml/steps/ui-fidelity-preview.js +9 -0
- package/dist/src/yaml/steps/ui-fidelity-preview.test.js +13 -3
- package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +16 -5
- package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +1 -1
- package/dist/src/yaml/steps/ui-fidelity-render-android.js +190 -26
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +165 -10
- package/dist/src/yaml/steps/ui-fidelity-render.d.ts +20 -4
- package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +1 -1
- package/dist/src/yaml/steps/ui-fidelity-render.js +186 -10
- package/dist/src/yaml/steps/ui-fidelity-render.test.js +180 -22
- 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
|
-
|
|
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
|
-
'
|
|
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
|
-
['
|
|
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;
|
|
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
|
-
'
|
|
56
|
-
'
|
|
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
|
|
111
|
-
*
|
|
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
|
-
*
|
|
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(
|
|
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,
|
|
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
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
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
|
|
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'
|
|
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(
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
|
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 =
|
|
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
|
}
|