@invarn/cibuild 2.7.4 → 2.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/cli.cjs +10 -10
  2. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts +38 -0
  3. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts.map +1 -0
  4. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.js +222 -0
  5. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts +41 -0
  6. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts.map +1 -0
  7. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.js +324 -0
  8. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts +29 -0
  9. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts.map +1 -0
  10. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.js +160 -0
  11. package/dist/src/commands/android-scanner.d.ts +9 -1
  12. package/dist/src/commands/android-scanner.d.ts.map +1 -1
  13. package/dist/src/commands/android-scanner.js +103 -3
  14. package/dist/src/commands/build.d.ts +128 -1
  15. package/dist/src/commands/build.d.ts.map +1 -1
  16. package/dist/src/commands/build.js +286 -49
  17. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts +26 -0
  18. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts.map +1 -0
  19. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.js +196 -0
  20. package/dist/src/commands/file-secret-collector.d.ts +13 -0
  21. package/dist/src/commands/file-secret-collector.d.ts.map +1 -1
  22. package/dist/src/commands/file-secret-collector.js +25 -7
  23. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts +38 -0
  24. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts.map +1 -0
  25. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.js +300 -0
  26. package/dist/src/commands/local-properties-reaches-every-workflow.test.js +2 -0
  27. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts +28 -0
  28. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts.map +1 -0
  29. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.js +223 -0
  30. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts +37 -0
  31. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts.map +1 -0
  32. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.js +255 -0
  33. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts +34 -0
  34. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts.map +1 -0
  35. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.js +218 -0
  36. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts +27 -0
  37. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts.map +1 -0
  38. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.js +126 -0
  39. package/dist/src/yaml/converter.d.ts +48 -0
  40. package/dist/src/yaml/converter.d.ts.map +1 -1
  41. package/dist/src/yaml/converter.js +117 -0
  42. package/dist/src/yaml/env-resolver.d.ts +52 -0
  43. package/dist/src/yaml/env-resolver.d.ts.map +1 -1
  44. package/dist/src/yaml/env-resolver.js +99 -1
  45. package/dist/src/yaml/secrets-manager.d.ts +13 -3
  46. package/dist/src/yaml/secrets-manager.d.ts.map +1 -1
  47. package/dist/src/yaml/secrets-manager.js +25 -14
  48. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +22 -0
  49. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +1 -0
  50. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +97 -0
  51. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts +17 -0
  52. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts.map +1 -0
  53. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.js +132 -0
  54. package/dist/src/yaml/steps/index.d.ts.map +1 -1
  55. package/dist/src/yaml/steps/index.js +9 -0
  56. package/dist/src/yaml/steps/ios-deps.d.ts +35 -0
  57. package/dist/src/yaml/steps/ios-deps.d.ts.map +1 -1
  58. package/dist/src/yaml/steps/ios-deps.js +19 -4
  59. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts +30 -0
  60. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts.map +1 -0
  61. package/dist/src/yaml/steps/xcode-derived-data.test.js +103 -0
  62. package/dist/src/yaml/steps/xcode.d.ts.map +1 -1
  63. package/dist/src/yaml/steps/xcode.js +73 -20
  64. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"env-resolver.d.ts","sourceRoot":"","sources":["../../../src/yaml/env-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAc,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiEnF,qBAAa,+BAAgC,SAAQ,KAAK;aAEtC,YAAY,EAAE,MAAM;aACpB,QAAQ,CAAC,EAAE,MAAM;aACjB,IAAI,CAAC,EAAE,MAAM;gBAFb,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,YAAA,EACjB,IAAI,CAAC,EAAE,MAAM,YAAA;CAUhC;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,QAAQ,EAAE,MAAM;aAChB,QAAQ,CAAC,EAAE,MAAM;gBADjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,YAAA;CAKpC;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAAiB;IACvC;;;OAGG;IACH,OAAO,CAAC,YAAY,CAA0B;gBAG5C,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM;IAkCvB;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKrC;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMtC;;;OAGG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQhC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAqCnD;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,GAAG;IAoBnD;;;OAGG;IACH,sBAAsB,IAAI,IAAI;IAQ9B;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAalB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA0BvB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA4FtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAoBpB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,4BAA4B;IAUpC;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;CAS5B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,WAAW,CAOb"}
1
+ {"version":3,"file":"env-resolver.d.ts","sourceRoot":"","sources":["../../../src/yaml/env-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAc,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkEnF,qBAAa,+BAAgC,SAAQ,KAAK;aAEtC,YAAY,EAAE,MAAM;aACpB,QAAQ,CAAC,EAAE,MAAM;aACjB,IAAI,CAAC,EAAE,MAAM;gBAFb,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,YAAA,EACjB,IAAI,CAAC,EAAE,MAAM,YAAA;CAUhC;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,QAAQ,EAAE,MAAM;aAChB,QAAQ,CAAC,EAAE,MAAM;gBADjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,YAAA;CAKpC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;CAgChF;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAAiB;IACvC;;;OAGG;IACH,OAAO,CAAC,YAAY,CAA0B;IAC9C,oFAAoF;IACpF,OAAO,CAAC,YAAY,CAAS;IAC7B,oFAAoF;IACpF,OAAO,CAAC,iBAAiB,CAAW;gBAGlC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM;IAoCvB;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKrC;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMtC;;;OAGG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQhC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAqCnD;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,GAAG;IAoBnD;;;OAGG;IACH,sBAAsB,IAAI,IAAI;IAQ9B;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAalB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA0BvB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA4FtB;;;OAGG;IACH;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,YAAY;IAsBpB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,4BAA4B;IAUpC;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;CAS5B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,WAAW,CAOb"}
@@ -8,6 +8,7 @@ import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import { execSync } from 'child_process';
10
10
  import { SecretsManager } from './secrets-manager.js';
11
+ import { decodeSecretName } from '../commands/secrets-upload.js';
11
12
  import { shellAssignedNamesIn } from './shell-assignments.js';
12
13
  /**
13
14
  * Hints for common variables that require user configuration
@@ -95,6 +96,58 @@ export class ChecksumFileMissingError extends Error {
95
96
  this.name = 'ChecksumFileMissingError';
96
97
  }
97
98
  }
99
+ /**
100
+ * A credential slot scoped to a workflow the pipeline does not have.
101
+ *
102
+ * Thrown at generation time, before any step converts, because every way of
103
+ * getting the name wrong is otherwise silent. A slot naming a workflow nobody
104
+ * runs declares the credential for nobody: `file@1.0.0` finds no key in its
105
+ * map, calls the credential optional and skips it with a warning, and
106
+ * `ci secrets upload` cheerfully pushes a value under the same wrong name.
107
+ * Two green halves and a build that dies later inside the project's own build
108
+ * tool.
109
+ *
110
+ * The underscore case gets its own sentence because the workflow is right
111
+ * there in the pipeline. `encodeSecretName` upper-cases the workflow and turns
112
+ * dashes into underscores, so decoding turns every underscore back into a
113
+ * dash: a workflow genuinely called `my_wf` can only ever be addressed as
114
+ * `my-wf`, and "no such workflow" about a workflow that plainly exists reads
115
+ * as a defect in us rather than a name that cannot survive the round trip.
116
+ */
117
+ export class UnknownWorkflowSlotError extends Error {
118
+ slot;
119
+ varName;
120
+ workflow;
121
+ constructor(slot, varName, workflow, declared) {
122
+ // A workflow the pipeline has that differs only in its underscores is the
123
+ // reason the round trip failed, and naming it is the whole fix.
124
+ const collapsed = declared.find((name) => name.replace(/_/g, '-') === workflow);
125
+ const because = collapsed
126
+ ? `This pipeline has a workflow called "${collapsed}". The name a slot ` +
127
+ `carries cannot hold an underscore: it is upper-cased and its dashes ` +
128
+ `become underscores, so decoding turns every underscore back into a ` +
129
+ `dash and "${collapsed}" can only be addressed as "${workflow}".\n` +
130
+ `\n` +
131
+ `Rename the workflow to "${workflow}", or give ${varName} one value ` +
132
+ `for every workflow by declaring it as "${varName}".`
133
+ : `This pipeline declares: ${declared.join(', ')}.\n` +
134
+ `\n` +
135
+ `Fix the workflow name in the slot, or give ${varName} one value for ` +
136
+ `every workflow by declaring it as "${varName}".`;
137
+ super(`${slot} is declared for a workflow this pipeline does not have.\n` +
138
+ `\n` +
139
+ `The slot asks for ${varName}, for a workflow called "${workflow}". ` +
140
+ `Nothing runs under that name, so nothing would ever read the value: ` +
141
+ `the step that writes ${varName} would find no value and skip, and the ` +
142
+ `build would fail later for a reason that names your project.\n` +
143
+ `\n` +
144
+ `${because}`);
145
+ this.name = 'UnknownWorkflowSlotError';
146
+ this.slot = slot;
147
+ this.varName = varName;
148
+ this.workflow = workflow;
149
+ }
150
+ }
98
151
  /**
99
152
  * EnvResolver handles environment variable merging and interpolation
100
153
  */
@@ -107,9 +160,15 @@ export class EnvResolver {
107
160
  * not it gave one a value. Read by `importDeclaredFromProcessEnv`.
108
161
  */
109
162
  declaredVars = new Set();
163
+ /** The workflow being resolved. Decides which scoped slots apply — `slotKeyFor`. */
164
+ workflowName;
165
+ /** Every workflow this pipeline declares. A scoped slot has to name one of them. */
166
+ declaredWorkflows;
110
167
  constructor(pipeline, workflow, workflowName, platform, stack, yamlFilePath) {
111
168
  this.envVars = new Map();
112
169
  this.secretsManager = new SecretsManager();
170
+ this.workflowName = workflowName;
171
+ this.declaredWorkflows = Object.keys(pipeline.workflows ?? {});
113
172
  // Step 1: Add built-in environment variables (auto-detected defaults)
114
173
  this.addBuiltInVars(workflowName, platform, stack, yamlFilePath);
115
174
  // Step 2: Load secrets — overrides auto-detected built-ins with user-stored values
@@ -378,9 +437,48 @@ export class EnvResolver {
378
437
  * Merges environment variables from YAML array format
379
438
  * @param envArray Array of environment variable objects
380
439
  */
440
+ /**
441
+ * The variable a declared `envs` key stands for, or null when the
442
+ * declaration belongs to a different workflow.
443
+ *
444
+ * A credential two workflows cannot share is declared once per workflow,
445
+ * under the same name a workflow-scoped secret carries — the pipeline says
446
+ * `CIBUILD_SW__RELEASE__LOCAL_PROPERTIES: ""` where it used to say
447
+ * `LOCAL_PROPERTIES: ""`. The build still reads the credential under its
448
+ * plain name, so the declaration has to mean the plain name: it is what puts
449
+ * an unsupplied key in the map for `file@1.0.0` to refuse over, and what
450
+ * lets `importDeclaredFromProcessEnv` accept a value delivered flat.
451
+ *
452
+ * A slot naming another workflow is dropped rather than declared. Declaring
453
+ * it would make this workflow demand a value only that one was ever going to
454
+ * be given. The decoding lives here, beside the decoding of the secret
455
+ * itself, so the steps keep reading their plain `var_name` and never learn
456
+ * the convention exists.
457
+ *
458
+ * A slot naming a workflow the pipeline does not have is refused rather than
459
+ * dropped — see `UnknownWorkflowSlotError`. Dropping it silently is the same
460
+ * outcome as never declaring the credential, which is the green skip issue 03
461
+ * exists to end.
462
+ */
463
+ slotKeyFor(key) {
464
+ const decoded = decodeSecretName(key);
465
+ if (!decoded)
466
+ return key;
467
+ if (decoded.workflow) {
468
+ if (!this.declaredWorkflows.includes(decoded.workflow)) {
469
+ throw new UnknownWorkflowSlotError(key, decoded.varName, decoded.workflow, this.declaredWorkflows);
470
+ }
471
+ if (decoded.workflow !== this.workflowName)
472
+ return null;
473
+ }
474
+ return decoded.varName;
475
+ }
381
476
  mergeEnvVars(envArray) {
382
477
  for (const envObj of envArray) {
383
- for (const [key, value] of Object.entries(envObj)) {
478
+ for (const [declaredKey, value] of Object.entries(envObj)) {
479
+ const key = this.slotKeyFor(declaredKey);
480
+ if (key === null)
481
+ continue;
384
482
  // Naming the key is the declaration, whatever value follows it
385
483
  this.declaredVars.add(key);
386
484
  // First resolve any secret references
@@ -43,17 +43,27 @@ export declare class SecretsManager {
43
43
  * Get a secret value by variable name.
44
44
  * Workflow-specific value takes precedence over global.
45
45
  * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
46
+ *
47
+ * The stored data is consulted before that fallback, and not only when a
48
+ * file was loaded. `storeSecret` writes the file but cannot make it one this
49
+ * instance *loaded*, so a value collected by `ci init` on a repository that
50
+ * had no store — which is the only kind of run that collects anything — read
51
+ * back as absent from the instance that had just stored it. `ci init`'s own
52
+ * `getSecret('KEYSTORE_PROPERTIES', wf)`, which is how a workflow's keystore
53
+ * target is taken from `storeFile=`, returned undefined on every fresh
54
+ * repository because of it.
46
55
  */
47
56
  getSecret(name: string, workflow?: string): string | undefined;
48
57
  /**
49
58
  * Get all secrets merged: global + workflow-specific (workflow overrides global).
50
- * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
59
+ * Falls back to CIBUILD_-prefixed env vars when no secrets file is present —
60
+ * for the keys the store does not hold. See `getSecret`.
51
61
  */
52
62
  getAll(workflow?: string): Record<string, string>;
53
63
  /**
54
64
  * Returns the variable name if a secret exists for it, undefined otherwise.
55
- * Checks workflow scope first, then global.
56
- * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
65
+ * Checks workflow scope first, then global, then the CIBUILD_-prefixed env
66
+ * vars when no secrets file is present. See `getSecret`.
57
67
  */
58
68
  getSecretIdByName(name: string, workflow?: string): string | undefined;
59
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"secrets-manager.d.ts","sourceRoot":"","sources":["../../../src/yaml/secrets-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAkB;gBAExB,eAAe,CAAC,EAAE,MAAM;IAMpC,OAAO,CAAC,WAAW;IAqCnB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAkB/B,OAAO,CAAC,WAAW;IAUnB;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAanE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAc;IAEhD;;;OAGG;IACH,OAAO,CAAC,aAAa;IAUrB;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAU9D;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAWjD;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAatE;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAInD;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAQ7B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAetD,kBAAkB,IAAI,MAAM;CAG7B"}
1
+ {"version":3,"file":"secrets-manager.d.ts","sourceRoot":"","sources":["../../../src/yaml/secrets-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAkB;gBAExB,eAAe,CAAC,EAAE,MAAM;IAMpC,OAAO,CAAC,WAAW;IAqCnB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAkB/B,OAAO,CAAC,WAAW;IAUnB;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAanE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAc;IAEhD;;;OAGG;IACH,OAAO,CAAC,aAAa;IAUrB;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAa9D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IASjD;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAatE;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAInD;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAQ7B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAetD,kBAAkB,IAAI,MAAM;CAG7B"}
@@ -129,25 +129,36 @@ export class SecretsManager {
129
129
  * Get a secret value by variable name.
130
130
  * Workflow-specific value takes precedence over global.
131
131
  * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
132
+ *
133
+ * The stored data is consulted before that fallback, and not only when a
134
+ * file was loaded. `storeSecret` writes the file but cannot make it one this
135
+ * instance *loaded*, so a value collected by `ci init` on a repository that
136
+ * had no store — which is the only kind of run that collects anything — read
137
+ * back as absent from the instance that had just stored it. `ci init`'s own
138
+ * `getSecret('KEYSTORE_PROPERTIES', wf)`, which is how a workflow's keystore
139
+ * target is taken from `storeFile=`, returned undefined on every fresh
140
+ * repository because of it.
132
141
  */
133
142
  getSecret(name, workflow) {
134
- if (!this.fileLoaded) {
135
- return process.env[SecretsManager.ENV_PREFIX + name];
136
- }
137
143
  if (workflow && this.data.workflows[workflow]?.[name] !== undefined) {
138
144
  return this.data.workflows[workflow][name];
139
145
  }
140
- return this.data.global[name];
146
+ if (this.data.global[name] !== undefined) {
147
+ return this.data.global[name];
148
+ }
149
+ if (!this.fileLoaded) {
150
+ return process.env[SecretsManager.ENV_PREFIX + name];
151
+ }
152
+ return undefined;
141
153
  }
142
154
  /**
143
155
  * Get all secrets merged: global + workflow-specific (workflow overrides global).
144
- * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
156
+ * Falls back to CIBUILD_-prefixed env vars when no secrets file is present —
157
+ * for the keys the store does not hold. See `getSecret`.
145
158
  */
146
159
  getAll(workflow) {
147
- if (!this.fileLoaded) {
148
- return this.getEnvSecrets();
149
- }
150
- const merged = { ...this.data.global };
160
+ const merged = this.fileLoaded ? {} : this.getEnvSecrets();
161
+ Object.assign(merged, this.data.global);
151
162
  if (workflow && this.data.workflows[workflow]) {
152
163
  Object.assign(merged, this.data.workflows[workflow]);
153
164
  }
@@ -155,19 +166,19 @@ export class SecretsManager {
155
166
  }
156
167
  /**
157
168
  * Returns the variable name if a secret exists for it, undefined otherwise.
158
- * Checks workflow scope first, then global.
159
- * Falls back to CIBUILD_-prefixed env vars when no secrets file is present.
169
+ * Checks workflow scope first, then global, then the CIBUILD_-prefixed env
170
+ * vars when no secrets file is present. See `getSecret`.
160
171
  */
161
172
  getSecretIdByName(name, workflow) {
162
- if (!this.fileLoaded) {
163
- return process.env[SecretsManager.ENV_PREFIX + name] !== undefined ? name : undefined;
164
- }
165
173
  if (workflow && this.data.workflows[workflow]?.[name] !== undefined) {
166
174
  return name;
167
175
  }
168
176
  if (this.data.global[name] !== undefined) {
169
177
  return name;
170
178
  }
179
+ if (!this.fileLoaded) {
180
+ return process.env[SecretsManager.ENV_PREFIX + name] !== undefined ? name : undefined;
181
+ }
171
182
  return undefined;
172
183
  }
173
184
  /**
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Every way of telling `cocoapods-install` where to look must emit shell that
3
+ * bash will actually parse.
4
+ *
5
+ * `podfile_path` did not. One stray quote —
6
+ *
7
+ * WORK_DIR="$(dirname '/path/to/Podfile')""
8
+ *
9
+ * — opened a double-quoted string that never closed, so bash swallowed the
10
+ * following line and died with `unexpected EOF while looking for matching '"'`.
11
+ * The input is documented in SPEC.md and in `steps/cocoapods-install.yml`, and
12
+ * it could never have worked. Nothing in Invarn's templates passes it, which is
13
+ * why no build ever reported it.
14
+ *
15
+ * No string assertion could have caught this, and the existing tests for the
16
+ * input asserted only that the rendered script *mentioned* the path. So this
17
+ * file parses the script instead of reading it, and it does so for **all four**
18
+ * working-directory branches rather than the one that was broken — the bug was
19
+ * in a branch nobody exercised, and the next one will be too.
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=cocoapods-install-emits-valid-bash.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cocoapods-install-emits-valid-bash.test.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cocoapods-install-emits-valid-bash.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Every way of telling `cocoapods-install` where to look must emit shell that
3
+ * bash will actually parse.
4
+ *
5
+ * `podfile_path` did not. One stray quote —
6
+ *
7
+ * WORK_DIR="$(dirname '/path/to/Podfile')""
8
+ *
9
+ * — opened a double-quoted string that never closed, so bash swallowed the
10
+ * following line and died with `unexpected EOF while looking for matching '"'`.
11
+ * The input is documented in SPEC.md and in `steps/cocoapods-install.yml`, and
12
+ * it could never have worked. Nothing in Invarn's templates passes it, which is
13
+ * why no build ever reported it.
14
+ *
15
+ * No string assertion could have caught this, and the existing tests for the
16
+ * input asserted only that the rendered script *mentioned* the path. So this
17
+ * file parses the script instead of reading it, and it does so for **all four**
18
+ * working-directory branches rather than the one that was broken — the bug was
19
+ * in a branch nobody exercised, and the next one will be too.
20
+ */
21
+ import { execFileSync } from 'node:child_process';
22
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
23
+ import { tmpdir } from 'node:os';
24
+ import { join } from 'node:path';
25
+ import { describe, test, expect } from '@jest/globals';
26
+ import { CocoapodsInstallStepExecutor } from './ios-deps.js';
27
+ import { testConfig } from './test-config.js';
28
+ const render = async (inputs) => {
29
+ const result = await new CocoapodsInstallStepExecutor().execute(inputs, {}, testConfig);
30
+ return result.script;
31
+ };
32
+ /** Throws with bash's own diagnostic if the script does not parse. */
33
+ const parses = (script) => {
34
+ const dir = mkdtempSync(join(tmpdir(), 'cibuild-pods-syntax-'));
35
+ const path = join(dir, 'step.sh');
36
+ writeFileSync(path, script);
37
+ try {
38
+ execFileSync('bash', ['-n', path], { stdio: 'pipe' });
39
+ }
40
+ finally {
41
+ rmSync(dir, { recursive: true, force: true });
42
+ }
43
+ };
44
+ // The precedence the step documents: podfile_path, source_root_path,
45
+ // project_path, then $CIBUILD_SOURCE_DIR. One case per branch.
46
+ const branches = [
47
+ ['podfile_path', { podfile_path: '/path/to/Podfile' }],
48
+ ['source_root_path', { source_root_path: 'ios' }],
49
+ ['project_path', { project_path: 'ios/MyApp.xcworkspace' }],
50
+ ['neither — falls back to CIBUILD_SOURCE_DIR', {}],
51
+ ];
52
+ describe('cocoapods-install emits bash that parses, on every path to a directory', () => {
53
+ for (const [name, inputs] of branches) {
54
+ test(name, async () => {
55
+ parses(await render(inputs));
56
+ });
57
+ }
58
+ // skip_if_absent rewrites the body of the `if [ ! -f "Podfile" ]` branch, so
59
+ // it is a second dimension over the same generated shell.
60
+ for (const [name, inputs] of branches) {
61
+ test(`${name}, with skip_if_absent`, async () => {
62
+ parses(await render({ ...inputs, skip_if_absent: true }));
63
+ });
64
+ }
65
+ test('pod update rather than install', async () => {
66
+ parses(await render({ source_root_path: 'ios', command: 'update' }));
67
+ });
68
+ test('a trailing flag on the command', async () => {
69
+ parses(await render({ project_path: 'ios/MyApp.xcodeproj', command: 'install --repo-update' }));
70
+ });
71
+ // A directory with a space in it is the case that makes quoting matter, and
72
+ // it is ordinary on iOS — "My App.xcworkspace".
73
+ test('a path containing a space', async () => {
74
+ parses(await render({ project_path: 'ios/My App.xcworkspace' }));
75
+ parses(await render({ podfile_path: '/Users/me/My Project/Podfile' }));
76
+ parses(await render({ source_root_path: 'My iOS Dir' }));
77
+ });
78
+ test('verbose logging', async () => {
79
+ parses(await render({ source_root_path: 'ios', verbose: true }));
80
+ });
81
+ });
82
+ describe('podfile_path still names the directory it was given', () => {
83
+ // Guarding the fix rather than only its syntax: the stray quote could have
84
+ // been "fixed" by deleting the line.
85
+ test('cds into the dirname of the Podfile', async () => {
86
+ const script = await render({ podfile_path: '/path/to/Podfile' });
87
+ expect(script).toContain(`WORK_DIR="$(dirname '/path/to/Podfile')"`);
88
+ expect(script).toContain('cd "$WORK_DIR"');
89
+ });
90
+ test('does not leave an unbalanced quote on the assignment', async () => {
91
+ const script = await render({ podfile_path: '/path/to/Podfile' });
92
+ const line = script.split('\n').find((l) => l.startsWith('WORK_DIR='));
93
+ expect(line).toBeDefined();
94
+ expect(line.split('"').length - 1).toBe(2);
95
+ });
96
+ });
97
+ //# sourceMappingURL=cocoapods-install-emits-valid-bash.test.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `cocoapods-install` can be asked to skip a project that has no Podfile.
3
+ *
4
+ * The default stays a hard failure, because a pipeline that names this step is
5
+ * asserting the project uses CocoaPods and a missing Podfile is a real error.
6
+ *
7
+ * The iOS template needs the other behaviour: it serves every iOS project and a
8
+ * large share of them resolve dependencies through Swift Package Manager, where
9
+ * "Podfile not found" would be a CocoaPods error in a project that does not use
10
+ * CocoaPods. Until this input existed the template could not use this step at
11
+ * all, so it ran `pod install` from an inline `script@1.0.0` — which put it out
12
+ * of reach of the refused-fetch retry this step carries. That is the gap this
13
+ * closes, and it is why the release order matters: this must ship before the
14
+ * template is pointed at the step, or every SPM-only iOS repo breaks.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=cocoapods-skip-if-absent.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cocoapods-skip-if-absent.test.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cocoapods-skip-if-absent.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * `cocoapods-install` can be asked to skip a project that has no Podfile.
3
+ *
4
+ * The default stays a hard failure, because a pipeline that names this step is
5
+ * asserting the project uses CocoaPods and a missing Podfile is a real error.
6
+ *
7
+ * The iOS template needs the other behaviour: it serves every iOS project and a
8
+ * large share of them resolve dependencies through Swift Package Manager, where
9
+ * "Podfile not found" would be a CocoaPods error in a project that does not use
10
+ * CocoaPods. Until this input existed the template could not use this step at
11
+ * all, so it ran `pod install` from an inline `script@1.0.0` — which put it out
12
+ * of reach of the refused-fetch retry this step carries. That is the gap this
13
+ * closes, and it is why the release order matters: this must ship before the
14
+ * template is pointed at the step, or every SPM-only iOS repo breaks.
15
+ */
16
+ import { describe, test, expect } from '@jest/globals';
17
+ import { CocoapodsInstallStepExecutor } from './ios-deps.js';
18
+ import { testConfig } from './test-config.js';
19
+ const build = async (inputs = {}) => {
20
+ const executor = new CocoapodsInstallStepExecutor();
21
+ const result = await executor.execute(inputs, {}, testConfig);
22
+ return result.script;
23
+ };
24
+ /** The body of the `if [ ! -f "Podfile" ]` branch, which is what changes. */
25
+ const absentBranch = (script) => {
26
+ const lines = script.split('\n');
27
+ const start = lines.findIndex((l) => l.includes('if [ ! -f "Podfile" ]'));
28
+ expect(start).toBeGreaterThanOrEqual(0);
29
+ const end = lines.findIndex((l, i) => i > start && l.trim() === 'fi');
30
+ return lines.slice(start, end + 1).join('\n');
31
+ };
32
+ describe('by default a missing Podfile is still an error', () => {
33
+ test('exits non-zero', async () => {
34
+ expect(absentBranch(await build())).toContain('exit 1');
35
+ });
36
+ test('says what was not found', async () => {
37
+ expect(absentBranch(await build())).toContain('Podfile not found');
38
+ });
39
+ test('an explicit false is the same as omitting it', async () => {
40
+ expect(absentBranch(await build({ skip_if_absent: false }))).toContain('exit 1');
41
+ });
42
+ });
43
+ describe('skip_if_absent turns it into a clean no-op', () => {
44
+ test('exits zero instead of one', async () => {
45
+ const branch = absentBranch(await build({ skip_if_absent: true }));
46
+ expect(branch).toContain('exit 0');
47
+ expect(branch).not.toContain('exit 1');
48
+ });
49
+ test('says it skipped rather than that something failed', async () => {
50
+ const branch = absentBranch(await build({ skip_if_absent: true }));
51
+ expect(branch).toContain('skipping CocoaPods install');
52
+ expect(branch).not.toContain('Error');
53
+ });
54
+ test('still runs pod install when a Podfile IS present', async () => {
55
+ // The skip is a branch, not a mode — the rest of the step is untouched.
56
+ const script = await build({ skip_if_absent: true });
57
+ expect(script).toContain('POD_INVOCATION');
58
+ expect(script).toContain('$POD_INVOCATION $POD_CMD');
59
+ });
60
+ test('still wraps the pod fetch in the refused-fetch retry', async () => {
61
+ // This is the entire point of letting the template use the step.
62
+ const script = await build({ skip_if_absent: true });
63
+ expect(script).toContain('cibuild_fetch_was_refused');
64
+ expect(script).toContain('CIBUILD_FETCH_MAX_ATTEMPTS');
65
+ });
66
+ test('still chooses between bundle exec and bare pod', async () => {
67
+ const script = await build({ skip_if_absent: true });
68
+ expect(script).toContain('USE_BUNDLE_EXEC');
69
+ expect(script).toContain('bundle exec pod');
70
+ });
71
+ });
72
+ /**
73
+ * The inline script the template used did two things, not one: it skipped a
74
+ * project with no Podfile, AND it looked for that Podfile beside the Xcode
75
+ * project rather than at the repository root. Both have to exist as inputs or
76
+ * the template cannot be switched — about a quarter of iOS repositories keep
77
+ * the app one directory down, and template inputs interpolate variables, not
78
+ * `$(dirname …)`.
79
+ */
80
+ describe('project_path finds the Podfile beside the project', () => {
81
+ test('changes into the project directory, not the repo root', async () => {
82
+ const script = await build({ project_path: 'ios/MyApp.xcworkspace' });
83
+ expect(script).toContain(`WORK_DIR="$(dirname 'ios/MyApp.xcworkspace')"`);
84
+ expect(script).toContain('cd "$WORK_DIR"');
85
+ });
86
+ test('does not fall back to CIBUILD_SOURCE_DIR when it is given', async () => {
87
+ const script = await build({ project_path: 'ios/MyApp.xcworkspace' });
88
+ expect(script).not.toContain('cd "$CIBUILD_SOURCE_DIR"');
89
+ });
90
+ test('still falls back to CIBUILD_SOURCE_DIR when nothing is given', async () => {
91
+ expect(await build()).toContain('cd "$CIBUILD_SOURCE_DIR"');
92
+ });
93
+ test('an explicit source_root_path still wins over project_path', async () => {
94
+ const script = await build({
95
+ project_path: 'ios/MyApp.xcworkspace',
96
+ source_root_path: 'somewhere/else',
97
+ });
98
+ expect(script).toContain(`cd 'somewhere/else'`);
99
+ expect(script).not.toContain('dirname');
100
+ });
101
+ test('composes with skip_if_absent, which is how the template uses it', async () => {
102
+ const script = await build({ project_path: 'ios/MyApp.xcworkspace', skip_if_absent: true });
103
+ expect(script).toContain(`WORK_DIR="$(dirname 'ios/MyApp.xcworkspace')"`);
104
+ expect(absentBranch(script)).toContain('exit 0');
105
+ expect(script).toContain('cibuild_fetch_was_refused');
106
+ });
107
+ });
108
+ /**
109
+ * The exact input combination the iOS template sends. Pinned here because the
110
+ * template lives in another repository and deploys on a different cadence, so
111
+ * nothing else would notice this step drifting away from what it sends.
112
+ */
113
+ describe('the iOS template combination', () => {
114
+ const templateInputs = {
115
+ project_path: 'MyApp.xcworkspace',
116
+ skip_if_absent: true,
117
+ command: 'install --repo-update',
118
+ };
119
+ test('produces pod install --repo-update, flags intact', async () => {
120
+ const script = await build(templateInputs);
121
+ expect(script).toContain('POD_CMD="install --repo-update"');
122
+ });
123
+ test('runs that under the refused-fetch retry, exactly once', async () => {
124
+ const script = await build(templateInputs);
125
+ expect(script).toContain('$POD_INVOCATION $POD_CMD');
126
+ expect(script.split('CIBUILD_FETCH_MAX_ATTEMPTS=').length - 1).toBe(1);
127
+ });
128
+ test('skips instead of failing when the repo has no Podfile', async () => {
129
+ expect(absentBranch(await build(templateInputs))).toContain('exit 0');
130
+ });
131
+ });
132
+ //# sourceMappingURL=cocoapods-skip-if-absent.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4CH;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CA07C7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4CH;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAq8C7C"}
@@ -451,11 +451,20 @@ export function initializeStepRegistry() {
451
451
  description: 'Explicit path to Podfile',
452
452
  required: false,
453
453
  },
454
+ project_path: {
455
+ description: 'A .xcodeproj/.xcworkspace whose directory holds the Podfile. For repositories that keep the app one directory down from the root.',
456
+ required: false,
457
+ },
454
458
  verbose: {
455
459
  description: 'Enable verbose CocoaPods logging',
456
460
  required: false,
457
461
  default: false,
458
462
  },
463
+ skip_if_absent: {
464
+ description: 'Succeed and do nothing when there is no Podfile, instead of failing. For pipelines that serve every iOS project, where plenty use Swift Package Manager and have no Podfile.',
465
+ required: false,
466
+ default: false,
467
+ },
459
468
  },
460
469
  });
461
470
  registerStep('carthage', new CarthageStepExecutor(), {
@@ -11,8 +11,43 @@ export interface CocoapodsInstallInputs {
11
11
  source_root_path?: string;
12
12
  /** Explicit path to Podfile (overrides source_root_path) */
13
13
  podfile_path?: string;
14
+ /**
15
+ * A .xcodeproj/.xcworkspace whose DIRECTORY holds the Podfile.
16
+ *
17
+ * About a quarter of iOS repositories keep the app one directory down from
18
+ * the repository root, and the Podfile is down there with it — so "run pod
19
+ * install at the root" is wrong for them. A caller that already knows where
20
+ * the project is should not have to compute its directory separately, and a
21
+ * pipeline template cannot: template inputs interpolate variables, not
22
+ * `$(dirname …)`, so `source_root_path` alone could not express it and the
23
+ * iOS template had to run `pod install` from an inline script to get at
24
+ * `dirname`. That inline script is what put it out of reach of the
25
+ * refused-fetch retry.
26
+ *
27
+ * Only the directory is used; the file itself need not exist.
28
+ *
29
+ * Precedence, most specific first: podfile_path, source_root_path,
30
+ * project_path, then $CIBUILD_SOURCE_DIR.
31
+ */
32
+ project_path?: string;
14
33
  /** Enable verbose CocoaPods logging */
15
34
  verbose?: boolean;
35
+ /**
36
+ * Succeed and do nothing when there is no Podfile, instead of failing.
37
+ *
38
+ * Defaults to false, which keeps the contract every existing caller was
39
+ * written against: a pipeline that names this step is asserting the project
40
+ * uses CocoaPods, and a missing Podfile is a real error worth stopping for.
41
+ *
42
+ * The iOS template is the caller that needs the other behaviour. It serves
43
+ * every iOS project, and a large share of them resolve dependencies through
44
+ * Swift Package Manager and have no Podfile at all — for those, "Podfile not
45
+ * found" is a CocoaPods error in a project that does not use CocoaPods.
46
+ * Until this existed the template could not use this step, and so ran
47
+ * `pod install` from an inline script instead, out of reach of the
48
+ * refused-fetch retry that this step carries.
49
+ */
50
+ skip_if_absent?: boolean;
16
51
  }
17
52
  /**
18
53
  * Runs `pod install` or `pod update`.
@@ -1 +1 @@
1
- {"version":3,"file":"ios-deps.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ios-deps.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAc,MAAM,wBAAwB,CAAC;AAMhF,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,4BAA6B,SAAQ,gBAAgB;IAChE,yBAAyB,CACvB,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAUpB,OAAO,CACX,MAAM,EAAE,sBAAsB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CAkFpB;AAMD,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,gBAAgB;IACxD,yBAAyB,CACvB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAUpB,OAAO,CACX,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CAyDpB"}
1
+ {"version":3,"file":"ios-deps.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ios-deps.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAc,MAAM,wBAAwB,CAAC;AAMhF,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,qBAAa,4BAA6B,SAAQ,gBAAgB;IAChE,yBAAyB,CACvB,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAUpB,OAAO,CACX,MAAM,EAAE,sBAAsB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CA+FpB;AAMD,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,gBAAgB;IACxD,yBAAyB,CACvB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAUpB,OAAO,CACX,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CAyDpB"}