@invarn/cibuild 2.7.5 → 2.7.7
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/commands/a-credential-target-follows-the-flavor-source-set.test.js +9 -6
- package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.js +71 -38
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts +29 -0
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts.map +1 -0
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.js +160 -0
- package/dist/src/commands/android-scanner.d.ts +9 -1
- package/dist/src/commands/android-scanner.d.ts.map +1 -1
- package/dist/src/commands/android-scanner.js +103 -3
- package/dist/src/commands/build.d.ts +16 -12
- package/dist/src/commands/build.d.ts.map +1 -1
- package/dist/src/commands/build.js +64 -45
- package/dist/src/commands/file-secret-collector.d.ts.map +1 -1
- package/dist/src/commands/file-secret-collector.js +24 -7
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts +38 -0
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts.map +1 -0
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.js +300 -0
- package/dist/src/commands/run.d.ts.map +1 -1
- package/dist/src/commands/run.js +1 -1
- package/dist/src/runner.d.ts +15 -1
- package/dist/src/runner.d.ts.map +1 -1
- package/dist/src/runner.js +24 -1
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts +18 -0
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts.map +1 -0
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.js +66 -0
- package/dist/src/yaml/converter.d.ts +22 -0
- package/dist/src/yaml/converter.d.ts.map +1 -1
- package/dist/src/yaml/converter.js +26 -0
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts +16 -0
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts.map +1 -0
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.js +126 -0
- package/dist/src/yaml/step-validator.d.ts.map +1 -1
- package/dist/src/yaml/step-validator.js +6 -2
- package/dist/src/yaml/steps/android.js +3 -3
- package/dist/src/yaml/steps/base.d.ts +6 -1
- package/dist/src/yaml/steps/base.d.ts.map +1 -1
- package/dist/src/yaml/steps/base.js +6 -1
- package/dist/src/yaml/steps/bitrise-android-tools.js +1 -1
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +22 -0
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +97 -0
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts +16 -0
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.js +127 -0
- package/dist/src/yaml/steps/git-clone.d.ts +1 -1
- package/dist/src/yaml/steps/git-clone.d.ts.map +1 -1
- package/dist/src/yaml/steps/git-clone.js +56 -2
- package/dist/src/yaml/steps/ios-deps.js +1 -1
- package/dist/src/yaml/steps/xcode.js +5 -5
- package/dist/src/yaml/validation-types.d.ts +7 -1
- package/dist/src/yaml/validation-types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@ import prompts from "prompts";
|
|
|
4
4
|
import { scanAndroidProject, formatScanResult, runnableJavaVersion, javaVersionProblem, } from "./android-scanner.js";
|
|
5
5
|
import { scanIosProject, formatIosScanResult } from "./ios-scanner.js";
|
|
6
6
|
import { SecretsManager } from "../yaml/secrets-manager.js";
|
|
7
|
-
import { encodeSecretName } from "./secrets-upload.js";
|
|
8
7
|
import { collectFileSecret } from "./file-secret-collector.js";
|
|
9
8
|
import { generateGitHubActionsWorkflow } from "./github-workflow.js";
|
|
10
9
|
import { detectDefaultBranch, FALLBACK_DEFAULT_BRANCH } from "../shared/git.js";
|
|
@@ -216,27 +215,31 @@ export function variantFlavors(variants, productFlavors) {
|
|
|
216
215
|
* genuinely release-only ones keep one slot however flavored the repository
|
|
217
216
|
* is.
|
|
218
217
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
218
|
+
* **The name never changes — the block it sits in does.** An agreed credential
|
|
219
|
+
* is declared once in `app.envs`; a disagreement is declared under the plain
|
|
220
|
+
* credential name in **each workflow's own `envs`**, which is where the YAML
|
|
221
|
+
* already says which workflow something belongs to. `EnvResolver` merges
|
|
222
|
+
* `app.envs` and then the running workflow's own, so `release`'s declaration is
|
|
223
|
+
* invisible to `primary` without anything being encoded.
|
|
225
224
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
225
|
+
* `CIBUILD_SW__<WORKFLOW>__<VAR>` is deliberately *not* used here. That prefix
|
|
226
|
+
* is the transport encoding `ci secrets upload` carries a workflow-scoped
|
|
227
|
+
* **value** under — Invarn's secret scopes have no workflow column, so the
|
|
228
|
+
* workflow travels in the key. A pipeline has structure instead, and spending
|
|
229
|
+
* the encoding there would be a second way to say what the block already says
|
|
230
|
+
* (Alex, 2026-09-10).
|
|
229
231
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
232
|
+
* Every workflow gets a slot, not only the odd one out: a workflow left without
|
|
233
|
+
* one would have neither a value nor the declaration `file@1.0.0` refuses over.
|
|
234
|
+
*
|
|
235
|
+
* The `file@` step's `var_name` is the same plain name either way.
|
|
233
236
|
*/
|
|
234
237
|
export function credentialSlots(varName, discriminatorByWorkflow) {
|
|
235
238
|
const distinct = new Set(Object.values(discriminatorByWorkflow));
|
|
236
239
|
if (distinct.size <= 1)
|
|
237
240
|
return [{ slot: varName, workflow: null }];
|
|
238
241
|
return Object.keys(discriminatorByWorkflow).map((workflow) => ({
|
|
239
|
-
slot:
|
|
242
|
+
slot: varName,
|
|
240
243
|
workflow,
|
|
241
244
|
}));
|
|
242
245
|
}
|
|
@@ -342,40 +345,56 @@ export function generateAndroidPipeline(javaVersion = 17, setup = { keystore: fa
|
|
|
342
345
|
const buildTypeEnvOverride = (v) => v.buildType !== "Debug" ? ` - BUILD_TYPE: ${v.buildType}\n` : "";
|
|
343
346
|
// Optional env var stubs (empty defaults — filled via ci secrets add).
|
|
344
347
|
//
|
|
345
|
-
// Declared
|
|
346
|
-
// searches the developer's machine by — reads `envs`, never step
|
|
347
|
-
// undeclared credential is never discovered and never offered for
|
|
348
|
-
// however many steps name it.
|
|
348
|
+
// Declared and not merely used by a step: `unsetEnvKeys` — what `invarn
|
|
349
|
+
// setup` searches the developer's machine by — reads `envs`, never step
|
|
350
|
+
// inputs. An undeclared credential is never discovered and never offered for
|
|
351
|
+
// upload, however many steps name it. The same declaration is what
|
|
352
|
+
// `file@1.0.0` refuses over when nobody supplied the value.
|
|
349
353
|
//
|
|
350
354
|
// One slot per credential where every workflow that reads it wants the same
|
|
351
|
-
// bytes, and one per workflow where they do not — see `credentialSlots`.
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
355
|
+
// bytes, and one per workflow where they do not — see `credentialSlots`. The
|
|
356
|
+
// name is the plain credential either way; a per-workflow one is declared in
|
|
357
|
+
// that workflow's own `envs` rather than under an encoded name.
|
|
358
|
+
const appEnvDeclarations = [];
|
|
359
|
+
const workflowEnvDeclarations = {};
|
|
360
|
+
const declare = (varName, discriminatorByWorkflow, comment) => {
|
|
361
|
+
for (const { slot, workflow } of credentialSlots(varName, discriminatorByWorkflow)) {
|
|
362
|
+
if (workflow === null) {
|
|
363
|
+
appEnvDeclarations.push(` - ${slot}: ""${comment(null)}\n`);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
// Six spaces, not four: a workflow's `envs` sits one level deeper.
|
|
367
|
+
(workflowEnvDeclarations[workflow] ??= []).push(` - ${slot}: ""${comment(workflow)}\n`);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
/** Every credential declaration this workflow owns, after its own variables. */
|
|
371
|
+
const workflowEnvFor = (workflow) => (workflowEnvDeclarations[workflow] ?? []).join("");
|
|
372
|
+
// Declared in the order they appear in `app.envs`, which the byte-identity
|
|
373
|
+
// test on an unflavored pipeline pins.
|
|
374
|
+
if (setup.keystore) {
|
|
375
|
+
declare("KEYSTORE_BASE64", discriminators(setup.keystorePaths, "keystore.jks"), (wf) => wf
|
|
358
376
|
? ` # Set via: ci secrets add KEYSTORE_BASE64 -w ${wf}`
|
|
359
|
-
: ` # Set via: ci secrets add KEYSTORE_BASE64 -w primary`)
|
|
360
|
-
|
|
377
|
+
: ` # Set via: ci secrets add KEYSTORE_BASE64 -w primary`);
|
|
378
|
+
}
|
|
361
379
|
// Release-only, and legitimately so: nothing but `release` signs a release
|
|
362
380
|
// or talks to Google Play, so there is no second workflow to disagree with.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
381
|
+
if (setup.keystoreProperties) {
|
|
382
|
+
appEnvDeclarations.push(` - KEYSTORE_PROPERTIES: "" # Release signing — set via: ci secrets add KEYSTORE_PROPERTIES -w release\n`);
|
|
383
|
+
}
|
|
384
|
+
if (setup.googleServices) {
|
|
385
|
+
declare("GOOGLE_SERVICES_JSON", discriminators(setup.googleServicesPaths, "app/google-services.json"), (wf) => wf
|
|
368
386
|
? ` # Set via: ci secrets add GOOGLE_SERVICES_JSON -w ${wf}`
|
|
369
|
-
: ` # Set via: ci secrets add GOOGLE_SERVICES_JSON`)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
387
|
+
: ` # Set via: ci secrets add GOOGLE_SERVICES_JSON`);
|
|
388
|
+
}
|
|
389
|
+
if (setup.googlePlayDeploy) {
|
|
390
|
+
appEnvDeclarations.push(` - GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: "" # Set via: ci secrets add GOOGLE_PLAY_SERVICE_ACCOUNT_JSON -w release\n`);
|
|
391
|
+
}
|
|
392
|
+
if (setup.secretsProperties) {
|
|
393
|
+
declare("LOCAL_PROPERTIES", discriminators(setup.workflowFlavors, ""), (wf) => wf
|
|
376
394
|
? ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES -w ${wf}`
|
|
377
|
-
: ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES`)
|
|
378
|
-
|
|
395
|
+
: ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES`);
|
|
396
|
+
}
|
|
397
|
+
const appEnv = appEnvDeclarations.join("");
|
|
379
398
|
/**
|
|
380
399
|
* The Secrets Gradle Plugin's properties file, written into every workflow
|
|
381
400
|
* that builds. Its evaluation listener runs for every variant, so a debug
|
|
@@ -468,12 +487,12 @@ app:
|
|
|
468
487
|
- MODULE: app
|
|
469
488
|
- BUILD_TYPE: Debug
|
|
470
489
|
- GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.parallel=true
|
|
471
|
-
${
|
|
490
|
+
${appEnv}
|
|
472
491
|
workflows:
|
|
473
492
|
primary:
|
|
474
493
|
envs:
|
|
475
494
|
- VARIANT: ${pv.variant}
|
|
476
|
-
${buildTypeEnvOverride(pv)}
|
|
495
|
+
${buildTypeEnvOverride(pv)}${workflowEnvFor("primary")}
|
|
477
496
|
steps:
|
|
478
497
|
${keystoreStepFor("primary")}${googleServicesStepFor("primary")}${secretsPropertiesStep}
|
|
479
498
|
- set-java-version@1.0.0:
|
|
@@ -513,7 +532,7 @@ ${keystoreStepFor("primary")}${googleServicesStepFor("primary")}${secretsPropert
|
|
|
513
532
|
pull-request:
|
|
514
533
|
envs:
|
|
515
534
|
- VARIANT: ${prv.variant}
|
|
516
|
-
${buildTypeEnvOverride(prv)}
|
|
535
|
+
${buildTypeEnvOverride(prv)}${workflowEnvFor("pull-request")}
|
|
517
536
|
steps:
|
|
518
537
|
${keystoreStepFor("pull-request")}${googleServicesStepFor("pull-request")}${secretsPropertiesStep}
|
|
519
538
|
- set-java-version@1.0.0:
|
|
@@ -552,7 +571,7 @@ ${keystoreStepFor("pull-request")}${googleServicesStepFor("pull-request")}${secr
|
|
|
552
571
|
release:
|
|
553
572
|
envs:
|
|
554
573
|
- VARIANT: ${rv.variant}
|
|
555
|
-
${buildTypeEnvOverride(rv)}
|
|
574
|
+
${buildTypeEnvOverride(rv)}${workflowEnvFor("release")}
|
|
556
575
|
steps:
|
|
557
576
|
${keystoreStepFor("release")}${keystorePropertiesStep}${googleServicesStepFor("release")}${secretsPropertiesStep}
|
|
558
577
|
- set-java-version@1.0.0:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-secret-collector.d.ts","sourceRoot":"","sources":["../../../src/commands/file-secret-collector.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,6CAA6C;IAC7C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAMrD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,EAAE,CAkBxF;AAiDD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,cAAc,EAC9B,cAAc,UAAQ,GACrB,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"file-secret-collector.d.ts","sourceRoot":"","sources":["../../../src/commands/file-secret-collector.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,6CAA6C;IAC7C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAMrD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,EAAE,CAkBxF;AAiDD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,cAAc,EAC9B,cAAc,UAAQ,GACrB,OAAO,CAAC,eAAe,CAAC,CA6I1B"}
|
|
@@ -77,18 +77,35 @@ async function promptTargetPathInProject(displayName) {
|
|
|
77
77
|
*/
|
|
78
78
|
export async function collectFileSecret(config, cwd, secretsManager, nonInteractive = false) {
|
|
79
79
|
const paths = {};
|
|
80
|
-
// Only process groups where at least one workflow is missing the secret
|
|
81
|
-
const pendingGroups = config.workflowGroups.filter((g) => !g.workflows.every((wf) => secretsManager.hasSecret(config.secretName, wf)));
|
|
82
|
-
if (pendingGroups.length === 0) {
|
|
83
|
-
console.log(` ✅ ${config.secretName} already in .cibuild-secrets.json — will be used at runtime.`);
|
|
84
|
-
return paths;
|
|
85
|
-
}
|
|
86
|
-
const pendingWorkflows = pendingGroups.flatMap((g) => g.workflows);
|
|
87
80
|
const readContent = (p) => config.readAs === "base64"
|
|
88
81
|
? readFileSync(p).toString("base64")
|
|
89
82
|
: readFileSync(p, "utf-8");
|
|
90
83
|
const relativeToCwd = (p) => relative(cwd, p).split(sep).join("/");
|
|
91
84
|
const candidates = findProjectFiles(cwd, config.fileMatch).filter((p) => config.pathMatch?.(relativeToCwd(p)) ?? true);
|
|
85
|
+
// A stored value answers "what are the bytes". It does not answer "where does
|
|
86
|
+
// each workflow write them", and `.cibuild-secrets.json` cannot: the store
|
|
87
|
+
// records values and never targets. So a caller that consumes the returned
|
|
88
|
+
// map has to be asked whichever state the value is in.
|
|
89
|
+
//
|
|
90
|
+
// `promptTargetPath` marks that caller — the keystore, the only one whose
|
|
91
|
+
// return value the generator assigns. Conflating the two questions lost the
|
|
92
|
+
// keystore split on every repository that had ever run `ci init`: a *global*
|
|
93
|
+
// entry satisfies `hasSecret` for every workflow, so nothing was pending, an
|
|
94
|
+
// empty map went back, and the generator's fallback handed all three
|
|
95
|
+
// workflows one path. A fresh clone asked and split correctly, which is why
|
|
96
|
+
// no fixture-built test saw it.
|
|
97
|
+
//
|
|
98
|
+
// Only when there is something on disk to choose between. With no candidate
|
|
99
|
+
// the question has no answer worth interrupting for, and the short-circuit is
|
|
100
|
+
// still the kinder branch.
|
|
101
|
+
const callerNeedsTargets = config.promptTargetPath === true && candidates.length > 0;
|
|
102
|
+
const pendingGroups = config.workflowGroups.filter((g) => callerNeedsTargets ||
|
|
103
|
+
!g.workflows.every((wf) => secretsManager.hasSecret(config.secretName, wf)));
|
|
104
|
+
if (pendingGroups.length === 0) {
|
|
105
|
+
console.log(` ✅ ${config.secretName} already in .cibuild-secrets.json — will be used at runtime.`);
|
|
106
|
+
return paths;
|
|
107
|
+
}
|
|
108
|
+
const pendingWorkflows = pendingGroups.flatMap((g) => g.workflows);
|
|
92
109
|
if (candidates.length === 1 && config.workflowGroups.length === 1) {
|
|
93
110
|
// Single file, single-group config — auto-apply without prompting
|
|
94
111
|
const content = readContent(candidates[0]);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product flavors a convention plugin creates are still the project's flavors.
|
|
3
|
+
*
|
|
4
|
+
* `detectBuildVariants` looked for a literal `productFlavors { … }` block in a
|
|
5
|
+
* Gradle file. Modern Android does not write one: the flavors are an enum in a
|
|
6
|
+
* convention plugin's Kotlin source and are created programmatically —
|
|
7
|
+
* `AppFlavor.values().forEach { create(flavor.name) }` — so the block the
|
|
8
|
+
* scan looks for does not exist anywhere in the repository.
|
|
9
|
+
*
|
|
10
|
+
* `android-variant-resolution.test.ts` already records the *runtime* half of
|
|
11
|
+
* this: nowinandroid declares its flavors the same way, Gradle refused
|
|
12
|
+
* `:app:lintDebug` as ambiguous, and the step now reads the candidates out of
|
|
13
|
+
* Gradle's own error. That recovers a task name during a build. It cannot
|
|
14
|
+
* recover anything decided *before* the build:
|
|
15
|
+
*
|
|
16
|
+
* - `googleServicesTargets` writes every workflow's `google-services.json`
|
|
17
|
+
* to the module root instead of its flavor's source set, which is the
|
|
18
|
+
* silent wrong-file failure `bld_8e60d90ab4e55f85d2f72459` spent 1292
|
|
19
|
+
* Gradle tasks discovering;
|
|
20
|
+
* - `variantFlavors` reports no flavor for any workflow, so `credentialSlots`
|
|
21
|
+
* sees no disagreement and declares one flat slot where the workflows need
|
|
22
|
+
* one value each;
|
|
23
|
+
* - the variant prompt offers `debug, release` and nothing else.
|
|
24
|
+
*
|
|
25
|
+
* Gradle is not available to the scan — it is a static read, and running a
|
|
26
|
+
* configuration phase to list flavors would cost minutes. The evidence that
|
|
27
|
+
* *is* on disk is the one AGP itself relies on: a flavor has a source set.
|
|
28
|
+
* `app/src/ciDynamic/`, `app/src/prod/` — the same directories
|
|
29
|
+
* `findGoogleServices` in `invarn setup` has always read the flavor from,
|
|
30
|
+
* which is why setup resolves this repository correctly while the generator
|
|
31
|
+
* does not.
|
|
32
|
+
*
|
|
33
|
+
* It is a **fallback**, not a merge: a project whose Gradle files declare
|
|
34
|
+
* flavors literally is decided by those files exactly as before, so this can
|
|
35
|
+
* only change the answer for a project that currently detects none.
|
|
36
|
+
*/
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=flavors-a-convention-plugin-creates-are-still-found.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flavors-a-convention-plugin-creates-are-still-found.test.d.ts","sourceRoot":"","sources":["../../../src/commands/flavors-a-convention-plugin-creates-are-still-found.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product flavors a convention plugin creates are still the project's flavors.
|
|
3
|
+
*
|
|
4
|
+
* `detectBuildVariants` looked for a literal `productFlavors { … }` block in a
|
|
5
|
+
* Gradle file. Modern Android does not write one: the flavors are an enum in a
|
|
6
|
+
* convention plugin's Kotlin source and are created programmatically —
|
|
7
|
+
* `AppFlavor.values().forEach { create(flavor.name) }` — so the block the
|
|
8
|
+
* scan looks for does not exist anywhere in the repository.
|
|
9
|
+
*
|
|
10
|
+
* `android-variant-resolution.test.ts` already records the *runtime* half of
|
|
11
|
+
* this: nowinandroid declares its flavors the same way, Gradle refused
|
|
12
|
+
* `:app:lintDebug` as ambiguous, and the step now reads the candidates out of
|
|
13
|
+
* Gradle's own error. That recovers a task name during a build. It cannot
|
|
14
|
+
* recover anything decided *before* the build:
|
|
15
|
+
*
|
|
16
|
+
* - `googleServicesTargets` writes every workflow's `google-services.json`
|
|
17
|
+
* to the module root instead of its flavor's source set, which is the
|
|
18
|
+
* silent wrong-file failure `bld_8e60d90ab4e55f85d2f72459` spent 1292
|
|
19
|
+
* Gradle tasks discovering;
|
|
20
|
+
* - `variantFlavors` reports no flavor for any workflow, so `credentialSlots`
|
|
21
|
+
* sees no disagreement and declares one flat slot where the workflows need
|
|
22
|
+
* one value each;
|
|
23
|
+
* - the variant prompt offers `debug, release` and nothing else.
|
|
24
|
+
*
|
|
25
|
+
* Gradle is not available to the scan — it is a static read, and running a
|
|
26
|
+
* configuration phase to list flavors would cost minutes. The evidence that
|
|
27
|
+
* *is* on disk is the one AGP itself relies on: a flavor has a source set.
|
|
28
|
+
* `app/src/ciDynamic/`, `app/src/prod/` — the same directories
|
|
29
|
+
* `findGoogleServices` in `invarn setup` has always read the flavor from,
|
|
30
|
+
* which is why setup resolves this repository correctly while the generator
|
|
31
|
+
* does not.
|
|
32
|
+
*
|
|
33
|
+
* It is a **fallback**, not a merge: a project whose Gradle files declare
|
|
34
|
+
* flavors literally is decided by those files exactly as before, so this can
|
|
35
|
+
* only change the answer for a project that currently detects none.
|
|
36
|
+
*/
|
|
37
|
+
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
|
38
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
39
|
+
import { tmpdir } from 'node:os';
|
|
40
|
+
import { join, resolve } from 'node:path';
|
|
41
|
+
import { load } from 'js-yaml';
|
|
42
|
+
import { detectBuildVariants, scanAndroidProject } from './android-scanner.js';
|
|
43
|
+
import { generateAndroidPipeline, googleServicesTargets, variantFlavors, } from './build.js';
|
|
44
|
+
let root;
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
root = resolve(mkdtempSync(join(tmpdir(), 'cibuild-convention-flavors-')));
|
|
47
|
+
});
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
rmSync(root, { recursive: true, force: true });
|
|
50
|
+
});
|
|
51
|
+
/** Writes `app/build.gradle.kts` with the given body and returns its path. */
|
|
52
|
+
function appModule(body) {
|
|
53
|
+
mkdirSync(join(root, 'app'), { recursive: true });
|
|
54
|
+
const path = join(root, 'app', 'build.gradle.kts');
|
|
55
|
+
writeFileSync(path, body, 'utf-8');
|
|
56
|
+
return path;
|
|
57
|
+
}
|
|
58
|
+
/** Creates `app/src/<name>/` holding each of `children`. */
|
|
59
|
+
function sourceSet(name, children = ['res']) {
|
|
60
|
+
const dir = join(root, 'app', 'src', name);
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
for (const child of children) {
|
|
63
|
+
if (child.includes('.'))
|
|
64
|
+
writeFileSync(join(dir, child), '{}', 'utf-8');
|
|
65
|
+
else
|
|
66
|
+
mkdirSync(join(dir, child), { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** An `android { buildTypes { … } }` block with no `productFlavors` anywhere. */
|
|
70
|
+
const CONVENTION_PLUGIN_MODULE = `
|
|
71
|
+
plugins {
|
|
72
|
+
id("acme.android.application")
|
|
73
|
+
id("acme.android.application.flavors")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
android {
|
|
77
|
+
namespace = "com.example.app"
|
|
78
|
+
buildTypes {
|
|
79
|
+
debug { isMinifyEnabled = false }
|
|
80
|
+
release { isMinifyEnabled = true }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
androidComponents {
|
|
85
|
+
onVariants { variant ->
|
|
86
|
+
if (variant.buildType == "release" &&
|
|
87
|
+
variant.productFlavors.any { it.second == "prod" }
|
|
88
|
+
) {
|
|
89
|
+
variant.signingConfig.setConfig(android.signingConfigs.getByName("release"))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
describe('flavors declared by a convention plugin', () => {
|
|
95
|
+
test('are read from the source sets the flavors have on disk', () => {
|
|
96
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
97
|
+
sourceSet('main', ['AndroidManifest.xml', 'java', 'res']);
|
|
98
|
+
sourceSet('ciDynamic', ['google-services.json', 'res']);
|
|
99
|
+
sourceSet('dev', ['google-services.json', 'res']);
|
|
100
|
+
sourceSet('prod', ['google-services.json', 'res']);
|
|
101
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['ciDynamic', 'dev', 'prod']);
|
|
102
|
+
});
|
|
103
|
+
test('become the variants the prompt offers', () => {
|
|
104
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
105
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
106
|
+
sourceSet('ciDynamic');
|
|
107
|
+
sourceSet('prod');
|
|
108
|
+
// Every flavor crossed with every build type, which is what the literal
|
|
109
|
+
// path has always produced.
|
|
110
|
+
expect(detectBuildVariants([gradle]).variants).toEqual([
|
|
111
|
+
'ciDynamicDebug',
|
|
112
|
+
'ciDynamicRelease',
|
|
113
|
+
'prodDebug',
|
|
114
|
+
'prodRelease',
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
test('are found in whichever module has them, not only `app`', () => {
|
|
118
|
+
const app = appModule(CONVENTION_PLUGIN_MODULE);
|
|
119
|
+
mkdirSync(join(root, 'core'), { recursive: true });
|
|
120
|
+
const core = join(root, 'core', 'build.gradle.kts');
|
|
121
|
+
writeFileSync(core, 'plugins { id("acme.android.library.flavors") }', 'utf-8');
|
|
122
|
+
mkdirSync(join(root, 'core', 'src', 'staging', 'res'), { recursive: true });
|
|
123
|
+
mkdirSync(join(root, 'core', 'src', 'main', 'java'), { recursive: true });
|
|
124
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
125
|
+
sourceSet('prod');
|
|
126
|
+
expect(detectBuildVariants([app, core]).productFlavors).toEqual(['prod', 'staging']);
|
|
127
|
+
});
|
|
128
|
+
test('are ordered the same way whatever order the filesystem lists them in', () => {
|
|
129
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
130
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
131
|
+
sourceSet('prod');
|
|
132
|
+
sourceSet('ciDynamic');
|
|
133
|
+
sourceSet('dev');
|
|
134
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['ciDynamic', 'dev', 'prod']);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('what a source set directory is not', () => {
|
|
138
|
+
test('the shared source set, the test source sets, and the build types', () => {
|
|
139
|
+
const gradle = appModule(`
|
|
140
|
+
android {
|
|
141
|
+
buildTypes {
|
|
142
|
+
debug { }
|
|
143
|
+
release { }
|
|
144
|
+
staging { }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
`);
|
|
148
|
+
sourceSet('main', ['AndroidManifest.xml', 'java']);
|
|
149
|
+
sourceSet('test', ['java']);
|
|
150
|
+
sourceSet('androidTest', ['java']);
|
|
151
|
+
sourceSet('testFixtures', ['java']);
|
|
152
|
+
sourceSet('debug', ['res']);
|
|
153
|
+
sourceSet('release', ['res']);
|
|
154
|
+
sourceSet('staging', ['res']);
|
|
155
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
test('a variant directory, which is a flavor and a build type already joined', () => {
|
|
158
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
159
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
160
|
+
sourceSet('ciDynamic');
|
|
161
|
+
sourceSet('ciDynamicDebug', ['res']);
|
|
162
|
+
sourceSet('prodRelease', ['res']);
|
|
163
|
+
// Taking `ciDynamicDebug` for a flavor would cross it with the build types
|
|
164
|
+
// again and offer `ciDynamicDebugDebug`.
|
|
165
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['ciDynamic']);
|
|
166
|
+
});
|
|
167
|
+
test('a test source set for one flavor', () => {
|
|
168
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
169
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
170
|
+
sourceSet('prod');
|
|
171
|
+
sourceSet('testProd', ['java']);
|
|
172
|
+
sourceSet('androidTestProd', ['java']);
|
|
173
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['prod']);
|
|
174
|
+
});
|
|
175
|
+
test('a flavor whose name merely starts with `test`', () => {
|
|
176
|
+
// `testing` is a legitimate flavor name; `testProd` is AGP's test source
|
|
177
|
+
// set for `prod`. The capital is the whole difference.
|
|
178
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
179
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
180
|
+
sourceSet('testing');
|
|
181
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['testing']);
|
|
182
|
+
});
|
|
183
|
+
test('a directory under `src/` that holds nothing a source set holds', () => {
|
|
184
|
+
const gradle = appModule(CONVENTION_PLUGIN_MODULE);
|
|
185
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
186
|
+
sourceSet('prod');
|
|
187
|
+
mkdirSync(join(root, 'app', 'src', 'notes'), { recursive: true });
|
|
188
|
+
writeFileSync(join(root, 'app', 'src', 'notes', 'README.md'), '# scratch', 'utf-8');
|
|
189
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['prod']);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('a project the literal scan already reads', () => {
|
|
193
|
+
test('is decided by its Gradle files, and the source sets change nothing', () => {
|
|
194
|
+
// The block declares two; the disk carries a third. A merge would invent a
|
|
195
|
+
// flavor the build does not have, so the literal answer wins outright.
|
|
196
|
+
const gradle = appModule(`
|
|
197
|
+
android {
|
|
198
|
+
flavorDimensions += "contentType"
|
|
199
|
+
productFlavors {
|
|
200
|
+
create("demo") { dimension = "contentType" }
|
|
201
|
+
create("prod") { dimension = "contentType" }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
`);
|
|
205
|
+
sourceSet('main', ['AndroidManifest.xml']);
|
|
206
|
+
sourceSet('demo');
|
|
207
|
+
sourceSet('prod');
|
|
208
|
+
sourceSet('leftover', ['res']);
|
|
209
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual(['demo', 'prod']);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe('a project with no flavors at all', () => {
|
|
213
|
+
test('is exactly what it was', () => {
|
|
214
|
+
const gradle = appModule(`
|
|
215
|
+
android {
|
|
216
|
+
buildTypes {
|
|
217
|
+
debug { }
|
|
218
|
+
release { }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
`);
|
|
222
|
+
sourceSet('main', ['AndroidManifest.xml', 'java', 'res']);
|
|
223
|
+
sourceSet('debug', ['res']);
|
|
224
|
+
const detected = detectBuildVariants([gradle]);
|
|
225
|
+
expect(detected.productFlavors).toEqual([]);
|
|
226
|
+
expect(detected.variants).toEqual(['debug', 'release']);
|
|
227
|
+
});
|
|
228
|
+
test('is unchanged when the module has no `src/` directory at all', () => {
|
|
229
|
+
const gradle = appModule('android { }');
|
|
230
|
+
expect(detectBuildVariants([gradle]).productFlavors).toEqual([]);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
const BASE_SETUP = {
|
|
234
|
+
keystore: false,
|
|
235
|
+
keystoreProperties: false,
|
|
236
|
+
googleServices: false,
|
|
237
|
+
googlePlayDeploy: false,
|
|
238
|
+
googlePlayPackageName: '',
|
|
239
|
+
artifactType: 'apk',
|
|
240
|
+
keystorePaths: {},
|
|
241
|
+
googleServicesPaths: {},
|
|
242
|
+
workflowFlavors: {},
|
|
243
|
+
secretsProperties: false,
|
|
244
|
+
secretsPropertiesPath: 'local.properties',
|
|
245
|
+
};
|
|
246
|
+
describe('what finding them buys the generator', () => {
|
|
247
|
+
/** The real repository's shape, as a project on disk. */
|
|
248
|
+
function conventionPluginRepository() {
|
|
249
|
+
appModule(CONVENTION_PLUGIN_MODULE);
|
|
250
|
+
writeFileSync(join(root, 'settings.gradle.kts'), 'include(":app")\n', 'utf-8');
|
|
251
|
+
sourceSet('main', ['AndroidManifest.xml', 'java', 'res']);
|
|
252
|
+
sourceSet('ciDynamic', ['google-services.json', 'res']);
|
|
253
|
+
sourceSet('dev', ['google-services.json', 'res']);
|
|
254
|
+
sourceSet('prod', ['google-services.json', 'res']);
|
|
255
|
+
}
|
|
256
|
+
const variant = (name) => ({
|
|
257
|
+
variant: name,
|
|
258
|
+
buildType: name.charAt(0).toUpperCase() + name.slice(1),
|
|
259
|
+
gradleTask: `assemble${name.charAt(0).toUpperCase()}${name.slice(1)}`,
|
|
260
|
+
});
|
|
261
|
+
const VARIANTS = {
|
|
262
|
+
primary: variant('ciDynamicDebug'),
|
|
263
|
+
pullRequest: variant('ciDynamicDebug'),
|
|
264
|
+
release: variant('prodRelease'),
|
|
265
|
+
};
|
|
266
|
+
test('each workflow writes google-services.json into its own source set', async () => {
|
|
267
|
+
conventionPluginRepository();
|
|
268
|
+
const { productFlavors } = (await scanAndroidProject(root)).buildVariants;
|
|
269
|
+
expect(googleServicesTargets(VARIANTS, productFlavors)).toEqual({
|
|
270
|
+
primary: 'app/src/ciDynamic/google-services.json',
|
|
271
|
+
'pull-request': 'app/src/ciDynamic/google-services.json',
|
|
272
|
+
release: 'app/src/prod/google-services.json',
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
test('the credential the two flavors cannot share is declared per workflow', async () => {
|
|
276
|
+
conventionPluginRepository();
|
|
277
|
+
const { productFlavors } = (await scanAndroidProject(root)).buildVariants;
|
|
278
|
+
const yaml = generateAndroidPipeline(17, {
|
|
279
|
+
...BASE_SETUP,
|
|
280
|
+
googleServices: true,
|
|
281
|
+
googleServicesPaths: googleServicesTargets(VARIANTS, productFlavors),
|
|
282
|
+
}, VARIANTS);
|
|
283
|
+
const parsed = load(yaml);
|
|
284
|
+
const declaringWorkflows = Object.keys(parsed.workflows ?? {}).filter((workflow) => 'GOOGLE_SERVICES_JSON' in Object.assign({}, ...(parsed.workflows?.[workflow]?.envs ?? [])));
|
|
285
|
+
// One declaration per workflow, in that workflow's own `envs`, under the
|
|
286
|
+
// plain name — the split the flavors bought, without the transport prefix.
|
|
287
|
+
expect(declaringWorkflows).toEqual(['primary', 'pull-request', 'release']);
|
|
288
|
+
expect(Object.assign({}, ...(parsed.app?.envs ?? []))).not.toHaveProperty('GOOGLE_SERVICES_JSON');
|
|
289
|
+
});
|
|
290
|
+
test('local.properties is split by the flavor each workflow builds', async () => {
|
|
291
|
+
conventionPluginRepository();
|
|
292
|
+
const { productFlavors } = (await scanAndroidProject(root)).buildVariants;
|
|
293
|
+
expect(variantFlavors(VARIANTS, productFlavors)).toEqual({
|
|
294
|
+
primary: 'ciDynamic',
|
|
295
|
+
'pull-request': 'ciDynamic',
|
|
296
|
+
release: 'prod',
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
//# sourceMappingURL=flavors-a-convention-plugin-creates-are-still-found.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/run.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/run.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkJ3E"}
|
package/dist/src/commands/run.js
CHANGED
|
@@ -106,7 +106,7 @@ export async function handleRunCommand(opts = {}) {
|
|
|
106
106
|
interactive: isInteractiveTTY(),
|
|
107
107
|
});
|
|
108
108
|
pipeline = conversionResult.pipeline;
|
|
109
|
-
await runner.runPipeline(pipeline, conversionResult.warnings, conversionResult.skippedSteps);
|
|
109
|
+
await runner.runPipeline(pipeline, conversionResult.warnings, conversionResult.skippedSteps, conversionResult.checkout);
|
|
110
110
|
}
|
|
111
111
|
else if (isJavaScriptModule) {
|
|
112
112
|
// The one dynamic import that has to stay dynamic: the specifier is a
|
package/dist/src/runner.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StepDef, PipelineDef, CIConfig } from "./types.js";
|
|
2
|
+
import type { CheckoutWitness } from "./yaml/converter.js";
|
|
2
3
|
export declare class StepExecutionError extends Error {
|
|
3
4
|
stepId: string;
|
|
4
5
|
stepName: string;
|
|
@@ -12,6 +13,19 @@ export declare class PipelineRunner {
|
|
|
12
13
|
private formatDuration;
|
|
13
14
|
validatePipeline(pipeline: PipelineDef): void;
|
|
14
15
|
displayWarnings(warnings: string[], skippedSteps: number): void;
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The commit this build compiled, printed under the banner.
|
|
18
|
+
*
|
|
19
|
+
* Every build carries it, whatever steps it declares. The dashboard's sha is
|
|
20
|
+
* the one that was *dispatched*; this one was read off the disk the compiler
|
|
21
|
+
* is about to read, and it is what a later argument about which code shipped
|
|
22
|
+
* has to be settled against. It used to live only in `git-clone`'s echo,
|
|
23
|
+
* which is optional and no longer in the curated templates.
|
|
24
|
+
*
|
|
25
|
+
* Labels match that echo's on purpose — where both appear they are the same
|
|
26
|
+
* three facts, and they have to agree.
|
|
27
|
+
*/
|
|
28
|
+
displayCheckout(checkout: CheckoutWitness): void;
|
|
29
|
+
runPipeline(pipeline: PipelineDef, warnings?: string[], skippedSteps?: number, checkout?: CheckoutWitness): Promise<void>;
|
|
16
30
|
}
|
|
17
31
|
//# sourceMappingURL=runner.d.ts.map
|
package/dist/src/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAO3D,qBAAa,kBAAmB,SAAQ,KAAK;IAElC,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;gBAFhB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAK1B;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,QAAQ;IAE9B,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiHhE,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI;IAwB7C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAc/D;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAO1C,WAAW,CACf,QAAQ,EAAE,WAAW,EACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,EACnB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,IAAI,CAAC;CA+CjB"}
|