indraq_cli 1.3.0 → 1.5.1

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 (86) hide show
  1. package/README.md +1031 -662
  2. package/dist/cli/create-program.d.ts.map +1 -1
  3. package/dist/cli/create-program.js +6 -6
  4. package/dist/cli/create-program.js.map +1 -1
  5. package/dist/cli/doctor.command.d.ts.map +1 -1
  6. package/dist/cli/doctor.command.js +23 -0
  7. package/dist/cli/doctor.command.js.map +1 -1
  8. package/dist/modules/configure/configure.command.d.ts.map +1 -0
  9. package/dist/modules/configure/configure.command.js +116 -0
  10. package/dist/modules/configure/configure.command.js.map +1 -0
  11. package/dist/modules/deploy/commands/build.command.d.ts +2 -2
  12. package/dist/modules/deploy/commands/build.command.d.ts.map +1 -1
  13. package/dist/modules/deploy/commands/build.command.js +45 -16
  14. package/dist/modules/deploy/commands/build.command.js.map +1 -1
  15. package/dist/modules/deploy/commands/configure-docker.command.d.ts +2 -0
  16. package/dist/modules/deploy/commands/configure-docker.command.d.ts.map +1 -0
  17. package/dist/modules/deploy/commands/configure-docker.command.js +169 -0
  18. package/dist/modules/deploy/commands/configure-docker.command.js.map +1 -0
  19. package/dist/modules/deploy/config/docker-config.d.ts +21 -0
  20. package/dist/modules/deploy/config/docker-config.d.ts.map +1 -0
  21. package/dist/modules/deploy/config/docker-config.js +187 -0
  22. package/dist/modules/deploy/config/docker-config.js.map +1 -0
  23. package/dist/modules/deploy/index.d.ts.map +1 -1
  24. package/dist/modules/deploy/index.js +9 -26
  25. package/dist/modules/deploy/index.js.map +1 -1
  26. package/dist/modules/mobile/commands/build-mobile.command.d.ts +11 -0
  27. package/dist/modules/mobile/commands/build-mobile.command.d.ts.map +1 -0
  28. package/dist/modules/mobile/commands/build-mobile.command.js +188 -0
  29. package/dist/modules/mobile/commands/build-mobile.command.js.map +1 -0
  30. package/dist/modules/mobile/commands/configure-mobile.command.d.ts +8 -0
  31. package/dist/modules/mobile/commands/configure-mobile.command.d.ts.map +1 -0
  32. package/dist/modules/mobile/commands/configure-mobile.command.js +353 -0
  33. package/dist/modules/mobile/commands/configure-mobile.command.js.map +1 -0
  34. package/dist/modules/mobile/config/mobile-config.d.ts +43 -0
  35. package/dist/modules/mobile/config/mobile-config.d.ts.map +1 -0
  36. package/dist/modules/mobile/config/mobile-config.js +211 -0
  37. package/dist/modules/mobile/config/mobile-config.js.map +1 -0
  38. package/dist/modules/mobile/index.d.ts +3 -0
  39. package/dist/modules/mobile/index.d.ts.map +1 -0
  40. package/dist/modules/mobile/index.js +50 -0
  41. package/dist/modules/mobile/index.js.map +1 -0
  42. package/dist/modules/mobile/services/jenkins-mobile.service.d.ts +31 -0
  43. package/dist/modules/mobile/services/jenkins-mobile.service.d.ts.map +1 -0
  44. package/dist/modules/mobile/services/jenkins-mobile.service.js +175 -0
  45. package/dist/modules/mobile/services/jenkins-mobile.service.js.map +1 -0
  46. package/dist/modules/mobile/services/project.service.d.ts +11 -0
  47. package/dist/modules/mobile/services/project.service.d.ts.map +1 -0
  48. package/dist/modules/mobile/services/project.service.js +73 -0
  49. package/dist/modules/mobile/services/project.service.js.map +1 -0
  50. package/dist/modules/mobile/services/source-bundle.service.d.ts +9 -0
  51. package/dist/modules/mobile/services/source-bundle.service.d.ts.map +1 -0
  52. package/dist/modules/mobile/services/source-bundle.service.js +114 -0
  53. package/dist/modules/mobile/services/source-bundle.service.js.map +1 -0
  54. package/dist/modules/mobile/services/version-sync.service.d.ts +7 -0
  55. package/dist/modules/mobile/services/version-sync.service.d.ts.map +1 -0
  56. package/dist/modules/mobile/services/version-sync.service.js +88 -0
  57. package/dist/modules/mobile/services/version-sync.service.js.map +1 -0
  58. package/dist/shared/config/config-files.d.ts +5 -0
  59. package/dist/shared/config/config-files.d.ts.map +1 -0
  60. package/dist/shared/config/config-files.js +88 -0
  61. package/dist/shared/config/config-files.js.map +1 -0
  62. package/dist/shared/config/jenkins-config.d.ts +36 -0
  63. package/dist/shared/config/jenkins-config.d.ts.map +1 -0
  64. package/dist/shared/config/jenkins-config.js +164 -0
  65. package/dist/shared/config/jenkins-config.js.map +1 -0
  66. package/dist/shared/jenkins/configure-jenkins.d.ts +7 -0
  67. package/dist/shared/jenkins/configure-jenkins.d.ts.map +1 -0
  68. package/dist/shared/jenkins/configure-jenkins.js +84 -0
  69. package/dist/shared/jenkins/configure-jenkins.js.map +1 -0
  70. package/dist/{modules/deploy/services → shared/jenkins}/jenkins.service.d.ts +5 -4
  71. package/dist/shared/jenkins/jenkins.service.d.ts.map +1 -0
  72. package/dist/{modules/deploy/services → shared/jenkins}/jenkins.service.js +43 -89
  73. package/dist/shared/jenkins/jenkins.service.js.map +1 -0
  74. package/dist/shared/runtime/runtime-doctor.js.map +1 -1
  75. package/package.json +10 -5
  76. package/templates/jenkins/Jenkinsfile-Mobile-App +633 -0
  77. package/dist/modules/deploy/commands/configure.command.d.ts.map +0 -1
  78. package/dist/modules/deploy/commands/configure.command.js +0 -404
  79. package/dist/modules/deploy/commands/configure.command.js.map +0 -1
  80. package/dist/modules/deploy/config/deployment-config.d.ts +0 -29
  81. package/dist/modules/deploy/config/deployment-config.d.ts.map +0 -1
  82. package/dist/modules/deploy/config/deployment-config.js +0 -164
  83. package/dist/modules/deploy/config/deployment-config.js.map +0 -1
  84. package/dist/modules/deploy/services/jenkins.service.d.ts.map +0 -1
  85. package/dist/modules/deploy/services/jenkins.service.js.map +0 -1
  86. /package/dist/modules/{deploy/commands → configure}/configure.command.d.ts +0 -0
@@ -0,0 +1,633 @@
1
+ pipeline {
2
+ agent any
3
+
4
+ options {
5
+ timestamps()
6
+ disableConcurrentBuilds()
7
+ buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '30', artifactDaysToKeepStr: '30', artifactNumToKeepStr: '30'))
8
+ skipDefaultCheckout(true)
9
+ }
10
+
11
+ environment {
12
+ ANDROID_HOME = '/opt/android-sdk'
13
+ ANDROID_SDK_ROOT = '/opt/android-sdk'
14
+ MOBILE_BUILDER_HOME = '/opt/mobile-builder'
15
+ CI = '1'
16
+ BUILD_RETENTION_DAYS = '30'
17
+ PATH = "/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/platform-tools:/usr/local/bin:/usr/bin:/bin:${env.PATH}"
18
+ }
19
+
20
+ parameters {
21
+ // Requires Jenkins "File Parameter" plugin (ID: file-parameters).
22
+ // IndraQ CLI uploads both files through Jenkins buildWithParameters.
23
+ stashedFile 'SOURCE_BUNDLE'
24
+ stashedFile 'ENV_FILE'
25
+
26
+ string(name: 'PROJECT_ID', defaultValue: '', description: 'Stable IndraQ project ID. Supplied by IndraQ CLI.')
27
+ string(name: 'APP_NAME', defaultValue: '', description: 'Application name. Supplied by IndraQ CLI.')
28
+ string(name: 'APP_VERSION', defaultValue: '0.1', description: 'Android versionName supplied by IndraQ CLI.')
29
+ string(name: 'APP_VERSION_CODE', defaultValue: '1', description: 'Android versionCode supplied by IndraQ CLI.')
30
+ string(name: 'SOURCE_SHA256', defaultValue: '', description: 'SHA-256 of the uploaded source archive.')
31
+
32
+ choice(name: 'PROJECT_TYPE', choices: ['Expo', 'React Native CLI'], description: 'Mobile project type')
33
+ choice(name: 'BUILD_ENVIRONMENT', choices: ['Development', 'Staging', 'Production'], description: 'Build environment')
34
+ choice(name: 'BUILD_OUTPUT', choices: ['Development Client', 'Debug APK', 'Release APK', 'Release AAB'], description: 'Build output')
35
+ choice(name: 'BUILD_PROFILE', choices: ['FAST', 'CLEAN', 'FULL_RESET'], description: 'Build profile')
36
+
37
+ string(name: 'SOURCE_REVISION', defaultValue: 'local', description: 'Local Git revision if available.')
38
+ booleanParam(name: 'SOURCE_DIRTY', defaultValue: false, description: 'True when the developer had uncommitted changes.')
39
+ booleanParam(name: 'VERBOSE_BUILD', defaultValue: false, description: 'Enable Gradle --stacktrace and --info logs.')
40
+ }
41
+
42
+ stages {
43
+ stage('Validate Request') {
44
+ steps {
45
+ script {
46
+ if (!params.PROJECT_ID?.trim() || !(params.PROJECT_ID ==~ /[A-Za-z0-9._-]+/)) {
47
+ error 'PROJECT_ID is missing or invalid.'
48
+ }
49
+ if (!params.APP_NAME?.trim() || params.APP_NAME.trim().length() > 80 || !(params.APP_NAME ==~ /[A-Za-z0-9][A-Za-z0-9 ._-]*/)) {
50
+ error 'APP_NAME must be 1-80 characters using letters, numbers, spaces, dots, underscores, or hyphens.'
51
+ }
52
+ if (!params.APP_VERSION?.trim() || !(params.APP_VERSION ==~ /\d+(?:\.\d+)*/)) {
53
+ error 'APP_VERSION must be numeric dotted text such as 0.1 or 1.2.3.'
54
+ }
55
+ if (!params.APP_VERSION_CODE?.trim() || !(params.APP_VERSION_CODE ==~ /\d+/)) {
56
+ error 'APP_VERSION_CODE must be a positive integer.'
57
+ }
58
+ def parsedVersionCode = params.APP_VERSION_CODE.toLong()
59
+ if (parsedVersionCode < 1 || parsedVersionCode > 2100000000L) {
60
+ error 'APP_VERSION_CODE must be between 1 and 2100000000.'
61
+ }
62
+ if (!(params.SOURCE_SHA256 ==~ /[a-fA-F0-9]{64}/)) {
63
+ error 'SOURCE_SHA256 must be a 64-character SHA-256 digest.'
64
+ }
65
+
66
+ env.APP_ROOT = "${env.MOBILE_BUILDER_HOME}/${params.PROJECT_ID}"
67
+ env.SOURCE_DIR = "${env.WORKSPACE}/source"
68
+ env.ARTIFACT_ROOT = "${env.APP_ROOT}/artifacts"
69
+ env.LOG_ROOT = "${env.APP_ROOT}/logs"
70
+ env.BUILD_ARTIFACT_DIR = "${env.ARTIFACT_ROOT}/build-${env.BUILD_NUMBER}"
71
+ env.BUILD_LOG_FILE = "${env.BUILD_ARTIFACT_DIR}/build.log"
72
+ env.BUILD_JSON_FILE = "${env.BUILD_ARTIFACT_DIR}/build.json"
73
+
74
+ // Project-scoped caches make FULL_RESET safe for other Jenkins jobs.
75
+ env.GRADLE_USER_HOME = "${env.APP_ROOT}/.gradle"
76
+ env.NPM_CONFIG_CACHE = "${env.APP_ROOT}/.npm-cache"
77
+ env.EXPO_HOME = "${env.APP_ROOT}/.expo-home"
78
+
79
+ env.NODE_ENV_VALUE = params.BUILD_ENVIRONMENT == 'Development' ? 'development' : 'production'
80
+
81
+ if (params.PROJECT_TYPE == 'Expo') {
82
+ if (params.BUILD_ENVIRONMENT == 'Development' && params.BUILD_OUTPUT != 'Development Client') {
83
+ error 'Invalid combination: Expo Development only supports Development Client.'
84
+ }
85
+ if ((params.BUILD_ENVIRONMENT == 'Staging' || params.BUILD_ENVIRONMENT == 'Production') &&
86
+ !(params.BUILD_OUTPUT in ['Release APK', 'Release AAB'])) {
87
+ error "Invalid combination: Expo ${params.BUILD_ENVIRONMENT} only supports Release APK or Release AAB."
88
+ }
89
+ } else if (params.PROJECT_TYPE == 'React Native CLI') {
90
+ if (params.BUILD_ENVIRONMENT == 'Development' && params.BUILD_OUTPUT != 'Debug APK') {
91
+ error 'Invalid combination: React Native CLI Development only supports Debug APK.'
92
+ }
93
+ if ((params.BUILD_ENVIRONMENT == 'Staging' || params.BUILD_ENVIRONMENT == 'Production') &&
94
+ !(params.BUILD_OUTPUT in ['Release APK', 'Release AAB'])) {
95
+ error "Invalid combination: React Native CLI ${params.BUILD_ENVIRONMENT} only supports Release APK or Release AAB."
96
+ }
97
+ } else {
98
+ error "Unknown PROJECT_TYPE: ${params.PROJECT_TYPE}"
99
+ }
100
+
101
+ switch (params.BUILD_OUTPUT) {
102
+ case 'Development Client':
103
+ env.GRADLE_TASK = 'assembleDebug'
104
+ env.NEEDS_ENV_FILE = 'false'
105
+ env.ARTIFACT_EXT = 'apk'
106
+ env.ARTIFACT_SEARCH_DIR = "${env.SOURCE_DIR}/android/app/build/outputs/apk/debug"
107
+ env.ARTIFACT_FILE_PATTERN = '*.apk'
108
+ break
109
+ case 'Debug APK':
110
+ env.GRADLE_TASK = 'assembleDebug'
111
+ env.NEEDS_ENV_FILE = 'true'
112
+ env.ARTIFACT_EXT = 'apk'
113
+ env.ARTIFACT_SEARCH_DIR = "${env.SOURCE_DIR}/android/app/build/outputs/apk/debug"
114
+ env.ARTIFACT_FILE_PATTERN = '*.apk'
115
+ break
116
+ case 'Release APK':
117
+ env.GRADLE_TASK = 'assembleRelease'
118
+ env.NEEDS_ENV_FILE = 'true'
119
+ env.ARTIFACT_EXT = 'apk'
120
+ env.ARTIFACT_SEARCH_DIR = "${env.SOURCE_DIR}/android/app/build/outputs/apk/release"
121
+ env.ARTIFACT_FILE_PATTERN = '*.apk'
122
+ break
123
+ case 'Release AAB':
124
+ env.GRADLE_TASK = 'bundleRelease'
125
+ env.NEEDS_ENV_FILE = 'true'
126
+ env.ARTIFACT_EXT = 'aab'
127
+ env.ARTIFACT_SEARCH_DIR = "${env.SOURCE_DIR}/android/app/build/outputs/bundle/release"
128
+ env.ARTIFACT_FILE_PATTERN = '*.aab'
129
+ break
130
+ default:
131
+ error "Unknown BUILD_OUTPUT: ${params.BUILD_OUTPUT}"
132
+ }
133
+
134
+ echo '========== IndraQ Mobile Build Request =========='
135
+ echo "App: ${params.APP_NAME}"
136
+ echo "Project ID: ${params.PROJECT_ID}"
137
+ echo "Version: ${params.APP_VERSION}"
138
+ echo "versionCode: ${params.APP_VERSION_CODE}"
139
+ echo "Project type: ${params.PROJECT_TYPE}"
140
+ echo "Environment: ${params.BUILD_ENVIRONMENT}"
141
+ echo "Output: ${params.BUILD_OUTPUT}"
142
+ echo "Profile: ${params.BUILD_PROFILE}"
143
+ echo "Source revision: ${params.SOURCE_REVISION}"
144
+ echo "Source has local changes: ${params.SOURCE_DIRTY}"
145
+ echo "Gradle task: ${env.GRADLE_TASK}"
146
+ }
147
+ }
148
+ }
149
+
150
+ stage('Prepare Folders') {
151
+ steps {
152
+ sh '''#!/usr/bin/env bash
153
+ set -Eeuo pipefail
154
+ mkdir -p "$APP_ROOT" "$ARTIFACT_ROOT" "$LOG_ROOT" "$BUILD_ARTIFACT_DIR" "$GRADLE_USER_HOME" "$NPM_CONFIG_CACHE" "$EXPO_HOME"
155
+ rm -rf "$SOURCE_DIR"
156
+ mkdir -p "$SOURCE_DIR"
157
+ touch "$BUILD_LOG_FILE"
158
+ '''
159
+ }
160
+ }
161
+
162
+ stage('Receive Source Snapshot') {
163
+ steps {
164
+ dir("${env.SOURCE_DIR}") {
165
+ unstash 'SOURCE_BUNDLE'
166
+ sh '''#!/usr/bin/env bash
167
+ set -Eeuo pipefail
168
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
169
+
170
+ ACTUAL_SHA=$(sha256sum SOURCE_BUNDLE | awk '{print $1}')
171
+ if [ "$ACTUAL_SHA" != "$SOURCE_SHA256" ]; then
172
+ echo "Source bundle SHA-256 mismatch."
173
+ echo "Expected: $SOURCE_SHA256"
174
+ echo "Actual: $ACTUAL_SHA"
175
+ exit 1
176
+ fi
177
+
178
+ if tar -tzf SOURCE_BUNDLE | grep -Eq '(^/|(^|/)[.][.](/|$))'; then
179
+ echo "Unsafe path detected inside uploaded source archive."
180
+ exit 1
181
+ fi
182
+
183
+ tar -xzf SOURCE_BUNDLE --strip-components=1
184
+ rm -f SOURCE_BUNDLE
185
+
186
+ echo "Source snapshot verified and extracted."
187
+ '''
188
+ }
189
+ }
190
+ }
191
+
192
+ stage('Prepare Environment') {
193
+ steps {
194
+ dir("${env.SOURCE_DIR}") {
195
+ unstash 'ENV_FILE'
196
+ sh '''#!/usr/bin/env bash
197
+ set -Eeuo pipefail
198
+ set +x
199
+
200
+ if [ "$NEEDS_ENV_FILE" = "true" ] && [ ! -s ENV_FILE ]; then
201
+ echo "A non-empty root .env file is required for this build output."
202
+ exit 1
203
+ fi
204
+
205
+ if [ -s ENV_FILE ]; then
206
+ mv ENV_FILE .env
207
+ chmod 600 .env || true
208
+ echo "Root .env received. Values are hidden."
209
+ else
210
+ rm -f ENV_FILE
211
+ echo "No root .env supplied; this build output does not require one."
212
+ fi
213
+ '''
214
+ }
215
+ }
216
+ }
217
+
218
+ stage('Cleanup Old Build Files') {
219
+ steps {
220
+ sh '''#!/usr/bin/env bash
221
+ set -Eeuo pipefail
222
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
223
+ RETENTION_DAYS="${BUILD_RETENTION_DAYS:-30}"
224
+
225
+ if ! [[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || [ "$RETENTION_DAYS" -lt 1 ]; then
226
+ echo "Invalid BUILD_RETENTION_DAYS: $RETENTION_DAYS"
227
+ exit 1
228
+ fi
229
+
230
+ if [ -d "$ARTIFACT_ROOT" ]; then
231
+ while IFS= read -r -d '' OLD_BUILD_DIR; do
232
+ [ "$OLD_BUILD_DIR" = "$BUILD_ARTIFACT_DIR" ] && continue
233
+ if ! find "$OLD_BUILD_DIR" -type f -mtime "-$RETENTION_DAYS" -print -quit | grep -q .; then
234
+ echo "Deleting old artifact directory: $OLD_BUILD_DIR"
235
+ rm -rf -- "$OLD_BUILD_DIR"
236
+ fi
237
+ done < <(find "$ARTIFACT_ROOT" -mindepth 1 -maxdepth 1 -type d -name 'build-*' -print0)
238
+ fi
239
+
240
+ echo "Cleanup completed."
241
+ '''
242
+ }
243
+ }
244
+
245
+ stage('FULL_RESET') {
246
+ when {
247
+ expression { params.BUILD_PROFILE == 'FULL_RESET' }
248
+ }
249
+ steps {
250
+ sh '''#!/usr/bin/env bash
251
+ set -Eeuo pipefail
252
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
253
+
254
+ echo "FULL_RESET: deleting only this project's Jenkins caches."
255
+ rm -rf "$GRADLE_USER_HOME" "$NPM_CONFIG_CACHE" "$EXPO_HOME"
256
+ mkdir -p "$GRADLE_USER_HOME" "$NPM_CONFIG_CACHE" "$EXPO_HOME"
257
+ '''
258
+ }
259
+ }
260
+
261
+ stage('Environment Check') {
262
+ steps {
263
+ dir("${env.SOURCE_DIR}") {
264
+ sh '''#!/usr/bin/env bash
265
+ set -Eeuo pipefail
266
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
267
+
268
+ echo "========== Build Machine =========="
269
+ java -version
270
+ node -v
271
+ npm -v
272
+ sdkmanager --version || true
273
+ echo "ANDROID_HOME=$ANDROID_HOME"
274
+ echo "GRADLE_USER_HOME=$GRADLE_USER_HOME"
275
+ '''
276
+ }
277
+ }
278
+ }
279
+
280
+ stage('CLEAN') {
281
+ when {
282
+ expression { params.BUILD_PROFILE == 'CLEAN' }
283
+ }
284
+ steps {
285
+ dir("${env.SOURCE_DIR}") {
286
+ sh '''#!/usr/bin/env bash
287
+ set -Eeuo pipefail
288
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
289
+
290
+ rm -rf node_modules .gradle .metro .turbo dist build coverage
291
+ if [ "$PROJECT_TYPE" = "Expo" ]; then
292
+ rm -rf android ios .expo
293
+ else
294
+ rm -rf android/.gradle android/build android/app/build android/.cxx android/app/.cxx
295
+ fi
296
+
297
+ echo "Project-level CLEAN completed."
298
+ '''
299
+ }
300
+ }
301
+ }
302
+
303
+ stage('Install Dependencies') {
304
+ steps {
305
+ dir("${env.SOURCE_DIR}") {
306
+ sh '''#!/usr/bin/env bash
307
+ set -Eeuo pipefail
308
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
309
+
310
+ if [ ! -f package.json ]; then
311
+ echo "package.json is missing from the uploaded project root."
312
+ exit 1
313
+ fi
314
+
315
+ if [ "$BUILD_PROFILE" = "FAST" ]; then
316
+ npm install --prefer-offline --no-audit
317
+ elif [ -f package-lock.json ]; then
318
+ npm ci --no-audit
319
+ else
320
+ npm install --no-audit
321
+ fi
322
+ '''
323
+ }
324
+ }
325
+ }
326
+
327
+ stage('Validate Project Type') {
328
+ steps {
329
+ dir("${env.SOURCE_DIR}") {
330
+ sh '''#!/usr/bin/env bash
331
+ set -Eeuo pipefail
332
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
333
+
334
+ node <<'NODE'
335
+ const fs = require('fs');
336
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
337
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
338
+ const projectType = process.env.PROJECT_TYPE;
339
+ if (projectType === 'Expo' && !deps.expo) throw new Error('Expo selected, but expo is missing from package.json.');
340
+ if (projectType === 'React Native CLI' && !deps['react-native']) throw new Error('React Native CLI selected, but react-native is missing from package.json.');
341
+ NODE
342
+
343
+ if [ "$PROJECT_TYPE" = "React Native CLI" ]; then
344
+ [ -d android ] || { echo "React Native CLI project must contain android/."; exit 1; }
345
+ [ -f android/gradlew ] || { echo "android/gradlew not found."; exit 1; }
346
+ fi
347
+ '''
348
+ }
349
+ }
350
+ }
351
+
352
+ stage('Validate Development Client') {
353
+ when {
354
+ expression { params.PROJECT_TYPE == 'Expo' && params.BUILD_OUTPUT == 'Development Client' }
355
+ }
356
+ steps {
357
+ dir("${env.SOURCE_DIR}") {
358
+ sh '''#!/usr/bin/env bash
359
+ set -Eeuo pipefail
360
+ node <<'NODE'
361
+ const fs = require('fs');
362
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
363
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
364
+ if (!deps['expo-dev-client']) throw new Error('Development Client requires expo-dev-client. Run: npx expo install expo-dev-client');
365
+ NODE
366
+ '''
367
+ }
368
+ }
369
+ }
370
+
371
+ stage('Expo Prebuild') {
372
+ when {
373
+ expression { params.PROJECT_TYPE == 'Expo' }
374
+ }
375
+ steps {
376
+ dir("${env.SOURCE_DIR}") {
377
+ sh '''#!/usr/bin/env bash
378
+ set -Eeuo pipefail
379
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
380
+ if [ "$BUILD_PROFILE" = "FAST" ]; then
381
+ NODE_ENV="$NODE_ENV_VALUE" CI=1 npx expo prebuild --platform android
382
+ else
383
+ NODE_ENV="$NODE_ENV_VALUE" CI=1 npx expo prebuild --platform android --clean
384
+ fi
385
+ '''
386
+ }
387
+ }
388
+ }
389
+
390
+ stage('Prepare Android Project') {
391
+ steps {
392
+ dir("${env.SOURCE_DIR}/android") {
393
+ sh '''#!/usr/bin/env bash
394
+ set -Eeuo pipefail
395
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
396
+ [ -f gradlew ] || { echo "gradlew not found inside android/."; exit 1; }
397
+ chmod +x gradlew
398
+ echo "sdk.dir=$ANDROID_HOME" > local.properties
399
+ '''
400
+ }
401
+ }
402
+ }
403
+
404
+ stage('Apply Android Version') {
405
+ steps {
406
+ dir("${env.SOURCE_DIR}") {
407
+ sh '''#!/usr/bin/env bash
408
+ set -Eeuo pipefail
409
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
410
+
411
+ node <<'NODE'
412
+ const fs = require('fs');
413
+ const path = require('path');
414
+ const version = process.env.APP_VERSION;
415
+ const versionCode = process.env.APP_VERSION_CODE;
416
+ const candidates = [
417
+ path.join('android', 'app', 'build.gradle'),
418
+ path.join('android', 'app', 'build.gradle.kts')
419
+ ];
420
+ const gradleFile = candidates.find((candidate) => fs.existsSync(candidate));
421
+ if (!gradleFile) throw new Error('android/app/build.gradle or build.gradle.kts was not found.');
422
+
423
+ const original = fs.readFileSync(gradleFile, 'utf8');
424
+ let codeUpdated = false;
425
+ let nameUpdated = false;
426
+ const lines = original.split(/\\r?\\n/).map((line) => {
427
+ if (!codeUpdated) {
428
+ let match = line.match(/^(\\s*versionCode\\s*=\\s*).+$/);
429
+ if (match) {
430
+ codeUpdated = true;
431
+ return `${match[1]}${versionCode}`;
432
+ }
433
+ match = line.match(/^(\\s*versionCode\\s+).+$/);
434
+ if (match) {
435
+ codeUpdated = true;
436
+ return `${match[1]}${versionCode}`;
437
+ }
438
+ }
439
+ if (!nameUpdated) {
440
+ let match = line.match(/^(\\s*versionName\\s*=\\s*).+$/);
441
+ if (match) {
442
+ nameUpdated = true;
443
+ return `${match[1]}"${version}"`;
444
+ }
445
+ match = line.match(/^(\\s*versionName\\s+).+$/);
446
+ if (match) {
447
+ nameUpdated = true;
448
+ return `${match[1]}"${version}"`;
449
+ }
450
+ }
451
+ return line;
452
+ });
453
+
454
+ if (!codeUpdated || !nameUpdated) {
455
+ throw new Error(`Unable to apply Android version. versionCode updated=${codeUpdated}, versionName updated=${nameUpdated}. Check ${gradleFile}.`);
456
+ }
457
+ fs.writeFileSync(gradleFile, lines.join('\\n'));
458
+ console.log(`Android version applied in ${gradleFile}: versionName=${version}, versionCode=${versionCode}`);
459
+ NODE
460
+ '''
461
+ }
462
+ }
463
+ }
464
+
465
+ stage('Gradle Clean') {
466
+ when {
467
+ expression { params.BUILD_PROFILE == 'CLEAN' || params.BUILD_PROFILE == 'FULL_RESET' }
468
+ }
469
+ steps {
470
+ dir("${env.SOURCE_DIR}/android") {
471
+ sh '''#!/usr/bin/env bash
472
+ set -Eeuo pipefail
473
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
474
+ ./gradlew --stop || true
475
+ NODE_ENV="$NODE_ENV_VALUE" ./gradlew clean --console=plain --no-build-cache
476
+ '''
477
+ }
478
+ }
479
+ }
480
+
481
+ stage('Build Android') {
482
+ steps {
483
+ dir("${env.SOURCE_DIR}/android") {
484
+ script {
485
+ if (params.BUILD_PROFILE == 'FAST') {
486
+ if (params.VERBOSE_BUILD) {
487
+ sh '''#!/usr/bin/env bash
488
+ set -Eeuo pipefail
489
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
490
+ NODE_ENV="$NODE_ENV_VALUE" ./gradlew "$GRADLE_TASK" --console=plain --stacktrace --info
491
+ '''
492
+ } else {
493
+ sh '''#!/usr/bin/env bash
494
+ set -Eeuo pipefail
495
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
496
+ NODE_ENV="$NODE_ENV_VALUE" ./gradlew "$GRADLE_TASK" --console=plain
497
+ '''
498
+ }
499
+ } else if (params.VERBOSE_BUILD) {
500
+ sh '''#!/usr/bin/env bash
501
+ set -Eeuo pipefail
502
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
503
+ NODE_ENV="$NODE_ENV_VALUE" ./gradlew "$GRADLE_TASK" --console=plain --no-build-cache --rerun-tasks --stacktrace --info
504
+ '''
505
+ } else {
506
+ sh '''#!/usr/bin/env bash
507
+ set -Eeuo pipefail
508
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
509
+ NODE_ENV="$NODE_ENV_VALUE" ./gradlew "$GRADLE_TASK" --console=plain --no-build-cache --rerun-tasks
510
+ '''
511
+ }
512
+ }
513
+ }
514
+ }
515
+ }
516
+
517
+ stage('Generate Build Metadata') {
518
+ steps {
519
+ sh '''#!/usr/bin/env bash
520
+ set -Eeuo pipefail
521
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
522
+ BUILD_TIME=$(TZ=Asia/Kolkata date '+%Y-%m-%dT%H:%M:%S%z')
523
+ cat > "$BUILD_JSON_FILE" <<JSON
524
+ {
525
+ "app": "$APP_NAME",
526
+ "projectId": "$PROJECT_ID",
527
+ "version": "$APP_VERSION",
528
+ "versionCode": $APP_VERSION_CODE,
529
+ "projectType": "$PROJECT_TYPE",
530
+ "environment": "$BUILD_ENVIRONMENT",
531
+ "output": "$BUILD_OUTPUT",
532
+ "profile": "$BUILD_PROFILE",
533
+ "buildNumber": "$BUILD_NUMBER",
534
+ "sourceRevision": "$SOURCE_REVISION",
535
+ "sourceDirty": "$SOURCE_DIRTY",
536
+ "sourceSha256": "$SOURCE_SHA256",
537
+ "buildTime": "$BUILD_TIME"
538
+ }
539
+ JSON
540
+ cat "$BUILD_JSON_FILE"
541
+ '''
542
+ }
543
+ }
544
+
545
+ stage('Collect Artifacts') {
546
+ steps {
547
+ sh '''#!/usr/bin/env bash
548
+ set -Eeuo pipefail
549
+ exec > >(tee -a "$BUILD_LOG_FILE") 2>&1
550
+
551
+ rm -rf "$WORKSPACE/jenkins-artifacts"
552
+ mkdir -p "$WORKSPACE/jenkins-artifacts"
553
+
554
+ [ -d "$ARTIFACT_SEARCH_DIR" ] || { echo "Artifact directory does not exist: $ARTIFACT_SEARCH_DIR"; exit 1; }
555
+ mapfile -t ARTIFACTS < <(find "$ARTIFACT_SEARCH_DIR" -maxdepth 1 -type f -name "$ARTIFACT_FILE_PATTERN" | sort)
556
+ [ "${#ARTIFACTS[@]}" -gt 0 ] || { echo "No .$ARTIFACT_EXT artifact found."; exit 1; }
557
+
558
+ PRIMARY_ARTIFACT="${ARTIFACTS[0]}"
559
+ SAFE_APP_NAME=$(printf '%s' "$APP_NAME" | sed -E 's/[^A-Za-z0-9._-]+/_/g; s/^_+//; s/_+$//')
560
+ [ -n "$SAFE_APP_NAME" ] || SAFE_APP_NAME="app"
561
+ BUILD_STAMP=$(TZ=Asia/Kolkata date '+%Y-%m-%d_%H%M')
562
+ PRIMARY_ARTIFACT_NAME="${SAFE_APP_NAME}_${APP_VERSION}_${APP_VERSION_CODE}_${BUILD_STAMP}.${ARTIFACT_EXT}"
563
+
564
+ cp "$PRIMARY_ARTIFACT" "$WORKSPACE/jenkins-artifacts/$PRIMARY_ARTIFACT_NAME"
565
+ cp "$PRIMARY_ARTIFACT" "$BUILD_ARTIFACT_DIR/$PRIMARY_ARTIFACT_NAME"
566
+ cp "$BUILD_LOG_FILE" "$WORKSPACE/jenkins-artifacts/build.log"
567
+ cp "$BUILD_JSON_FILE" "$WORKSPACE/jenkins-artifacts/build.json"
568
+ echo "$PRIMARY_ARTIFACT_NAME" > "$WORKSPACE/jenkins-artifacts/primary-artifact-name.txt"
569
+
570
+ echo "Primary artifact: $PRIMARY_ARTIFACT_NAME"
571
+ echo "Artifact timestamp timezone: Asia/Kolkata (IST)"
572
+ '''
573
+ }
574
+ }
575
+
576
+ stage('Archive & Publish Download Link') {
577
+ steps {
578
+ script {
579
+ archiveArtifacts(artifacts: 'jenkins-artifacts/**/*', allowEmptyArchive: false, fingerprint: true)
580
+ env.PRIMARY_ARTIFACT_NAME = readFile("${env.WORKSPACE}/jenkins-artifacts/primary-artifact-name.txt").trim()
581
+ env.PRIMARY_ARTIFACT_URL = "${env.BUILD_URL}artifact/jenkins-artifacts/${env.PRIMARY_ARTIFACT_NAME}"
582
+ currentBuild.description = "Download: ${env.PRIMARY_ARTIFACT_NAME}"
583
+ echo '=============================================='
584
+ echo 'DOWNLOAD READY'
585
+ echo "File: ${env.PRIMARY_ARTIFACT_NAME}"
586
+ echo "URL: ${env.PRIMARY_ARTIFACT_URL}"
587
+ echo '=============================================='
588
+ }
589
+ }
590
+ }
591
+ }
592
+
593
+ post {
594
+ always {
595
+ sh '''#!/usr/bin/env bash
596
+ set +e
597
+ # Never leave the developer's environment file in the Jenkins workspace.
598
+ if [ -n "${SOURCE_DIR:-}" ] && [ "$SOURCE_DIR" != "/" ]; then
599
+ rm -f "$SOURCE_DIR/.env" "$SOURCE_DIR/ENV_FILE"
600
+ fi
601
+ if [ -n "${WORKSPACE:-}" ] && [ "$WORKSPACE" != "/" ]; then
602
+ rm -f "$WORKSPACE/ENV_FILE" "$WORKSPACE/SOURCE_BUNDLE"
603
+ fi
604
+ '''
605
+ }
606
+ success {
607
+ echo '=============================================='
608
+ echo 'BUILD SUCCESSFUL'
609
+ echo "App: ${params.APP_NAME}"
610
+ echo "Version: ${params.APP_VERSION}"
611
+ echo "versionCode: ${params.APP_VERSION_CODE}"
612
+ echo "Environment: ${params.BUILD_ENVIRONMENT}"
613
+ echo "Output: ${params.BUILD_OUTPUT}"
614
+ echo "Profile: ${params.BUILD_PROFILE}"
615
+ echo "File: ${env.PRIMARY_ARTIFACT_NAME}"
616
+ echo "Download: ${env.PRIMARY_ARTIFACT_URL}"
617
+ echo "Server backup: ${env.BUILD_ARTIFACT_DIR}"
618
+ echo '=============================================='
619
+ }
620
+ failure {
621
+ echo '=============================================='
622
+ echo 'BUILD FAILED'
623
+ echo "App: ${params.APP_NAME}"
624
+ echo "Version: ${params.APP_VERSION}"
625
+ echo "versionCode: ${params.APP_VERSION_CODE}"
626
+ echo "Environment: ${params.BUILD_ENVIRONMENT}"
627
+ echo "Output: ${params.BUILD_OUTPUT}"
628
+ echo "Profile: ${params.BUILD_PROFILE}"
629
+ echo 'Check console logs above.'
630
+ echo '=============================================='
631
+ }
632
+ }
633
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"configure.command.d.ts","sourceRoot":"","sources":["../../../../src/modules/deploy/commands/configure.command.ts"],"names":[],"mappings":"AA2iBA,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,IAAI,CAkB9C,CAAC"}