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.
- package/README.md +1031 -662
- package/dist/cli/create-program.d.ts.map +1 -1
- package/dist/cli/create-program.js +6 -6
- package/dist/cli/create-program.js.map +1 -1
- package/dist/cli/doctor.command.d.ts.map +1 -1
- package/dist/cli/doctor.command.js +23 -0
- package/dist/cli/doctor.command.js.map +1 -1
- package/dist/modules/configure/configure.command.d.ts.map +1 -0
- package/dist/modules/configure/configure.command.js +116 -0
- package/dist/modules/configure/configure.command.js.map +1 -0
- package/dist/modules/deploy/commands/build.command.d.ts +2 -2
- package/dist/modules/deploy/commands/build.command.d.ts.map +1 -1
- package/dist/modules/deploy/commands/build.command.js +45 -16
- package/dist/modules/deploy/commands/build.command.js.map +1 -1
- package/dist/modules/deploy/commands/configure-docker.command.d.ts +2 -0
- package/dist/modules/deploy/commands/configure-docker.command.d.ts.map +1 -0
- package/dist/modules/deploy/commands/configure-docker.command.js +169 -0
- package/dist/modules/deploy/commands/configure-docker.command.js.map +1 -0
- package/dist/modules/deploy/config/docker-config.d.ts +21 -0
- package/dist/modules/deploy/config/docker-config.d.ts.map +1 -0
- package/dist/modules/deploy/config/docker-config.js +187 -0
- package/dist/modules/deploy/config/docker-config.js.map +1 -0
- package/dist/modules/deploy/index.d.ts.map +1 -1
- package/dist/modules/deploy/index.js +9 -26
- package/dist/modules/deploy/index.js.map +1 -1
- package/dist/modules/mobile/commands/build-mobile.command.d.ts +11 -0
- package/dist/modules/mobile/commands/build-mobile.command.d.ts.map +1 -0
- package/dist/modules/mobile/commands/build-mobile.command.js +188 -0
- package/dist/modules/mobile/commands/build-mobile.command.js.map +1 -0
- package/dist/modules/mobile/commands/configure-mobile.command.d.ts +8 -0
- package/dist/modules/mobile/commands/configure-mobile.command.d.ts.map +1 -0
- package/dist/modules/mobile/commands/configure-mobile.command.js +353 -0
- package/dist/modules/mobile/commands/configure-mobile.command.js.map +1 -0
- package/dist/modules/mobile/config/mobile-config.d.ts +43 -0
- package/dist/modules/mobile/config/mobile-config.d.ts.map +1 -0
- package/dist/modules/mobile/config/mobile-config.js +211 -0
- package/dist/modules/mobile/config/mobile-config.js.map +1 -0
- package/dist/modules/mobile/index.d.ts +3 -0
- package/dist/modules/mobile/index.d.ts.map +1 -0
- package/dist/modules/mobile/index.js +50 -0
- package/dist/modules/mobile/index.js.map +1 -0
- package/dist/modules/mobile/services/jenkins-mobile.service.d.ts +31 -0
- package/dist/modules/mobile/services/jenkins-mobile.service.d.ts.map +1 -0
- package/dist/modules/mobile/services/jenkins-mobile.service.js +175 -0
- package/dist/modules/mobile/services/jenkins-mobile.service.js.map +1 -0
- package/dist/modules/mobile/services/project.service.d.ts +11 -0
- package/dist/modules/mobile/services/project.service.d.ts.map +1 -0
- package/dist/modules/mobile/services/project.service.js +73 -0
- package/dist/modules/mobile/services/project.service.js.map +1 -0
- package/dist/modules/mobile/services/source-bundle.service.d.ts +9 -0
- package/dist/modules/mobile/services/source-bundle.service.d.ts.map +1 -0
- package/dist/modules/mobile/services/source-bundle.service.js +114 -0
- package/dist/modules/mobile/services/source-bundle.service.js.map +1 -0
- package/dist/modules/mobile/services/version-sync.service.d.ts +7 -0
- package/dist/modules/mobile/services/version-sync.service.d.ts.map +1 -0
- package/dist/modules/mobile/services/version-sync.service.js +88 -0
- package/dist/modules/mobile/services/version-sync.service.js.map +1 -0
- package/dist/shared/config/config-files.d.ts +5 -0
- package/dist/shared/config/config-files.d.ts.map +1 -0
- package/dist/shared/config/config-files.js +88 -0
- package/dist/shared/config/config-files.js.map +1 -0
- package/dist/shared/config/jenkins-config.d.ts +36 -0
- package/dist/shared/config/jenkins-config.d.ts.map +1 -0
- package/dist/shared/config/jenkins-config.js +164 -0
- package/dist/shared/config/jenkins-config.js.map +1 -0
- package/dist/shared/jenkins/configure-jenkins.d.ts +7 -0
- package/dist/shared/jenkins/configure-jenkins.d.ts.map +1 -0
- package/dist/shared/jenkins/configure-jenkins.js +84 -0
- package/dist/shared/jenkins/configure-jenkins.js.map +1 -0
- package/dist/{modules/deploy/services → shared/jenkins}/jenkins.service.d.ts +5 -4
- package/dist/shared/jenkins/jenkins.service.d.ts.map +1 -0
- package/dist/{modules/deploy/services → shared/jenkins}/jenkins.service.js +43 -89
- package/dist/shared/jenkins/jenkins.service.js.map +1 -0
- package/dist/shared/runtime/runtime-doctor.js.map +1 -1
- package/package.json +10 -5
- package/templates/jenkins/Jenkinsfile-Mobile-App +633 -0
- package/dist/modules/deploy/commands/configure.command.d.ts.map +0 -1
- package/dist/modules/deploy/commands/configure.command.js +0 -404
- package/dist/modules/deploy/commands/configure.command.js.map +0 -1
- package/dist/modules/deploy/config/deployment-config.d.ts +0 -29
- package/dist/modules/deploy/config/deployment-config.d.ts.map +0 -1
- package/dist/modules/deploy/config/deployment-config.js +0 -164
- package/dist/modules/deploy/config/deployment-config.js.map +0 -1
- package/dist/modules/deploy/services/jenkins.service.d.ts.map +0 -1
- package/dist/modules/deploy/services/jenkins.service.js.map +0 -1
- /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"}
|