@invarn/cibuild 1.3.16 → 1.3.18
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 +47 -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 +58 -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 +352 -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 +505 -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 +17 -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,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable resolution and interpolation
|
|
3
|
+
* Merges global and workflow-level environment variables
|
|
4
|
+
* Supports variable interpolation in multiple formats
|
|
5
|
+
*/
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { SecretsManager } from './secrets-manager.js';
|
|
11
|
+
/**
|
|
12
|
+
* Hints for common variables that require user configuration
|
|
13
|
+
*/
|
|
14
|
+
const CIBUILD_VARIABLE_HINTS = {
|
|
15
|
+
CIBUILD_PUBLIC_INSTALL_PAGE_URL: 'Public URL where users can download/install the built app.\n\n' +
|
|
16
|
+
'Purpose:\n' +
|
|
17
|
+
' • Used in Slack notifications and other build artifacts\n' +
|
|
18
|
+
' • Provides a download link for QA, stakeholders, or beta testers\n' +
|
|
19
|
+
' • Commonly points to artifact hosting (Firebase App Distribution, TestFlight, etc.)\n\n' +
|
|
20
|
+
'Expected format:\n' +
|
|
21
|
+
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
22
|
+
' │ https://example.com/downloads/myapp-v1.0.0.apk │\n' +
|
|
23
|
+
' │ https://appdistribution.firebase.dev/i/abc123 │\n' +
|
|
24
|
+
' │ https://testflight.apple.com/join/xyz456 │\n' +
|
|
25
|
+
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
26
|
+
'How to set:\n' +
|
|
27
|
+
' • Add to your YAML under app.envs or workflows.[name].envs:\n' +
|
|
28
|
+
' envs:\n' +
|
|
29
|
+
' - CIBUILD_PUBLIC_INSTALL_PAGE_URL: "https://your-url-here"\n' +
|
|
30
|
+
' • Or set dynamically in a previous step that uploads the build\n' +
|
|
31
|
+
' • Or leave it as a placeholder if not using artifact distribution',
|
|
32
|
+
CIBUILD_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL: 'URL to a QR code image for easy mobile download/install.\n\n' +
|
|
33
|
+
'Purpose:\n' +
|
|
34
|
+
' • Used in Slack notifications for quick mobile access\n' +
|
|
35
|
+
' • QR code typically points to CIBUILD_PUBLIC_INSTALL_PAGE_URL\n\n' +
|
|
36
|
+
'Expected format:\n' +
|
|
37
|
+
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
38
|
+
' │ https://example.com/qr-codes/myapp-v1.0.0.png │\n' +
|
|
39
|
+
' │ https://api.qrserver.com/v1/create-qr-code/?data=... │\n' +
|
|
40
|
+
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
41
|
+
'How to set:\n' +
|
|
42
|
+
' • Add to your YAML under app.envs or workflows.[name].envs\n' +
|
|
43
|
+
' • Or generate dynamically in a previous step\n' +
|
|
44
|
+
' • Or use a QR code generation service with the install URL',
|
|
45
|
+
CIBUILD_PULL_REQUEST: 'Pull request number or ID for PR builds.\n\n' +
|
|
46
|
+
'Expected format:\n' +
|
|
47
|
+
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
48
|
+
' │ 123 │\n' +
|
|
49
|
+
' │ pr-456 │\n' +
|
|
50
|
+
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
51
|
+
'How to set:\n' +
|
|
52
|
+
' • Typically provided by CI system for PR builds\n' +
|
|
53
|
+
' • Or set manually: export CIBUILD_PULL_REQUEST="123"',
|
|
54
|
+
CIBUILD_GIT_TAG: 'Git tag name for tag-triggered builds.\n\n' +
|
|
55
|
+
'Expected format:\n' +
|
|
56
|
+
' ┌────────────────────────────────────────────────────────┐\n' +
|
|
57
|
+
' │ v1.0.0 │\n' +
|
|
58
|
+
' │ release-2024-01-15 │\n' +
|
|
59
|
+
' └────────────────────────────────────────────────────────┘\n\n' +
|
|
60
|
+
'How to set:\n' +
|
|
61
|
+
' • Typically provided by CI system for tag builds\n' +
|
|
62
|
+
' • Or set manually: export CIBUILD_GIT_TAG="v1.0.0"',
|
|
63
|
+
};
|
|
64
|
+
export class MissingEnvironmentVariableError extends Error {
|
|
65
|
+
variableName;
|
|
66
|
+
stepName;
|
|
67
|
+
hint;
|
|
68
|
+
constructor(variableName, stepName, hint) {
|
|
69
|
+
const stepInfo = stepName ? ` required by step: '${stepName}'` : '';
|
|
70
|
+
const hintInfo = hint ? `\n\n${hint}` : '';
|
|
71
|
+
super(`Missing environment variable: '${variableName}'${stepInfo}\n` +
|
|
72
|
+
`Check your YAML file's 'app.envs' or 'workflows.[name].envs' sections.${hintInfo}`);
|
|
73
|
+
this.variableName = variableName;
|
|
74
|
+
this.stepName = stepName;
|
|
75
|
+
this.hint = hint;
|
|
76
|
+
this.name = 'MissingEnvironmentVariableError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* EnvResolver handles environment variable merging and interpolation
|
|
81
|
+
*/
|
|
82
|
+
export class EnvResolver {
|
|
83
|
+
envVars;
|
|
84
|
+
referencedVars = new Set();
|
|
85
|
+
secretsManager;
|
|
86
|
+
constructor(pipeline, workflow, workflowName, platform, stack, yamlFilePath) {
|
|
87
|
+
this.envVars = new Map();
|
|
88
|
+
this.secretsManager = new SecretsManager();
|
|
89
|
+
// Step 1: Add built-in environment variables (auto-detected defaults)
|
|
90
|
+
this.addBuiltInVars(workflowName, platform, stack, yamlFilePath);
|
|
91
|
+
// Step 2: Load secrets — overrides auto-detected built-ins with user-stored values
|
|
92
|
+
// Workflow-specific secrets take precedence over global secrets
|
|
93
|
+
const allSecrets = this.secretsManager.getAll(workflowName);
|
|
94
|
+
for (const [key, value] of Object.entries(allSecrets)) {
|
|
95
|
+
this.envVars.set(key, value);
|
|
96
|
+
}
|
|
97
|
+
// Step 3: Add global environment variables (app.envs) — overrides secrets
|
|
98
|
+
if (pipeline.app?.envs) {
|
|
99
|
+
this.mergeEnvVars(pipeline.app.envs);
|
|
100
|
+
}
|
|
101
|
+
// Step 4: Add workflow-level environment variables — highest priority, overrides everything
|
|
102
|
+
if (workflow.envs) {
|
|
103
|
+
this.mergeEnvVars(workflow.envs);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Gets the value of an environment variable
|
|
108
|
+
* @param name Variable name
|
|
109
|
+
* @returns Variable value or undefined if not found
|
|
110
|
+
*/
|
|
111
|
+
get(name) {
|
|
112
|
+
this.referencedVars.add(name);
|
|
113
|
+
return this.envVars.get(name);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Checks if an environment variable is defined
|
|
117
|
+
* @param name Variable name
|
|
118
|
+
* @returns True if variable exists
|
|
119
|
+
*/
|
|
120
|
+
has(name) {
|
|
121
|
+
return this.envVars.has(name);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Registers a variable as a step output passthrough.
|
|
125
|
+
* When this variable appears in a later step's inputs it will be kept as
|
|
126
|
+
* the literal shell reference "$NAME" so the shell resolves it at runtime.
|
|
127
|
+
*/
|
|
128
|
+
registerStepOutput(name) {
|
|
129
|
+
if (!this.envVars.has(name)) {
|
|
130
|
+
this.envVars.set(name, `$${name}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Gets all environment variables as a plain object
|
|
135
|
+
* @returns Object with all environment variables
|
|
136
|
+
*/
|
|
137
|
+
getAll() {
|
|
138
|
+
const result = {};
|
|
139
|
+
for (const [key, value] of this.envVars.entries()) {
|
|
140
|
+
result[key] = value;
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Interpolates environment variables in a string
|
|
146
|
+
* Supports four formats:
|
|
147
|
+
* - $VAR
|
|
148
|
+
* - ${VAR}
|
|
149
|
+
* - {{getenv "VAR"}}
|
|
150
|
+
* - {{checksum "path/to/file"}}
|
|
151
|
+
*
|
|
152
|
+
* @param str String to interpolate
|
|
153
|
+
* @param stepName Optional step name for error messages
|
|
154
|
+
* @returns Interpolated string
|
|
155
|
+
* @throws MissingEnvironmentVariableError if referenced variable not found
|
|
156
|
+
*/
|
|
157
|
+
interpolate(str, stepName) {
|
|
158
|
+
let result = str;
|
|
159
|
+
// Format 1: {{checksum "file"}}
|
|
160
|
+
result = result.replace(/\{\{checksum "([^"]+)"\}\}/g, (match, filePath) => {
|
|
161
|
+
return this.computeChecksum(filePath);
|
|
162
|
+
});
|
|
163
|
+
// Format 2: {{getenv "VAR"}}
|
|
164
|
+
result = result.replace(/\{\{getenv "([^"]+)"\}\}/g, (match, varName) => {
|
|
165
|
+
return this.resolveVar(varName, stepName);
|
|
166
|
+
});
|
|
167
|
+
// Format 3: ${VAR}
|
|
168
|
+
result = result.replace(/\$\{([A-Z_][A-Z0-9_]*)\}/g, (match, varName) => {
|
|
169
|
+
return this.resolveVar(varName, stepName);
|
|
170
|
+
});
|
|
171
|
+
// Format 4: $VAR (must not be followed by alphanumeric or underscore)
|
|
172
|
+
result = result.replace(/\$([A-Z_][A-Z0-9_]*)(?![A-Z0-9_])/g, (match, varName) => {
|
|
173
|
+
return this.resolveVar(varName, stepName);
|
|
174
|
+
});
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Interpolates environment variables in an object recursively
|
|
179
|
+
* @param obj Object to interpolate
|
|
180
|
+
* @param stepName Optional step name for error messages
|
|
181
|
+
* @returns Interpolated object
|
|
182
|
+
*/
|
|
183
|
+
interpolateObject(obj, stepName) {
|
|
184
|
+
if (typeof obj === 'string') {
|
|
185
|
+
return this.interpolate(obj, stepName);
|
|
186
|
+
}
|
|
187
|
+
if (Array.isArray(obj)) {
|
|
188
|
+
return obj.map((item) => this.interpolateObject(item, stepName));
|
|
189
|
+
}
|
|
190
|
+
if (obj && typeof obj === 'object') {
|
|
191
|
+
const result = {};
|
|
192
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
193
|
+
result[key] = this.interpolateObject(value, stepName);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
return obj;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Validates that all referenced variables are defined
|
|
201
|
+
* @throws MissingEnvironmentVariableError if any referenced variable is missing
|
|
202
|
+
*/
|
|
203
|
+
validateReferencedVars() {
|
|
204
|
+
for (const varName of this.referencedVars) {
|
|
205
|
+
if (!this.envVars.has(varName)) {
|
|
206
|
+
throw new MissingEnvironmentVariableError(varName);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Resolves a single variable reference
|
|
212
|
+
* @param varName Variable name
|
|
213
|
+
* @param stepName Optional step name for error messages
|
|
214
|
+
* @returns Variable value
|
|
215
|
+
* @throws MissingEnvironmentVariableError if variable not found
|
|
216
|
+
*/
|
|
217
|
+
resolveVar(varName, stepName) {
|
|
218
|
+
this.referencedVars.add(varName);
|
|
219
|
+
const value = this.envVars.get(varName);
|
|
220
|
+
if (value === undefined) {
|
|
221
|
+
// Check if we have a hint for this variable
|
|
222
|
+
const hint = CIBUILD_VARIABLE_HINTS[varName];
|
|
223
|
+
throw new MissingEnvironmentVariableError(varName, stepName, hint);
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Computes MD5 checksum of a file
|
|
229
|
+
* @param filePath Path to file (relative to current working directory)
|
|
230
|
+
* @returns MD5 hash of file contents
|
|
231
|
+
* @throws Error if file does not exist or cannot be read
|
|
232
|
+
*/
|
|
233
|
+
computeChecksum(filePath) {
|
|
234
|
+
try {
|
|
235
|
+
// Resolve relative path to absolute path
|
|
236
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
237
|
+
// Check if file exists
|
|
238
|
+
if (!fs.existsSync(absolutePath)) {
|
|
239
|
+
throw new Error(`File not found: ${filePath}`);
|
|
240
|
+
}
|
|
241
|
+
// Read file and compute MD5 hash
|
|
242
|
+
const fileBuffer = fs.readFileSync(absolutePath);
|
|
243
|
+
const hash = crypto.createHash('md5');
|
|
244
|
+
hash.update(fileBuffer);
|
|
245
|
+
return hash.digest('hex');
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
if (error instanceof Error) {
|
|
249
|
+
throw new Error(`Failed to compute checksum for "${filePath}": ${error.message}`);
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Adds built-in environment variables
|
|
256
|
+
* @param workflowName Name of the current workflow
|
|
257
|
+
* @param platform Detected platform
|
|
258
|
+
* @param stack Optional stack identifier
|
|
259
|
+
*/
|
|
260
|
+
addBuiltInVars(workflowName, platform, stack, yamlFilePath) {
|
|
261
|
+
// CI Build native variables (FR-9.2)
|
|
262
|
+
const buildNumber = process.env.BUILD_NUMBER || '1';
|
|
263
|
+
const buildUrl = process.env.BUILD_URL || 'https://cibuild.io/builds/1';
|
|
264
|
+
const appTitle = process.env.APP_TITLE || 'CI Build Build';
|
|
265
|
+
// Extract YAML filename (without extension) for use in naming resources like SSH keys
|
|
266
|
+
if (yamlFilePath) {
|
|
267
|
+
const yamlFilename = path.basename(yamlFilePath, path.extname(yamlFilePath));
|
|
268
|
+
this.envVars.set('CIBUILD_YAML_FILENAME', yamlFilename);
|
|
269
|
+
}
|
|
270
|
+
this.envVars.set('CIBUILD_BUILD_NUMBER', buildNumber);
|
|
271
|
+
this.envVars.set('CIBUILD_BUILD_URL', buildUrl);
|
|
272
|
+
this.envVars.set('CIBUILD_APP_TITLE', appTitle);
|
|
273
|
+
// Writable scratch path on local APFS disk. The sandbox runner sets this
|
|
274
|
+
// in the guest shell; outside a sandbox we fall back to TMPDIR so
|
|
275
|
+
// pipelines that reference $CIBUILD_SCRATCH still interpolate to
|
|
276
|
+
// something writable.
|
|
277
|
+
this.envVars.set('CIBUILD_SCRATCH', process.env.CIBUILD_SCRATCH || process.env.TMPDIR || '/tmp');
|
|
278
|
+
// Auto-detect git metadata from local repo
|
|
279
|
+
const git = (cmd, fallback) => {
|
|
280
|
+
try {
|
|
281
|
+
return execSync(`git ${cmd}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim() || fallback;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return fallback;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
// Common built-in variables
|
|
288
|
+
const gitBranch = process.env.GIT_BRANCH || git('rev-parse --abbrev-ref HEAD', 'main');
|
|
289
|
+
const gitCommit = git('rev-parse HEAD', process.env.GIT_COMMIT || 'HEAD');
|
|
290
|
+
const gitRepoUrl = process.env.CIBUILD_GIT_REPOSITORY_URL || process.env.GIT_REPOSITORY_URL || git('remote get-url origin', '');
|
|
291
|
+
// Source directory
|
|
292
|
+
this.envVars.set('CIBUILD_SOURCE_DIR', process.cwd());
|
|
293
|
+
// Git variables
|
|
294
|
+
this.envVars.set('CIBUILD_GIT_BRANCH', gitBranch);
|
|
295
|
+
this.envVars.set('CIBUILD_GIT_COMMIT', gitCommit);
|
|
296
|
+
this.envVars.set('CIBUILD_GIT_REPOSITORY_URL', gitRepoUrl);
|
|
297
|
+
this.envVars.set('CIBUILD_TRIGGERED_WORKFLOW_ID', workflowName);
|
|
298
|
+
// Git metadata (auto-detected from local repo)
|
|
299
|
+
this.envVars.set('GIT_CLONE_COMMIT_HASH', gitCommit);
|
|
300
|
+
this.envVars.set('GIT_CLONE_COMMIT_AUTHOR_NAME', git('log -1 --format=%an', process.env.GIT_AUTHOR_NAME || ''));
|
|
301
|
+
this.envVars.set('GIT_CLONE_COMMIT_AUTHOR_EMAIL', git('log -1 --format=%ae', process.env.GIT_AUTHOR_EMAIL || ''));
|
|
302
|
+
this.envVars.set('GIT_CLONE_COMMIT_COMMITER_NAME', git('log -1 --format=%cn', process.env.GIT_COMMITTER_NAME || ''));
|
|
303
|
+
this.envVars.set('GIT_CLONE_COMMIT_COMMITER_EMAIL', git('log -1 --format=%ce', process.env.GIT_COMMITTER_EMAIL || ''));
|
|
304
|
+
this.envVars.set('GIT_CLONE_COMMIT_MESSAGE_SUBJECT', git('log -1 --format=%s', process.env.GIT_COMMIT_MESSAGE || ''));
|
|
305
|
+
this.envVars.set('GIT_CLONE_COMMIT_MESSAGE_BODY', git('log -1 --format=%b', ''));
|
|
306
|
+
// Platform-specific built-in variables
|
|
307
|
+
if (stack) {
|
|
308
|
+
this.envVars.set('CIBUILD_STACK', stack);
|
|
309
|
+
}
|
|
310
|
+
if (platform === 'macos') {
|
|
311
|
+
// Extract Xcode version from stack if available
|
|
312
|
+
if (stack) {
|
|
313
|
+
const xcodeMatch = stack.match(/xcode-(\d+\.\d+)/);
|
|
314
|
+
if (xcodeMatch) {
|
|
315
|
+
this.envVars.set('CIBUILD_XCODE_VERSION', xcodeMatch[1]);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Default Xcode scheme (can be overridden by user)
|
|
319
|
+
if (!this.envVars.has('CIBUILD_SCHEME')) {
|
|
320
|
+
this.envVars.set('CIBUILD_SCHEME', process.env.XCODE_SCHEME || 'Release');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (platform === 'linux') {
|
|
324
|
+
// JAVA_HOME (can be overridden by user)
|
|
325
|
+
this.envVars.set('JAVA_HOME', process.env.JAVA_HOME || '/usr/lib/jvm/java-17-openjdk');
|
|
326
|
+
this.envVars.set('CIBUILD_GRADLE_VERSION', process.env.GRADLE_VERSION || '8.0');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Merges environment variables from YAML array format
|
|
331
|
+
* @param envArray Array of environment variable objects
|
|
332
|
+
*/
|
|
333
|
+
mergeEnvVars(envArray) {
|
|
334
|
+
for (const envObj of envArray) {
|
|
335
|
+
for (const [key, value] of Object.entries(envObj)) {
|
|
336
|
+
// First resolve any secret references
|
|
337
|
+
const secretResolved = this.resolveSecretReference(value);
|
|
338
|
+
// Then interpolate in case it references other env vars
|
|
339
|
+
const interpolated = this.interpolateEnvValue(secretResolved);
|
|
340
|
+
// Don't overwrite a non-empty value (e.g. a loaded secret) with an empty
|
|
341
|
+
// YAML placeholder string — secrets are loaded before YAML envs and should
|
|
342
|
+
// survive empty placeholder declarations like `KEYSTORE_BASE64: ""`
|
|
343
|
+
if (interpolated === '' && this.envVars.get(key)) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
this.envVars.set(key, interpolated);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Resolves $SECRET{secret_id} references to actual secret values
|
|
352
|
+
* @param value Value that may contain a secret reference
|
|
353
|
+
* @returns Resolved value with secret content, or empty string if secret not found
|
|
354
|
+
*/
|
|
355
|
+
resolveSecretReference(value) {
|
|
356
|
+
// Convert non-strings to strings first
|
|
357
|
+
if (typeof value !== 'string') {
|
|
358
|
+
return String(value);
|
|
359
|
+
}
|
|
360
|
+
// Check for $SECRET{secret_id} pattern
|
|
361
|
+
const secretMatch = value.match(/^\$SECRET\{([^}]+)\}$/);
|
|
362
|
+
if (!secretMatch) {
|
|
363
|
+
return value;
|
|
364
|
+
}
|
|
365
|
+
const secretId = secretMatch[1];
|
|
366
|
+
const secretValue = this.secretsManager.getSecret(secretId);
|
|
367
|
+
if (!secretValue) {
|
|
368
|
+
// Return empty string silently — the validation step will report this
|
|
369
|
+
return '';
|
|
370
|
+
}
|
|
371
|
+
return secretValue;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Interpolates environment variable value during initial setup
|
|
375
|
+
* This allows env vars to reference other env vars
|
|
376
|
+
* @param value Raw value
|
|
377
|
+
* @returns Interpolated value or original if interpolation fails
|
|
378
|
+
*/
|
|
379
|
+
interpolateEnvValue(value) {
|
|
380
|
+
try {
|
|
381
|
+
return this.interpolate(value);
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
// If interpolation fails during setup, return the raw value
|
|
385
|
+
// It will be interpolated later when all vars are available
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Helper function to create an EnvResolver for a workflow
|
|
392
|
+
* @param pipeline Parsed YAML pipeline
|
|
393
|
+
* @param workflowName Name of the workflow
|
|
394
|
+
* @param platform Detected platform
|
|
395
|
+
* @param stack Optional stack identifier
|
|
396
|
+
* @returns EnvResolver instance
|
|
397
|
+
*/
|
|
398
|
+
export function createEnvResolver(pipeline, workflowName, platform, stack) {
|
|
399
|
+
const workflow = pipeline.workflows[workflowName];
|
|
400
|
+
if (!workflow) {
|
|
401
|
+
throw new Error(`Workflow '${workflowName}' not found`);
|
|
402
|
+
}
|
|
403
|
+
return new EnvResolver(pipeline, workflow, workflowName, platform, stack);
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=env-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-resolver.test.d.ts","sourceRoot":"","sources":["../../../src/yaml/env-resolver.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|