@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,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-execution validation orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Validates all steps in a workflow BEFORE execution begins.
|
|
5
|
+
* All validation checks are READ-ONLY and safe to run on the user's machine.
|
|
6
|
+
* This enables catching all issues at once instead of failing on the first error.
|
|
7
|
+
*/
|
|
8
|
+
import { getStepExecutor, hasStep, getStepMetadata } from './steps/registry.js';
|
|
9
|
+
import { EnvResolver } from './env-resolver.js';
|
|
10
|
+
import { detectPlatformInfo } from './platform-detector.js';
|
|
11
|
+
// POSIX env vars the shell always provides — don't flag these as undeclared.
|
|
12
|
+
// The runner's guest shell seeds HOME, TMPDIR, PATH, etc.; requiring
|
|
13
|
+
// pipelines to redeclare them forces literal-path-only scripts.
|
|
14
|
+
const POSIX_ENV_ALLOWLIST = new Set([
|
|
15
|
+
'HOME',
|
|
16
|
+
'USER',
|
|
17
|
+
'PATH',
|
|
18
|
+
'TMPDIR',
|
|
19
|
+
'TMP',
|
|
20
|
+
'TEMP',
|
|
21
|
+
'SHELL',
|
|
22
|
+
'LANG',
|
|
23
|
+
'LC_ALL',
|
|
24
|
+
'PWD',
|
|
25
|
+
'LOGNAME',
|
|
26
|
+
'HOSTNAME',
|
|
27
|
+
]);
|
|
28
|
+
/**
|
|
29
|
+
* StepValidator orchestrates pre-execution validation for a workflow.
|
|
30
|
+
* It iterates through all steps, collects validation requirements,
|
|
31
|
+
* runs pre-execution checks, and aggregates all issues.
|
|
32
|
+
*/
|
|
33
|
+
export class StepValidator {
|
|
34
|
+
pipeline;
|
|
35
|
+
workflow;
|
|
36
|
+
workflowName;
|
|
37
|
+
config;
|
|
38
|
+
envResolver;
|
|
39
|
+
yamlFilePath;
|
|
40
|
+
// Track outputs provided by earlier steps for dependency validation
|
|
41
|
+
availableOutputs = new Map();
|
|
42
|
+
// Platform established by the first platform-specific step ('ios' or 'android')
|
|
43
|
+
establishedPlatform = null;
|
|
44
|
+
constructor(pipeline, workflowName, config, yamlFilePath) {
|
|
45
|
+
this.pipeline = pipeline;
|
|
46
|
+
this.workflowName = workflowName;
|
|
47
|
+
this.workflow = pipeline.workflows[workflowName];
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.yamlFilePath = yamlFilePath;
|
|
50
|
+
if (!this.workflow) {
|
|
51
|
+
throw new Error(`Workflow '${workflowName}' not found in pipeline`);
|
|
52
|
+
}
|
|
53
|
+
// Create env resolver for variable interpolation
|
|
54
|
+
const platformInfo = detectPlatformInfo(pipeline, workflowName);
|
|
55
|
+
this.envResolver = new EnvResolver(pipeline, this.workflow, workflowName, platformInfo.platform, platformInfo.stack, yamlFilePath);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validates all steps in the workflow.
|
|
59
|
+
* Returns a complete validation result with all issues grouped by step and category.
|
|
60
|
+
*/
|
|
61
|
+
async validateWorkflow() {
|
|
62
|
+
const issues = [];
|
|
63
|
+
const issuesByStep = new Map();
|
|
64
|
+
const issuesByCategory = new Map();
|
|
65
|
+
let stepIndex = 0;
|
|
66
|
+
for (const yamlStep of this.workflow.steps) {
|
|
67
|
+
const parsedStep = this.parseStep(yamlStep, stepIndex);
|
|
68
|
+
// Skip unrecognized steps (they'll be handled by converter with warnings)
|
|
69
|
+
if (!hasStep(parsedStep.name)) {
|
|
70
|
+
stepIndex++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const executor = getStepExecutor(parsedStep.name);
|
|
74
|
+
// Platform compatibility check
|
|
75
|
+
const stepMetadata = getStepMetadata(parsedStep.name);
|
|
76
|
+
const stepPlatform = stepMetadata?.platform ?? 'all';
|
|
77
|
+
if (stepPlatform !== 'all') {
|
|
78
|
+
if (this.establishedPlatform === null) {
|
|
79
|
+
// First platform-specific step establishes the workflow platform
|
|
80
|
+
this.establishedPlatform = stepPlatform;
|
|
81
|
+
}
|
|
82
|
+
else if (this.establishedPlatform !== stepPlatform) {
|
|
83
|
+
// This step conflicts with the established platform
|
|
84
|
+
const platformIssue = {
|
|
85
|
+
stepName: parsedStep.title || parsedStep.name,
|
|
86
|
+
stepIndex,
|
|
87
|
+
requirement: {
|
|
88
|
+
category: 'platform',
|
|
89
|
+
name: parsedStep.name,
|
|
90
|
+
description: `'${parsedStep.name}' is an ${stepPlatform} step but this workflow targets ${this.establishedPlatform}`,
|
|
91
|
+
timing: 'pre-execution',
|
|
92
|
+
severity: 'error',
|
|
93
|
+
hint: `Remove this step or move it to a dedicated ${stepPlatform} workflow`,
|
|
94
|
+
check: async () => ({
|
|
95
|
+
passed: false,
|
|
96
|
+
message: `Cross-platform step: '${parsedStep.name}' (${stepPlatform}) cannot be used in an ${this.establishedPlatform} workflow`,
|
|
97
|
+
}),
|
|
98
|
+
},
|
|
99
|
+
result: {
|
|
100
|
+
passed: false,
|
|
101
|
+
message: `Cross-platform step: '${parsedStep.name}' (${stepPlatform}) cannot be used in an ${this.establishedPlatform} workflow`,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
issues.push(platformIssue);
|
|
105
|
+
const stepIssues = issuesByStep.get(platformIssue.stepName) || [];
|
|
106
|
+
stepIssues.push(platformIssue);
|
|
107
|
+
issuesByStep.set(platformIssue.stepName, stepIssues);
|
|
108
|
+
const catIssues = issuesByCategory.get('platform') || [];
|
|
109
|
+
catIssues.push(platformIssue);
|
|
110
|
+
issuesByCategory.set('platform', catIssues);
|
|
111
|
+
stepIndex++;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Skip all validation for steps that will be skipped in local mode
|
|
116
|
+
if (this.config.local && executor.isSkippedInLocalMode?.()) {
|
|
117
|
+
if (executor.getOutputs) {
|
|
118
|
+
for (const output of executor.getOutputs()) {
|
|
119
|
+
this.availableOutputs.set(output.name, { stepName: parsedStep.name, output });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
stepIndex++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Get validation requirements if the executor supports it
|
|
126
|
+
if (executor.getValidationRequirements) {
|
|
127
|
+
// Interpolate inputs (but don't throw on missing vars during validation)
|
|
128
|
+
let interpolatedInputs = parsedStep.inputs;
|
|
129
|
+
try {
|
|
130
|
+
interpolatedInputs = this.envResolver.interpolateObject(parsedStep.inputs, parsedStep.title || parsedStep.name);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Interpolation failed because a variable isn't set yet.
|
|
134
|
+
// Use a lenient copy: strip unresolved $VAR / ${VAR} references to ''
|
|
135
|
+
// so getValidationRequirements can correctly detect them as missing.
|
|
136
|
+
interpolatedInputs = this.stripUnresolvedVars(parsedStep.inputs);
|
|
137
|
+
}
|
|
138
|
+
const requirements = executor.getValidationRequirements(interpolatedInputs, this.envResolver.getAll(), this.config);
|
|
139
|
+
// Validate each requirement
|
|
140
|
+
for (const req of requirements) {
|
|
141
|
+
const issue = await this.validateRequirement(req, parsedStep.title || parsedStep.name, stepIndex);
|
|
142
|
+
if (issue) {
|
|
143
|
+
issues.push(issue);
|
|
144
|
+
// Group by step
|
|
145
|
+
const stepIssues = issuesByStep.get(issue.stepName) || [];
|
|
146
|
+
stepIssues.push(issue);
|
|
147
|
+
issuesByStep.set(issue.stepName, stepIssues);
|
|
148
|
+
// Group by category
|
|
149
|
+
const catIssues = issuesByCategory.get(issue.requirement.category) || [];
|
|
150
|
+
catIssues.push(issue);
|
|
151
|
+
issuesByCategory.set(issue.requirement.category, catIssues);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Auto-discover any $VAR references in raw inputs that aren't covered above
|
|
155
|
+
const resolvedEnv = this.envResolver.getAll();
|
|
156
|
+
const alreadyDeclared = new Set(requirements.map((r) => r.name));
|
|
157
|
+
const referencedVars = this.extractVariableReferences(parsedStep.inputs);
|
|
158
|
+
for (const varName of referencedVars) {
|
|
159
|
+
// Skip POSIX vars the shell always provides
|
|
160
|
+
if (POSIX_ENV_ALLOWLIST.has(varName))
|
|
161
|
+
continue;
|
|
162
|
+
// Skip if already in resolved env (use hasOwnProperty so empty-string
|
|
163
|
+
// values set by the YAML/secrets are still treated as "provided")
|
|
164
|
+
if (Object.prototype.hasOwnProperty.call(resolvedEnv, varName))
|
|
165
|
+
continue;
|
|
166
|
+
// Skip if provided as output by an earlier step
|
|
167
|
+
if (this.availableOutputs.has(varName))
|
|
168
|
+
continue;
|
|
169
|
+
// Skip if already declared by getValidationRequirements
|
|
170
|
+
if (alreadyDeclared.has(varName))
|
|
171
|
+
continue;
|
|
172
|
+
// Skip if already added by a previous step
|
|
173
|
+
if (issues.some((i) => i.requirement.name === varName))
|
|
174
|
+
continue;
|
|
175
|
+
// Skippable steps won't block the build if they fail — downgrade to warning
|
|
176
|
+
// so missing vars don't prevent the run when the step can safely be skipped.
|
|
177
|
+
const severity = parsedStep.is_skippable ? 'warning' : 'error';
|
|
178
|
+
const req = {
|
|
179
|
+
category: 'environment',
|
|
180
|
+
name: varName,
|
|
181
|
+
description: `${varName} is not set`,
|
|
182
|
+
timing: 'pre-execution',
|
|
183
|
+
severity,
|
|
184
|
+
check: async () => ({ passed: false, message: `${varName} is not set` }),
|
|
185
|
+
};
|
|
186
|
+
const issue = await this.validateRequirement(req, parsedStep.title || parsedStep.name, stepIndex);
|
|
187
|
+
if (issue) {
|
|
188
|
+
issues.push(issue);
|
|
189
|
+
const stepIssues = issuesByStep.get(issue.stepName) || [];
|
|
190
|
+
stepIssues.push(issue);
|
|
191
|
+
issuesByStep.set(issue.stepName, stepIssues);
|
|
192
|
+
const catIssues = issuesByCategory.get(issue.requirement.category) || [];
|
|
193
|
+
catIssues.push(issue);
|
|
194
|
+
issuesByCategory.set(issue.requirement.category, catIssues);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Record outputs this step provides for later steps
|
|
199
|
+
if (executor.getOutputs) {
|
|
200
|
+
for (const output of executor.getOutputs()) {
|
|
201
|
+
this.availableOutputs.set(output.name, {
|
|
202
|
+
stepName: parsedStep.name,
|
|
203
|
+
output,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
stepIndex++;
|
|
208
|
+
}
|
|
209
|
+
// Count issues by severity
|
|
210
|
+
const counts = {
|
|
211
|
+
errors: issues.filter((i) => i.requirement.severity === 'error').length,
|
|
212
|
+
warnings: issues.filter((i) => i.requirement.severity === 'warning').length,
|
|
213
|
+
info: issues.filter((i) => i.requirement.severity === 'info').length,
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
valid: counts.errors === 0,
|
|
217
|
+
issues,
|
|
218
|
+
issuesByStep,
|
|
219
|
+
issuesByCategory,
|
|
220
|
+
counts,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Validates a single requirement and returns an issue if it fails
|
|
225
|
+
*/
|
|
226
|
+
async validateRequirement(requirement, stepName, stepIndex) {
|
|
227
|
+
// Skip runtime-only checks (they can't be validated before execution)
|
|
228
|
+
if (requirement.timing === 'runtime') {
|
|
229
|
+
// For runtime dependencies, check if the providing step exists earlier
|
|
230
|
+
if (requirement.providedBy) {
|
|
231
|
+
const provider = this.availableOutputs.get(requirement.name);
|
|
232
|
+
if (!provider) {
|
|
233
|
+
// Output not provided by any earlier step - this is an info message
|
|
234
|
+
return {
|
|
235
|
+
stepName,
|
|
236
|
+
stepIndex,
|
|
237
|
+
requirement,
|
|
238
|
+
result: {
|
|
239
|
+
passed: false,
|
|
240
|
+
message: `'${requirement.name}' will be provided by '${requirement.providedBy}' step at runtime`,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null; // Skip other runtime checks
|
|
246
|
+
}
|
|
247
|
+
// Run the check if available
|
|
248
|
+
if (requirement.check) {
|
|
249
|
+
try {
|
|
250
|
+
const result = await requirement.check();
|
|
251
|
+
if (!result.passed) {
|
|
252
|
+
return {
|
|
253
|
+
stepName,
|
|
254
|
+
stepIndex,
|
|
255
|
+
requirement,
|
|
256
|
+
result,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
stepName,
|
|
263
|
+
stepIndex,
|
|
264
|
+
requirement,
|
|
265
|
+
result: {
|
|
266
|
+
passed: false,
|
|
267
|
+
message: `Check failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Extracts all $VAR and ${VAR} variable names referenced in an object recursively.
|
|
276
|
+
*/
|
|
277
|
+
extractVariableReferences(obj) {
|
|
278
|
+
const vars = new Set();
|
|
279
|
+
if (typeof obj === 'string') {
|
|
280
|
+
const pattern = /\$\{([^}]+)\}|\$([A-Z_][A-Z_0-9]*)/g;
|
|
281
|
+
let match;
|
|
282
|
+
while ((match = pattern.exec(obj)) !== null) {
|
|
283
|
+
vars.add(match[1] || match[2]);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else if (Array.isArray(obj)) {
|
|
287
|
+
for (const item of obj) {
|
|
288
|
+
for (const v of this.extractVariableReferences(item))
|
|
289
|
+
vars.add(v);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if (obj && typeof obj === 'object') {
|
|
293
|
+
for (const value of Object.values(obj)) {
|
|
294
|
+
for (const v of this.extractVariableReferences(value))
|
|
295
|
+
vars.add(v);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return vars;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Replaces all unresolved $VAR and ${VAR} references with empty string.
|
|
302
|
+
* Used as a lenient fallback when full interpolation fails, so that
|
|
303
|
+
* getValidationRequirements can detect truly-missing variables correctly.
|
|
304
|
+
*/
|
|
305
|
+
stripUnresolvedVars(obj) {
|
|
306
|
+
if (typeof obj === 'string') {
|
|
307
|
+
return obj.replace(/\$\{[^}]+\}|\$[A-Z_][A-Z_0-9]*/g, '');
|
|
308
|
+
}
|
|
309
|
+
if (Array.isArray(obj)) {
|
|
310
|
+
return obj.map((item) => this.stripUnresolvedVars(item));
|
|
311
|
+
}
|
|
312
|
+
if (obj && typeof obj === 'object') {
|
|
313
|
+
const result = {};
|
|
314
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
315
|
+
result[key] = this.stripUnresolvedVars(value);
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
return obj;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Parses a YAML step into a ParsedStep (simplified version of converter logic)
|
|
323
|
+
*/
|
|
324
|
+
parseStep(yamlStep, _index) {
|
|
325
|
+
const stepKeys = Object.keys(yamlStep);
|
|
326
|
+
const stepNameWithVersion = stepKeys[0];
|
|
327
|
+
const stepConfig = yamlStep[stepNameWithVersion];
|
|
328
|
+
const atIndex = stepNameWithVersion.indexOf('@');
|
|
329
|
+
const name = atIndex === -1 ? stepNameWithVersion : stepNameWithVersion.substring(0, atIndex);
|
|
330
|
+
const version = atIndex === -1 ? undefined : stepNameWithVersion.substring(atIndex + 1);
|
|
331
|
+
let inputs = stepConfig?.inputs || {};
|
|
332
|
+
if (Array.isArray(inputs)) {
|
|
333
|
+
const inputsObj = {};
|
|
334
|
+
for (const input of inputs) {
|
|
335
|
+
if (typeof input === 'object' && input !== null) {
|
|
336
|
+
Object.assign(inputsObj, input);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
inputs = inputsObj;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
name,
|
|
343
|
+
version,
|
|
344
|
+
title: stepConfig?.title,
|
|
345
|
+
inputs,
|
|
346
|
+
run_if: stepConfig?.run_if,
|
|
347
|
+
is_always_run: stepConfig?.is_always_run,
|
|
348
|
+
is_skippable: stepConfig?.is_skippable,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Format validation result for console output
|
|
354
|
+
*/
|
|
355
|
+
export function formatValidationResult(result) {
|
|
356
|
+
if (result.valid && result.issues.length === 0) {
|
|
357
|
+
return '\n✅ All pre-execution validations passed\n';
|
|
358
|
+
}
|
|
359
|
+
const lines = [];
|
|
360
|
+
lines.push('\n' + '═'.repeat(70));
|
|
361
|
+
lines.push('PRE-EXECUTION VALIDATION RESULTS');
|
|
362
|
+
lines.push('═'.repeat(70));
|
|
363
|
+
// Summary
|
|
364
|
+
lines.push(`\n📊 Summary: ${result.counts.errors} error${result.counts.errors !== 1 ? 's' : ''}, ` +
|
|
365
|
+
`${result.counts.warnings} warning${result.counts.warnings !== 1 ? 's' : ''}, ` +
|
|
366
|
+
`${result.counts.info} info\n`);
|
|
367
|
+
// Group by step
|
|
368
|
+
for (const [stepName, stepIssues] of result.issuesByStep) {
|
|
369
|
+
const errors = stepIssues.filter((i) => i.requirement.severity === 'error');
|
|
370
|
+
const warnings = stepIssues.filter((i) => i.requirement.severity === 'warning');
|
|
371
|
+
const infos = stepIssues.filter((i) => i.requirement.severity === 'info');
|
|
372
|
+
lines.push(`\n📦 Step: ${stepName}`);
|
|
373
|
+
lines.push('─'.repeat(50));
|
|
374
|
+
for (const issue of errors) {
|
|
375
|
+
lines.push(` ❌ ERROR: ${issue.requirement.description}`);
|
|
376
|
+
lines.push(` ${issue.result.message}`);
|
|
377
|
+
if (issue.requirement.hint) {
|
|
378
|
+
lines.push(` 💡 ${issue.requirement.hint}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
for (const issue of warnings) {
|
|
382
|
+
lines.push(` ⚠️ WARNING: ${issue.requirement.description}`);
|
|
383
|
+
lines.push(` ${issue.result.message}`);
|
|
384
|
+
if (issue.requirement.hint) {
|
|
385
|
+
lines.push(` 💡 ${issue.requirement.hint}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
for (const issue of infos) {
|
|
389
|
+
lines.push(` ℹ️ INFO: ${issue.requirement.description}`);
|
|
390
|
+
lines.push(` ${issue.result.message}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
lines.push('\n' + '═'.repeat(70));
|
|
394
|
+
if (result.counts.errors > 0) {
|
|
395
|
+
lines.push('❌ Validation failed. Please fix the errors above before running the pipeline.');
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
lines.push('⚠️ Validation passed with warnings. Pipeline can proceed.');
|
|
399
|
+
}
|
|
400
|
+
lines.push('═'.repeat(70) + '\n');
|
|
401
|
+
return lines.join('\n');
|
|
402
|
+
}
|
|
403
|
+
//# sourceMappingURL=step-validator.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android signing step: sign-apk
|
|
3
|
+
*/
|
|
4
|
+
import { BaseStepExecutor } from './base.js';
|
|
5
|
+
import type { StepDef, CIConfig } from '../../types.js';
|
|
6
|
+
import type { ValidationRequirement, StepOutput } from '../validation-types.js';
|
|
7
|
+
export interface SignApkInputs {
|
|
8
|
+
/** Path(s) to APK or AAB file(s), pipe-separated */
|
|
9
|
+
android_app?: string;
|
|
10
|
+
/** Keystore file URL or local path (file://...) */
|
|
11
|
+
keystore_url?: string;
|
|
12
|
+
/** Keystore password */
|
|
13
|
+
keystore_password?: string;
|
|
14
|
+
/** Key alias inside the keystore */
|
|
15
|
+
keystore_alias?: string;
|
|
16
|
+
/** Private key password (defaults to keystore_password) */
|
|
17
|
+
private_key_password?: string;
|
|
18
|
+
/** Page alignment for .so files: automatic | true | false */
|
|
19
|
+
page_align?: string;
|
|
20
|
+
/** Signing tool: automatic | apksigner | jarsigner */
|
|
21
|
+
signer_tool?: string;
|
|
22
|
+
/** Output artifact name (without extension) */
|
|
23
|
+
output_name?: string;
|
|
24
|
+
/** Enable verbose logging */
|
|
25
|
+
verbose_log?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Signs APK/AAB files using apksigner or jarsigner with zipalign.
|
|
29
|
+
*/
|
|
30
|
+
export declare class SignApkStepExecutor extends BaseStepExecutor {
|
|
31
|
+
getValidationRequirements(inputs: SignApkInputs, env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
|
|
32
|
+
getOutputs(): StepOutput[];
|
|
33
|
+
execute(inputs: SignApkInputs, _env: Record<string, string>, _config: CIConfig): Promise<StepDef>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=android-sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-sign.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android-sign.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,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,gBAAgB;IACvD,yBAAyB,CACvB,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAM1B,UAAU,IAAI,UAAU,EAAE;IAOpB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CA0IxG"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android signing step: sign-apk
|
|
3
|
+
*/
|
|
4
|
+
import { BaseStepExecutor } from './base.js';
|
|
5
|
+
/**
|
|
6
|
+
* Signs APK/AAB files using apksigner or jarsigner with zipalign.
|
|
7
|
+
*/
|
|
8
|
+
export class SignApkStepExecutor extends BaseStepExecutor {
|
|
9
|
+
getValidationRequirements(inputs, env, _config) {
|
|
10
|
+
return [
|
|
11
|
+
this.requireCommand('zipalign', 'Android zipalign tool is required', 'Install Android SDK Build Tools'),
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
getOutputs() {
|
|
15
|
+
return [
|
|
16
|
+
{ name: 'CIBUILD_SIGNED_APK_PATH', type: 'environment', description: 'Path to the signed APK' },
|
|
17
|
+
{ name: 'CIBUILD_SIGNED_AAB_PATH', type: 'environment', description: 'Path to the signed AAB' },
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
async execute(inputs, _env, _config) {
|
|
21
|
+
const stepName = 'sign-apk';
|
|
22
|
+
const androidApp = this.getInput(inputs, 'android_app', '$CIBUILD_APK_PATH');
|
|
23
|
+
const keystoreUrl = this.getInput(inputs, 'keystore_url', '$KEYSTORE_URL');
|
|
24
|
+
const keystorePassword = this.getInput(inputs, 'keystore_password', '$KEYSTORE_PASSWORD');
|
|
25
|
+
const keystoreAlias = this.getInput(inputs, 'keystore_alias', '$KEYSTORE_ALIAS');
|
|
26
|
+
const privateKeyPassword = this.getInput(inputs, 'private_key_password', '');
|
|
27
|
+
const pageAlign = this.getInput(inputs, 'page_align', 'automatic');
|
|
28
|
+
const signerTool = this.getInput(inputs, 'signer_tool', 'automatic');
|
|
29
|
+
const outputName = this.getInput(inputs, 'output_name', '');
|
|
30
|
+
const verboseLog = this.getInput(inputs, 'verbose_log', false);
|
|
31
|
+
const commands = [];
|
|
32
|
+
commands.push('# sign-apk — sign Android APK/AAB');
|
|
33
|
+
commands.push('echo "🔐 Signing Android app..."');
|
|
34
|
+
commands.push('');
|
|
35
|
+
// Resolve keystore
|
|
36
|
+
commands.push('# Resolve keystore file');
|
|
37
|
+
commands.push(`KEYSTORE_URL="${keystoreUrl}"`);
|
|
38
|
+
commands.push(`KEYSTORE_PASSWORD="${keystorePassword}"`);
|
|
39
|
+
commands.push(`KEYSTORE_ALIAS="${keystoreAlias}"`);
|
|
40
|
+
if (privateKeyPassword) {
|
|
41
|
+
commands.push(`KEY_PASSWORD="${this.escapeBash(privateKeyPassword)}"`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
commands.push('KEY_PASSWORD="$KEYSTORE_PASSWORD"');
|
|
45
|
+
}
|
|
46
|
+
commands.push('');
|
|
47
|
+
commands.push('if [ -z "$KEYSTORE_URL" ] || [ -z "$KEYSTORE_PASSWORD" ] || [ -z "$KEYSTORE_ALIAS" ]; then');
|
|
48
|
+
commands.push(' echo "❌ Error: keystore_url, keystore_password, and keystore_alias are required"');
|
|
49
|
+
commands.push(' exit 1');
|
|
50
|
+
commands.push('fi');
|
|
51
|
+
commands.push('');
|
|
52
|
+
// Download or resolve keystore
|
|
53
|
+
commands.push('# Download or resolve keystore file');
|
|
54
|
+
commands.push('if [[ "$KEYSTORE_URL" == file://* ]]; then');
|
|
55
|
+
commands.push(' KEYSTORE_PATH="${KEYSTORE_URL#file://}"');
|
|
56
|
+
commands.push('elif [[ "$KEYSTORE_URL" == http* ]]; then');
|
|
57
|
+
commands.push(' KEYSTORE_PATH="$(mktemp -t keystore).jks"');
|
|
58
|
+
commands.push(' curl -sSfL "$KEYSTORE_URL" -o "$KEYSTORE_PATH"');
|
|
59
|
+
commands.push('else');
|
|
60
|
+
commands.push(' KEYSTORE_PATH="$KEYSTORE_URL"');
|
|
61
|
+
commands.push('fi');
|
|
62
|
+
commands.push('');
|
|
63
|
+
commands.push('if [ ! -f "$KEYSTORE_PATH" ]; then');
|
|
64
|
+
commands.push(' echo "❌ Error: Keystore file not found: $KEYSTORE_PATH"');
|
|
65
|
+
commands.push(' exit 1');
|
|
66
|
+
commands.push('fi');
|
|
67
|
+
commands.push('');
|
|
68
|
+
// Determine signer tool
|
|
69
|
+
commands.push('# Determine signing tool');
|
|
70
|
+
if (signerTool === 'automatic') {
|
|
71
|
+
commands.push('if command -v apksigner &>/dev/null; then');
|
|
72
|
+
commands.push(' SIGNER="apksigner"');
|
|
73
|
+
commands.push('elif command -v jarsigner &>/dev/null; then');
|
|
74
|
+
commands.push(' SIGNER="jarsigner"');
|
|
75
|
+
commands.push('else');
|
|
76
|
+
commands.push(' echo "❌ Error: Neither apksigner nor jarsigner found"');
|
|
77
|
+
commands.push(' exit 1');
|
|
78
|
+
commands.push('fi');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
commands.push(`SIGNER="${this.escapeBash(signerTool)}"`);
|
|
82
|
+
}
|
|
83
|
+
commands.push('echo "Using signer: $SIGNER"');
|
|
84
|
+
commands.push('');
|
|
85
|
+
// Process each app file
|
|
86
|
+
commands.push(`ANDROID_APP="${androidApp}"`);
|
|
87
|
+
commands.push('SIGNED_APK_PATH=""');
|
|
88
|
+
commands.push('SIGNED_AAB_PATH=""');
|
|
89
|
+
commands.push('');
|
|
90
|
+
commands.push('IFS="|" read -ra APP_FILES <<< "$ANDROID_APP"');
|
|
91
|
+
commands.push('for APP_FILE in "${APP_FILES[@]}"; do');
|
|
92
|
+
commands.push(' APP_FILE="$(echo "$APP_FILE" | xargs)" # trim whitespace');
|
|
93
|
+
commands.push(' [ -z "$APP_FILE" ] && continue');
|
|
94
|
+
commands.push(' ');
|
|
95
|
+
commands.push(' if [ ! -f "$APP_FILE" ]; then');
|
|
96
|
+
commands.push(' echo "⚠️ File not found: $APP_FILE — skipping"');
|
|
97
|
+
commands.push(' continue');
|
|
98
|
+
commands.push(' fi');
|
|
99
|
+
commands.push(' ');
|
|
100
|
+
commands.push(' EXT="${APP_FILE##*.}"');
|
|
101
|
+
commands.push(' DIR="$(dirname "$APP_FILE")"');
|
|
102
|
+
commands.push(' BASE="$(basename "$APP_FILE" ".$EXT")"');
|
|
103
|
+
if (outputName) {
|
|
104
|
+
commands.push(` OUT_NAME="${this.escapeBash(outputName)}"`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
commands.push(' OUT_NAME="${BASE}-signed"');
|
|
108
|
+
}
|
|
109
|
+
commands.push(' SIGNED_FILE="$DIR/${OUT_NAME}.${EXT}"');
|
|
110
|
+
commands.push(' ');
|
|
111
|
+
// zipalign for APKs
|
|
112
|
+
commands.push(' if [ "$EXT" = "apk" ]; then');
|
|
113
|
+
commands.push(' echo "Zipaligning: $APP_FILE"');
|
|
114
|
+
const alignFlag = pageAlign === 'true' ? '-p' : '';
|
|
115
|
+
commands.push(` zipalign ${alignFlag} -f 4 "$APP_FILE" "$DIR/\${BASE}-aligned.apk"`);
|
|
116
|
+
commands.push(' APP_FILE="$DIR/${BASE}-aligned.apk"');
|
|
117
|
+
commands.push(' fi');
|
|
118
|
+
commands.push(' ');
|
|
119
|
+
// Sign
|
|
120
|
+
commands.push(' echo "Signing: $APP_FILE → $SIGNED_FILE"');
|
|
121
|
+
commands.push(' if [ "$SIGNER" = "apksigner" ]; then');
|
|
122
|
+
const verboseFlag = verboseLog ? ' --verbose' : '';
|
|
123
|
+
commands.push(` apksigner sign${verboseFlag} --ks "$KEYSTORE_PATH" --ks-pass "pass:$KEYSTORE_PASSWORD" --ks-key-alias "$KEYSTORE_ALIAS" --key-pass "pass:$KEY_PASSWORD" --out "$SIGNED_FILE" "$APP_FILE"`);
|
|
124
|
+
commands.push(' else');
|
|
125
|
+
commands.push(' cp "$APP_FILE" "$SIGNED_FILE"');
|
|
126
|
+
commands.push(` jarsigner${verboseLog ? ' -verbose' : ''} -keystore "$KEYSTORE_PATH" -storepass "$KEYSTORE_PASSWORD" -keypass "$KEY_PASSWORD" "$SIGNED_FILE" "$KEYSTORE_ALIAS"`);
|
|
127
|
+
commands.push(' fi');
|
|
128
|
+
commands.push(' ');
|
|
129
|
+
// Track outputs
|
|
130
|
+
commands.push(' if [ "$EXT" = "apk" ]; then');
|
|
131
|
+
commands.push(' SIGNED_APK_PATH="$SIGNED_FILE"');
|
|
132
|
+
commands.push(' elif [ "$EXT" = "aab" ]; then');
|
|
133
|
+
commands.push(' SIGNED_AAB_PATH="$SIGNED_FILE"');
|
|
134
|
+
commands.push(' fi');
|
|
135
|
+
commands.push(' echo "✅ Signed: $SIGNED_FILE"');
|
|
136
|
+
commands.push('done');
|
|
137
|
+
commands.push('');
|
|
138
|
+
// Export
|
|
139
|
+
commands.push('# Export signed paths');
|
|
140
|
+
commands.push('[ -n "$SIGNED_APK_PATH" ] && envman add --key CIBUILD_SIGNED_APK_PATH --value "$SIGNED_APK_PATH"');
|
|
141
|
+
commands.push('[ -n "$SIGNED_AAB_PATH" ] && envman add --key CIBUILD_SIGNED_AAB_PATH --value "$SIGNED_AAB_PATH"');
|
|
142
|
+
commands.push('echo "✅ Signing complete"');
|
|
143
|
+
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
144
|
+
return this.createScriptStep(script, stepName);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=android-sign.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android version code and version name step implementation
|
|
3
|
+
* Updates versionCode and versionName in build.gradle
|
|
4
|
+
*/
|
|
5
|
+
import { BaseStepExecutor } from './base.js';
|
|
6
|
+
import type { StepDef, CIConfig } from '../../types.js';
|
|
7
|
+
import type { ValidationRequirement, StepOutput } from '../validation-types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Inputs for change-android-versioncode-and-versionname step
|
|
10
|
+
*/
|
|
11
|
+
export interface ChangeAndroidVersionInputs {
|
|
12
|
+
build_gradle_path?: string;
|
|
13
|
+
new_version_name?: string;
|
|
14
|
+
new_version_code?: string;
|
|
15
|
+
version_code_offset?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Change Android versionCode and versionName step executor
|
|
19
|
+
* Modifies build.gradle in-place using sed before the build runs
|
|
20
|
+
*/
|
|
21
|
+
export declare class ChangeAndroidVersionStepExecutor extends BaseStepExecutor {
|
|
22
|
+
getOutputs(): StepOutput[];
|
|
23
|
+
getValidationRequirements(inputs: ChangeAndroidVersionInputs, _env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
|
|
24
|
+
execute(inputs: ChangeAndroidVersionInputs, _env: Record<string, string>, _config: CIConfig): Promise<StepDef>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=android-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-version.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/android-version.ts"],"names":[],"mappings":"AAAA;;;GAGG;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,0BAA0B;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,qBAAa,gCAAiC,SAAQ,gBAAgB;IACpE,UAAU,IAAI,UAAU,EAAE;IAO1B,yBAAyB,CACvB,MAAM,EAAE,0BAA0B,EAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAMpB,OAAO,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAoHrH"}
|