@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.
Files changed (242) hide show
  1. package/dist/cli.cjs +1 -1
  2. package/dist/src/cli.d.ts +3 -0
  3. package/dist/src/cli.d.ts.map +1 -0
  4. package/dist/src/cli.js +987 -0
  5. package/dist/src/commands/android-scanner.d.ts +32 -0
  6. package/dist/src/commands/android-scanner.d.ts.map +1 -0
  7. package/dist/src/commands/android-scanner.js +667 -0
  8. package/dist/src/commands/build.d.ts +5 -0
  9. package/dist/src/commands/build.d.ts.map +1 -0
  10. package/dist/src/commands/build.js +1096 -0
  11. package/dist/src/commands/edit.d.ts +3 -0
  12. package/dist/src/commands/edit.d.ts.map +1 -0
  13. package/dist/src/commands/edit.js +651 -0
  14. package/dist/src/commands/file-secret-collector.d.ts +37 -0
  15. package/dist/src/commands/file-secret-collector.d.ts.map +1 -0
  16. package/dist/src/commands/file-secret-collector.js +199 -0
  17. package/dist/src/commands/github-workflow.d.ts +5 -0
  18. package/dist/src/commands/github-workflow.d.ts.map +1 -0
  19. package/dist/src/commands/github-workflow.js +45 -0
  20. package/dist/src/commands/ios-scanner.d.ts +27 -0
  21. package/dist/src/commands/ios-scanner.d.ts.map +1 -0
  22. package/dist/src/commands/ios-scanner.js +337 -0
  23. package/dist/src/commands/reset.d.ts +7 -0
  24. package/dist/src/commands/reset.d.ts.map +1 -0
  25. package/dist/src/commands/reset.js +81 -0
  26. package/dist/src/commands/secrets-sync-workflow.d.ts +15 -0
  27. package/dist/src/commands/secrets-sync-workflow.d.ts.map +1 -0
  28. package/dist/src/commands/secrets-sync-workflow.js +255 -0
  29. package/dist/src/commands/secrets-upload.d.ts +21 -0
  30. package/dist/src/commands/secrets-upload.d.ts.map +1 -0
  31. package/dist/src/commands/secrets-upload.js +177 -0
  32. package/dist/src/commands/secrets-upload.test.d.ts +5 -0
  33. package/dist/src/commands/secrets-upload.test.d.ts.map +1 -0
  34. package/dist/src/commands/secrets-upload.test.js +60 -0
  35. package/dist/src/config.d.ts +3 -0
  36. package/dist/src/config.d.ts.map +1 -0
  37. package/dist/src/config.js +46 -0
  38. package/dist/src/envman/cli.d.ts +21 -0
  39. package/dist/src/envman/cli.d.ts.map +1 -0
  40. package/dist/src/envman/cli.js +240 -0
  41. package/dist/src/envman/envman.d.ts +83 -0
  42. package/dist/src/envman/envman.d.ts.map +1 -0
  43. package/dist/src/envman/envman.js +361 -0
  44. package/dist/src/envman/envman.test.d.ts +5 -0
  45. package/dist/src/envman/envman.test.d.ts.map +1 -0
  46. package/dist/src/envman/envman.test.js +236 -0
  47. package/dist/src/envman/index.d.ts +23 -0
  48. package/dist/src/envman/index.d.ts.map +1 -0
  49. package/dist/src/envman/index.js +23 -0
  50. package/dist/src/envman/types.d.ts +55 -0
  51. package/dist/src/envman/types.d.ts.map +1 -0
  52. package/dist/src/envman/types.js +12 -0
  53. package/dist/src/lib.d.ts +27 -0
  54. package/dist/src/lib.d.ts.map +1 -0
  55. package/dist/src/lib.js +32 -0
  56. package/dist/src/pipeline.d.ts +3 -0
  57. package/dist/src/pipeline.d.ts.map +1 -0
  58. package/dist/src/pipeline.js +57 -0
  59. package/dist/src/runner.d.ts +17 -0
  60. package/dist/src/runner.d.ts.map +1 -0
  61. package/dist/src/runner.js +234 -0
  62. package/dist/src/types.d.ts +57 -0
  63. package/dist/src/types.d.ts.map +1 -0
  64. package/dist/src/types.js +2 -0
  65. package/dist/src/yaml/bitrise-compat.d.ts +65 -0
  66. package/dist/src/yaml/bitrise-compat.d.ts.map +1 -0
  67. package/dist/src/yaml/bitrise-compat.js +206 -0
  68. package/dist/src/yaml/bitrise-compat.test.d.ts +5 -0
  69. package/dist/src/yaml/bitrise-compat.test.d.ts.map +1 -0
  70. package/dist/src/yaml/bitrise-compat.test.js +347 -0
  71. package/dist/src/yaml/converter.d.ts +33 -0
  72. package/dist/src/yaml/converter.d.ts.map +1 -0
  73. package/dist/src/yaml/converter.js +222 -0
  74. package/dist/src/yaml/converter.test.d.ts +5 -0
  75. package/dist/src/yaml/converter.test.d.ts.map +1 -0
  76. package/dist/src/yaml/converter.test.js +348 -0
  77. package/dist/src/yaml/e2e.test.d.ts +6 -0
  78. package/dist/src/yaml/e2e.test.d.ts.map +1 -0
  79. package/dist/src/yaml/e2e.test.js +446 -0
  80. package/dist/src/yaml/env-resolver.d.ts +120 -0
  81. package/dist/src/yaml/env-resolver.d.ts.map +1 -0
  82. package/dist/src/yaml/env-resolver.js +405 -0
  83. package/dist/src/yaml/env-resolver.test.d.ts +5 -0
  84. package/dist/src/yaml/env-resolver.test.d.ts.map +1 -0
  85. package/dist/src/yaml/env-resolver.test.js +502 -0
  86. package/dist/src/yaml/interactive-prompts.d.ts +71 -0
  87. package/dist/src/yaml/interactive-prompts.d.ts.map +1 -0
  88. package/dist/src/yaml/interactive-prompts.js +258 -0
  89. package/dist/src/yaml/missing-env-handler.d.ts +45 -0
  90. package/dist/src/yaml/missing-env-handler.d.ts.map +1 -0
  91. package/dist/src/yaml/missing-env-handler.js +64 -0
  92. package/dist/src/yaml/parser.d.ts +33 -0
  93. package/dist/src/yaml/parser.d.ts.map +1 -0
  94. package/dist/src/yaml/parser.js +145 -0
  95. package/dist/src/yaml/pipeline-with-secrets.d.ts +25 -0
  96. package/dist/src/yaml/pipeline-with-secrets.d.ts.map +1 -0
  97. package/dist/src/yaml/pipeline-with-secrets.js +76 -0
  98. package/dist/src/yaml/platform-detector.d.ts +83 -0
  99. package/dist/src/yaml/platform-detector.d.ts.map +1 -0
  100. package/dist/src/yaml/platform-detector.js +188 -0
  101. package/dist/src/yaml/platform-detector.test.d.ts +5 -0
  102. package/dist/src/yaml/platform-detector.test.d.ts.map +1 -0
  103. package/dist/src/yaml/platform-detector.test.js +414 -0
  104. package/dist/src/yaml/preflight-validation.d.ts +40 -0
  105. package/dist/src/yaml/preflight-validation.d.ts.map +1 -0
  106. package/dist/src/yaml/preflight-validation.js +152 -0
  107. package/dist/src/yaml/secrets-manager.d.ts +77 -0
  108. package/dist/src/yaml/secrets-manager.d.ts.map +1 -0
  109. package/dist/src/yaml/secrets-manager.js +219 -0
  110. package/dist/src/yaml/step-validator.d.ts +54 -0
  111. package/dist/src/yaml/step-validator.d.ts.map +1 -0
  112. package/dist/src/yaml/step-validator.js +403 -0
  113. package/dist/src/yaml/steps/android-sign.d.ts +35 -0
  114. package/dist/src/yaml/steps/android-sign.d.ts.map +1 -0
  115. package/dist/src/yaml/steps/android-sign.js +147 -0
  116. package/dist/src/yaml/steps/android-version.d.ts +26 -0
  117. package/dist/src/yaml/steps/android-version.d.ts.map +1 -0
  118. package/dist/src/yaml/steps/android-version.js +128 -0
  119. package/dist/src/yaml/steps/android-version.test.d.ts +5 -0
  120. package/dist/src/yaml/steps/android-version.test.d.ts.map +1 -0
  121. package/dist/src/yaml/steps/android-version.test.js +196 -0
  122. package/dist/src/yaml/steps/android.d.ts +95 -0
  123. package/dist/src/yaml/steps/android.d.ts.map +1 -0
  124. package/dist/src/yaml/steps/android.js +916 -0
  125. package/dist/src/yaml/steps/app-store-deploy.d.ts +48 -0
  126. package/dist/src/yaml/steps/app-store-deploy.d.ts.map +1 -0
  127. package/dist/src/yaml/steps/app-store-deploy.js +162 -0
  128. package/dist/src/yaml/steps/base.d.ts +238 -0
  129. package/dist/src/yaml/steps/base.d.ts.map +1 -0
  130. package/dist/src/yaml/steps/base.js +345 -0
  131. package/dist/src/yaml/steps/bitrise-android-tools.d.ts +26 -0
  132. package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +1 -0
  133. package/dist/src/yaml/steps/bitrise-android-tools.js +198 -0
  134. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +5 -0
  135. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +1 -0
  136. package/dist/src/yaml/steps/bitrise-android-tools.test.js +280 -0
  137. package/dist/src/yaml/steps/bitrise-apk-info.d.ts +22 -0
  138. package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +1 -0
  139. package/dist/src/yaml/steps/bitrise-apk-info.js +144 -0
  140. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +5 -0
  141. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +1 -0
  142. package/dist/src/yaml/steps/bitrise-apk-info.test.js +331 -0
  143. package/dist/src/yaml/steps/bitrise-slack.d.ts +49 -0
  144. package/dist/src/yaml/steps/bitrise-slack.d.ts.map +1 -0
  145. package/dist/src/yaml/steps/bitrise-slack.js +280 -0
  146. package/dist/src/yaml/steps/bitrise-slack.test.d.ts +5 -0
  147. package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +1 -0
  148. package/dist/src/yaml/steps/bitrise-slack.test.js +484 -0
  149. package/dist/src/yaml/steps/bitrise-ssh.d.ts +27 -0
  150. package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +1 -0
  151. package/dist/src/yaml/steps/bitrise-ssh.js +134 -0
  152. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +5 -0
  153. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +1 -0
  154. package/dist/src/yaml/steps/bitrise-ssh.test.js +205 -0
  155. package/dist/src/yaml/steps/cache.d.ts +52 -0
  156. package/dist/src/yaml/steps/cache.d.ts.map +1 -0
  157. package/dist/src/yaml/steps/cache.js +351 -0
  158. package/dist/src/yaml/steps/fastlane.d.ts +27 -0
  159. package/dist/src/yaml/steps/fastlane.d.ts.map +1 -0
  160. package/dist/src/yaml/steps/fastlane.js +79 -0
  161. package/dist/src/yaml/steps/file.d.ts +27 -0
  162. package/dist/src/yaml/steps/file.d.ts.map +1 -0
  163. package/dist/src/yaml/steps/file.js +35 -0
  164. package/dist/src/yaml/steps/flutter.d.ts +63 -0
  165. package/dist/src/yaml/steps/flutter.d.ts.map +1 -0
  166. package/dist/src/yaml/steps/flutter.js +215 -0
  167. package/dist/src/yaml/steps/git-clone.d.ts +26 -0
  168. package/dist/src/yaml/steps/git-clone.d.ts.map +1 -0
  169. package/dist/src/yaml/steps/git-clone.js +111 -0
  170. package/dist/src/yaml/steps/google-play-deploy.d.ts +37 -0
  171. package/dist/src/yaml/steps/google-play-deploy.d.ts.map +1 -0
  172. package/dist/src/yaml/steps/google-play-deploy.js +193 -0
  173. package/dist/src/yaml/steps/google-play-deploy.test.d.ts +5 -0
  174. package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +1 -0
  175. package/dist/src/yaml/steps/google-play-deploy.test.js +310 -0
  176. package/dist/src/yaml/steps/index.d.ts +10 -0
  177. package/dist/src/yaml/steps/index.d.ts.map +1 -0
  178. package/dist/src/yaml/steps/index.js +1361 -0
  179. package/dist/src/yaml/steps/ios-deps.d.ts +43 -0
  180. package/dist/src/yaml/steps/ios-deps.d.ts.map +1 -0
  181. package/dist/src/yaml/steps/ios-deps.js +141 -0
  182. package/dist/src/yaml/steps/ios-deps.test.d.ts +5 -0
  183. package/dist/src/yaml/steps/ios-deps.test.d.ts.map +1 -0
  184. package/dist/src/yaml/steps/ios-deps.test.js +90 -0
  185. package/dist/src/yaml/steps/ios-signing.d.ts +31 -0
  186. package/dist/src/yaml/steps/ios-signing.d.ts.map +1 -0
  187. package/dist/src/yaml/steps/ios-signing.js +144 -0
  188. package/dist/src/yaml/steps/ios-version.d.ts +47 -0
  189. package/dist/src/yaml/steps/ios-version.d.ts.map +1 -0
  190. package/dist/src/yaml/steps/ios-version.js +151 -0
  191. package/dist/src/yaml/steps/linting.d.ts +47 -0
  192. package/dist/src/yaml/steps/linting.d.ts.map +1 -0
  193. package/dist/src/yaml/steps/linting.js +148 -0
  194. package/dist/src/yaml/steps/phase2.test.d.ts +6 -0
  195. package/dist/src/yaml/steps/phase2.test.d.ts.map +1 -0
  196. package/dist/src/yaml/steps/phase2.test.js +197 -0
  197. package/dist/src/yaml/steps/phase3.test.d.ts +5 -0
  198. package/dist/src/yaml/steps/phase3.test.d.ts.map +1 -0
  199. package/dist/src/yaml/steps/phase3.test.js +144 -0
  200. package/dist/src/yaml/steps/phase4.test.d.ts +5 -0
  201. package/dist/src/yaml/steps/phase4.test.d.ts.map +1 -0
  202. package/dist/src/yaml/steps/phase4.test.js +166 -0
  203. package/dist/src/yaml/steps/phase5.test.d.ts +6 -0
  204. package/dist/src/yaml/steps/phase5.test.d.ts.map +1 -0
  205. package/dist/src/yaml/steps/phase5.test.js +263 -0
  206. package/dist/src/yaml/steps/registry.d.ts +88 -0
  207. package/dist/src/yaml/steps/registry.d.ts.map +1 -0
  208. package/dist/src/yaml/steps/registry.js +125 -0
  209. package/dist/src/yaml/steps/registry.test.d.ts +5 -0
  210. package/dist/src/yaml/steps/registry.test.d.ts.map +1 -0
  211. package/dist/src/yaml/steps/registry.test.js +235 -0
  212. package/dist/src/yaml/steps/release.d.ts +50 -0
  213. package/dist/src/yaml/steps/release.d.ts.map +1 -0
  214. package/dist/src/yaml/steps/release.js +154 -0
  215. package/dist/src/yaml/steps/script.d.ts +23 -0
  216. package/dist/src/yaml/steps/script.d.ts.map +1 -0
  217. package/dist/src/yaml/steps/script.js +63 -0
  218. package/dist/src/yaml/steps/spec-validation.test.d.ts +6 -0
  219. package/dist/src/yaml/steps/spec-validation.test.d.ts.map +1 -0
  220. package/dist/src/yaml/steps/spec-validation.test.js +130 -0
  221. package/dist/src/yaml/steps/steps.test.d.ts +6 -0
  222. package/dist/src/yaml/steps/steps.test.d.ts.map +1 -0
  223. package/dist/src/yaml/steps/steps.test.js +474 -0
  224. package/dist/src/yaml/steps/test-config.d.ts +3 -0
  225. package/dist/src/yaml/steps/test-config.d.ts.map +1 -0
  226. package/dist/src/yaml/steps/test-config.js +16 -0
  227. package/dist/src/yaml/steps/xcode-new.test.d.ts +5 -0
  228. package/dist/src/yaml/steps/xcode-new.test.d.ts.map +1 -0
  229. package/dist/src/yaml/steps/xcode-new.test.js +211 -0
  230. package/dist/src/yaml/steps/xcode.d.ts +222 -0
  231. package/dist/src/yaml/steps/xcode.d.ts.map +1 -0
  232. package/dist/src/yaml/steps/xcode.js +999 -0
  233. package/dist/src/yaml/types.d.ts +68 -0
  234. package/dist/src/yaml/types.d.ts.map +1 -0
  235. package/dist/src/yaml/types.js +5 -0
  236. package/dist/src/yaml/validation-types.d.ts +96 -0
  237. package/dist/src/yaml/validation-types.d.ts.map +1 -0
  238. package/dist/src/yaml/validation-types.js +8 -0
  239. package/dist/src/yaml/yaml-updater.d.ts +24 -0
  240. package/dist/src/yaml/yaml-updater.d.ts.map +1 -0
  241. package/dist/src/yaml/yaml-updater.js +128 -0
  242. package/package.json +16 -4
@@ -0,0 +1,361 @@
1
+ /**
2
+ * CI Build Environment Variable Manager (envman)
3
+ *
4
+ * A tool for managing environment variables between CI/CD pipeline steps.
5
+ * Similar to Bitrise's envman, this allows scripts to register outputs
6
+ * that subsequent steps can use as environment variables.
7
+ *
8
+ * Usage:
9
+ * envman add --key KEY --value 'value'
10
+ * envman add --key KEY --valuefile /path/to/file
11
+ * echo "value" | envman add --key KEY
12
+ * envman run <command>
13
+ * envman print [--format json|export]
14
+ * envman clear
15
+ */
16
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'node:fs';
17
+ import { spawn } from 'node:child_process';
18
+ import { resolve } from 'node:path';
19
+ import { DEFAULT_STORE_FILE, STORE_VERSION } from './types.js';
20
+ /**
21
+ * Get the path to the envstore file
22
+ * Uses ENVMAN_ENVSTORE_PATH env var if set, otherwise defaults to .envstore.json in cwd
23
+ */
24
+ export function getStorePath() {
25
+ return process.env.ENVMAN_ENVSTORE_PATH || resolve(process.cwd(), DEFAULT_STORE_FILE);
26
+ }
27
+ /**
28
+ * Load the environment store from disk
29
+ * Returns an empty store if file doesn't exist
30
+ */
31
+ export function loadStore(storePath) {
32
+ const path = storePath || getStorePath();
33
+ if (!existsSync(path)) {
34
+ return createEmptyStore();
35
+ }
36
+ try {
37
+ const content = readFileSync(path, 'utf-8');
38
+ const store = JSON.parse(content);
39
+ // Validate store structure
40
+ if (!store.version || !Array.isArray(store.envs)) {
41
+ console.error('Warning: Invalid envstore format, creating new store');
42
+ return createEmptyStore();
43
+ }
44
+ return store;
45
+ }
46
+ catch (error) {
47
+ console.error(`Warning: Failed to read envstore at ${path}, creating new store`);
48
+ return createEmptyStore();
49
+ }
50
+ }
51
+ /**
52
+ * Save the environment store to disk
53
+ */
54
+ export function saveStore(store, storePath) {
55
+ const path = storePath || getStorePath();
56
+ writeFileSync(path, JSON.stringify(store, null, 2), 'utf-8');
57
+ }
58
+ /**
59
+ * Create an empty environment store
60
+ */
61
+ export function createEmptyStore() {
62
+ return {
63
+ version: STORE_VERSION,
64
+ envs: [],
65
+ };
66
+ }
67
+ /**
68
+ * Initialize a new envstore file
69
+ * Creates an empty store at the specified path (or default)
70
+ */
71
+ export function init(storePath) {
72
+ const path = storePath || getStorePath();
73
+ if (existsSync(path)) {
74
+ return {
75
+ success: false,
76
+ error: `Envstore already exists at ${path}. Use 'clear' to reset.`,
77
+ };
78
+ }
79
+ const store = createEmptyStore();
80
+ saveStore(store, path);
81
+ return {
82
+ success: true,
83
+ message: `Created envstore at ${path}`,
84
+ };
85
+ }
86
+ /**
87
+ * Add an environment variable to the store
88
+ */
89
+ export function add(options, storePath) {
90
+ const { key, value, valueFile, sensitive, skipIfEmpty, append } = options;
91
+ if (!key || key.trim() === '') {
92
+ return {
93
+ success: false,
94
+ error: 'Key is required',
95
+ };
96
+ }
97
+ // Validate key format (must be valid env var name)
98
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
99
+ return {
100
+ success: false,
101
+ error: `Invalid key '${key}'. Must start with letter or underscore, contain only letters, numbers, and underscores.`,
102
+ };
103
+ }
104
+ let finalValue;
105
+ // Determine the value source
106
+ if (valueFile) {
107
+ // Read from file
108
+ if (!existsSync(valueFile)) {
109
+ return {
110
+ success: false,
111
+ error: `Value file not found: ${valueFile}`,
112
+ };
113
+ }
114
+ finalValue = readFileSync(valueFile, 'utf-8');
115
+ }
116
+ else if (value !== undefined) {
117
+ // Use provided value
118
+ finalValue = value;
119
+ }
120
+ else {
121
+ return {
122
+ success: false,
123
+ error: 'Either --value or --valuefile must be provided',
124
+ };
125
+ }
126
+ // Handle skip if empty
127
+ if (skipIfEmpty && finalValue.trim() === '') {
128
+ return {
129
+ success: true,
130
+ message: `Skipped empty value for key '${key}'`,
131
+ };
132
+ }
133
+ // Load existing store
134
+ const store = loadStore(storePath);
135
+ const now = new Date().toISOString();
136
+ // Find existing entry
137
+ const existingIndex = store.envs.findIndex(e => e.key === key);
138
+ if (existingIndex >= 0) {
139
+ // Update existing entry
140
+ const existing = store.envs[existingIndex];
141
+ if (append) {
142
+ existing.value += finalValue;
143
+ }
144
+ else {
145
+ existing.value = finalValue;
146
+ }
147
+ existing.sensitive = sensitive ?? existing.sensitive;
148
+ existing.updatedAt = now;
149
+ }
150
+ else {
151
+ // Add new entry
152
+ const entry = {
153
+ key,
154
+ value: finalValue,
155
+ sensitive: sensitive ?? false,
156
+ createdAt: now,
157
+ updatedAt: now,
158
+ };
159
+ store.envs.push(entry);
160
+ }
161
+ // Save store
162
+ saveStore(store, storePath);
163
+ const displayValue = sensitive ? '[REDACTED]' : (finalValue.length > 50 ? finalValue.substring(0, 47) + '...' : finalValue);
164
+ return {
165
+ success: true,
166
+ message: `Added ${key}=${displayValue}`,
167
+ };
168
+ }
169
+ /**
170
+ * Add an environment variable from stdin
171
+ */
172
+ export async function addFromStdin(key, sensitive, storePath) {
173
+ return new Promise((resolve) => {
174
+ let data = '';
175
+ process.stdin.setEncoding('utf-8');
176
+ process.stdin.on('data', (chunk) => {
177
+ data += chunk;
178
+ });
179
+ process.stdin.on('end', () => {
180
+ // Remove trailing newline (like envman does)
181
+ const value = data.replace(/\n$/, '');
182
+ const result = add({ key, value, sensitive }, storePath);
183
+ resolve(result);
184
+ });
185
+ process.stdin.on('error', (err) => {
186
+ resolve({
187
+ success: false,
188
+ error: `Failed to read from stdin: ${err.message}`,
189
+ });
190
+ });
191
+ });
192
+ }
193
+ /**
194
+ * Get all environment variables from the store as a Record
195
+ */
196
+ export function getAll(storePath) {
197
+ const store = loadStore(storePath);
198
+ const result = {};
199
+ for (const entry of store.envs) {
200
+ result[entry.key] = entry.value;
201
+ }
202
+ return result;
203
+ }
204
+ /**
205
+ * Get a single environment variable from the store
206
+ */
207
+ export function get(key, storePath) {
208
+ const store = loadStore(storePath);
209
+ const entry = store.envs.find(e => e.key === key);
210
+ return entry?.value;
211
+ }
212
+ /**
213
+ * Print environment variables in various formats
214
+ */
215
+ export function print(format = 'table', storePath) {
216
+ const store = loadStore(storePath);
217
+ switch (format) {
218
+ case 'json':
219
+ return JSON.stringify(store.envs.map(e => ({
220
+ key: e.key,
221
+ value: e.sensitive ? '[REDACTED]' : e.value,
222
+ sensitive: e.sensitive,
223
+ })), null, 2);
224
+ case 'export':
225
+ return store.envs.map(e => {
226
+ const escapedValue = e.value.replace(/'/g, "'\\''");
227
+ return `export ${e.key}='${escapedValue}'`;
228
+ }).join('\n');
229
+ case 'table':
230
+ default:
231
+ if (store.envs.length === 0) {
232
+ return 'No environment variables stored.';
233
+ }
234
+ const lines = [];
235
+ lines.push('┌─────────────────────────────────────────────────────────────────┐');
236
+ lines.push('│ Environment Variables │');
237
+ lines.push('├─────────────────────────────────────────────────────────────────┤');
238
+ for (const entry of store.envs) {
239
+ const displayValue = entry.sensitive
240
+ ? '[REDACTED]'
241
+ : (entry.value.length > 40 ? entry.value.substring(0, 37) + '...' : entry.value);
242
+ const line = `│ ${entry.key.padEnd(20)} = ${displayValue.padEnd(38)}│`;
243
+ lines.push(line);
244
+ }
245
+ lines.push('└─────────────────────────────────────────────────────────────────┘');
246
+ return lines.join('\n');
247
+ }
248
+ }
249
+ /**
250
+ * Run a command with the environment variables from the store
251
+ */
252
+ export async function run(command, storePath) {
253
+ if (command.length === 0) {
254
+ console.error('Error: No command specified');
255
+ return 1;
256
+ }
257
+ // Load environment variables
258
+ const envVars = getAll(storePath);
259
+ // Merge with current environment
260
+ const env = {
261
+ ...process.env,
262
+ ...envVars,
263
+ };
264
+ // Spawn the command
265
+ const [cmd, ...args] = command;
266
+ return new Promise((resolve) => {
267
+ const child = spawn(cmd, args, {
268
+ env,
269
+ stdio: 'inherit',
270
+ shell: true,
271
+ });
272
+ child.on('error', (err) => {
273
+ console.error(`Failed to execute command: ${err.message}`);
274
+ resolve(1);
275
+ });
276
+ child.on('exit', (code) => {
277
+ resolve(code ?? 0);
278
+ });
279
+ });
280
+ }
281
+ /**
282
+ * Clear the environment store
283
+ */
284
+ export function clear(storePath) {
285
+ const path = storePath || getStorePath();
286
+ if (!existsSync(path)) {
287
+ return {
288
+ success: true,
289
+ message: 'Envstore does not exist, nothing to clear.',
290
+ };
291
+ }
292
+ try {
293
+ unlinkSync(path);
294
+ return {
295
+ success: true,
296
+ message: `Cleared envstore at ${path}`,
297
+ };
298
+ }
299
+ catch (error) {
300
+ return {
301
+ success: false,
302
+ error: `Failed to clear envstore: ${error.message}`,
303
+ };
304
+ }
305
+ }
306
+ /**
307
+ * Unset (remove) a specific environment variable from the store
308
+ */
309
+ export function unset(key, storePath) {
310
+ const store = loadStore(storePath);
311
+ const index = store.envs.findIndex(e => e.key === key);
312
+ if (index === -1) {
313
+ return {
314
+ success: false,
315
+ error: `Key '${key}' not found in envstore`,
316
+ };
317
+ }
318
+ store.envs.splice(index, 1);
319
+ saveStore(store, storePath);
320
+ return {
321
+ success: true,
322
+ message: `Removed ${key} from envstore`,
323
+ };
324
+ }
325
+ /**
326
+ * Generate a bash script that sources the envstore
327
+ * This can be prepended to step scripts to load environment variables
328
+ */
329
+ export function generateSourceScript(storePath) {
330
+ const path = storePath || getStorePath();
331
+ return `
332
+ # CI Build envman: Load environment variables from store
333
+ if [ -f "${path}" ]; then
334
+ while IFS= read -r line; do
335
+ if [ -n "$line" ]; then
336
+ export "$line"
337
+ fi
338
+ done < <(node -e "
339
+ const fs = require('fs');
340
+ try {
341
+ const store = JSON.parse(fs.readFileSync('${path}', 'utf-8'));
342
+ for (const e of store.envs || []) {
343
+ const escaped = e.value.replace(/'/g, \"'\\\\\\\\\\\\\\''\" );
344
+ console.log(e.key + '=' + escaped);
345
+ }
346
+ } catch(e) {}
347
+ ")
348
+ fi
349
+ `;
350
+ }
351
+ /**
352
+ * Export all environment variables to the current process
353
+ * Useful for programmatic usage
354
+ */
355
+ export function exportToProcess(storePath) {
356
+ const envVars = getAll(storePath);
357
+ for (const [key, value] of Object.entries(envVars)) {
358
+ process.env[key] = value;
359
+ }
360
+ }
361
+ //# sourceMappingURL=envman.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unit tests for the CI Build Environment Variable Manager (envman)
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=envman.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envman.test.d.ts","sourceRoot":"","sources":["../../../src/envman/envman.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Unit tests for the CI Build Environment Variable Manager (envman)
3
+ */
4
+ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import * as envman from './envman.js';
8
+ import { STORE_VERSION } from './types.js';
9
+ describe('envman', () => {
10
+ const testStorePath = path.join(process.cwd(), '.envstore.test.json');
11
+ beforeEach(() => {
12
+ // Clean up any existing test store
13
+ if (fs.existsSync(testStorePath)) {
14
+ fs.unlinkSync(testStorePath);
15
+ }
16
+ });
17
+ afterEach(() => {
18
+ // Clean up test store after each test
19
+ if (fs.existsSync(testStorePath)) {
20
+ fs.unlinkSync(testStorePath);
21
+ }
22
+ });
23
+ describe('init', () => {
24
+ test('should create an empty envstore file', () => {
25
+ const result = envman.init(testStorePath);
26
+ expect(result.success).toBe(true);
27
+ expect(fs.existsSync(testStorePath)).toBe(true);
28
+ const content = JSON.parse(fs.readFileSync(testStorePath, 'utf-8'));
29
+ expect(content.version).toBe(STORE_VERSION);
30
+ expect(content.envs).toEqual([]);
31
+ });
32
+ test('should fail if envstore already exists', () => {
33
+ // Create the store first
34
+ envman.init(testStorePath);
35
+ // Try to init again
36
+ const result = envman.init(testStorePath);
37
+ expect(result.success).toBe(false);
38
+ expect(result.error).toContain('already exists');
39
+ });
40
+ });
41
+ describe('add', () => {
42
+ test('should add a new environment variable', () => {
43
+ const result = envman.add({
44
+ key: 'TEST_VAR',
45
+ value: 'test_value',
46
+ }, testStorePath);
47
+ expect(result.success).toBe(true);
48
+ const value = envman.get('TEST_VAR', testStorePath);
49
+ expect(value).toBe('test_value');
50
+ });
51
+ test('should update an existing environment variable', () => {
52
+ // Add initial value
53
+ envman.add({ key: 'TEST_VAR', value: 'initial' }, testStorePath);
54
+ // Update value
55
+ const result = envman.add({ key: 'TEST_VAR', value: 'updated' }, testStorePath);
56
+ expect(result.success).toBe(true);
57
+ expect(envman.get('TEST_VAR', testStorePath)).toBe('updated');
58
+ });
59
+ test('should append to existing value with --append flag', () => {
60
+ envman.add({ key: 'TEST_VAR', value: 'hello' }, testStorePath);
61
+ envman.add({ key: 'TEST_VAR', value: ' world', append: true }, testStorePath);
62
+ expect(envman.get('TEST_VAR', testStorePath)).toBe('hello world');
63
+ });
64
+ test('should read value from file', () => {
65
+ const valueFilePath = path.join(process.cwd(), '.test-value-file.txt');
66
+ fs.writeFileSync(valueFilePath, 'value_from_file');
67
+ try {
68
+ const result = envman.add({
69
+ key: 'FILE_VAR',
70
+ valueFile: valueFilePath,
71
+ }, testStorePath);
72
+ expect(result.success).toBe(true);
73
+ expect(envman.get('FILE_VAR', testStorePath)).toBe('value_from_file');
74
+ }
75
+ finally {
76
+ fs.unlinkSync(valueFilePath);
77
+ }
78
+ });
79
+ test('should fail with invalid key name', () => {
80
+ const result = envman.add({
81
+ key: '123-invalid',
82
+ value: 'test',
83
+ }, testStorePath);
84
+ expect(result.success).toBe(false);
85
+ expect(result.error).toContain('Invalid key');
86
+ });
87
+ test('should skip empty values with --skip-if-empty', () => {
88
+ const result = envman.add({
89
+ key: 'EMPTY_VAR',
90
+ value: ' ',
91
+ skipIfEmpty: true,
92
+ }, testStorePath);
93
+ expect(result.success).toBe(true);
94
+ expect(result.message).toContain('Skipped');
95
+ expect(envman.get('EMPTY_VAR', testStorePath)).toBeUndefined();
96
+ });
97
+ test('should mark sensitive variables', () => {
98
+ const result = envman.add({
99
+ key: 'SECRET_VAR',
100
+ value: 'super_secret',
101
+ sensitive: true,
102
+ }, testStorePath);
103
+ expect(result.success).toBe(true);
104
+ expect(result.message).toContain('REDACTED');
105
+ const store = envman.loadStore(testStorePath);
106
+ const entry = store.envs.find(e => e.key === 'SECRET_VAR');
107
+ expect(entry?.sensitive).toBe(true);
108
+ });
109
+ });
110
+ describe('get', () => {
111
+ test('should return undefined for non-existent key', () => {
112
+ const value = envman.get('NON_EXISTENT', testStorePath);
113
+ expect(value).toBeUndefined();
114
+ });
115
+ test('should return value for existing key', () => {
116
+ envman.add({ key: 'TEST_VAR', value: 'test_value' }, testStorePath);
117
+ const value = envman.get('TEST_VAR', testStorePath);
118
+ expect(value).toBe('test_value');
119
+ });
120
+ });
121
+ describe('getAll', () => {
122
+ test('should return empty object for empty store', () => {
123
+ const all = envman.getAll(testStorePath);
124
+ expect(all).toEqual({});
125
+ });
126
+ test('should return all variables as key-value pairs', () => {
127
+ envman.add({ key: 'VAR1', value: 'value1' }, testStorePath);
128
+ envman.add({ key: 'VAR2', value: 'value2' }, testStorePath);
129
+ const all = envman.getAll(testStorePath);
130
+ expect(all).toEqual({
131
+ VAR1: 'value1',
132
+ VAR2: 'value2',
133
+ });
134
+ });
135
+ });
136
+ describe('unset', () => {
137
+ test('should remove an existing variable', () => {
138
+ envman.add({ key: 'TO_REMOVE', value: 'test' }, testStorePath);
139
+ const result = envman.unset('TO_REMOVE', testStorePath);
140
+ expect(result.success).toBe(true);
141
+ expect(envman.get('TO_REMOVE', testStorePath)).toBeUndefined();
142
+ });
143
+ test('should fail for non-existent key', () => {
144
+ const result = envman.unset('NON_EXISTENT', testStorePath);
145
+ expect(result.success).toBe(false);
146
+ expect(result.error).toContain('not found');
147
+ });
148
+ });
149
+ describe('clear', () => {
150
+ test('should remove the envstore file', () => {
151
+ envman.add({ key: 'TEST_VAR', value: 'test' }, testStorePath);
152
+ expect(fs.existsSync(testStorePath)).toBe(true);
153
+ const result = envman.clear(testStorePath);
154
+ expect(result.success).toBe(true);
155
+ expect(fs.existsSync(testStorePath)).toBe(false);
156
+ });
157
+ test('should succeed even if envstore does not exist', () => {
158
+ const result = envman.clear(testStorePath);
159
+ expect(result.success).toBe(true);
160
+ });
161
+ });
162
+ describe('print', () => {
163
+ beforeEach(() => {
164
+ envman.add({ key: 'VAR1', value: 'value1' }, testStorePath);
165
+ envman.add({ key: 'VAR2', value: 'value2' }, testStorePath);
166
+ });
167
+ test('should print in table format by default', () => {
168
+ const output = envman.print('table', testStorePath);
169
+ expect(output).toContain('VAR1');
170
+ expect(output).toContain('value1');
171
+ expect(output).toContain('VAR2');
172
+ expect(output).toContain('value2');
173
+ });
174
+ test('should print in export format', () => {
175
+ const output = envman.print('export', testStorePath);
176
+ expect(output).toContain("export VAR1='value1'");
177
+ expect(output).toContain("export VAR2='value2'");
178
+ });
179
+ test('should print in JSON format', () => {
180
+ const output = envman.print('json', testStorePath);
181
+ const parsed = JSON.parse(output);
182
+ expect(parsed).toHaveLength(2);
183
+ expect(parsed.find((e) => e.key === 'VAR1')?.value).toBe('value1');
184
+ });
185
+ test('should redact sensitive values in table format', () => {
186
+ envman.add({ key: 'SECRET', value: 'secret_value', sensitive: true }, testStorePath);
187
+ const output = envman.print('table', testStorePath);
188
+ expect(output).toContain('SECRET');
189
+ expect(output).toContain('[REDACTED]');
190
+ expect(output).not.toContain('secret_value');
191
+ });
192
+ });
193
+ describe('loadStore', () => {
194
+ test('should return empty store if file does not exist', () => {
195
+ const store = envman.loadStore(testStorePath);
196
+ expect(store.version).toBe(STORE_VERSION);
197
+ expect(store.envs).toEqual([]);
198
+ });
199
+ test('should load existing store from file', () => {
200
+ const testStore = {
201
+ version: STORE_VERSION,
202
+ envs: [
203
+ { key: 'TEST', value: 'value', createdAt: '', updatedAt: '' },
204
+ ],
205
+ };
206
+ fs.writeFileSync(testStorePath, JSON.stringify(testStore));
207
+ const store = envman.loadStore(testStorePath);
208
+ expect(store.envs).toHaveLength(1);
209
+ expect(store.envs[0].key).toBe('TEST');
210
+ });
211
+ });
212
+ describe('createEmptyStore', () => {
213
+ test('should create a valid empty store', () => {
214
+ const store = envman.createEmptyStore();
215
+ expect(store.version).toBe(STORE_VERSION);
216
+ expect(store.envs).toEqual([]);
217
+ });
218
+ });
219
+ describe('special characters handling', () => {
220
+ test('should handle values with single quotes', () => {
221
+ envman.add({ key: 'QUOTED', value: "it's a test" }, testStorePath);
222
+ expect(envman.get('QUOTED', testStorePath)).toBe("it's a test");
223
+ const exported = envman.print('export', testStorePath);
224
+ expect(exported).toContain('QUOTED');
225
+ });
226
+ test('should handle values with newlines', () => {
227
+ envman.add({ key: 'MULTILINE', value: 'line1\nline2\nline3' }, testStorePath);
228
+ expect(envman.get('MULTILINE', testStorePath)).toBe('line1\nline2\nline3');
229
+ });
230
+ test('should handle empty values', () => {
231
+ envman.add({ key: 'EMPTY', value: '' }, testStorePath);
232
+ expect(envman.get('EMPTY', testStorePath)).toBe('');
233
+ });
234
+ });
235
+ });
236
+ //# sourceMappingURL=envman.test.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * CI Build Environment Variable Manager (envman)
3
+ *
4
+ * A tool for managing environment variables between CI/CD pipeline steps.
5
+ * Similar to Bitrise's envman, this allows scripts to register outputs
6
+ * that subsequent steps can use as environment variables.
7
+ *
8
+ * @example
9
+ * // In a script step:
10
+ * // envman add --key BUILD_VERSION --value '1.2.3'
11
+ * // envman add --key APK_PATH --valuefile ./build/output/path.txt
12
+ *
13
+ * @example
14
+ * // Programmatic usage:
15
+ * import { add, getAll, run } from './envman/index.js';
16
+ *
17
+ * add({ key: 'BUILD_VERSION', value: '1.2.3' });
18
+ * const envs = getAll();
19
+ * await run(['gradle', 'assembleRelease']);
20
+ */
21
+ export * from './envman.js';
22
+ export * from './types.js';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/envman/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * CI Build Environment Variable Manager (envman)
3
+ *
4
+ * A tool for managing environment variables between CI/CD pipeline steps.
5
+ * Similar to Bitrise's envman, this allows scripts to register outputs
6
+ * that subsequent steps can use as environment variables.
7
+ *
8
+ * @example
9
+ * // In a script step:
10
+ * // envman add --key BUILD_VERSION --value '1.2.3'
11
+ * // envman add --key APK_PATH --valuefile ./build/output/path.txt
12
+ *
13
+ * @example
14
+ * // Programmatic usage:
15
+ * import { add, getAll, run } from './envman/index.js';
16
+ *
17
+ * add({ key: 'BUILD_VERSION', value: '1.2.3' });
18
+ * const envs = getAll();
19
+ * await run(['gradle', 'assembleRelease']);
20
+ */
21
+ export * from './envman.js';
22
+ export * from './types.js';
23
+ //# sourceMappingURL=index.js.map