@invarn/cibuild 2.8.2 → 2.8.4

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.
@@ -351,4 +351,32 @@ describe("a build that reads the properties file directly", () => {
351
351
  expect(await keys()).toEqual(["api.key"]);
352
352
  });
353
353
  });
354
+ /**
355
+ * A scan that answers well on a small repository and thins out on a large one
356
+ * is the worse of the two failures, because the large one is the one someone
357
+ * is paid to build.
358
+ *
359
+ * Manifests and source shared a file budget at first, and source was walked
360
+ * first. Measured on a real application — 1743 source files, 47 manifests —
361
+ * the source walk spent the whole budget and the manifests were never opened,
362
+ * so every key referenced only as `${KEY}` in a manifest came back missing.
363
+ * Nine of that application's twenty declared keys were lost that way, and the
364
+ * scan reported the other eleven with no sign anything was wrong.
365
+ *
366
+ * This is the only test here that builds a tree big enough to exhaust a
367
+ * budget, because that is the only way to express the defect. It costs about
368
+ * half a second.
369
+ */
370
+ describe("a repository larger than the source budget", () => {
371
+ test("still reads its manifests", async () => {
372
+ projectApplyingTheSecretsPlugin();
373
+ write("app/src/main/AndroidManifest.xml", '<manifest android:value="${MANIFEST_ONLY_KEY}" />\n');
374
+ // Past SOURCE_FILE_CAP, spread over directories so no single readdir is
375
+ // unrepresentative.
376
+ for (let i = 0; i < 8100; i++) {
377
+ write(`app/src/main/java/p${i % 40}/F${i}.kt`, `val x${i} = 1\n`);
378
+ }
379
+ expect(await keys()).toContain("MANIFEST_ONLY_KEY");
380
+ }, 60_000);
381
+ });
354
382
  //# sourceMappingURL=a-properties-stand-in-declares-the-keys-the-build-reads.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"android-scanner.d.ts","sourceRoot":"","sources":["../../../src/commands/android-scanner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"android-scanner.d.ts","sourceRoot":"","sources":["../../../src/commands/android-scanner.ts"],"names":[],"mappings":"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"}
@@ -433,7 +433,23 @@ const MACHINE_LOCAL_KEYS = new Set(["sdk.dir", "ndk.dir", "cmake.dir"]);
433
433
  // runs on every scan of one.
434
434
  const SOURCE_EXTENSIONS = [".kt", ".kts", ".java"];
435
435
  const SOURCE_SKIP_DIRS = new Set(["build", "node_modules"]);
436
- const SOURCE_FILE_CAP = 1500;
436
+ /**
437
+ * **Manifests get their own budget, and a source walk cannot spend it.**
438
+ *
439
+ * They shared one at first, with source walked first, and on a real application
440
+ * that meant the manifests were never reached at all: 1743 source files against
441
+ * a 1500-file cap left nothing, and the keys referenced only as `${KEY}` in a
442
+ * manifest — which is most of them — came back missing. A scan that answers
443
+ * well on a small repository and silently thins out on a large one is the worse
444
+ * of the two failures, because the large one is the one someone is paid to
445
+ * build.
446
+ *
447
+ * There are few manifests and many source files — 47 against 1743 in that same
448
+ * application — so the two are not competing for a scarce resource, they were
449
+ * just sharing the wrong one.
450
+ */
451
+ const SOURCE_FILE_CAP = 8000;
452
+ const MANIFEST_FILE_CAP = 500;
437
453
  const SOURCE_DEPTH_CAP = 12;
438
454
  /** Source files under `dir`, up to the shared file budget. */
439
455
  function findSourceFiles(dir, budget, depth = 0) {
@@ -536,14 +552,17 @@ function collectPropertyKeys(gradleFiles) {
536
552
  }
537
553
  if (moduleDirs.size === 0)
538
554
  return [];
539
- const budget = { left: SOURCE_FILE_CAP };
555
+ const sourceBudget = { left: SOURCE_FILE_CAP };
556
+ const manifestBudget = { left: MANIFEST_FILE_CAP };
540
557
  for (const dir of outermost([...moduleDirs])) {
541
- for (const file of findSourceFiles(dir, budget)) {
542
- for (const name of extractBuildConfigFieldRefs(safeRead(file)))
558
+ // Manifests first. They are the likeliest place for a key and the cheapest
559
+ // to read, so if anything is ever going to run short it must not be these.
560
+ for (const manifest of findManifests(dir, manifestBudget)) {
561
+ for (const name of extractManifestXmlPlaceholders(safeRead(manifest)))
543
562
  keys.add(name);
544
563
  }
545
- for (const manifest of findManifests(dir, budget)) {
546
- for (const name of extractManifestXmlPlaceholders(safeRead(manifest)))
564
+ for (const file of findSourceFiles(dir, sourceBudget)) {
565
+ for (const name of extractBuildConfigFieldRefs(safeRead(file)))
547
566
  keys.add(name);
548
567
  }
549
568
  }
@@ -15,7 +15,11 @@
15
15
  * not re-implemented.
16
16
  */
17
17
  import { execFileSync } from "node:child_process";
18
- import { PICK_VARIANT_TASK_FUNCTION, variantTaskCommands } from "./android.js";
18
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ import { AndroidBuildForUITestingStepExecutor, AndroidLintStepExecutor, AndroidUnitTestStepExecutor, GradleBuildStepExecutor, PICK_VARIANT_TASK_FUNCTION, gradleProjectDir, gradleTaskPath, variantTaskCommands, } from "./android.js";
22
+ import { testConfig } from "./test-config.js";
19
23
  /** Run the shipped shell function over some Gradle output. */
20
24
  function pick(gradleOutput, prefix, suffix, buildType) {
21
25
  const script = `${PICK_VARIANT_TASK_FUNCTION}\ncibuild_pick_variant_task "${prefix}" "${suffix}" "${buildType}"`;
@@ -114,4 +118,143 @@ describe("the step that uses it", () => {
114
118
  expect(script).toContain('exit "$VARIANT_STATUS"');
115
119
  });
116
120
  });
121
+ /**
122
+ * An app whose root project is the application: no `include`, the Android
123
+ * application plugin applied in the root build file, sources in `src/`.
124
+ * Gradle's path for that project is `:`, so a pipeline says `module: ":"`.
125
+ */
126
+ describe("a module that names the root project", () => {
127
+ const escape = (value) => value;
128
+ test("runs the root project's own task, with no empty module segment", () => {
129
+ const script = variantTaskCommands({
130
+ module: ":",
131
+ variant: "debug",
132
+ prefix: "test",
133
+ suffix: "UnitTest",
134
+ escape,
135
+ }).join("\n");
136
+ expect(script).toContain("$GRADLE_CMD :testDebugUnitTest");
137
+ expect(script).not.toContain("::");
138
+ });
139
+ test("retries the resolved task on the root project too", () => {
140
+ const script = variantTaskCommands({
141
+ module: ":",
142
+ variant: "debug",
143
+ prefix: "lint",
144
+ suffix: "",
145
+ escape,
146
+ }).join("\n");
147
+ expect(script).toContain("$GRADLE_CMD :lintDebug");
148
+ expect(script).toContain("$GRADLE_CMD :$RESOLVED_TASK");
149
+ expect(script).toContain('echo "Using :$RESOLVED_TASK');
150
+ expect(script).not.toContain("::");
151
+ });
152
+ // An explicit empty string reaches the step as is; it must not become
153
+ // `::testDebugUnitTest`.
154
+ test("treats an empty module as the root", () => {
155
+ const script = variantTaskCommands({
156
+ module: "",
157
+ variant: "debug",
158
+ prefix: "test",
159
+ suffix: "UnitTest",
160
+ escape,
161
+ }).join("\n");
162
+ expect(script).toContain("$GRADLE_CMD :testDebugUnitTest");
163
+ expect(script).not.toContain("::");
164
+ });
165
+ test("leaves a named module exactly as it was", () => {
166
+ const options = { variant: "debug", prefix: "test", suffix: "UnitTest", escape };
167
+ const script = variantTaskCommands({ module: "app", ...options }).join("\n");
168
+ expect(script).toContain("$GRADLE_CMD :app:testDebugUnitTest 2>&1");
169
+ expect(script).toContain(" echo \"Using :app:$RESOLVED_TASK — resolved VARIANT=$RESOLVED_VARIANT\"");
170
+ expect(script).toContain(" $GRADLE_CMD :app:$RESOLVED_TASK");
171
+ });
172
+ test("keeps a nested module's path", () => {
173
+ const script = variantTaskCommands({
174
+ module: "feature:store",
175
+ variant: "debug",
176
+ prefix: "lint",
177
+ suffix: "",
178
+ escape,
179
+ }).join("\n");
180
+ expect(script).toContain("$GRADLE_CMD :feature:store:lintDebug");
181
+ });
182
+ });
183
+ describe("gradleTaskPath and gradleProjectDir", () => {
184
+ test("compose a task path", () => {
185
+ expect(gradleTaskPath(":", "lintDebug")).toBe(":lintDebug");
186
+ expect(gradleTaskPath("", "lintDebug")).toBe(":lintDebug");
187
+ expect(gradleTaskPath("app", "lintDebug")).toBe(":app:lintDebug");
188
+ });
189
+ test("compose the project directory, empty for the root", () => {
190
+ expect(gradleProjectDir(":")).toBe("");
191
+ expect(gradleProjectDir("")).toBe("");
192
+ expect(gradleProjectDir("app")).toBe("app/");
193
+ });
194
+ });
195
+ describe("the steps, for the root project", () => {
196
+ test("lint runs :lintDebug and reports under build/", async () => {
197
+ const result = await new AndroidLintStepExecutor().execute({ module: ":" }, {}, testConfig);
198
+ const script = result.script;
199
+ expect(script).toContain("$GRADLE_CMD :lintDebug");
200
+ expect(script).not.toContain("::");
201
+ expect(script).toContain('echo "Lint report location: build/reports/lint-results-debug.html"');
202
+ expect(script).toContain('echo "Running lint on the root project"');
203
+ });
204
+ test("unit tests run :testDebugUnitTest and report under build/", async () => {
205
+ const result = await new AndroidUnitTestStepExecutor().execute({ module: ":" }, {}, testConfig);
206
+ const script = result.script;
207
+ expect(script).toContain("$GRADLE_CMD :testDebugUnitTest");
208
+ expect(script).not.toContain("::");
209
+ expect(script).toContain('echo "Test report location: build/reports/tests/testDebugUnitTest/index.html"');
210
+ });
211
+ test("an absent module still means app", async () => {
212
+ const lint = await new AndroidLintStepExecutor().execute({}, {}, testConfig);
213
+ const unit = await new AndroidUnitTestStepExecutor().execute({}, {}, testConfig);
214
+ expect(lint.script).toContain("$GRADLE_CMD :app:lintDebug");
215
+ expect(lint.script).toContain('echo "Running lint on module: app"');
216
+ expect(unit.script).toContain('echo "Running unit tests on module: app"');
217
+ expect(lint.script).toContain('echo "Lint report location: app/build/reports/lint-results-debug.html"');
218
+ expect(unit.script).toContain("$GRADLE_CMD :app:testDebugUnitTest");
219
+ });
220
+ test("UI testing assembles on the root and finds its APKs", async () => {
221
+ const result = await new AndroidBuildForUITestingStepExecutor().execute({ module: ":" }, {}, testConfig);
222
+ const script = result.script;
223
+ expect(script).toContain("$GRADLE_CMD :assembleDebug :assembleDebugAndroidTest");
224
+ expect(script).not.toContain("::");
225
+ const find = script.split("\n").find((l) => l.startsWith("APP_APK="));
226
+ const pattern = find.match(/-path "([^"]+)"/)[1];
227
+ const dir = mkdtempSync(join(tmpdir(), "cibuild-root-apk-"));
228
+ try {
229
+ mkdirSync(join(dir, "build/outputs/apk/debug"), { recursive: true });
230
+ writeFileSync(join(dir, "build/outputs/apk/debug/x.apk"), "");
231
+ const found = execFileSync("find", [".", "-path", pattern], { cwd: dir, encoding: "utf-8" }).trim();
232
+ expect(found).toBe("./build/outputs/apk/debug/x.apk");
233
+ }
234
+ finally {
235
+ rmSync(dir, { recursive: true, force: true });
236
+ }
237
+ });
238
+ });
239
+ /**
240
+ * gradle-build takes a bare `assembleDebug`, which a root project satisfies.
241
+ * Its artifact collection must still see `./build/outputs/...`.
242
+ */
243
+ describe("gradle-build's artifact collection", () => {
244
+ test("finds an APK the root project wrote", async () => {
245
+ const result = await new GradleBuildStepExecutor().execute({}, {}, testConfig);
246
+ const script = result.script;
247
+ const pattern = script.match(/-path "(\*\/build\/outputs\/apk\/[^"]+)"/)[1];
248
+ const dir = mkdtempSync(join(tmpdir(), "cibuild-root-apk-"));
249
+ try {
250
+ mkdirSync(join(dir, "build/outputs/apk/debug"), { recursive: true });
251
+ writeFileSync(join(dir, "build/outputs/apk/debug/x.apk"), "");
252
+ const found = execFileSync("find", [".", "-path", pattern], { cwd: dir, encoding: "utf-8" }).trim();
253
+ expect(found).toBe("./build/outputs/apk/debug/x.apk");
254
+ }
255
+ finally {
256
+ rmSync(dir, { recursive: true, force: true });
257
+ }
258
+ });
259
+ });
117
260
  //# sourceMappingURL=android-variant-resolution.test.js.map
@@ -85,6 +85,14 @@ export declare class GradleBuildStepExecutor extends BaseStepExecutor {
85
85
  * Written as a shell function so what ships is what the test runs.
86
86
  */
87
87
  export declare const PICK_VARIANT_TASK_FUNCTION = "cibuild_pick_variant_task() {\n # $1 task prefix (lint, test), $2 task suffix (empty, UnitTest),\n # $3 the build type to prefer (Debug). Gradle's output arrives on stdin.\n awk '/Candidates are:/ { sub(/.*Candidates are:/, \"\"); print; exit }' |\n tr ',' '\\n' |\n tr -d \" '\\\".\" |\n grep -E \"^${1}[A-Za-z0-9]*${3}${2}$\" |\n head -1\n}";
88
+ /**
89
+ * The Gradle path of a task in a module: `:app:lintDebug`, or `:lintDebug`
90
+ * for an app whose root project is the application. The leading `:` keeps it
91
+ * the root project's own task rather than every project's task of that name.
92
+ */
93
+ export declare function gradleTaskPath(module: string, task: string): string;
94
+ /** A module's directory with a trailing slash, or `''` for the root project. */
95
+ export declare function gradleProjectDir(module: string): string;
88
96
  /**
89
97
  * The commands that run a variant-specific Gradle task and, if the variant
90
98
  * turns out to be a guess Gradle cannot resolve, resolve it and retry once.
@@ -1 +1 @@
1
- {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android.ts"],"names":[],"mappings":"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"}
@@ -544,6 +544,31 @@ export const PICK_VARIANT_TASK_FUNCTION = `cibuild_pick_variant_task() {
544
544
  grep -E "^\${1}[A-Za-z0-9]*\${3}\${2}$" |
545
545
  head -1
546
546
  }`;
547
+ /**
548
+ * Whether a `module` input names the root project. Gradle's own path for the
549
+ * root is `:`; an explicit empty string is taken to mean the same, since it
550
+ * would otherwise compose `::<task>`. An absent input never reaches here —
551
+ * the steps default it to `app`.
552
+ */
553
+ function isRootProject(module) {
554
+ return module === ':' || module === '';
555
+ }
556
+ /**
557
+ * The Gradle path of a task in a module: `:app:lintDebug`, or `:lintDebug`
558
+ * for an app whose root project is the application. The leading `:` keeps it
559
+ * the root project's own task rather than every project's task of that name.
560
+ */
561
+ export function gradleTaskPath(module, task) {
562
+ return isRootProject(module) ? `:${task}` : `:${module}:${task}`;
563
+ }
564
+ /** A module's directory with a trailing slash, or `''` for the root project. */
565
+ export function gradleProjectDir(module) {
566
+ return isRootProject(module) ? '' : `${module}/`;
567
+ }
568
+ /** How a log line names the project a step runs on. */
569
+ function gradleProjectLabel(module) {
570
+ return isRootProject(module) ? 'the root project' : `module: ${module}`;
571
+ }
547
572
  /**
548
573
  * The commands that run a variant-specific Gradle task and, if the variant
549
574
  * turns out to be a guess Gradle cannot resolve, resolve it and retry once.
@@ -555,7 +580,7 @@ export const PICK_VARIANT_TASK_FUNCTION = `cibuild_pick_variant_task() {
555
580
  export function variantTaskCommands(options) {
556
581
  const { module, variant, prefix, suffix, gradleArgs = '', escape } = options;
557
582
  const capitalized = variant.charAt(0).toUpperCase() + variant.slice(1);
558
- const task = `:${escape(module)}:${prefix}${capitalized}${suffix}`;
583
+ const task = gradleTaskPath(escape(module), `${prefix}${capitalized}${suffix}`);
559
584
  const args = gradleArgs === '' ? '' : ` ${gradleArgs}`;
560
585
  return [
561
586
  '',
@@ -580,10 +605,10 @@ export function variantTaskCommands(options) {
580
605
  ` RESOLVED_VARIANT=\${RESOLVED_TASK#${prefix}}`,
581
606
  ...(suffix === '' ? [] : [` RESOLVED_VARIANT=\${RESOLVED_VARIANT%${suffix}}`]),
582
607
  ` RESOLVED_VARIANT="$(echo "\${RESOLVED_VARIANT:0:1}" | tr '[:upper:]' '[:lower:]')\${RESOLVED_VARIANT:1}"`,
583
- ` echo "Using :${escape(module)}:$RESOLVED_TASK — resolved VARIANT=$RESOLVED_VARIANT"`,
608
+ ` echo "Using ${gradleTaskPath(escape(module), '$RESOLVED_TASK')} — resolved VARIANT=$RESOLVED_VARIANT"`,
584
609
  // Best-effort: the discovery is worth having even where envman is not.
585
610
  ' envman add --key VARIANT --value "$RESOLVED_VARIANT" || true',
586
- ` $GRADLE_CMD :${escape(module)}:$RESOLVED_TASK${args}`,
611
+ ` $GRADLE_CMD ${gradleTaskPath(escape(module), '$RESOLVED_TASK')}${args}`,
587
612
  'elif [ "$VARIANT_STATUS" -ne 0 ]; then',
588
613
  ' rm -f "$VARIANT_LOG"',
589
614
  ' exit "$VARIANT_STATUS"',
@@ -736,7 +761,7 @@ export class AndroidLintStepExecutor extends BaseStepExecutor {
736
761
  commands.push('PROJECT_DIR="${CIBUILD_SOURCE_DIR:-.}"');
737
762
  }
738
763
  commands.push('echo "Project directory: $PROJECT_DIR"');
739
- commands.push(`echo "Running lint on module: ${this.escapeBash(module)}"`);
764
+ commands.push(`echo "Running lint on ${gradleProjectLabel(this.escapeBash(module))}"`);
740
765
  commands.push(`echo "Variant: ${this.escapeBash(variant)}"`);
741
766
  // Change to project directory
742
767
  commands.push('');
@@ -769,7 +794,7 @@ export class AndroidLintStepExecutor extends BaseStepExecutor {
769
794
  commands.push('');
770
795
  commands.push('# Lint completed');
771
796
  commands.push('echo "Lint analysis completed successfully"');
772
- commands.push(`echo "Lint report location: ${this.escapeBash(module)}/build/reports/lint-results-${this.escapeBash(variant)}.html"`);
797
+ commands.push(`echo "Lint report location: ${gradleProjectDir(this.escapeBash(module))}build/reports/lint-results-${this.escapeBash(variant)}.html"`);
773
798
  const script = this.createBashScriptFromCommands(commands, stepName);
774
799
  return this.createScriptStep(script, stepName);
775
800
  }
@@ -925,7 +950,7 @@ export class AndroidUnitTestStepExecutor extends BaseStepExecutor {
925
950
  commands.push('PROJECT_DIR="${CIBUILD_SOURCE_DIR:-.}"');
926
951
  }
927
952
  commands.push('echo "Project directory: $PROJECT_DIR"');
928
- commands.push(`echo "Running unit tests on module: ${this.escapeBash(module)}"`);
953
+ commands.push(`echo "Running unit tests on ${gradleProjectLabel(this.escapeBash(module))}"`);
929
954
  commands.push(`echo "Variant: ${this.escapeBash(variant)}"`);
930
955
  // Change to project directory
931
956
  commands.push('');
@@ -964,7 +989,7 @@ export class AndroidUnitTestStepExecutor extends BaseStepExecutor {
964
989
  commands.push('');
965
990
  commands.push('# Tests completed');
966
991
  commands.push('echo "Unit tests completed successfully"');
967
- commands.push(`echo "Test report location: ${this.escapeBash(module)}/build/reports/tests/test${variantCapitalized}UnitTest/index.html"`);
992
+ commands.push(`echo "Test report location: ${gradleProjectDir(this.escapeBash(module))}build/reports/tests/test${variantCapitalized}UnitTest/index.html"`);
968
993
  const script = this.createBashScriptFromCommands(commands, stepName);
969
994
  return this.createScriptStep(script, stepName);
970
995
  }
@@ -1013,8 +1038,8 @@ export class AndroidBuildForUITestingStepExecutor extends BaseStepExecutor {
1013
1038
  commands.push('fi');
1014
1039
  commands.push('');
1015
1040
  // Build both app and test APKs
1016
- const appTask = `:${this.escapeBash(module)}:assemble${variantCap}`;
1017
- const testTask = `:${this.escapeBash(module)}:assemble${variantCap}AndroidTest`;
1041
+ const appTask = gradleTaskPath(this.escapeBash(module), `assemble${variantCap}`);
1042
+ const testTask = gradleTaskPath(this.escapeBash(module), `assemble${variantCap}AndroidTest`);
1018
1043
  let cmd = `$GRADLE_CMD ${appTask} ${testTask}`;
1019
1044
  if (extraArgs) {
1020
1045
  cmd += ` ${extraArgs}`;
@@ -1024,8 +1049,8 @@ export class AndroidBuildForUITestingStepExecutor extends BaseStepExecutor {
1024
1049
  commands.push('');
1025
1050
  // Locate APKs
1026
1051
  commands.push('# Locate generated APKs');
1027
- commands.push(`APP_APK=$(find . -path "*/${this.escapeBash(module)}/build/outputs/apk/*/*.apk" ! -name "*androidTest*" | head -1)`);
1028
- commands.push(`TEST_APK=$(find . -path "*/${this.escapeBash(module)}/build/outputs/apk/*/*.apk" -name "*androidTest*" | head -1)`);
1052
+ commands.push(`APP_APK=$(find . -path "*/${gradleProjectDir(this.escapeBash(module))}build/outputs/apk/*/*.apk" ! -name "*androidTest*" | head -1)`);
1053
+ commands.push(`TEST_APK=$(find . -path "*/${gradleProjectDir(this.escapeBash(module))}build/outputs/apk/*/*.apk" -name "*androidTest*" | head -1)`);
1029
1054
  commands.push('');
1030
1055
  commands.push('if [ -z "$APP_APK" ]; then');
1031
1056
  commands.push(' echo "⚠️ App APK not found"');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invarn/cibuild",
3
- "version": "2.8.2",
3
+ "version": "2.8.4",
4
4
  "description": "CI Build CLI — local pipeline orchestration and validation",
5
5
  "type": "module",
6
6
  "main": "dist/cli.cjs",