@invarn/cibuild 2.4.0 → 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 +10 -10
- 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 +183 -10
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +156 -1
- 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 +185 -9
- package/dist/src/yaml/steps/ui-fidelity-render.test.js +155 -2
- package/package.json +1 -1
|
@@ -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. */
|
|
@@ -1114,6 +1118,145 @@ function buildAndroidReferenceRepoMain(base) {
|
|
|
1114
1118
|
' }\n' +
|
|
1115
1119
|
' var source = path.resolve(refPath);');
|
|
1116
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);
|
|
1117
1260
|
/**
|
|
1118
1261
|
* Generates the self-contained runtime node script for the step. Pure function
|
|
1119
1262
|
* of its config — exported so tests can execute the script directly.
|
|
@@ -1123,10 +1266,15 @@ function buildAndroidReferenceRepoMain(base) {
|
|
|
1123
1266
|
* runtime is used).
|
|
1124
1267
|
*/
|
|
1125
1268
|
export function generateAndroidRenderScript(config) {
|
|
1126
|
-
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;
|
|
1127
1274
|
// Reference-from-repo is an independent dimension layered on top of whichever
|
|
1128
1275
|
// package base applies. Absent → byte-identical reference block (default
|
|
1129
|
-
// basename behaviour).
|
|
1276
|
+
// basename behaviour). URL mode embeds its own reference sourcing and the
|
|
1277
|
+
// executor rejects the combination before this point.
|
|
1130
1278
|
if (config.referenceSource === 'repo') {
|
|
1131
1279
|
main = buildAndroidReferenceRepoMain(main);
|
|
1132
1280
|
}
|
|
@@ -1148,13 +1296,19 @@ export function generateAndroidRenderScript(config) {
|
|
|
1148
1296
|
* script performs the render at runtime on the macOS runner.
|
|
1149
1297
|
*/
|
|
1150
1298
|
export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
|
|
1151
|
-
getValidationRequirements(
|
|
1299
|
+
getValidationRequirements(inputs, _env, _config) {
|
|
1152
1300
|
// No platform-toolchain requirement: trim/align is pure JS inside the ci
|
|
1153
1301
|
// binary (fidelity-linux 06), so the step validates on macOS and Linux
|
|
1154
1302
|
// runners alike.
|
|
1155
|
-
|
|
1303
|
+
const requirements = [
|
|
1156
1304
|
this.requireCommand('tar', 'Archive tool used to extract the shipped Gradle project', 'Install tar on the runner'),
|
|
1157
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;
|
|
1158
1312
|
}
|
|
1159
1313
|
async execute(inputs, _env, _config) {
|
|
1160
1314
|
const stepName = 'ui-fidelity-render-android';
|
|
@@ -1165,9 +1319,11 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
|
|
|
1165
1319
|
'expected a Gradle task path (letters, digits, and : - _)');
|
|
1166
1320
|
}
|
|
1167
1321
|
// package_source: validate when provided, but only record (and patch the
|
|
1168
|
-
// script for) the non-default
|
|
1322
|
+
// script for) the non-default modes so omitting it — or setting the default
|
|
1169
1323
|
// "inputs" — keeps the default archive-extract script byte-identical. In
|
|
1170
|
-
// 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).
|
|
1171
1327
|
const rawPackageSource = this.getInput(inputs, 'package_source', undefined);
|
|
1172
1328
|
let packageSource;
|
|
1173
1329
|
if (rawPackageSource !== undefined) {
|
|
@@ -1176,14 +1332,21 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
|
|
|
1176
1332
|
throw new Error(`Invalid package_source '${value}' for step '${stepName}': ` +
|
|
1177
1333
|
`expected one of: ${PACKAGE_SOURCES.join(', ')}`);
|
|
1178
1334
|
}
|
|
1179
|
-
if (value === 'repo') {
|
|
1180
|
-
packageSource =
|
|
1335
|
+
if (value === 'repo' || value === 'url') {
|
|
1336
|
+
packageSource = value;
|
|
1181
1337
|
}
|
|
1182
1338
|
}
|
|
1183
1339
|
let packagePath;
|
|
1184
|
-
if (packageSource === 'repo') {
|
|
1340
|
+
if (packageSource === 'repo' || packageSource === 'url') {
|
|
1185
1341
|
packagePath = String(this.getRequiredInput(inputs, 'package_path', stepName));
|
|
1186
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
|
+
}
|
|
1187
1350
|
// reference_source: validate when provided, but only record (and patch the
|
|
1188
1351
|
// script for) the non-default "repo" so omitting it — or setting the default
|
|
1189
1352
|
// "inputs" — keeps the default basename reference block intact.
|
|
@@ -1195,6 +1358,13 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
|
|
|
1195
1358
|
throw new Error(`Invalid reference_source '${value}' for step '${stepName}': ` +
|
|
1196
1359
|
`expected one of: ${REFERENCE_SOURCES.join(', ')}`);
|
|
1197
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
|
+
}
|
|
1198
1368
|
if (value === 'repo') {
|
|
1199
1369
|
referenceSource = 'repo';
|
|
1200
1370
|
}
|
|
@@ -1206,6 +1376,9 @@ export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
|
|
|
1206
1376
|
if (packagePath !== undefined) {
|
|
1207
1377
|
config.packagePath = packagePath;
|
|
1208
1378
|
}
|
|
1379
|
+
if (packageUrl !== undefined) {
|
|
1380
|
+
config.packageUrl = packageUrl;
|
|
1381
|
+
}
|
|
1209
1382
|
if (referenceSource !== undefined) {
|
|
1210
1383
|
config.referenceSource = referenceSource;
|
|
1211
1384
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
2
2
|
import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import { gzipSync } from 'node:zlib';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
@@ -826,6 +826,161 @@ describe('package_source: repo', () => {
|
|
|
826
826
|
expect(screen?.reference_image_path).toBe('ui-fidelity/references/XProof.png');
|
|
827
827
|
});
|
|
828
828
|
});
|
|
829
|
+
// Clone-free package delivery (fidelity-linux 03): the dispatched YAML carries
|
|
830
|
+
// a signed package_url; the runtime downloads it to .ci/inputs/package.tar.gz
|
|
831
|
+
// and proceeds byte-for-byte like inputs mode, resolving each screen's
|
|
832
|
+
// committed reference INSIDE the extracted package (repo-relative path minus
|
|
833
|
+
// the package_path prefix) — there is no checkout on a clone-free worker.
|
|
834
|
+
describe('package_source: url', () => {
|
|
835
|
+
/**
|
|
836
|
+
* Serve one tar.gz (or a 404) from a loopback HTTP server IN A CHILD
|
|
837
|
+
* PROCESS. runScript executes the generated script via spawnSync, which
|
|
838
|
+
* blocks this process's event loop — an in-process server would deadlock
|
|
839
|
+
* against the script's curl. The child prints its port once listening.
|
|
840
|
+
*/
|
|
841
|
+
const SERVE_PACKAGE_SRC = [
|
|
842
|
+
"var http = require('http');",
|
|
843
|
+
"var fs = require('fs');",
|
|
844
|
+
'var filePath = process.argv[1];',
|
|
845
|
+
'var server = http.createServer(function (req, res) {',
|
|
846
|
+
" if (filePath === '-') { res.statusCode = 404; res.end('gone'); return; }",
|
|
847
|
+
" res.setHeader('content-type', 'application/gzip');",
|
|
848
|
+
' res.end(fs.readFileSync(filePath));',
|
|
849
|
+
'});',
|
|
850
|
+
"server.listen(0, '127.0.0.1', function () {",
|
|
851
|
+
" console.log('PORT ' + server.address().port);",
|
|
852
|
+
'});',
|
|
853
|
+
].join('\n');
|
|
854
|
+
async function servePackage(archive) {
|
|
855
|
+
let filePath = '-';
|
|
856
|
+
if (archive !== null) {
|
|
857
|
+
const dir = mkdtempSync(join(tmpdir(), 'ui-fidelity-url-serve-'));
|
|
858
|
+
tempDirs.push(dir);
|
|
859
|
+
filePath = join(dir, 'package.tar.gz');
|
|
860
|
+
writeFileSync(filePath, archive);
|
|
861
|
+
}
|
|
862
|
+
const child = spawn('node', ['-e', SERVE_PACKAGE_SRC, filePath], {
|
|
863
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
864
|
+
});
|
|
865
|
+
const port = await new Promise((resolvePort, rejectPort) => {
|
|
866
|
+
let buffered = '';
|
|
867
|
+
const timer = setTimeout(() => rejectPort(new Error('package server did not start')), 10_000);
|
|
868
|
+
child.stdout.on('data', (chunk) => {
|
|
869
|
+
buffered += chunk.toString();
|
|
870
|
+
const match = /PORT (\d+)/.exec(buffered);
|
|
871
|
+
if (match) {
|
|
872
|
+
clearTimeout(timer);
|
|
873
|
+
resolvePort(Number(match[1]));
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
child.on('exit', () => rejectPort(new Error('package server exited early')));
|
|
877
|
+
});
|
|
878
|
+
return {
|
|
879
|
+
url: `http://127.0.0.1:${port}/api/fidelity/packages/blob1?exp=1&sig=ab`,
|
|
880
|
+
close: () => new Promise((resolveClose) => {
|
|
881
|
+
child.once('exit', () => resolveClose(undefined));
|
|
882
|
+
child.kill('SIGKILL');
|
|
883
|
+
}),
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
/** Archive shaped like the dashboard's assembly: package-dir contents at the
|
|
887
|
+
* archive root, committed references under references/. */
|
|
888
|
+
function packagedProject() {
|
|
889
|
+
return makeTarGz([
|
|
890
|
+
...gradleProjectEntries(''),
|
|
891
|
+
{ name: 'references/x.png', content: Buffer.concat([PNG_MAGIC, Buffer.from('ref:url-x')]) },
|
|
892
|
+
]);
|
|
893
|
+
}
|
|
894
|
+
test('package_source url requires package_url and package_path', async () => {
|
|
895
|
+
const executor = new UiFidelityRenderAndroidStepExecutor();
|
|
896
|
+
await expect(executor.execute({ package_source: 'url', package_path: 'android-app' }, {}, testConfig)).rejects.toThrow(/package_url/);
|
|
897
|
+
await expect(executor.execute({ package_source: 'url', package_url: 'https://x.test/p' }, {}, testConfig)).rejects.toThrow(/package_path/);
|
|
898
|
+
});
|
|
899
|
+
test('rejects reference_source alongside package_source url (URL mode owns references)', async () => {
|
|
900
|
+
const executor = new UiFidelityRenderAndroidStepExecutor();
|
|
901
|
+
await expect(executor.execute({
|
|
902
|
+
package_source: 'url',
|
|
903
|
+
package_url: 'https://x.test/p',
|
|
904
|
+
package_path: 'android-app',
|
|
905
|
+
reference_source: 'repo',
|
|
906
|
+
}, {}, testConfig)).rejects.toThrow(/reference_source/);
|
|
907
|
+
});
|
|
908
|
+
test('url mode requires curl on the runner (validation requirement)', () => {
|
|
909
|
+
const executor = new UiFidelityRenderAndroidStepExecutor();
|
|
910
|
+
const urlReqs = executor.getValidationRequirements({ package_source: 'url' }, {}, testConfig);
|
|
911
|
+
expect(urlReqs.some((r) => r.name === 'curl')).toBe(true);
|
|
912
|
+
// Default inputs mode keeps tar only — no new requirement for existing runs.
|
|
913
|
+
const defaultReqs = executor.getValidationRequirements({}, {}, testConfig);
|
|
914
|
+
expect(defaultReqs.some((r) => r.name === 'curl')).toBe(false);
|
|
915
|
+
});
|
|
916
|
+
test('downloads the package, renders, and resolves references inside it', async () => {
|
|
917
|
+
const project = makeProject({ screens: { XProof: 'android-app/references/x.png' } });
|
|
918
|
+
const served = await servePackage(packagedProject());
|
|
919
|
+
try {
|
|
920
|
+
const run = runScript(project, await buildScript({
|
|
921
|
+
package_source: 'url',
|
|
922
|
+
package_url: served.url,
|
|
923
|
+
package_path: 'android-app',
|
|
924
|
+
}), { FAKE_ROBORAZZI_SCREENS: 'XProof' });
|
|
925
|
+
expect(run.status).toBe(0);
|
|
926
|
+
const doc = readResult(project);
|
|
927
|
+
expect(doc.package_source).toBe('url');
|
|
928
|
+
const screen = doc.screens.find((s) => s.screen === 'XProof');
|
|
929
|
+
expect(screen?.status).toBe('rendered');
|
|
930
|
+
expect(screen?.reference_image_path).toBe('ui-fidelity/references/XProof.png');
|
|
931
|
+
expect(isPng(artifact(project, 'ui-fidelity/references/XProof.png'))).toBe(true);
|
|
932
|
+
}
|
|
933
|
+
finally {
|
|
934
|
+
await served.close();
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
test('a failed download is one structured build error, not per-screen noise', async () => {
|
|
938
|
+
const project = makeProject({ screens: { XProof: 'android-app/references/x.png' } });
|
|
939
|
+
const served = await servePackage(null); // 404
|
|
940
|
+
try {
|
|
941
|
+
const run = runScript(project, await buildScript({
|
|
942
|
+
package_source: 'url',
|
|
943
|
+
package_url: served.url,
|
|
944
|
+
package_path: 'android-app',
|
|
945
|
+
}));
|
|
946
|
+
expect(run.status).not.toBe(0);
|
|
947
|
+
const doc = readResult(project);
|
|
948
|
+
expect(doc.error?.code).toBe('ANDROID_PACKAGE_DOWNLOAD_FAILED');
|
|
949
|
+
const screen = doc.screens.find((s) => s.screen === 'XProof');
|
|
950
|
+
// The failure is the build's, not the screen's — no bogus
|
|
951
|
+
// REFERENCE_MISSING masking the real cause.
|
|
952
|
+
expect(screen?.error).toBeNull();
|
|
953
|
+
}
|
|
954
|
+
finally {
|
|
955
|
+
await served.close();
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
test('a reference outside the package directory fails that screen only', async () => {
|
|
959
|
+
const project = makeProject({
|
|
960
|
+
screens: {
|
|
961
|
+
XProof: 'android-app/references/x.png',
|
|
962
|
+
Stray: 'design/elsewhere/stray.png',
|
|
963
|
+
},
|
|
964
|
+
});
|
|
965
|
+
const served = await servePackage(packagedProject());
|
|
966
|
+
try {
|
|
967
|
+
const run = runScript(project, await buildScript({
|
|
968
|
+
package_source: 'url',
|
|
969
|
+
package_url: served.url,
|
|
970
|
+
package_path: 'android-app',
|
|
971
|
+
}), { FAKE_ROBORAZZI_SCREENS: 'XProof' });
|
|
972
|
+
expect(run.status).not.toBe(0);
|
|
973
|
+
const doc = readResult(project);
|
|
974
|
+
expect(doc.screens.find((s) => s.screen === 'XProof')?.status).toBe('rendered');
|
|
975
|
+
const stray = doc.screens.find((s) => s.screen === 'Stray');
|
|
976
|
+
expect(stray?.status).toBe('render_failed');
|
|
977
|
+
expect(stray?.error?.code).toBe('REFERENCE_MISSING');
|
|
978
|
+
}
|
|
979
|
+
finally {
|
|
980
|
+
await served.close();
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
});
|
|
829
984
|
describe('reference_source: repo', () => {
|
|
830
985
|
test('rejects an unknown reference_source value', async () => {
|
|
831
986
|
const executor = new UiFidelityRenderAndroidStepExecutor();
|
|
@@ -71,8 +71,17 @@ export interface UiFidelityRenderInputs {
|
|
|
71
71
|
* behavior; omitting package_source generates a byte-identical script).
|
|
72
72
|
* - "inputs": the package ships as .ci/inputs/package.tar.gz and is
|
|
73
73
|
* extracted, validated, and built from a scratch directory at runtime.
|
|
74
|
+
* - "url" (fidelity-linux 03): the same package.tar.gz is downloaded from
|
|
75
|
+
* the signed package_url into the run-inputs directory, then extracted
|
|
76
|
+
* and built exactly like inputs mode — the clone-free delivery for
|
|
77
|
+
* triggered runs. References resolve INSIDE the extracted package.
|
|
74
78
|
*/
|
|
75
|
-
package_source?: 'repo' | 'inputs';
|
|
79
|
+
package_source?: 'repo' | 'inputs' | 'url';
|
|
80
|
+
/**
|
|
81
|
+
* Signed download URL for package.tar.gz. Required with package_source
|
|
82
|
+
* "url"; ignored otherwise.
|
|
83
|
+
*/
|
|
84
|
+
package_url?: string;
|
|
76
85
|
/**
|
|
77
86
|
* Path to the user's SwiftPM package (the package that defines the
|
|
78
87
|
* screens). Required in repo mode; ignored with package_source "inputs".
|
|
@@ -110,7 +119,7 @@ export declare const DEFAULT_RENDER_SIZE = "393x852";
|
|
|
110
119
|
/** Default display scale (@2x), matching how references are typically exported. */
|
|
111
120
|
export declare const DEFAULT_SCALE = 2;
|
|
112
121
|
/** Valid values for the package_source input. */
|
|
113
|
-
export declare const PACKAGE_SOURCES: readonly ["repo", "inputs"];
|
|
122
|
+
export declare const PACKAGE_SOURCES: readonly ["repo", "inputs", "url"];
|
|
114
123
|
export type PackageSource = (typeof PACKAGE_SOURCES)[number];
|
|
115
124
|
/** Default package source: the package lives in the repo checkout. */
|
|
116
125
|
export declare const DEFAULT_PACKAGE_SOURCE: PackageSource;
|
|
@@ -157,14 +166,21 @@ export declare function parseScale(value: number | string): number;
|
|
|
157
166
|
* `error` (null, or `{ code, message }` for per-build package failures).
|
|
158
167
|
*/
|
|
159
168
|
export interface RenderScriptConfig {
|
|
160
|
-
/** null only with packageSource "inputs", where the package is shipped.
|
|
169
|
+
/** null only with packageSource "inputs", where the package is shipped.
|
|
170
|
+
* With packageSource "url" it carries the repo-relative package directory
|
|
171
|
+
* committed reference paths are resolved under (never a local path). */
|
|
161
172
|
packagePath: string | null;
|
|
162
|
-
/** null
|
|
173
|
+
/** null with packageSource "inputs"/"url" when it is to be discovered. */
|
|
163
174
|
target: string | null;
|
|
164
175
|
width: number;
|
|
165
176
|
height: number;
|
|
166
177
|
scale: number;
|
|
167
178
|
packageSource?: PackageSource;
|
|
179
|
+
/**
|
|
180
|
+
* Signed download URL for package.tar.gz. Present (and required) only with
|
|
181
|
+
* packageSource "url"; ignored otherwise.
|
|
182
|
+
*/
|
|
183
|
+
packageUrl?: string;
|
|
168
184
|
/**
|
|
169
185
|
* Only present (and only ever "repo") when reference_source was explicitly set
|
|
170
186
|
* to "repo". When absent, references are read from `.ci/inputs/<basename>` and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-fidelity-render.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;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;AAIpE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC
|
|
1
|
+
{"version":3,"file":"ui-fidelity-render.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render.ts"],"names":[],"mappings":"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"}
|