@invarn/cibuild 1.3.16 ā 1.3.17
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 +1 -1
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +987 -0
- package/dist/src/commands/android-scanner.d.ts +32 -0
- package/dist/src/commands/android-scanner.d.ts.map +1 -0
- package/dist/src/commands/android-scanner.js +667 -0
- package/dist/src/commands/build.d.ts +5 -0
- package/dist/src/commands/build.d.ts.map +1 -0
- package/dist/src/commands/build.js +1096 -0
- package/dist/src/commands/edit.d.ts +3 -0
- package/dist/src/commands/edit.d.ts.map +1 -0
- package/dist/src/commands/edit.js +651 -0
- package/dist/src/commands/file-secret-collector.d.ts +37 -0
- package/dist/src/commands/file-secret-collector.d.ts.map +1 -0
- package/dist/src/commands/file-secret-collector.js +199 -0
- package/dist/src/commands/github-workflow.d.ts +5 -0
- package/dist/src/commands/github-workflow.d.ts.map +1 -0
- package/dist/src/commands/github-workflow.js +45 -0
- package/dist/src/commands/ios-scanner.d.ts +27 -0
- package/dist/src/commands/ios-scanner.d.ts.map +1 -0
- package/dist/src/commands/ios-scanner.js +337 -0
- package/dist/src/commands/reset.d.ts +7 -0
- package/dist/src/commands/reset.d.ts.map +1 -0
- package/dist/src/commands/reset.js +81 -0
- package/dist/src/commands/secrets-sync-workflow.d.ts +15 -0
- package/dist/src/commands/secrets-sync-workflow.d.ts.map +1 -0
- package/dist/src/commands/secrets-sync-workflow.js +255 -0
- package/dist/src/commands/secrets-upload.d.ts +21 -0
- package/dist/src/commands/secrets-upload.d.ts.map +1 -0
- package/dist/src/commands/secrets-upload.js +177 -0
- package/dist/src/commands/secrets-upload.test.d.ts +5 -0
- package/dist/src/commands/secrets-upload.test.d.ts.map +1 -0
- package/dist/src/commands/secrets-upload.test.js +60 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +46 -0
- package/dist/src/envman/cli.d.ts +21 -0
- package/dist/src/envman/cli.d.ts.map +1 -0
- package/dist/src/envman/cli.js +240 -0
- package/dist/src/envman/envman.d.ts +83 -0
- package/dist/src/envman/envman.d.ts.map +1 -0
- package/dist/src/envman/envman.js +361 -0
- package/dist/src/envman/envman.test.d.ts +5 -0
- package/dist/src/envman/envman.test.d.ts.map +1 -0
- package/dist/src/envman/envman.test.js +236 -0
- package/dist/src/envman/index.d.ts +23 -0
- package/dist/src/envman/index.d.ts.map +1 -0
- package/dist/src/envman/index.js +23 -0
- package/dist/src/envman/types.d.ts +55 -0
- package/dist/src/envman/types.d.ts.map +1 -0
- package/dist/src/envman/types.js +12 -0
- package/dist/src/lib.d.ts +27 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/lib.js +32 -0
- package/dist/src/pipeline.d.ts +3 -0
- package/dist/src/pipeline.d.ts.map +1 -0
- package/dist/src/pipeline.js +57 -0
- package/dist/src/runner.d.ts +17 -0
- package/dist/src/runner.d.ts.map +1 -0
- package/dist/src/runner.js +234 -0
- package/dist/src/types.d.ts +57 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/yaml/bitrise-compat.d.ts +65 -0
- package/dist/src/yaml/bitrise-compat.d.ts.map +1 -0
- package/dist/src/yaml/bitrise-compat.js +206 -0
- package/dist/src/yaml/bitrise-compat.test.d.ts +5 -0
- package/dist/src/yaml/bitrise-compat.test.d.ts.map +1 -0
- package/dist/src/yaml/bitrise-compat.test.js +347 -0
- package/dist/src/yaml/converter.d.ts +33 -0
- package/dist/src/yaml/converter.d.ts.map +1 -0
- package/dist/src/yaml/converter.js +222 -0
- package/dist/src/yaml/converter.test.d.ts +5 -0
- package/dist/src/yaml/converter.test.d.ts.map +1 -0
- package/dist/src/yaml/converter.test.js +348 -0
- package/dist/src/yaml/e2e.test.d.ts +6 -0
- package/dist/src/yaml/e2e.test.d.ts.map +1 -0
- package/dist/src/yaml/e2e.test.js +446 -0
- package/dist/src/yaml/env-resolver.d.ts +120 -0
- package/dist/src/yaml/env-resolver.d.ts.map +1 -0
- package/dist/src/yaml/env-resolver.js +405 -0
- package/dist/src/yaml/env-resolver.test.d.ts +5 -0
- package/dist/src/yaml/env-resolver.test.d.ts.map +1 -0
- package/dist/src/yaml/env-resolver.test.js +502 -0
- package/dist/src/yaml/interactive-prompts.d.ts +71 -0
- package/dist/src/yaml/interactive-prompts.d.ts.map +1 -0
- package/dist/src/yaml/interactive-prompts.js +258 -0
- package/dist/src/yaml/missing-env-handler.d.ts +45 -0
- package/dist/src/yaml/missing-env-handler.d.ts.map +1 -0
- package/dist/src/yaml/missing-env-handler.js +64 -0
- package/dist/src/yaml/parser.d.ts +33 -0
- package/dist/src/yaml/parser.d.ts.map +1 -0
- package/dist/src/yaml/parser.js +145 -0
- package/dist/src/yaml/pipeline-with-secrets.d.ts +25 -0
- package/dist/src/yaml/pipeline-with-secrets.d.ts.map +1 -0
- package/dist/src/yaml/pipeline-with-secrets.js +76 -0
- package/dist/src/yaml/platform-detector.d.ts +83 -0
- package/dist/src/yaml/platform-detector.d.ts.map +1 -0
- package/dist/src/yaml/platform-detector.js +188 -0
- package/dist/src/yaml/platform-detector.test.d.ts +5 -0
- package/dist/src/yaml/platform-detector.test.d.ts.map +1 -0
- package/dist/src/yaml/platform-detector.test.js +414 -0
- package/dist/src/yaml/preflight-validation.d.ts +40 -0
- package/dist/src/yaml/preflight-validation.d.ts.map +1 -0
- package/dist/src/yaml/preflight-validation.js +152 -0
- package/dist/src/yaml/secrets-manager.d.ts +77 -0
- package/dist/src/yaml/secrets-manager.d.ts.map +1 -0
- package/dist/src/yaml/secrets-manager.js +219 -0
- package/dist/src/yaml/step-validator.d.ts +54 -0
- package/dist/src/yaml/step-validator.d.ts.map +1 -0
- package/dist/src/yaml/step-validator.js +403 -0
- package/dist/src/yaml/steps/android-sign.d.ts +35 -0
- package/dist/src/yaml/steps/android-sign.d.ts.map +1 -0
- package/dist/src/yaml/steps/android-sign.js +147 -0
- package/dist/src/yaml/steps/android-version.d.ts +26 -0
- package/dist/src/yaml/steps/android-version.d.ts.map +1 -0
- package/dist/src/yaml/steps/android-version.js +128 -0
- package/dist/src/yaml/steps/android-version.test.d.ts +5 -0
- package/dist/src/yaml/steps/android-version.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/android-version.test.js +196 -0
- package/dist/src/yaml/steps/android.d.ts +95 -0
- package/dist/src/yaml/steps/android.d.ts.map +1 -0
- package/dist/src/yaml/steps/android.js +916 -0
- package/dist/src/yaml/steps/app-store-deploy.d.ts +48 -0
- package/dist/src/yaml/steps/app-store-deploy.d.ts.map +1 -0
- package/dist/src/yaml/steps/app-store-deploy.js +162 -0
- package/dist/src/yaml/steps/base.d.ts +238 -0
- package/dist/src/yaml/steps/base.d.ts.map +1 -0
- package/dist/src/yaml/steps/base.js +345 -0
- package/dist/src/yaml/steps/bitrise-android-tools.d.ts +26 -0
- package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-android-tools.js +198 -0
- package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +5 -0
- package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-android-tools.test.js +280 -0
- package/dist/src/yaml/steps/bitrise-apk-info.d.ts +22 -0
- package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-apk-info.js +144 -0
- package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +5 -0
- package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-apk-info.test.js +331 -0
- package/dist/src/yaml/steps/bitrise-slack.d.ts +49 -0
- package/dist/src/yaml/steps/bitrise-slack.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-slack.js +280 -0
- package/dist/src/yaml/steps/bitrise-slack.test.d.ts +5 -0
- package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-slack.test.js +484 -0
- package/dist/src/yaml/steps/bitrise-ssh.d.ts +27 -0
- package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-ssh.js +134 -0
- package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +5 -0
- package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/bitrise-ssh.test.js +205 -0
- package/dist/src/yaml/steps/cache.d.ts +52 -0
- package/dist/src/yaml/steps/cache.d.ts.map +1 -0
- package/dist/src/yaml/steps/cache.js +351 -0
- package/dist/src/yaml/steps/fastlane.d.ts +27 -0
- package/dist/src/yaml/steps/fastlane.d.ts.map +1 -0
- package/dist/src/yaml/steps/fastlane.js +79 -0
- package/dist/src/yaml/steps/file.d.ts +27 -0
- package/dist/src/yaml/steps/file.d.ts.map +1 -0
- package/dist/src/yaml/steps/file.js +35 -0
- package/dist/src/yaml/steps/flutter.d.ts +63 -0
- package/dist/src/yaml/steps/flutter.d.ts.map +1 -0
- package/dist/src/yaml/steps/flutter.js +215 -0
- package/dist/src/yaml/steps/git-clone.d.ts +26 -0
- package/dist/src/yaml/steps/git-clone.d.ts.map +1 -0
- package/dist/src/yaml/steps/git-clone.js +111 -0
- package/dist/src/yaml/steps/google-play-deploy.d.ts +37 -0
- package/dist/src/yaml/steps/google-play-deploy.d.ts.map +1 -0
- package/dist/src/yaml/steps/google-play-deploy.js +193 -0
- package/dist/src/yaml/steps/google-play-deploy.test.d.ts +5 -0
- package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/google-play-deploy.test.js +310 -0
- package/dist/src/yaml/steps/index.d.ts +10 -0
- package/dist/src/yaml/steps/index.d.ts.map +1 -0
- package/dist/src/yaml/steps/index.js +1361 -0
- package/dist/src/yaml/steps/ios-deps.d.ts +43 -0
- package/dist/src/yaml/steps/ios-deps.d.ts.map +1 -0
- package/dist/src/yaml/steps/ios-deps.js +141 -0
- package/dist/src/yaml/steps/ios-deps.test.d.ts +5 -0
- package/dist/src/yaml/steps/ios-deps.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/ios-deps.test.js +90 -0
- package/dist/src/yaml/steps/ios-signing.d.ts +31 -0
- package/dist/src/yaml/steps/ios-signing.d.ts.map +1 -0
- package/dist/src/yaml/steps/ios-signing.js +144 -0
- package/dist/src/yaml/steps/ios-version.d.ts +47 -0
- package/dist/src/yaml/steps/ios-version.d.ts.map +1 -0
- package/dist/src/yaml/steps/ios-version.js +151 -0
- package/dist/src/yaml/steps/linting.d.ts +47 -0
- package/dist/src/yaml/steps/linting.d.ts.map +1 -0
- package/dist/src/yaml/steps/linting.js +148 -0
- package/dist/src/yaml/steps/phase2.test.d.ts +6 -0
- package/dist/src/yaml/steps/phase2.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/phase2.test.js +197 -0
- package/dist/src/yaml/steps/phase3.test.d.ts +5 -0
- package/dist/src/yaml/steps/phase3.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/phase3.test.js +144 -0
- package/dist/src/yaml/steps/phase4.test.d.ts +5 -0
- package/dist/src/yaml/steps/phase4.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/phase4.test.js +166 -0
- package/dist/src/yaml/steps/phase5.test.d.ts +6 -0
- package/dist/src/yaml/steps/phase5.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/phase5.test.js +263 -0
- package/dist/src/yaml/steps/registry.d.ts +88 -0
- package/dist/src/yaml/steps/registry.d.ts.map +1 -0
- package/dist/src/yaml/steps/registry.js +125 -0
- package/dist/src/yaml/steps/registry.test.d.ts +5 -0
- package/dist/src/yaml/steps/registry.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/registry.test.js +235 -0
- package/dist/src/yaml/steps/release.d.ts +50 -0
- package/dist/src/yaml/steps/release.d.ts.map +1 -0
- package/dist/src/yaml/steps/release.js +154 -0
- package/dist/src/yaml/steps/script.d.ts +23 -0
- package/dist/src/yaml/steps/script.d.ts.map +1 -0
- package/dist/src/yaml/steps/script.js +63 -0
- package/dist/src/yaml/steps/spec-validation.test.d.ts +6 -0
- package/dist/src/yaml/steps/spec-validation.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/spec-validation.test.js +130 -0
- package/dist/src/yaml/steps/steps.test.d.ts +6 -0
- package/dist/src/yaml/steps/steps.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/steps.test.js +474 -0
- package/dist/src/yaml/steps/test-config.d.ts +3 -0
- package/dist/src/yaml/steps/test-config.d.ts.map +1 -0
- package/dist/src/yaml/steps/test-config.js +16 -0
- package/dist/src/yaml/steps/xcode-new.test.d.ts +5 -0
- package/dist/src/yaml/steps/xcode-new.test.d.ts.map +1 -0
- package/dist/src/yaml/steps/xcode-new.test.js +211 -0
- package/dist/src/yaml/steps/xcode.d.ts +222 -0
- package/dist/src/yaml/steps/xcode.d.ts.map +1 -0
- package/dist/src/yaml/steps/xcode.js +999 -0
- package/dist/src/yaml/types.d.ts +68 -0
- package/dist/src/yaml/types.d.ts.map +1 -0
- package/dist/src/yaml/types.js +5 -0
- package/dist/src/yaml/validation-types.d.ts +96 -0
- package/dist/src/yaml/validation-types.d.ts.map +1 -0
- package/dist/src/yaml/validation-types.js +8 -0
- package/dist/src/yaml/yaml-updater.d.ts +24 -0
- package/dist/src/yaml/yaml-updater.d.ts.map +1 -0
- package/dist/src/yaml/yaml-updater.js +128 -0
- package/package.json +16 -4
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for missing environment variables
|
|
3
|
+
* Allows users to fill in missing values or cancel the build
|
|
4
|
+
*/
|
|
5
|
+
import prompts from 'prompts';
|
|
6
|
+
/**
|
|
7
|
+
* Interactive prompt manager for collecting missing environment variables
|
|
8
|
+
*/
|
|
9
|
+
export class InteractivePrompts {
|
|
10
|
+
secretsManager;
|
|
11
|
+
workflow;
|
|
12
|
+
constructor(secretsManager, workflow) {
|
|
13
|
+
this.secretsManager = secretsManager;
|
|
14
|
+
this.workflow = workflow;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Prompt user for a missing environment variable
|
|
18
|
+
* @param varName Variable name
|
|
19
|
+
* @param stepName Optional step name that requires this variable
|
|
20
|
+
* @param hint Optional hint describing what the variable is for and expected format
|
|
21
|
+
* @returns Promise with the entered value or cancellation
|
|
22
|
+
*/
|
|
23
|
+
async promptForVariable(varName, stepName, hint) {
|
|
24
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
25
|
+
console.log('ā MISSING REQUIRED ENVIRONMENT VARIABLE ā');
|
|
26
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
27
|
+
console.log(`Variable: ${varName}`);
|
|
28
|
+
if (stepName) {
|
|
29
|
+
console.log(`Required by: ${stepName} step`);
|
|
30
|
+
}
|
|
31
|
+
// Display hint if provided
|
|
32
|
+
if (hint) {
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log('š” Hint:');
|
|
35
|
+
// Process hint lines - preserve formatting, only wrap plain text
|
|
36
|
+
const hintLines = this.processHintText(hint, 67);
|
|
37
|
+
hintLines.forEach(line => console.log(line));
|
|
38
|
+
}
|
|
39
|
+
console.log('');
|
|
40
|
+
// Check if this variable is already in secrets (workflow-scoped first, then global)
|
|
41
|
+
const existingSecretId = this.secretsManager.getSecretIdByName(varName, this.workflow);
|
|
42
|
+
if (existingSecretId) {
|
|
43
|
+
const secretName = this.secretsManager.getSecretName(existingSecretId);
|
|
44
|
+
const workflowInfo = this.workflow ? ` (workflow: ${this.workflow})` : ' (global)';
|
|
45
|
+
console.log(`\nā¹ļø This variable already exists in your secrets file${workflowInfo}.`);
|
|
46
|
+
const useExisting = await this.promptYesNo('Use existing value?');
|
|
47
|
+
if (useExisting) {
|
|
48
|
+
const value = this.secretsManager.getSecret(existingSecretId);
|
|
49
|
+
if (value) {
|
|
50
|
+
return { value, cancelled: false };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
console.log('');
|
|
55
|
+
// Show interactive menu
|
|
56
|
+
const menuResponse = await prompts({
|
|
57
|
+
type: 'select',
|
|
58
|
+
name: 'action',
|
|
59
|
+
message: 'What would you like to do?',
|
|
60
|
+
choices: [
|
|
61
|
+
{ title: 'Enter value now (will be saved to secrets file)', value: 'enter' },
|
|
62
|
+
{ title: 'Copy prompt for AI (get guidance from AI assistant)', value: 'ai' },
|
|
63
|
+
{ title: 'Cancel build', value: 'cancel' }
|
|
64
|
+
],
|
|
65
|
+
initial: 0,
|
|
66
|
+
});
|
|
67
|
+
if (!menuResponse.action || menuResponse.action === 'cancel') {
|
|
68
|
+
return { value: '', cancelled: true };
|
|
69
|
+
}
|
|
70
|
+
if (menuResponse.action === 'ai') {
|
|
71
|
+
// Copy AI prompt to clipboard
|
|
72
|
+
if (hint) {
|
|
73
|
+
const aiPrompt = this.generateAIPrompt(hint);
|
|
74
|
+
await this.copyToClipboard(aiPrompt);
|
|
75
|
+
console.log('\nā
AI prompt copied to clipboard!');
|
|
76
|
+
console.log('š Paste it into your AI assistant to get guidance.\n');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log('\nā ļø No hint available for this variable.\n');
|
|
80
|
+
}
|
|
81
|
+
return { value: '', cancelled: true, copiedToAI: true };
|
|
82
|
+
}
|
|
83
|
+
// Prompt for value
|
|
84
|
+
const isSensitive = this.isSensitiveVariable(varName);
|
|
85
|
+
console.log('');
|
|
86
|
+
if (isSensitive) {
|
|
87
|
+
console.log('ā ļø This appears to be a sensitive value (input will be hidden)\n');
|
|
88
|
+
}
|
|
89
|
+
const valueResponse = await prompts({
|
|
90
|
+
type: isSensitive ? 'password' : 'text',
|
|
91
|
+
name: 'value',
|
|
92
|
+
message: `Enter value for ${varName}:`,
|
|
93
|
+
validate: value => value.trim() === '' ? 'Value cannot be empty' : true
|
|
94
|
+
});
|
|
95
|
+
if (!valueResponse.value || valueResponse.value.trim() === '') {
|
|
96
|
+
console.log('ā Error: Value cannot be empty');
|
|
97
|
+
return { value: '', cancelled: true };
|
|
98
|
+
}
|
|
99
|
+
return { value: valueResponse.value, cancelled: false };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Determine if a variable name suggests it's sensitive
|
|
103
|
+
* @param varName Variable name
|
|
104
|
+
* @returns True if variable appears to be sensitive
|
|
105
|
+
*/
|
|
106
|
+
isSensitiveVariable(varName) {
|
|
107
|
+
const sensitivePatterns = [
|
|
108
|
+
/KEY/i,
|
|
109
|
+
/SECRET/i,
|
|
110
|
+
/PASSWORD/i,
|
|
111
|
+
/TOKEN/i,
|
|
112
|
+
/API/i,
|
|
113
|
+
/WEBHOOK/i,
|
|
114
|
+
/PRIVATE/i,
|
|
115
|
+
/CREDENTIAL/i,
|
|
116
|
+
];
|
|
117
|
+
return sensitivePatterns.some((pattern) => pattern.test(varName));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Prompt for yes/no confirmation
|
|
121
|
+
* @param message Prompt message
|
|
122
|
+
* @returns Promise with true for yes, false for no
|
|
123
|
+
*/
|
|
124
|
+
async promptYesNo(message) {
|
|
125
|
+
const response = await prompts({
|
|
126
|
+
type: 'confirm',
|
|
127
|
+
name: 'value',
|
|
128
|
+
message,
|
|
129
|
+
initial: true,
|
|
130
|
+
});
|
|
131
|
+
return response.value ?? false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Process hint text to preserve formatting while wrapping plain text
|
|
135
|
+
* @param hint Hint text (may contain newlines and formatted sections)
|
|
136
|
+
* @param maxWidth Maximum width for wrapping plain text lines
|
|
137
|
+
* @returns Array of processed lines ready for display
|
|
138
|
+
*/
|
|
139
|
+
processHintText(hint, maxWidth) {
|
|
140
|
+
const lines = hint.split('\n');
|
|
141
|
+
const result = [];
|
|
142
|
+
for (const line of lines) {
|
|
143
|
+
// Check if this line is formatted (contains box chars, bullets, or is indented)
|
|
144
|
+
const isFormatted = line.includes('ā') || line.includes('ā') ||
|
|
145
|
+
line.includes('ā') || line.includes('ā') ||
|
|
146
|
+
line.includes('ā') || line.includes('ā') ||
|
|
147
|
+
line.trimStart().startsWith('ā¢') ||
|
|
148
|
+
(line.startsWith(' ') && line.trim().length > 0);
|
|
149
|
+
if (isFormatted || line.trim() === '') {
|
|
150
|
+
// Preserve formatted lines and empty lines as-is with indent
|
|
151
|
+
result.push(` ${line}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Wrap plain text lines
|
|
155
|
+
const wrapped = this.wrapText(line, maxWidth);
|
|
156
|
+
wrapped.forEach(wrappedLine => result.push(` ${wrappedLine}`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Wrap text to fit within a specified width
|
|
163
|
+
* @param text Text to wrap
|
|
164
|
+
* @param maxWidth Maximum width per line
|
|
165
|
+
* @returns Array of wrapped lines
|
|
166
|
+
*/
|
|
167
|
+
wrapText(text, maxWidth) {
|
|
168
|
+
const words = text.split(' ');
|
|
169
|
+
const lines = [];
|
|
170
|
+
let currentLine = '';
|
|
171
|
+
for (const word of words) {
|
|
172
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
173
|
+
if (testLine.length <= maxWidth) {
|
|
174
|
+
currentLine = testLine;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
if (currentLine) {
|
|
178
|
+
lines.push(currentLine);
|
|
179
|
+
}
|
|
180
|
+
currentLine = word;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (currentLine) {
|
|
184
|
+
lines.push(currentLine);
|
|
185
|
+
}
|
|
186
|
+
return lines.length > 0 ? lines : [''];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Generate AI prompt with hint for getting guidance
|
|
190
|
+
* @param hint Hint text from the step
|
|
191
|
+
* @returns Formatted AI prompt
|
|
192
|
+
*/
|
|
193
|
+
generateAIPrompt(hint) {
|
|
194
|
+
return `This is a mobile CI/CD build step. Provide a brief, senior-level explanation of its purpose and when it is used. Do not execute anything.
|
|
195
|
+
|
|
196
|
+
${hint}
|
|
197
|
+
|
|
198
|
+
Instructions for the assistant:
|
|
199
|
+
- Give a short purpose summary (what it enables in CI/CD).
|
|
200
|
+
- List only the required setup/usage steps.
|
|
201
|
+
- Include example commands if relevant (e.g., how to generate, retrieve, or configure the value).
|
|
202
|
+
- Include key security handling notes (storage, scope, rotation).
|
|
203
|
+
- Skip beginner explanations and background theory.
|
|
204
|
+
- Do not request or expose real secret values.
|
|
205
|
+
- Do not simulate execution.
|
|
206
|
+
- Ask only the minimum clarification questions required to avoid provider-specific mistakes. If setup differs by CI system, Git provider, signing provider, artifact host, or mobile platform ā ask first; otherwise answer directly.
|
|
207
|
+
|
|
208
|
+
Finish with a short "Follow-up questions" numbered list only if clarification would change the setup guidance.`;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Copy text to system clipboard
|
|
212
|
+
* @param text Text to copy
|
|
213
|
+
*/
|
|
214
|
+
async copyToClipboard(text) {
|
|
215
|
+
const { spawn } = await import('child_process');
|
|
216
|
+
return new Promise((resolve, reject) => {
|
|
217
|
+
// Detect platform and use appropriate clipboard command
|
|
218
|
+
const platform = process.platform;
|
|
219
|
+
let clipboardProcess;
|
|
220
|
+
if (platform === 'darwin') {
|
|
221
|
+
// macOS
|
|
222
|
+
clipboardProcess = spawn('pbcopy');
|
|
223
|
+
}
|
|
224
|
+
else if (platform === 'linux') {
|
|
225
|
+
// Linux - try xclip first, then xsel
|
|
226
|
+
clipboardProcess = spawn('xclip', ['-selection', 'clipboard']);
|
|
227
|
+
}
|
|
228
|
+
else if (platform === 'win32') {
|
|
229
|
+
// Windows
|
|
230
|
+
clipboardProcess = spawn('clip');
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
reject(new Error(`Clipboard not supported on platform: ${platform}`));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
clipboardProcess.stdin.write(text);
|
|
237
|
+
clipboardProcess.stdin.end();
|
|
238
|
+
clipboardProcess.on('close', (code) => {
|
|
239
|
+
if (code === 0) {
|
|
240
|
+
resolve();
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
reject(new Error(`Clipboard command failed with code ${code}`));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
clipboardProcess.on('error', (error) => {
|
|
247
|
+
reject(error);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Close the prompts interface (no-op with prompts library)
|
|
253
|
+
*/
|
|
254
|
+
close() {
|
|
255
|
+
// No cleanup needed with prompts library
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=interactive-prompts.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler for missing environment variables with interactive prompts
|
|
3
|
+
*/
|
|
4
|
+
import { SecretsManager } from './secrets-manager.js';
|
|
5
|
+
import { MissingEnvironmentVariableError } from './env-resolver.js';
|
|
6
|
+
/**
|
|
7
|
+
* Options for MissingEnvHandler
|
|
8
|
+
*/
|
|
9
|
+
export interface MissingEnvHandlerOptions {
|
|
10
|
+
interactive: boolean;
|
|
11
|
+
workflow?: string;
|
|
12
|
+
secretsManager?: SecretsManager;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of handling a missing environment variable
|
|
16
|
+
*/
|
|
17
|
+
export interface HandleResult {
|
|
18
|
+
value?: string;
|
|
19
|
+
cancelled: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Handler for missing environment variables
|
|
23
|
+
* Stores values in the universal secrets file ā no YAML modification
|
|
24
|
+
*/
|
|
25
|
+
export declare class MissingEnvHandler {
|
|
26
|
+
private secretsManager;
|
|
27
|
+
private prompts;
|
|
28
|
+
private interactive;
|
|
29
|
+
private workflow?;
|
|
30
|
+
constructor(options: MissingEnvHandlerOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Handle a missing environment variable error
|
|
33
|
+
* Prompts user for value and saves to the universal secrets file
|
|
34
|
+
*/
|
|
35
|
+
handleMissingVariable(error: MissingEnvironmentVariableError): Promise<HandleResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Close the prompts interface
|
|
38
|
+
*/
|
|
39
|
+
close(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get the secrets manager instance
|
|
42
|
+
*/
|
|
43
|
+
getSecretsManager(): SecretsManager;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=missing-env-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-env-handler.d.ts","sourceRoot":"","sources":["../../../src/yaml/missing-env-handler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,CAAS;gBAEd,OAAO,EAAE,wBAAwB;IAO7C;;;OAGG;IACG,qBAAqB,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,YAAY,CAAC;IAgC1F;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,iBAAiB,IAAI,cAAc;CAGpC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler for missing environment variables with interactive prompts
|
|
3
|
+
*/
|
|
4
|
+
import { SecretsManager } from './secrets-manager.js';
|
|
5
|
+
import { InteractivePrompts } from './interactive-prompts.js';
|
|
6
|
+
/**
|
|
7
|
+
* Handler for missing environment variables
|
|
8
|
+
* Stores values in the universal secrets file ā no YAML modification
|
|
9
|
+
*/
|
|
10
|
+
export class MissingEnvHandler {
|
|
11
|
+
secretsManager;
|
|
12
|
+
prompts;
|
|
13
|
+
interactive;
|
|
14
|
+
workflow;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.secretsManager = options.secretsManager || new SecretsManager();
|
|
17
|
+
this.workflow = options.workflow;
|
|
18
|
+
this.prompts = new InteractivePrompts(this.secretsManager, this.workflow);
|
|
19
|
+
this.interactive = options.interactive;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Handle a missing environment variable error
|
|
23
|
+
* Prompts user for value and saves to the universal secrets file
|
|
24
|
+
*/
|
|
25
|
+
async handleMissingVariable(error) {
|
|
26
|
+
const { variableName, stepName, hint } = error;
|
|
27
|
+
if (!this.interactive) {
|
|
28
|
+
return { cancelled: true };
|
|
29
|
+
}
|
|
30
|
+
const promptResult = await this.prompts.promptForVariable(variableName, stepName, hint);
|
|
31
|
+
if (promptResult.cancelled) {
|
|
32
|
+
if (promptResult.copiedToAI) {
|
|
33
|
+
console.log('š” After getting guidance from AI, run the pipeline again to enter the value.');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log('\nā Build cancelled by user');
|
|
37
|
+
}
|
|
38
|
+
return { cancelled: true };
|
|
39
|
+
}
|
|
40
|
+
// Store in secrets file (workflow-scoped if workflow is set) and inject into current process env
|
|
41
|
+
this.secretsManager.storeSecret(variableName, promptResult.value, this.workflow);
|
|
42
|
+
process.env[variableName] = promptResult.value;
|
|
43
|
+
console.log(`\nā
Secret stored: ${variableName}`);
|
|
44
|
+
if (this.workflow) {
|
|
45
|
+
console.log(` Workflow: ${this.workflow}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(` Location: ${this.secretsManager.getSecretsFilePath()}`);
|
|
48
|
+
console.log(`\nš” Next time you run this pipeline, the value will be loaded automatically.\n`);
|
|
49
|
+
return { value: promptResult.value, cancelled: false };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Close the prompts interface
|
|
53
|
+
*/
|
|
54
|
+
close() {
|
|
55
|
+
this.prompts.close();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the secrets manager instance
|
|
59
|
+
*/
|
|
60
|
+
getSecretsManager() {
|
|
61
|
+
return this.secretsManager;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=missing-env-handler.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML pipeline parser - loads and validates YAML pipeline files
|
|
3
|
+
*/
|
|
4
|
+
import type { YAMLPipeline } from './types.js';
|
|
5
|
+
export declare class YAMLParseError extends Error {
|
|
6
|
+
readonly filePath?: string | undefined;
|
|
7
|
+
constructor(message: string, filePath?: string | undefined);
|
|
8
|
+
}
|
|
9
|
+
export declare class YAMLValidationError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Loads and parses a YAML pipeline file
|
|
14
|
+
* @param filePath Path to the YAML file
|
|
15
|
+
* @returns Parsed YAML pipeline object
|
|
16
|
+
* @throws YAMLParseError if file doesn't exist or YAML syntax is invalid
|
|
17
|
+
* @throws YAMLValidationError if YAML structure is invalid
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadYAMLPipeline(filePath: string): YAMLPipeline;
|
|
20
|
+
/**
|
|
21
|
+
* Gets the list of workflow names from a YAML pipeline
|
|
22
|
+
* @param pipeline Parsed YAML pipeline
|
|
23
|
+
* @returns Array of workflow names
|
|
24
|
+
*/
|
|
25
|
+
export declare function getWorkflowNames(pipeline: YAMLPipeline): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Checks if a specific workflow exists in the pipeline
|
|
28
|
+
* @param pipeline Parsed YAML pipeline
|
|
29
|
+
* @param workflowName Name of the workflow to check
|
|
30
|
+
* @returns True if workflow exists
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasWorkflow(pipeline: YAMLPipeline, workflowName: string): boolean;
|
|
33
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/yaml/parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,qBAAa,cAAe,SAAQ,KAAK;aACM,QAAQ,CAAC,EAAE,MAAM;gBAAlD,OAAO,EAAE,MAAM,EAAkB,QAAQ,CAAC,EAAE,MAAM,YAAA;CAI/D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CA0C/D;AA8GD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,EAAE,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAEjF"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML pipeline parser - loads and validates YAML pipeline files
|
|
3
|
+
*/
|
|
4
|
+
import * as yaml from 'js-yaml';
|
|
5
|
+
import { readFileSync, existsSync } from 'fs';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { convertBitriseMetadata } from './bitrise-compat.js';
|
|
8
|
+
export class YAMLParseError extends Error {
|
|
9
|
+
filePath;
|
|
10
|
+
constructor(message, filePath) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.filePath = filePath;
|
|
13
|
+
this.name = 'YAMLParseError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class YAMLValidationError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'YAMLValidationError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Loads and parses a YAML pipeline file
|
|
24
|
+
* @param filePath Path to the YAML file
|
|
25
|
+
* @returns Parsed YAML pipeline object
|
|
26
|
+
* @throws YAMLParseError if file doesn't exist or YAML syntax is invalid
|
|
27
|
+
* @throws YAMLValidationError if YAML structure is invalid
|
|
28
|
+
*/
|
|
29
|
+
export function loadYAMLPipeline(filePath) {
|
|
30
|
+
const absolutePath = resolve(filePath);
|
|
31
|
+
// Check if file exists
|
|
32
|
+
if (!existsSync(absolutePath)) {
|
|
33
|
+
throw new YAMLParseError(`YAML pipeline file not found: ${filePath}`, absolutePath);
|
|
34
|
+
}
|
|
35
|
+
let fileContent;
|
|
36
|
+
try {
|
|
37
|
+
fileContent = readFileSync(absolutePath, 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw new YAMLParseError(`Failed to read YAML file: ${filePath}\n${err.message}`, absolutePath);
|
|
41
|
+
}
|
|
42
|
+
// Parse YAML
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = yaml.load(fileContent);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const yamlError = err;
|
|
49
|
+
throw new YAMLParseError(`Failed to parse YAML file: ${filePath}\n${yamlError.name}: ${yamlError.message}`, absolutePath);
|
|
50
|
+
}
|
|
51
|
+
// Validate structure
|
|
52
|
+
validateYAMLPipeline(parsed, filePath);
|
|
53
|
+
const pipeline = parsed;
|
|
54
|
+
// Convert Bitrise metadata to CI Build format (FR-1.1, FR-1.2)
|
|
55
|
+
convertBitriseMetadata(pipeline);
|
|
56
|
+
return pipeline;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validates the basic structure of a parsed YAML pipeline
|
|
60
|
+
* @param parsed Parsed YAML object
|
|
61
|
+
* @param filePath File path for error messages
|
|
62
|
+
* @throws YAMLValidationError if validation fails
|
|
63
|
+
*/
|
|
64
|
+
function validateYAMLPipeline(parsed, filePath) {
|
|
65
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
66
|
+
throw new YAMLValidationError(`Invalid YAML structure in ${filePath}: Expected an object at root level`);
|
|
67
|
+
}
|
|
68
|
+
// Check required fields
|
|
69
|
+
if (!parsed.format_version) {
|
|
70
|
+
throw new YAMLValidationError(`Missing required field 'format_version' in ${filePath}`);
|
|
71
|
+
}
|
|
72
|
+
if (!parsed.workflows || typeof parsed.workflows !== 'object') {
|
|
73
|
+
throw new YAMLValidationError(`Missing or invalid 'workflows' section in ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
// Check that workflows is not empty
|
|
76
|
+
const workflowNames = Object.keys(parsed.workflows);
|
|
77
|
+
if (workflowNames.length === 0) {
|
|
78
|
+
throw new YAMLValidationError(`No workflows defined in ${filePath}. At least one workflow is required.`);
|
|
79
|
+
}
|
|
80
|
+
// Validate each workflow has required fields
|
|
81
|
+
for (const workflowName of workflowNames) {
|
|
82
|
+
const workflow = parsed.workflows[workflowName];
|
|
83
|
+
if (!workflow || typeof workflow !== 'object') {
|
|
84
|
+
throw new YAMLValidationError(`Invalid workflow '${workflowName}' in ${filePath}: Expected an object`);
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(workflow.steps)) {
|
|
87
|
+
throw new YAMLValidationError(`Missing or invalid 'steps' array in workflow '${workflowName}' in ${filePath}`);
|
|
88
|
+
}
|
|
89
|
+
if (workflow.steps.length === 0) {
|
|
90
|
+
throw new YAMLValidationError(`Workflow '${workflowName}' in ${filePath} has no steps defined`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Validate meta section if present
|
|
94
|
+
if (parsed.meta) {
|
|
95
|
+
if (typeof parsed.meta !== 'object') {
|
|
96
|
+
throw new YAMLValidationError(`Invalid 'meta' section in ${filePath}: Expected an object`);
|
|
97
|
+
}
|
|
98
|
+
// Validate platform if specified
|
|
99
|
+
if (parsed.meta.platform) {
|
|
100
|
+
const validPlatforms = ['macos', 'linux', 'windows'];
|
|
101
|
+
if (!validPlatforms.includes(parsed.meta.platform)) {
|
|
102
|
+
throw new YAMLValidationError(`Invalid platform '${parsed.meta.platform}' in ${filePath}. Must be one of: ${validPlatforms.join(', ')}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Validate cibuild.io section if present
|
|
106
|
+
if (parsed.meta['cibuild.io']) {
|
|
107
|
+
const cibuildMeta = parsed.meta['cibuild.io'];
|
|
108
|
+
if (typeof cibuildMeta !== 'object') {
|
|
109
|
+
throw new YAMLValidationError(`Invalid 'meta.cibuild.io' section in ${filePath}: Expected an object`);
|
|
110
|
+
}
|
|
111
|
+
// Validate stack pattern if specified
|
|
112
|
+
if (cibuildMeta.stack && typeof cibuildMeta.stack === 'string') {
|
|
113
|
+
const stackPattern = /^(macos|linux|windows)-/;
|
|
114
|
+
if (cibuildMeta.stack !== 'local' && !stackPattern.test(cibuildMeta.stack)) {
|
|
115
|
+
throw new YAMLValidationError(`Invalid stack '${cibuildMeta.stack}' in ${filePath}. Stack must be 'local' or start with 'macos-', 'linux-', or 'windows-'`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Validate machine_type if specified
|
|
119
|
+
if (cibuildMeta.machine_type) {
|
|
120
|
+
const validMachineTypes = ['standard', 'performance'];
|
|
121
|
+
if (!validMachineTypes.includes(cibuildMeta.machine_type)) {
|
|
122
|
+
throw new YAMLValidationError(`Invalid machine_type '${cibuildMeta.machine_type}' in ${filePath}. Must be one of: ${validMachineTypes.join(', ')}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Gets the list of workflow names from a YAML pipeline
|
|
130
|
+
* @param pipeline Parsed YAML pipeline
|
|
131
|
+
* @returns Array of workflow names
|
|
132
|
+
*/
|
|
133
|
+
export function getWorkflowNames(pipeline) {
|
|
134
|
+
return Object.keys(pipeline.workflows);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Checks if a specific workflow exists in the pipeline
|
|
138
|
+
* @param pipeline Parsed YAML pipeline
|
|
139
|
+
* @param workflowName Name of the workflow to check
|
|
140
|
+
* @returns True if workflow exists
|
|
141
|
+
*/
|
|
142
|
+
export function hasWorkflow(pipeline, workflowName) {
|
|
143
|
+
return workflowName in pipeline.workflows;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline execution with secrets management integration
|
|
3
|
+
* Wraps converter and runner with interactive prompts for missing environment variables
|
|
4
|
+
*/
|
|
5
|
+
import { type ConversionResult } from './converter.js';
|
|
6
|
+
import type { YAMLPipeline } from './types.js';
|
|
7
|
+
import type { CIConfig } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Options for pipeline conversion with secrets
|
|
10
|
+
*/
|
|
11
|
+
export interface ConvertYAMLWithSecretsOptions {
|
|
12
|
+
yamlFilePath: string;
|
|
13
|
+
interactive: boolean;
|
|
14
|
+
config: CIConfig;
|
|
15
|
+
workflowName?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Converts YAML pipeline with automatic secrets management
|
|
19
|
+
* Prompts user for missing environment variables and updates YAML file
|
|
20
|
+
* @param pipeline Parsed YAML pipeline
|
|
21
|
+
* @param options Execution options
|
|
22
|
+
* @returns ConversionResult with pipeline and warnings
|
|
23
|
+
*/
|
|
24
|
+
export declare function convertYAMLWithSecrets(pipeline: YAMLPipeline, options: ConvertYAMLWithSecretsOptions): Promise<ConversionResult>;
|
|
25
|
+
//# sourceMappingURL=pipeline-with-secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-with-secrets.d.ts","sourceRoot":"","sources":["../../../src/yaml/pipeline-with-secrets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA4B,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,QAAQ,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,gBAAgB,CAAC,CAiF3B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline execution with secrets management integration
|
|
3
|
+
* Wraps converter and runner with interactive prompts for missing environment variables
|
|
4
|
+
*/
|
|
5
|
+
import { convertYAMLToPipelineDef } from './converter.js';
|
|
6
|
+
import { MissingEnvironmentVariableError } from './env-resolver.js';
|
|
7
|
+
import { MissingEnvHandler } from './missing-env-handler.js';
|
|
8
|
+
import { SecretsManager } from './secrets-manager.js';
|
|
9
|
+
import { PreFlightValidator } from './preflight-validation.js';
|
|
10
|
+
/**
|
|
11
|
+
* Converts YAML pipeline with automatic secrets management
|
|
12
|
+
* Prompts user for missing environment variables and updates YAML file
|
|
13
|
+
* @param pipeline Parsed YAML pipeline
|
|
14
|
+
* @param options Execution options
|
|
15
|
+
* @returns ConversionResult with pipeline and warnings
|
|
16
|
+
*/
|
|
17
|
+
export async function convertYAMLWithSecrets(pipeline, options) {
|
|
18
|
+
const secretsManager = new SecretsManager();
|
|
19
|
+
const missingEnvHandler = new MissingEnvHandler({
|
|
20
|
+
interactive: options.interactive,
|
|
21
|
+
workflow: options.workflowName,
|
|
22
|
+
secretsManager,
|
|
23
|
+
});
|
|
24
|
+
// Pre-flight validation: Check repository access before workflow execution
|
|
25
|
+
// Get the selected workflow for validation
|
|
26
|
+
const selectedWorkflowName = options.workflowName || Object.keys(pipeline.workflows)[0];
|
|
27
|
+
const selectedWorkflow = pipeline.workflows[selectedWorkflowName];
|
|
28
|
+
const preFlightValidator = new PreFlightValidator();
|
|
29
|
+
const preFlightResult = await preFlightValidator.validate(process.env, secretsManager, selectedWorkflow);
|
|
30
|
+
if (!preFlightResult.success && preFlightResult.error) {
|
|
31
|
+
console.log('\n'); // Add spacing before prompt
|
|
32
|
+
// Handle the missing or invalid repository URL
|
|
33
|
+
const handleResult = await missingEnvHandler.handleMissingVariable(preFlightResult.error);
|
|
34
|
+
missingEnvHandler.close();
|
|
35
|
+
if (handleResult.cancelled) {
|
|
36
|
+
throw new Error('Build cancelled: Repository validation failed');
|
|
37
|
+
}
|
|
38
|
+
// Retry validation after secret is stored
|
|
39
|
+
console.log('\nš Retrying pre-flight validation with stored secret...\n');
|
|
40
|
+
// Import the parser to reload the YAML
|
|
41
|
+
const { loadYAMLPipeline } = await import('./parser.js');
|
|
42
|
+
const reloadedPipeline = loadYAMLPipeline(options.yamlFilePath);
|
|
43
|
+
// Retry conversion (which will re-run pre-flight)
|
|
44
|
+
return await convertYAMLWithSecrets(reloadedPipeline, options);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Attempt conversion
|
|
48
|
+
const result = await convertYAMLToPipelineDef(pipeline, options.config, options.workflowName);
|
|
49
|
+
missingEnvHandler.close();
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// Check if it's a missing environment variable error
|
|
54
|
+
if (error instanceof MissingEnvironmentVariableError) {
|
|
55
|
+
console.log('\n'); // Add spacing before prompt
|
|
56
|
+
// Handle the missing variable
|
|
57
|
+
const handleResult = await missingEnvHandler.handleMissingVariable(error);
|
|
58
|
+
missingEnvHandler.close();
|
|
59
|
+
if (handleResult.cancelled) {
|
|
60
|
+
throw new Error('Build cancelled: Missing required environment variable');
|
|
61
|
+
}
|
|
62
|
+
// Retry conversion after secret is stored
|
|
63
|
+
// The YAML file has been updated with the secret reference
|
|
64
|
+
// We need to reload and convert again
|
|
65
|
+
console.log('\nš Retrying pipeline conversion with stored secret...\n');
|
|
66
|
+
// Import the parser to reload the YAML
|
|
67
|
+
const { loadYAMLPipeline } = await import('./parser.js');
|
|
68
|
+
const reloadedPipeline = loadYAMLPipeline(options.yamlFilePath);
|
|
69
|
+
// Retry conversion with the new secrets manager that has the value
|
|
70
|
+
return await convertYAMLWithSecrets(reloadedPipeline, options);
|
|
71
|
+
}
|
|
72
|
+
// Re-throw other errors
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=pipeline-with-secrets.js.map
|