@invarn/cibuild 2.6.3 → 2.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +10 -10
- package/dist/src/yaml/preflight-validation.d.ts +12 -23
- package/dist/src/yaml/preflight-validation.d.ts.map +1 -1
- package/dist/src/yaml/preflight-validation.js +11 -142
- 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/dist/src/yaml/steps/git-clone.d.ts +8 -2
- package/dist/src/yaml/steps/git-clone.d.ts.map +1 -1
- package/dist/src/yaml/steps/git-clone.js +8 -2
- package/dist/src/yaml/steps/index.d.ts.map +1 -1
- package/dist/src/yaml/steps/index.js +9 -5
- package/package.json +1 -1
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pre-flight validation
|
|
3
|
-
*
|
|
2
|
+
* Pre-flight validation hook that runs before any workflow step executes.
|
|
3
|
+
*
|
|
4
|
+
* It no longer checks anything. The checks it used to carry — that
|
|
5
|
+
* CIBUILD_GIT_REPOSITORY_URL is set and reachable, and that activate-ssh-key
|
|
6
|
+
* precedes git-clone — assumed cibuild would clone the repository itself. It
|
|
7
|
+
* does not: the source is already on disk when a pipeline runs (a runner
|
|
8
|
+
* checks out the dispatched commit before cibuild starts; locally it is the
|
|
9
|
+
* working copy), so there is no remote to validate before execution. The
|
|
10
|
+
* class and its `validate()` signature are kept so the call site in
|
|
11
|
+
* pipeline-with-secrets.ts is unchanged.
|
|
4
12
|
*/
|
|
5
|
-
import { MissingEnvironmentVariableError } from './env-resolver.js';
|
|
13
|
+
import type { MissingEnvironmentVariableError } from './env-resolver.js';
|
|
6
14
|
import type { SecretsManager } from './secrets-manager.js';
|
|
7
15
|
import type { YAMLWorkflow } from './types.js';
|
|
8
16
|
/**
|
|
@@ -12,28 +20,9 @@ export interface PreFlightResult {
|
|
|
12
20
|
success: boolean;
|
|
13
21
|
error?: MissingEnvironmentVariableError;
|
|
14
22
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Validates that required environment variables exist and repository is accessible
|
|
17
|
-
* This runs before any workflow steps execute
|
|
18
|
-
*/
|
|
19
23
|
export declare class PreFlightValidator {
|
|
20
24
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param workflow The workflow to check
|
|
23
|
-
* @returns True if SSH key setup is in the workflow
|
|
24
|
-
*/
|
|
25
|
-
private hasSSHKeySetup;
|
|
26
|
-
/**
|
|
27
|
-
* Validates CIBUILD_GIT_REPOSITORY_URL exists and is accessible
|
|
28
|
-
* @param env Environment variables
|
|
29
|
-
* @param secretsManager Secrets manager to check for stored secrets
|
|
30
|
-
* @param workflow The workflow to check for SSH key setup
|
|
31
|
-
* @returns Validation result
|
|
32
|
-
*/
|
|
33
|
-
validateRepositoryAccess(env: Record<string, string>, secretsManager?: SecretsManager, workflow?: YAMLWorkflow): Promise<PreFlightResult>;
|
|
34
|
-
/**
|
|
35
|
-
* Run all pre-flight validations
|
|
36
|
-
* git-clone now uses the local checkout — no remote URL or connectivity check needed.
|
|
25
|
+
* Run all pre-flight validations. Always succeeds — see the file comment.
|
|
37
26
|
*/
|
|
38
27
|
validate(_env: Record<string, string>, _secretsManager?: SecretsManager, _workflow?: YAMLWorkflow): Promise<PreFlightResult>;
|
|
39
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preflight-validation.d.ts","sourceRoot":"","sources":["../../../src/yaml/preflight-validation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"preflight-validation.d.ts","sourceRoot":"","sources":["../../../src/yaml/preflight-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,+BAA+B,CAAC;CACzC;AAED,qBAAa,kBAAkB;IAC7B;;OAEG;IACG,QAAQ,CACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,eAAe,CAAC,EAAE,cAAc,EAChC,SAAS,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,eAAe,CAAC;CAG5B"}
|
|
@@ -1,149 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pre-flight validation
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
2
|
+
* Pre-flight validation hook that runs before any workflow step executes.
|
|
3
|
+
*
|
|
4
|
+
* It no longer checks anything. The checks it used to carry — that
|
|
5
|
+
* CIBUILD_GIT_REPOSITORY_URL is set and reachable, and that activate-ssh-key
|
|
6
|
+
* precedes git-clone — assumed cibuild would clone the repository itself. It
|
|
7
|
+
* does not: the source is already on disk when a pipeline runs (a runner
|
|
8
|
+
* checks out the dispatched commit before cibuild starts; locally it is the
|
|
9
|
+
* working copy), so there is no remote to validate before execution. The
|
|
10
|
+
* class and its `validate()` signature are kept so the call site in
|
|
11
|
+
* pipeline-with-secrets.ts is unchanged.
|
|
10
12
|
*/
|
|
11
13
|
export class PreFlightValidator {
|
|
12
14
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param workflow The workflow to check
|
|
15
|
-
* @returns True if SSH key setup is in the workflow
|
|
16
|
-
*/
|
|
17
|
-
hasSSHKeySetup(workflow) {
|
|
18
|
-
if (!workflow.steps || workflow.steps.length === 0) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
let foundSSHKeyStep = false;
|
|
22
|
-
for (const step of workflow.steps) {
|
|
23
|
-
// Step can be an object with step name as key
|
|
24
|
-
const stepName = typeof step === 'string' ? step : Object.keys(step)[0];
|
|
25
|
-
// Check for git-clone step first - if we hit it before finding SSH key, return false
|
|
26
|
-
if (stepName.startsWith('git-clone')) {
|
|
27
|
-
return foundSSHKeyStep; // Return true only if we found SSH key before this
|
|
28
|
-
}
|
|
29
|
-
// Check for activate-ssh-key step
|
|
30
|
-
if (stepName.startsWith('activate-ssh-key')) {
|
|
31
|
-
foundSSHKeyStep = true;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
// If we got here, no git-clone step was found, so return if SSH key exists
|
|
35
|
-
return foundSSHKeyStep;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Validates CIBUILD_GIT_REPOSITORY_URL exists and is accessible
|
|
39
|
-
* @param env Environment variables
|
|
40
|
-
* @param secretsManager Secrets manager to check for stored secrets
|
|
41
|
-
* @param workflow The workflow to check for SSH key setup
|
|
42
|
-
* @returns Validation result
|
|
43
|
-
*/
|
|
44
|
-
async validateRepositoryAccess(env, secretsManager, workflow) {
|
|
45
|
-
// Check environment variables first, then secrets manager
|
|
46
|
-
let repoUrl = env.CIBUILD_GIT_REPOSITORY_URL || env.GIT_REPOSITORY_URL || '';
|
|
47
|
-
// If not in env, check secrets manager
|
|
48
|
-
if ((!repoUrl || repoUrl.trim() === '') && secretsManager) {
|
|
49
|
-
const secretId = secretsManager.getSecretIdByName('CIBUILD_GIT_REPOSITORY_URL');
|
|
50
|
-
if (secretId) {
|
|
51
|
-
repoUrl = secretsManager.getSecret(secretId) || '';
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Check if repository URL is provided
|
|
55
|
-
if (!repoUrl || repoUrl.trim() === '') {
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
error: new MissingEnvironmentVariableError('CIBUILD_GIT_REPOSITORY_URL', 'preflight-validation', 'Git repository URL for cloning source code in CI/CD builds.\n\n' +
|
|
59
|
-
'Expected format:\n' +
|
|
60
|
-
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
61
|
-
' │ https://github.com/username/repository.git │\n' +
|
|
62
|
-
' │ git@github.com:username/repository.git │\n' +
|
|
63
|
-
' │ https://gitlab.com/username/repository.git │\n' +
|
|
64
|
-
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
65
|
-
'How to set it:\n' +
|
|
66
|
-
' • Environment variable: GIT_REPOSITORY_URL=<your-repo-url>\n' +
|
|
67
|
-
' • In YAML inputs: repository: <your-repo-url>\n' +
|
|
68
|
-
' • CI platform automatically provides this in most cases'),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// If the workflow uses activate-ssh-key, skip connectivity test entirely.
|
|
72
|
-
// SSH authentication isn't available pre-execution, so any network test would fail
|
|
73
|
-
// for private repos regardless of whether the URL is SSH or HTTPS format.
|
|
74
|
-
const hasSSHKeyInWorkflow = workflow ? this.hasSSHKeySetup(workflow) : false;
|
|
75
|
-
if (hasSSHKeyInWorkflow) {
|
|
76
|
-
console.log('🔍 Pre-flight: Validating repository URL...');
|
|
77
|
-
console.log(` Repository: ${repoUrl}`);
|
|
78
|
-
console.log(' SSH authentication will be configured by activate-ssh-key step');
|
|
79
|
-
console.log('✅ Pre-flight: Repository URL validated (connectivity test skipped)\n');
|
|
80
|
-
return { success: true };
|
|
81
|
-
}
|
|
82
|
-
// Test repository connectivity using git ls-remote
|
|
83
|
-
try {
|
|
84
|
-
console.log('🔍 Pre-flight: Validating repository access...');
|
|
85
|
-
console.log(` Repository: ${repoUrl}`);
|
|
86
|
-
// Execute git ls-remote to check if repository is accessible
|
|
87
|
-
// This command lists remote references without cloning
|
|
88
|
-
// Timeout after 10 seconds to prevent hanging
|
|
89
|
-
execSync(`git ls-remote "${repoUrl}" HEAD`, {
|
|
90
|
-
stdio: 'pipe',
|
|
91
|
-
timeout: 10000,
|
|
92
|
-
encoding: 'utf-8',
|
|
93
|
-
});
|
|
94
|
-
console.log('✅ Pre-flight: Repository is accessible\n');
|
|
95
|
-
return { success: true };
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
// Repository is not accessible - analyze the error
|
|
99
|
-
const errorMessage = error.message || String(error);
|
|
100
|
-
console.log(`❌ Pre-flight: Repository access failed`);
|
|
101
|
-
console.log(` Error: ${errorMessage}\n`);
|
|
102
|
-
// Check if this is an SSH authentication error
|
|
103
|
-
const isSSHAuthError = errorMessage.includes('Permission denied') &&
|
|
104
|
-
errorMessage.includes('publickey');
|
|
105
|
-
const isSSHUrl = repoUrl.startsWith('git@') || repoUrl.includes('ssh://');
|
|
106
|
-
if (isSSHAuthError || (isSSHUrl && errorMessage.includes('Could not read from remote'))) {
|
|
107
|
-
// SSH authentication is the issue, not the repository URL
|
|
108
|
-
console.log('⚠️ Repository URL is valid, but SSH authentication is required.\n');
|
|
109
|
-
console.log('Please ensure:');
|
|
110
|
-
console.log(' 1. SSH key is configured in your workflow (activate-ssh-key step)');
|
|
111
|
-
console.log(' 2. SSH key has access to this repository');
|
|
112
|
-
console.log(' 3. SSH key is added to your GitHub/GitLab account\n');
|
|
113
|
-
throw new Error('Pre-flight validation failed: SSH authentication required.\n\n' +
|
|
114
|
-
`Repository: ${repoUrl}\n\n` +
|
|
115
|
-
'The repository URL is valid but requires SSH authentication.\n' +
|
|
116
|
-
'Make sure your workflow includes an "activate-ssh-key" step before "git-clone".\n\n' +
|
|
117
|
-
'Example workflow:\n' +
|
|
118
|
-
' steps:\n' +
|
|
119
|
-
' - activate-ssh-key@4: # Add SSH key first\n' +
|
|
120
|
-
' inputs:\n' +
|
|
121
|
-
' ssh_key_save_path: $HOME/.ssh/bitrise_rsa\n' +
|
|
122
|
-
' - git-clone@6: # Then clone\n\n' +
|
|
123
|
-
`Error details: ${errorMessage}`);
|
|
124
|
-
}
|
|
125
|
-
// For other errors (invalid URL, network issues, etc.), return error
|
|
126
|
-
return {
|
|
127
|
-
success: false,
|
|
128
|
-
error: new MissingEnvironmentVariableError('CIBUILD_GIT_REPOSITORY_URL', 'preflight-validation', 'Git repository URL validation failed. The repository is not accessible.\n\n' +
|
|
129
|
-
'Expected format:\n' +
|
|
130
|
-
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
131
|
-
' │ https://github.com/username/repository.git │\n' +
|
|
132
|
-
' │ git@github.com:username/repository.git │\n' +
|
|
133
|
-
' │ https://gitlab.com/username/repository.git │\n' +
|
|
134
|
-
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
135
|
-
'Common issues:\n' +
|
|
136
|
-
' • Invalid repository URL\n' +
|
|
137
|
-
' • Network connectivity issues\n' +
|
|
138
|
-
' • Repository does not exist or is private\n' +
|
|
139
|
-
' • Wrong URL format (check for typos)\n\n' +
|
|
140
|
-
`Error details: ${errorMessage}`),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Run all pre-flight validations
|
|
146
|
-
* git-clone now uses the local checkout — no remote URL or connectivity check needed.
|
|
15
|
+
* Run all pre-flight validations. Always succeeds — see the file comment.
|
|
147
16
|
*/
|
|
148
17
|
async validate(_env, _secretsManager, _workflow) {
|
|
149
18
|
return { success: true };
|
|
@@ -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"');
|
|
@@ -15,8 +15,14 @@ export interface GitCloneInputs {
|
|
|
15
15
|
clone_into_dir?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* git-clone step executor.
|
|
19
|
+
*
|
|
20
|
+
* Does not clone. The source is already on disk when this runs — on a runner
|
|
21
|
+
* the dispatched commit was checked out before cibuild started (see
|
|
22
|
+
* cibuild-runner's GitCloneRunner); locally it is the developer's working copy.
|
|
23
|
+
* The step locates that checkout and republishes its commit metadata. Every
|
|
24
|
+
* input in GitCloneInputs is accepted so existing YAML keeps parsing, and none
|
|
25
|
+
* of them is read.
|
|
20
26
|
*/
|
|
21
27
|
export declare class GitCloneStepExecutor extends BaseStepExecutor {
|
|
22
28
|
getValidationRequirements(_inputs: GitCloneInputs, _env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-clone.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/git-clone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"git-clone.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/git-clone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;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;IAU1B,UAAU,IAAI,UAAU,EAAE;IAkDpB,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAqD1G"}
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { BaseStepExecutor } from './base.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* git-clone step executor.
|
|
7
|
+
*
|
|
8
|
+
* Does not clone. The source is already on disk when this runs — on a runner
|
|
9
|
+
* the dispatched commit was checked out before cibuild started (see
|
|
10
|
+
* cibuild-runner's GitCloneRunner); locally it is the developer's working copy.
|
|
11
|
+
* The step locates that checkout and republishes its commit metadata. Every
|
|
12
|
+
* input in GitCloneInputs is accepted so existing YAML keeps parsing, and none
|
|
13
|
+
* of them is read.
|
|
8
14
|
*/
|
|
9
15
|
export class GitCloneStepExecutor extends BaseStepExecutor {
|
|
10
16
|
getValidationRequirements(_inputs, _env, _config) {
|
|
@@ -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,
|
|
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"}
|
|
@@ -32,28 +32,32 @@ import { UiFidelityRenderAndroidStepExecutor, DEFAULT_GRADLE_TASK, DEFAULT_PACKA
|
|
|
32
32
|
*/
|
|
33
33
|
export function initializeStepRegistry() {
|
|
34
34
|
// Git steps
|
|
35
|
+
// Does not clone. The source is already on disk when any step runs — on a
|
|
36
|
+
// runner the dispatched commit is checked out before cibuild starts; locally
|
|
37
|
+
// it is the developer's working copy. The inputs are kept so existing YAML
|
|
38
|
+
// keeps parsing; the executor never reads them.
|
|
35
39
|
registerStep('git-clone', new GitCloneStepExecutor(), {
|
|
36
40
|
name: 'git-clone',
|
|
37
|
-
description: '
|
|
41
|
+
description: 'Locate the source checkout already on disk and record its commit. Does not clone — on a runner the dispatched commit is checked out before any step runs; locally it is your working copy. Inputs are ignored.',
|
|
38
42
|
platform: 'all',
|
|
39
43
|
inputs: {
|
|
40
44
|
repository: {
|
|
41
|
-
description: '
|
|
45
|
+
description: 'Ignored — no clone is performed; the checkout already exists',
|
|
42
46
|
required: false,
|
|
43
47
|
default: '$CIBUILD_GIT_REPOSITORY_URL',
|
|
44
48
|
},
|
|
45
49
|
branch: {
|
|
46
|
-
description: '
|
|
50
|
+
description: 'Ignored — on a runner the dispatched ref is checked out, not this value',
|
|
47
51
|
required: false,
|
|
48
52
|
default: '$CIBUILD_GIT_BRANCH or main',
|
|
49
53
|
},
|
|
50
54
|
clone_depth: {
|
|
51
|
-
description: '
|
|
55
|
+
description: 'Ignored — no clone is performed; a runner checkout is always depth 1',
|
|
52
56
|
required: false,
|
|
53
57
|
default: 0,
|
|
54
58
|
},
|
|
55
59
|
clone_into_dir: {
|
|
56
|
-
description: '
|
|
60
|
+
description: 'Ignored — the checkout location is not chosen by this step',
|
|
57
61
|
required: false,
|
|
58
62
|
default: '.',
|
|
59
63
|
},
|