@invarn/cibuild 2.6.2 → 2.6.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.
- package/dist/cli.cjs +10 -10
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +3 -1
- package/dist/src/commands/run.d.ts.map +1 -1
- package/dist/src/commands/run.js +7 -0
- package/dist/src/yaml/interactive-prompts.d.ts.map +1 -1
- package/dist/src/yaml/interactive-prompts.js +3 -1
- package/dist/src/yaml/step-validator-check-failure.test.d.ts +24 -0
- package/dist/src/yaml/step-validator-check-failure.test.d.ts.map +1 -0
- package/dist/src/yaml/step-validator-check-failure.test.js +130 -0
- package/dist/src/yaml/step-validator.d.ts.map +1 -1
- package/dist/src/yaml/step-validator.js +13 -1
- package/dist/src/yaml/steps/android.d.ts.map +1 -1
- package/dist/src/yaml/steps/android.js +7 -3
- package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +1 -1
- package/dist/src/yaml/steps/bitrise-android-tools.js +4 -2
- package/dist/src/yaml/steps/cache-pull-daemon.test.js +11 -2
- package/dist/src/yaml/steps/cache-pull-restore-roots.test.js +10 -1
- package/dist/src/yaml/steps/cache-push-daemon.test.js +10 -1
- package/dist/src/yaml/steps/cache.d.ts.map +1 -1
- package/dist/src/yaml/steps/cache.js +31 -8
- package/package.json +1 -1
package/dist/src/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AASA,OAAO,uBAAuB,CAAC"}
|
package/dist/src/cli.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve, extname } from "node:path";
|
|
3
3
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
// Static — see the note in `yaml/steps/android.ts`. `pkg` cannot evaluate a
|
|
5
|
+
// dynamic `import()` of a builtin.
|
|
6
|
+
import { createInterface } from "node:readline";
|
|
4
7
|
import prompts from "prompts";
|
|
5
8
|
import { SecretsManager } from "./yaml/secrets-manager.js";
|
|
6
9
|
import "./yaml/steps/index.js"; // Initialize step registry
|
|
@@ -255,7 +258,6 @@ Examples:
|
|
|
255
258
|
}
|
|
256
259
|
else {
|
|
257
260
|
// Collect value interactively via readline (handles multiline paste cleanly)
|
|
258
|
-
const { createInterface } = await import('node:readline');
|
|
259
261
|
console.log(`\n Enter value for ${varName}`);
|
|
260
262
|
console.log(' Paste content, then type . on its own line and press Enter:\n');
|
|
261
263
|
if (process.stdin.isTTY) {
|
|
@@ -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,CAiJ3E"}
|
package/dist/src/commands/run.js
CHANGED
|
@@ -109,6 +109,13 @@ export async function handleRunCommand(opts = {}) {
|
|
|
109
109
|
await runner.runPipeline(pipeline, conversionResult.warnings, conversionResult.skippedSteps);
|
|
110
110
|
}
|
|
111
111
|
else if (isJavaScriptModule) {
|
|
112
|
+
// The one dynamic import that has to stay dynamic: the specifier is a
|
|
113
|
+
// user-supplied path, so no bundler can inline it. That means this
|
|
114
|
+
// branch does NOT work in the `pkg`-built macOS binary — it throws
|
|
115
|
+
// `Invalid host defined options` before the module loads. Nothing
|
|
116
|
+
// reaches it today: every curated template is YAML, and the runner only
|
|
117
|
+
// ever executes YAML pipelines. If JS pipelines are ever exposed as a
|
|
118
|
+
// supported path, they need a loader that does not rely on `import()`.
|
|
112
119
|
console.log(`Loading pipeline: ${pipelinePath}`);
|
|
113
120
|
const pipelineUrl = pathToFileURL(pipelinePath).href;
|
|
114
121
|
const pipelineModule = await import(pipelineUrl);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-prompts.d.ts","sourceRoot":"","sources":["../../../src/yaml/interactive-prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"interactive-prompts.d.ts","sourceRoot":"","sources":["../../../src/yaml/interactive-prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAS;gBAEd,cAAc,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM;IAK7D;;;;;;OAMG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,CAAC;IA4FxB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;OAIG;YACW,WAAW;IAWzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA0BvB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAyBhB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;OAGG;YACW,eAAe;IAqC7B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Interactive prompts for missing environment variables
|
|
3
3
|
* Allows users to fill in missing values or cancel the build
|
|
4
4
|
*/
|
|
5
|
+
// Static — see the note in `steps/android.ts`. `pkg` cannot evaluate a
|
|
6
|
+
// dynamic `import()` of a builtin.
|
|
7
|
+
import { spawn } from 'node:child_process';
|
|
5
8
|
import prompts from 'prompts';
|
|
6
9
|
/**
|
|
7
10
|
* Interactive prompt manager for collecting missing environment variables
|
|
@@ -212,7 +215,6 @@ Finish with a short "Follow-up questions" numbered list only if clarification wo
|
|
|
212
215
|
* @param text Text to copy
|
|
213
216
|
*/
|
|
214
217
|
async copyToClipboard(text) {
|
|
215
|
-
const { spawn } = await import('child_process');
|
|
216
218
|
return new Promise((resolve, reject) => {
|
|
217
219
|
// Detect platform and use appropriate clipboard command
|
|
218
220
|
const platform = process.platform;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for what happens when a pre-execution check cannot run.
|
|
3
|
+
*
|
|
4
|
+
* A check that *ran* and found nothing, and a check that *threw* before it
|
|
5
|
+
* tested anything, are different events. Until 2026-09-01 they were reported
|
|
6
|
+
* identically: the throw was wrapped in a `Check failed:` message and carried
|
|
7
|
+
* the requirement's own declared severity. For the Java JDK requirement that
|
|
8
|
+
* severity is `info` — chosen because the check looks in several places and a
|
|
9
|
+
* miss is not fatal — so a check that never looked anywhere at all was
|
|
10
|
+
* reported as information and read as noise.
|
|
11
|
+
*
|
|
12
|
+
* It was not noise. The released macOS binary is built with `pkg`, which
|
|
13
|
+
* cannot evaluate a dynamic `import()` of a module it did not bundle; the JDK
|
|
14
|
+
* check opened with two of them and threw `Invalid host defined options` on
|
|
15
|
+
* every macOS build. It went unseen for as long as the fleet has existed.
|
|
16
|
+
*
|
|
17
|
+
* Two rules are pinned here:
|
|
18
|
+
* 1. a throw is reported at `warning`, whatever the requirement declares;
|
|
19
|
+
* 2. no check may reach for a Node builtin through a dynamic import, because
|
|
20
|
+
* nothing else in the test suite can see that failure — under plain
|
|
21
|
+
* `node`, and in the npm bundle, the code works.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=step-validator-check-failure.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-validator-check-failure.test.d.ts","sourceRoot":"","sources":["../../../src/yaml/step-validator-check-failure.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for what happens when a pre-execution check cannot run.
|
|
3
|
+
*
|
|
4
|
+
* A check that *ran* and found nothing, and a check that *threw* before it
|
|
5
|
+
* tested anything, are different events. Until 2026-09-01 they were reported
|
|
6
|
+
* identically: the throw was wrapped in a `Check failed:` message and carried
|
|
7
|
+
* the requirement's own declared severity. For the Java JDK requirement that
|
|
8
|
+
* severity is `info` — chosen because the check looks in several places and a
|
|
9
|
+
* miss is not fatal — so a check that never looked anywhere at all was
|
|
10
|
+
* reported as information and read as noise.
|
|
11
|
+
*
|
|
12
|
+
* It was not noise. The released macOS binary is built with `pkg`, which
|
|
13
|
+
* cannot evaluate a dynamic `import()` of a module it did not bundle; the JDK
|
|
14
|
+
* check opened with two of them and threw `Invalid host defined options` on
|
|
15
|
+
* every macOS build. It went unseen for as long as the fleet has existed.
|
|
16
|
+
*
|
|
17
|
+
* Two rules are pinned here:
|
|
18
|
+
* 1. a throw is reported at `warning`, whatever the requirement declares;
|
|
19
|
+
* 2. no check may reach for a Node builtin through a dynamic import, because
|
|
20
|
+
* nothing else in the test suite can see that failure — under plain
|
|
21
|
+
* `node`, and in the npm bundle, the code works.
|
|
22
|
+
*/
|
|
23
|
+
import { describe, test, expect } from '@jest/globals';
|
|
24
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { registerStep } from './steps/registry.js';
|
|
27
|
+
import { BaseStepExecutor } from './steps/base.js';
|
|
28
|
+
import { StepValidator } from './step-validator.js';
|
|
29
|
+
import { loadConfig } from '../config.js';
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
/** A step whose single requirement behaves however the test needs it to. */
|
|
32
|
+
function stepWhoseCheck(severity, check) {
|
|
33
|
+
const name = `probe-${Math.random().toString(36).slice(2)}`;
|
|
34
|
+
class ProbeStep extends BaseStepExecutor {
|
|
35
|
+
async execute() {
|
|
36
|
+
return this.createScriptStep('echo probe', name);
|
|
37
|
+
}
|
|
38
|
+
getValidationRequirements() {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
category: 'command',
|
|
42
|
+
name: 'probe-tool',
|
|
43
|
+
description: 'A tool the probe step needs',
|
|
44
|
+
timing: 'pre-execution',
|
|
45
|
+
severity,
|
|
46
|
+
check,
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
registerStep(name, new ProbeStep());
|
|
52
|
+
return name;
|
|
53
|
+
}
|
|
54
|
+
async function validate(stepName) {
|
|
55
|
+
const validator = new StepValidator({
|
|
56
|
+
format_version: '1',
|
|
57
|
+
meta: { invarn: { stack: 'macos-xcode-26.4' } },
|
|
58
|
+
workflows: { primary: { steps: [{ [`${stepName}@1.0.0`]: { inputs: {} } }] } },
|
|
59
|
+
}, 'primary', config);
|
|
60
|
+
return validator.validateWorkflow();
|
|
61
|
+
}
|
|
62
|
+
describe('a check that could not run', () => {
|
|
63
|
+
test('is reported as a warning even though the requirement declares info', async () => {
|
|
64
|
+
const name = stepWhoseCheck('info', async () => {
|
|
65
|
+
throw new Error('Invalid host defined options');
|
|
66
|
+
});
|
|
67
|
+
const result = await validate(name);
|
|
68
|
+
expect(result.counts).toMatchObject({ errors: 0, warnings: 1, info: 0 });
|
|
69
|
+
expect(result.issues[0].requirement.severity).toBe('warning');
|
|
70
|
+
expect(result.issues[0].result.message).toBe('Check failed: Invalid host defined options');
|
|
71
|
+
});
|
|
72
|
+
test('does not turn a hard requirement into a soft one', async () => {
|
|
73
|
+
const name = stepWhoseCheck('error', async () => {
|
|
74
|
+
throw new Error('boom');
|
|
75
|
+
});
|
|
76
|
+
const result = await validate(name);
|
|
77
|
+
// An `error` requirement whose check throws must not be downgraded — that
|
|
78
|
+
// would let a pipeline proceed past a requirement nothing verified.
|
|
79
|
+
expect(result.counts).toMatchObject({ errors: 1, warnings: 0, info: 0 });
|
|
80
|
+
expect(result.valid).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
test('leaves a check that ran and failed at its declared severity', async () => {
|
|
83
|
+
const name = stepWhoseCheck('info', async () => ({
|
|
84
|
+
passed: false,
|
|
85
|
+
message: 'Java 17 not found',
|
|
86
|
+
}));
|
|
87
|
+
const result = await validate(name);
|
|
88
|
+
// The whole point of the distinction: this check *worked*. `info` is the
|
|
89
|
+
// right severity for it and must survive.
|
|
90
|
+
expect(result.counts).toMatchObject({ errors: 0, warnings: 0, info: 1 });
|
|
91
|
+
expect(result.issues[0].result.message).toBe('Java 17 not found');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* The structural half.
|
|
96
|
+
*
|
|
97
|
+
* esbuild leaves a dynamic `import()` of a Node builtin in the bundle — the
|
|
98
|
+
* builtins are external — and `pkg` then cannot evaluate it. Relative
|
|
99
|
+
* specifiers are inlined by the bundler and are unaffected, which is why
|
|
100
|
+
* `envman` and `fidelity-trim` work while the JDK check did not.
|
|
101
|
+
*
|
|
102
|
+
* There is no runtime test that can catch this: it reproduces only in a
|
|
103
|
+
* `pkg`-built binary, and `./test.sh` runs under plain `node`. So the rule is
|
|
104
|
+
* enforced on the source.
|
|
105
|
+
*/
|
|
106
|
+
describe('no source file reaches for a Node builtin through a dynamic import', () => {
|
|
107
|
+
const BUILTIN = /await import\(\s*['"](?:node:)?(fs|path|os|child_process|readline|crypto|util|url|http|https|net|stream|zlib|tty|events)(?:\/[\w-]+)?['"]\s*\)/;
|
|
108
|
+
function sourceFiles(dir) {
|
|
109
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
110
|
+
const full = join(dir, entry);
|
|
111
|
+
if (statSync(full).isDirectory())
|
|
112
|
+
return sourceFiles(full);
|
|
113
|
+
if (!full.endsWith('.ts') || full.endsWith('.test.ts'))
|
|
114
|
+
return [];
|
|
115
|
+
return [full];
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
test('src is clean', () => {
|
|
119
|
+
// Jest runs from the package root; `src` is the whole surface that gets
|
|
120
|
+
// bundled into the released binary.
|
|
121
|
+
const offenders = sourceFiles(join(process.cwd(), 'src'))
|
|
122
|
+
.flatMap((file) => readFileSync(file, 'utf8')
|
|
123
|
+
.split('\n')
|
|
124
|
+
.map((line, i) => ({ file, line: line.trim(), n: i + 1 }))
|
|
125
|
+
.filter(({ line }) => BUILTIN.test(line)))
|
|
126
|
+
.map(({ file, n, line }) => `${file.replace(/.*\/src\//, 'src/')}:${n} ${line}`);
|
|
127
|
+
expect(offenders).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=step-validator-check-failure.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-validator.d.ts","sourceRoot":"","sources":["../../../src/yaml/step-validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,YAAY,EAA4B,MAAM,YAAY,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAGV,wBAAwB,EAGzB,MAAM,uBAAuB,CAAC;AAwB/B;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAC,CAAS;IAG9B,OAAO,CAAC,gBAAgB,CAAoE;IAG5F,OAAO,CAAC,mBAAmB,CAA6B;IAKxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;gBAGvC,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,QAAQ,EAChB,YAAY,CAAC,EAAE,MAAM;IA0BvB;;;OAGG;IACG,gBAAgB,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAkN3D;;OAEG;YACW,mBAAmB;
|
|
1
|
+
{"version":3,"file":"step-validator.d.ts","sourceRoot":"","sources":["../../../src/yaml/step-validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,YAAY,EAA4B,MAAM,YAAY,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAGV,wBAAwB,EAGzB,MAAM,uBAAuB,CAAC;AAwB/B;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAC,CAAS;IAG9B,OAAO,CAAC,gBAAgB,CAAoE;IAG5F,OAAO,CAAC,mBAAmB,CAA6B;IAKxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;gBAGvC,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,QAAQ,EAChB,YAAY,CAAC,EAAE,MAAM;IA0BvB;;;OAGG;IACG,gBAAgB,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAkN3D;;OAEG;YACW,mBAAmB;IAmEjC;;;;;;;;;OASG;IACH,OAAO,CAAC,yBAAyB;IAsBjC;;;;;;;;;OASG;IACH,OAAO,CAAC,mBAAmB;IAoB3B;;OAEG;IACH,OAAO,CAAC,SAAS;CA8BlB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CA4D/E"}
|
|
@@ -280,10 +280,22 @@ export class StepValidator {
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
catch (error) {
|
|
283
|
+
// A check that threw did not run. That is a different event from a
|
|
284
|
+
// check that ran and found nothing, and it must not inherit the
|
|
285
|
+
// severity chosen for the latter: `info` is a reasonable verdict for
|
|
286
|
+
// "looked in several places, found no JDK" and a useless one for
|
|
287
|
+
// "never looked". Reported at `warning` so it is visible on the first
|
|
288
|
+
// build — a check whose own failure reads as information can stay
|
|
289
|
+
// broken for as long as nobody reads carefully.
|
|
290
|
+
//
|
|
291
|
+
// `error` is never downgraded. A hard requirement nothing could verify
|
|
292
|
+
// is not softer than one that was verified and failed.
|
|
283
293
|
return {
|
|
284
294
|
stepName,
|
|
285
295
|
stepIndex,
|
|
286
|
-
requirement
|
|
296
|
+
requirement: requirement.severity === 'error'
|
|
297
|
+
? requirement
|
|
298
|
+
: { ...requirement, severity: 'warning' },
|
|
287
299
|
result: {
|
|
288
300
|
passed: false,
|
|
289
301
|
message: `Check failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Android step implementations (set-java-version, gradle-build, android-lint, android-unit-test)
|
|
3
3
|
*/
|
|
4
|
+
// Static, deliberately. The released macOS binary is built with `pkg`, which
|
|
5
|
+
// cannot evaluate a dynamic `import()` of a builtin — esbuild leaves those
|
|
6
|
+
// external, so they survive into the bundle as real imports and throw
|
|
7
|
+
// `Invalid host defined options` at runtime. A validation check that reaches
|
|
8
|
+
// for one never runs on a runner.
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
4
11
|
import { BaseStepExecutor } from './base.js';
|
|
5
12
|
/**
|
|
6
13
|
* Set Java version step executor
|
|
@@ -28,8 +35,6 @@ export class SetJavaVersionStepExecutor extends BaseStepExecutor {
|
|
|
28
35
|
* Matches the logic in execute() method
|
|
29
36
|
*/
|
|
30
37
|
async checkJavaVersionWithHomebrew(version) {
|
|
31
|
-
const { existsSync } = await import('node:fs');
|
|
32
|
-
const { execSync } = await import('node:child_process');
|
|
33
38
|
// Check Homebrew paths first (same order as execute())
|
|
34
39
|
const homebrewPaths = [
|
|
35
40
|
`/opt/homebrew/opt/openjdk@${version}`, // Apple Silicon
|
|
@@ -48,7 +53,6 @@ export class SetJavaVersionStepExecutor extends BaseStepExecutor {
|
|
|
48
53
|
const jbrPath = '/Applications/Android Studio.app/Contents/jbr/Contents/Home';
|
|
49
54
|
if (existsSync(jbrPath)) {
|
|
50
55
|
try {
|
|
51
|
-
const { readFileSync } = await import('node:fs');
|
|
52
56
|
const releaseContent = readFileSync(`${jbrPath}/release`, 'utf-8');
|
|
53
57
|
const jbrVersionMatch = /JAVA_VERSION="(\d+)/.exec(releaseContent);
|
|
54
58
|
if (jbrVersionMatch && jbrVersionMatch[1] === String(version)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitrise-android-tools.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/bitrise-android-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"bitrise-android-tools.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/bitrise-android-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAAa,+BAAgC,SAAQ,gBAAgB;IACnE,yBAAyB,CACvB,OAAO,EAAE,yBAAyB,EAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IA+C1B,UAAU,IAAI,UAAU,EAAE;IAMpB,OAAO,CAAC,MAAM,EAAE,yBAAyB,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAyJlH"}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Bitrise install-missing-android-tools step implementation
|
|
3
3
|
* Maps Bitrise install-missing-android-tools@* to CI Build Android environment setup
|
|
4
4
|
*/
|
|
5
|
+
// Static — see the note in `android.ts`. A dynamic `import()` of a builtin
|
|
6
|
+
// throws under the `pkg`-built binary the macOS runners use.
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
5
9
|
import { BaseStepExecutor } from './base.js';
|
|
6
10
|
/**
|
|
7
11
|
* Bitrise install-missing-android-tools step executor (FR-3)
|
|
@@ -28,8 +32,6 @@ export class BitriseAndroidToolsStepExecutor extends BaseStepExecutor {
|
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
// Check if we can auto-detect
|
|
31
|
-
const { existsSync } = await import('node:fs');
|
|
32
|
-
const { homedir } = await import('node:os');
|
|
33
35
|
const homeDir = homedir();
|
|
34
36
|
const commonPaths = [
|
|
35
37
|
`${homeDir}/Library/Android/sdk`, // macOS
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { describe, test, expect } from '@jest/globals';
|
|
27
27
|
import { execFile } from 'child_process';
|
|
28
|
+
import { createHash } from 'crypto';
|
|
28
29
|
import { mkdtempSync, writeFileSync, rmSync, mkdirSync, chmodSync } from 'fs';
|
|
29
30
|
import { createServer } from 'http';
|
|
30
31
|
import { join } from 'path';
|
|
@@ -40,6 +41,14 @@ import { testConfigNoPeer } from './test-config.js';
|
|
|
40
41
|
*/
|
|
41
42
|
const run = promisify(execFile);
|
|
42
43
|
const CACHE_KEY = 'pods-abc123def456-release-1.4';
|
|
44
|
+
/**
|
|
45
|
+
* An explicit `cache_key` is prefixed with a project scope so two repositories
|
|
46
|
+
* writing the same key don't share a tarball. These fixtures run outside a git
|
|
47
|
+
* remote, so the step's shell hashes the literal `"no-remote"` — derived here
|
|
48
|
+
* rather than pasted, so it tracks the step.
|
|
49
|
+
*/
|
|
50
|
+
const PROJECT_SCOPE = createHash('sha256').update('no-remote', 'utf8').digest('hex').slice(0, 8);
|
|
51
|
+
const SCOPED_CACHE_KEY = `${PROJECT_SCOPE}-${CACHE_KEY}`;
|
|
43
52
|
async function listen(handler, paths) {
|
|
44
53
|
const server = createServer((req, res) => {
|
|
45
54
|
const path = req.url ?? '/';
|
|
@@ -145,8 +154,8 @@ describe('cache-pull with an explicit key, over the daemon', () => {
|
|
|
145
154
|
// 404 is the daemon's answer for `cache_miss`. The normal case on any
|
|
146
155
|
// first build: it must not start printing warnings.
|
|
147
156
|
const { stdout, paths } = await runPull({ keyStatus: 404 });
|
|
148
|
-
expect(paths).toEqual([`/cache/${
|
|
149
|
-
expect(stdout).toContain(`No cache found for key: ${
|
|
157
|
+
expect(paths).toEqual([`/cache/${SCOPED_CACHE_KEY}.tar.zst`]);
|
|
158
|
+
expect(stdout).toContain(`No cache found for key: ${SCOPED_CACHE_KEY}`);
|
|
150
159
|
expect(stdout).toContain('CACHE_SOURCE=cold');
|
|
151
160
|
expect(stdout).not.toContain('Warning');
|
|
152
161
|
});
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
import { describe, test, expect } from '@jest/globals';
|
|
42
42
|
import { execFile } from 'child_process';
|
|
43
|
+
import { createHash } from 'crypto';
|
|
43
44
|
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, realpathSync, rmSync, chmodSync, } from 'fs';
|
|
44
45
|
import { join } from 'path';
|
|
45
46
|
import { tmpdir } from 'os';
|
|
@@ -48,6 +49,14 @@ import { CachePullStepExecutor } from './cache.js';
|
|
|
48
49
|
import { testConfigNoPeer } from './test-config.js';
|
|
49
50
|
const run = promisify(execFile);
|
|
50
51
|
const CACHE_KEY = 'gradle-abc123def456-fingerprint';
|
|
52
|
+
/**
|
|
53
|
+
* An explicit `cache_key` is prefixed with a project scope so two repositories
|
|
54
|
+
* that write the same key don't share one tarball. These fixtures run outside a
|
|
55
|
+
* git remote, so the step's shell falls back to hashing the literal
|
|
56
|
+
* `"no-remote"` — derived here rather than pasted, so it tracks the step.
|
|
57
|
+
*/
|
|
58
|
+
const PROJECT_SCOPE = createHash('sha256').update('no-remote', 'utf8').digest('hex').slice(0, 8);
|
|
59
|
+
const SCOPED_CACHE_KEY = `${PROJECT_SCOPE}-${CACHE_KEY}`;
|
|
51
60
|
async function restore(shape) {
|
|
52
61
|
// Resolved: see the header — tar will not extract through a symlink, and on
|
|
53
62
|
// macOS the temp root sits behind one.
|
|
@@ -83,7 +92,7 @@ async function restore(shape) {
|
|
|
83
92
|
// `tar -cf - "${PATHS_TO_CACHE[@]}"` produces.
|
|
84
93
|
const cacheDir = join(checkout, '.ci-cache');
|
|
85
94
|
mkdirSync(cacheDir, { recursive: true });
|
|
86
|
-
const tarball = join(cacheDir, `${
|
|
95
|
+
const tarball = join(cacheDir, `${SCOPED_CACHE_KEY}.tar.zst`);
|
|
87
96
|
await run('tar', ['-cf', tarball, ...operands], { cwd: checkout });
|
|
88
97
|
// Remove the originals: "it landed" is only meaningful if it was gone.
|
|
89
98
|
for (const l of landings)
|
|
@@ -25,12 +25,21 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { describe, test, expect } from '@jest/globals';
|
|
27
27
|
import { execFileSync } from 'child_process';
|
|
28
|
+
import { createHash } from 'crypto';
|
|
28
29
|
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, existsSync, chmodSync } from 'fs';
|
|
29
30
|
import { join } from 'path';
|
|
30
31
|
import { tmpdir } from 'os';
|
|
31
32
|
import { CachePushStepExecutor } from './cache.js';
|
|
32
33
|
import { testConfigNoPeer } from './test-config.js';
|
|
33
34
|
const CACHE_KEY = 'pods-abc123def456-main';
|
|
35
|
+
/**
|
|
36
|
+
* An explicit `cache_key` is prefixed with a project scope so two repositories
|
|
37
|
+
* writing the same key don't share a tarball. These fixtures run outside a git
|
|
38
|
+
* remote, so the step's shell hashes the literal `"no-remote"` — derived here
|
|
39
|
+
* rather than pasted, so it tracks the step.
|
|
40
|
+
*/
|
|
41
|
+
const PROJECT_SCOPE = createHash('sha256').update('no-remote', 'utf8').digest('hex').slice(0, 8);
|
|
42
|
+
const SCOPED_CACHE_KEY = `${PROJECT_SCOPE}-${CACHE_KEY}`;
|
|
34
43
|
/**
|
|
35
44
|
* Generates the explicit-key push step, runs it, and reports what `curl` was
|
|
36
45
|
* actually asked to do.
|
|
@@ -107,7 +116,7 @@ describe('cache-push with an explicit key, over the daemon', () => {
|
|
|
107
116
|
test('uploads under the key it was given', async () => {
|
|
108
117
|
const { urls } = await runPush();
|
|
109
118
|
expect(urls).toHaveLength(1);
|
|
110
|
-
expect(urls[0]).toBe(`http://daemon.invalid/cache/${
|
|
119
|
+
expect(urls[0]).toBe(`http://daemon.invalid/cache/${SCOPED_CACHE_KEY}.tar.zst`);
|
|
111
120
|
});
|
|
112
121
|
test('never uploads to an empty key', async () => {
|
|
113
122
|
// The defect, named. `<daemon>/cache/.tar.zst` is what an unassigned
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkBxD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,4HAA4H;IAC5H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sHAAsH;IACtH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAUrD,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkBxD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,4HAA4H;IAC5H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sHAAsH;IACtH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAUrD,CAAC;AA4bF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,gBAAgB;IACnD,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;YAiJzF,iBAAiB;CAkNhC;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,gBAAgB;IACnD,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;YA2KzF,iBAAiB;CAgJhC"}
|
|
@@ -36,7 +36,12 @@ export const CACHE_PRESETS = {
|
|
|
36
36
|
function emitProjectScopeCommands() {
|
|
37
37
|
return [
|
|
38
38
|
'__ci_git_remote=$(git config --get remote.origin.url 2>/dev/null | sed -E "s#(://)[^@/]+@#\\1#" || echo "no-remote")',
|
|
39
|
-
|
|
39
|
+
// `shasum` is Perl-provided and is not guaranteed on a slim Linux image;
|
|
40
|
+
// `sha256sum` is coreutils and is absent from macOS. Both print
|
|
41
|
+
// `<hex> -`, so either yields the same 8-char project id — which is what
|
|
42
|
+
// keeps this byte-identical to `deriveProjectId` in the runner.
|
|
43
|
+
'__ci_sha256() { if command -v shasum >/dev/null 2>&1; then shasum -a 256; else sha256sum; fi; }',
|
|
44
|
+
'__ci_proj=$(printf "%s" "${__ci_git_remote:-no-remote}" | __ci_sha256 | cut -c1-8)',
|
|
40
45
|
];
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
@@ -458,7 +463,6 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
458
463
|
const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
|
|
459
464
|
const commands = [];
|
|
460
465
|
commands.push('# Cache Pull');
|
|
461
|
-
commands.push(`echo "Cache key: ${this.escapeBash(cacheKey)}"`);
|
|
462
466
|
// Create cache directory if it doesn't exist
|
|
463
467
|
commands.push('');
|
|
464
468
|
commands.push('# Ensure cache directory exists');
|
|
@@ -468,11 +472,25 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
468
472
|
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
469
473
|
// whole step under errexit.
|
|
470
474
|
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
475
|
+
// Project scope: an explicit `cache_key` is author-written and carries no
|
|
476
|
+
// repo identity, so two projects that happen to write the same key (e.g.
|
|
477
|
+
// `gradle-{{checksum "gradle/wrapper/gradle-wrapper.properties"}}`, which
|
|
478
|
+
// depends only on the Gradle version) would share one tarball on a shared
|
|
479
|
+
// runner. Prefixing the project id keeps each repo's cache its own.
|
|
480
|
+
//
|
|
481
|
+
// The id goes at the FRONT, deliberately. The preset path spells its key
|
|
482
|
+
// `<keyPrefix>-<projectId>-<checksum>`, and that middle-segment shape is
|
|
483
|
+
// load-bearing elsewhere: it is how a scope is recovered from a tarball
|
|
484
|
+
// filename. An explicit key is author-written and must stay outside the
|
|
485
|
+
// scope model, so it must not be able to imitate that shape by accident.
|
|
486
|
+
commands.push('');
|
|
487
|
+
commands.push(...emitProjectScopeCommands());
|
|
471
488
|
// Generate cache file name based on cache key
|
|
472
489
|
commands.push('');
|
|
473
490
|
commands.push('# Generate cache file path');
|
|
474
|
-
commands.push(`CACHE_KEY="
|
|
491
|
+
commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
|
|
475
492
|
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
493
|
+
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
476
494
|
if (isDebugMode) {
|
|
477
495
|
commands.push('echo "Debug mode enabled"');
|
|
478
496
|
commands.push('echo "Cache file: $CACHE_FILE"');
|
|
@@ -482,7 +500,7 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
482
500
|
if (peerCacheDir) {
|
|
483
501
|
commands.push('');
|
|
484
502
|
commands.push(`PEER_CACHE_DIR="${this.escapeBash(peerCacheDir)}"`);
|
|
485
|
-
commands.push(
|
|
503
|
+
commands.push('PEER_CACHE_FILE="$PEER_CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
486
504
|
}
|
|
487
505
|
// Where each class of member is restored to. Emitted before every
|
|
488
506
|
// transport, because all of them extract.
|
|
@@ -501,7 +519,7 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
501
519
|
commands.push(' echo "CACHE_SOURCE=daemon"');
|
|
502
520
|
commands.push(' else');
|
|
503
521
|
commands.push(...emitDaemonPullFailureDiagnosis(' ', '$CACHE_KEY'));
|
|
504
|
-
commands.push(
|
|
522
|
+
commands.push(' echo "No cache found for key: $CACHE_KEY"');
|
|
505
523
|
commands.push(' echo "CACHE_SOURCE=cold"');
|
|
506
524
|
commands.push(' fi');
|
|
507
525
|
commands.push(' rm -f "$__ci_cache_diag"');
|
|
@@ -522,7 +540,7 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
522
540
|
commands.push(' echo "CACHE_SOURCE=peer"');
|
|
523
541
|
}
|
|
524
542
|
commands.push('else');
|
|
525
|
-
commands.push(
|
|
543
|
+
commands.push(' echo "No cache found for key: $CACHE_KEY"');
|
|
526
544
|
commands.push(' echo "CACHE_SOURCE=cold"');
|
|
527
545
|
commands.push('fi');
|
|
528
546
|
// Closes the daemon/filesystem transport split.
|
|
@@ -766,7 +784,6 @@ export class CachePushStepExecutor extends BaseStepExecutor {
|
|
|
766
784
|
const ignoreCheckOnPaths = this.getInput(inputs, 'ignore_check_on_paths', false);
|
|
767
785
|
const commands = [];
|
|
768
786
|
commands.push('# Cache Push');
|
|
769
|
-
commands.push(`echo "Cache key: ${this.escapeBash(cacheKey)}"`);
|
|
770
787
|
// Create cache directory if it doesn't exist
|
|
771
788
|
commands.push('');
|
|
772
789
|
commands.push('# Ensure cache directory exists');
|
|
@@ -776,14 +793,20 @@ export class CachePushStepExecutor extends BaseStepExecutor {
|
|
|
776
793
|
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
777
794
|
// whole step under errexit.
|
|
778
795
|
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
796
|
+
// Project scope — must match the pull side exactly, or a push writes a
|
|
797
|
+
// tarball no pull can ever find. See the note there for why an explicit
|
|
798
|
+
// key needs the repo identity prefixed.
|
|
799
|
+
commands.push('');
|
|
800
|
+
commands.push(...emitProjectScopeCommands());
|
|
779
801
|
// Generate cache file name based on cache key
|
|
780
802
|
commands.push('');
|
|
781
803
|
commands.push('# Generate cache file path');
|
|
782
804
|
// CACHE_KEY first, CACHE_FILE derived from it — exactly as the pull side
|
|
783
805
|
// does. This step used to set only CACHE_FILE while the daemon URL below
|
|
784
806
|
// interpolated `$CACHE_KEY`; see emitDaemonPushCommands.
|
|
785
|
-
commands.push(`CACHE_KEY="
|
|
807
|
+
commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
|
|
786
808
|
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
809
|
+
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
787
810
|
if (isDebugMode) {
|
|
788
811
|
commands.push('echo "Debug mode enabled"');
|
|
789
812
|
commands.push('echo "Cache file: $CACHE_FILE"');
|