expo-release-it 0.1.0 → 0.1.2
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/build/command/build.js +156 -94
- package/build/command/build.js.map +1 -1
- package/build/command/bump.js +33 -27
- package/build/command/bump.js.map +1 -1
- package/build/command/init.js +161 -47
- package/build/command/init.js.map +1 -1
- package/build/command/pull.d.ts +5 -0
- package/build/command/pull.js +525 -0
- package/build/command/pull.js.map +1 -0
- package/build/command/submit.js +139 -84
- package/build/command/submit.js.map +1 -1
- package/build/command/upload.js +154 -104
- package/build/command/upload.js.map +1 -1
- package/build/index.js +414 -219
- package/build/index.js.map +1 -1
- package/build/util/FastlaneOption.js +9 -14
- package/build/util/FastlaneOption.js.map +1 -1
- package/build/util/FileUtil.d.ts +2 -1
- package/build/util/FileUtil.js +4 -0
- package/build/util/FileUtil.js.map +1 -1
- package/build/util/OptionHolder.d.ts +1 -3
- package/build/util/OptionHolder.js +7 -8
- package/build/util/OptionHolder.js.map +1 -1
- package/build/util/generateAppStoreConnectApiKeyFile.d.ts +3 -0
- package/build/util/{setup/promptCommonInputs.js → generateAppStoreConnectApiKeyFile.js} +36 -20
- package/build/util/generateAppStoreConnectApiKeyFile.js.map +1 -0
- package/build/util/injectTemplatePlaceHolders.js +8 -11
- package/build/util/injectTemplatePlaceHolders.js.map +1 -1
- package/build/util/input/InqueryInputs.d.ts +7 -1
- package/build/util/input/InqueryInputs.js +51 -13
- package/build/util/input/InqueryInputs.js.map +1 -1
- package/build/util/iterateAllFiles.js.map +1 -1
- package/build/util/logger.d.ts +1 -0
- package/build/util/logger.js +1 -0
- package/build/util/logger.js.map +1 -1
- package/build/util/{VersionUtil.js → setup/VersionUtil.js} +9 -9
- package/build/util/setup/VersionUtil.js.map +1 -0
- package/build/util/setup/execShellScript.d.ts +19 -0
- package/build/util/setup/execShellScript.js +19 -0
- package/build/util/setup/execShellScript.js.map +1 -0
- package/build/util/{hydrateKeyholderFileValues.js → setup/hydrateKeyholderFileValues.js} +14 -10
- package/build/util/setup/hydrateKeyholderFileValues.js.map +1 -0
- package/build/util/setup/prepareAndroid.js +64 -37
- package/build/util/setup/prepareAndroid.js.map +1 -1
- package/build/util/setup/prepareIos.js +63 -35
- package/build/util/setup/prepareIos.js.map +1 -1
- package/build/util/setup/setup.d.ts +2 -1
- package/build/util/setup/setup.js +30 -25
- package/build/util/setup/setup.js.map +1 -1
- package/build/util/spinner.d.ts +3 -0
- package/build/util/spinner.js +14 -0
- package/build/util/spinner.js.map +1 -0
- package/build/util/throwError.js +1 -0
- package/build/util/throwError.js.map +1 -1
- package/package.json +5 -5
- package/template/android/Gemfile +1 -1
- package/template/android/fastlane/Fastfile +2 -2
- package/template/ios/Gemfile +2 -1
- package/template/ios/fastlane/Appfile +0 -2
- package/template/ios/fastlane/Fastfile +20 -3
- package/build/util/VersionUtil.js.map +0 -1
- package/build/util/hydrateKeyholderFileValues.js.map +0 -1
- package/build/util/setup/promptCommonInputs.d.ts +0 -3
- package/build/util/setup/promptCommonInputs.js.map +0 -1
- package/template/ios/fastlane/Matchfile +0 -5
- /package/build/util/{VersionUtil.d.ts → setup/VersionUtil.d.ts} +0 -0
- /package/build/util/{hydrateKeyholderFileValues.d.ts → setup/hydrateKeyholderFileValues.d.ts} +0 -0
package/build/index.js
CHANGED
|
@@ -6,11 +6,11 @@ var package_default = {
|
|
|
6
6
|
name: "expo-release-it",
|
|
7
7
|
module: "src/index.ts",
|
|
8
8
|
type: "module",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.1.1",
|
|
10
10
|
license: "MIT",
|
|
11
11
|
scripts: {
|
|
12
12
|
build: "tsup",
|
|
13
|
-
dev: "
|
|
13
|
+
dev: "yarn run build && NODE_ENV=development node bin/expo-release-it.mjs",
|
|
14
14
|
release: "yarn build && release-it",
|
|
15
15
|
android: "expo run:android",
|
|
16
16
|
ios: "expo run:ios",
|
|
@@ -21,15 +21,15 @@ var package_default = {
|
|
|
21
21
|
"@mj-studio/js-util": "^1.1.20",
|
|
22
22
|
chalk: "^5.4.1",
|
|
23
23
|
commander: "^13.1.0",
|
|
24
|
+
execa: "^9.5.2",
|
|
24
25
|
"fs-extra": "^11.3.0",
|
|
26
|
+
ora: "^8.2.0",
|
|
25
27
|
react: "18.3.1",
|
|
26
28
|
"react-native": "0.76.9",
|
|
27
29
|
semver: "^7.7.1",
|
|
28
|
-
url: "^0.11.4"
|
|
29
|
-
zx: "^8.5.3"
|
|
30
|
+
url: "^0.11.4"
|
|
30
31
|
},
|
|
31
32
|
devDependencies: {
|
|
32
|
-
"@types/bun": "latest",
|
|
33
33
|
"@types/fs-extra": "^11.0.4",
|
|
34
34
|
"@types/semver": "^7.7.0",
|
|
35
35
|
prettier: "3.5.3",
|
|
@@ -87,6 +87,9 @@ function write(p, content) {
|
|
|
87
87
|
function copy(src, dest, options) {
|
|
88
88
|
fs.copySync(src, dest, options);
|
|
89
89
|
}
|
|
90
|
+
function move(src, dest, options) {
|
|
91
|
+
fs.moveSync(src, dest, options);
|
|
92
|
+
}
|
|
90
93
|
function writeJson(path3, json) {
|
|
91
94
|
return write(path3, JSON.stringify(json, null, 2));
|
|
92
95
|
}
|
|
@@ -120,20 +123,20 @@ var OptionHolder = {
|
|
|
120
123
|
input: {},
|
|
121
124
|
keyholderMap: {},
|
|
122
125
|
templateDirNames: ["key"],
|
|
123
|
-
iosBundleIdentifier: "",
|
|
124
|
-
androidPackageName: "",
|
|
125
126
|
projectDir: "",
|
|
126
127
|
outputOfInitDir: "",
|
|
127
128
|
keyDir: "",
|
|
128
129
|
keyholderFilePath: "",
|
|
129
130
|
versionName: "",
|
|
130
|
-
versionCode: ""
|
|
131
|
+
versionCode: "",
|
|
132
|
+
tempDir: ""
|
|
131
133
|
};
|
|
132
134
|
|
|
133
135
|
// src/util/logger.ts
|
|
134
136
|
import chalk from "chalk";
|
|
135
137
|
var logger = {
|
|
136
138
|
success: (...msg) => console.log(chalk.green(`\u2714 ${msg.join(", ")}`)),
|
|
139
|
+
done: (...msg) => console.log(chalk.green(`\u2714 ${msg.join(", ")} - Done`)),
|
|
137
140
|
error: (...msg) => console.log(chalk.red(`\u2716 ${msg.join(", ")}`)),
|
|
138
141
|
warn: (...msg) => console.log(chalk.yellow(`\u26A0 ${msg.join(", ")}`)),
|
|
139
142
|
info: (...msg) => console.log(chalk.cyan(`\u2139 ${msg.join(", ")}`)),
|
|
@@ -143,12 +146,105 @@ ${msg.join(", ")}`)),
|
|
|
143
146
|
};
|
|
144
147
|
|
|
145
148
|
// src/command/init.ts
|
|
146
|
-
import
|
|
149
|
+
import chalk3 from "chalk";
|
|
147
150
|
import { input } from "@inquirer/prompts";
|
|
148
151
|
|
|
149
|
-
// src/util/
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
// src/util/input/InqueryInputs.ts
|
|
153
|
+
import { select } from "@inquirer/prompts";
|
|
154
|
+
import { is } from "@mj-studio/js-util";
|
|
155
|
+
import chalk2 from "chalk";
|
|
156
|
+
var InqueryInputs = {
|
|
157
|
+
platform: async () => {
|
|
158
|
+
if (!OptionHolder.input.platform) {
|
|
159
|
+
OptionHolder.input.platform = await select({
|
|
160
|
+
message: constructInquirerFormattedMessage({
|
|
161
|
+
name: "Platform",
|
|
162
|
+
explanation: `Platform to run action`
|
|
163
|
+
}),
|
|
164
|
+
choices: [
|
|
165
|
+
{ name: "ios", value: "ios", description: "Release ios" },
|
|
166
|
+
{ name: "android", value: "android", description: "Release android" }
|
|
167
|
+
]
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
podinstall: async () => {
|
|
172
|
+
if (OptionHolder.input.platform === "ios") {
|
|
173
|
+
OptionHolder.input.pod = await select({
|
|
174
|
+
message: constructInquirerFormattedMessage({
|
|
175
|
+
name: "Install CocoaPods?"
|
|
176
|
+
}),
|
|
177
|
+
choices: [
|
|
178
|
+
{ name: "yes", value: true, description: "Install pods before release" },
|
|
179
|
+
{ name: "no", value: false, description: "Skip pods install" }
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
androidBuildOutput: async () => {
|
|
185
|
+
if (OptionHolder.input.platform === "android") {
|
|
186
|
+
if (!OptionHolder.input.androidOutput) {
|
|
187
|
+
OptionHolder.input.androidOutput = await select({
|
|
188
|
+
message: constructInquirerFormattedMessage({
|
|
189
|
+
name: "Android output artifact type"
|
|
190
|
+
}),
|
|
191
|
+
choices: [
|
|
192
|
+
{ name: "aab", value: "aab", description: "Build as App bundle" },
|
|
193
|
+
{ name: "apk", value: "apk", description: "Build as APK" }
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
uploadMetadata: async () => {
|
|
200
|
+
if (!is.boolean(OptionHolder.input.uploadMetadata)) {
|
|
201
|
+
OptionHolder.input.uploadMetadata = await select({
|
|
202
|
+
message: constructInquirerFormattedMessage({
|
|
203
|
+
name: "Upload Metadata(screenshot, title, description, ...)"
|
|
204
|
+
}),
|
|
205
|
+
choices: [
|
|
206
|
+
{ name: "yes", value: true, description: "Upload" },
|
|
207
|
+
{ name: "no", value: false, description: "Don't Upload" }
|
|
208
|
+
]
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function constructInquirerFormattedMessage({
|
|
214
|
+
example,
|
|
215
|
+
explanation,
|
|
216
|
+
referUrl,
|
|
217
|
+
name
|
|
218
|
+
}) {
|
|
219
|
+
let ret = `${chalk2.bold(name)}`;
|
|
220
|
+
if (explanation) {
|
|
221
|
+
ret += `
|
|
222
|
+
|
|
223
|
+
${chalk2.italic.blue(explanation)}`;
|
|
224
|
+
}
|
|
225
|
+
if (example) {
|
|
226
|
+
ret += `
|
|
227
|
+
${chalk2.italic(`${chalk2.greenBright("example :")} ${example}`)}`;
|
|
228
|
+
}
|
|
229
|
+
if (referUrl) {
|
|
230
|
+
ret += `
|
|
231
|
+
${chalk2.italic(`${chalk2.greenBright("references :")} ${chalk2.underline.blue(referUrl)}`)}`;
|
|
232
|
+
}
|
|
233
|
+
if (explanation) {
|
|
234
|
+
ret += "\n";
|
|
235
|
+
if (example || referUrl) {
|
|
236
|
+
ret += "\n";
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return ret;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/util/setup/hydrateKeyholderFileValues.ts
|
|
243
|
+
async function hydrateKeyholderFileValues() {
|
|
244
|
+
if (exist(OptionHolder.keyholderFilePath)) {
|
|
245
|
+
Object.assign(OptionHolder.keyholderMap, readJsonSlow(OptionHolder.keyholderFilePath));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
152
248
|
|
|
153
249
|
// src/util/throwError.ts
|
|
154
250
|
function throwError(message, e) {
|
|
@@ -160,7 +256,7 @@ function throwError(message, e) {
|
|
|
160
256
|
process.exit(1);
|
|
161
257
|
}
|
|
162
258
|
|
|
163
|
-
// src/util/VersionUtil.ts
|
|
259
|
+
// src/util/setup/VersionUtil.ts
|
|
164
260
|
import semver from "semver";
|
|
165
261
|
async function parseBinaryVersions() {
|
|
166
262
|
const files = readdir(OptionHolder.projectDir);
|
|
@@ -257,73 +353,151 @@ async function injectBinaryVersions({
|
|
|
257
353
|
}
|
|
258
354
|
}
|
|
259
355
|
|
|
260
|
-
// src/util/
|
|
261
|
-
|
|
356
|
+
// src/util/EnvUtil.ts
|
|
357
|
+
var isDev = process.env.NODE_ENV === "development";
|
|
358
|
+
var isWin = process.platform.startsWith("win");
|
|
359
|
+
|
|
360
|
+
// src/util/setup/setup.ts
|
|
361
|
+
async function setup() {
|
|
362
|
+
await setupBasicOptions();
|
|
363
|
+
await hydrateKeyholderFileValues();
|
|
364
|
+
await parseBinaryVersions();
|
|
365
|
+
}
|
|
366
|
+
async function setupBasicOptions() {
|
|
262
367
|
OptionHolder.projectDir = isDev ? resolve2("example") : resolve2(".");
|
|
263
368
|
OptionHolder.outputOfInitDir = resolve2(OptionHolder.projectDir, "expo-release-it");
|
|
264
369
|
OptionHolder.keyDir = resolve2(OptionHolder.outputOfInitDir, "key");
|
|
265
370
|
OptionHolder.keyholderFilePath = resolve2(OptionHolder.outputOfInitDir, "keyholder.json");
|
|
371
|
+
OptionHolder.tempDir = resolve2(OptionHolder.outputOfInitDir, ".temp");
|
|
266
372
|
}
|
|
267
373
|
|
|
268
374
|
// src/command/init.ts
|
|
269
375
|
async function init({ options }) {
|
|
270
376
|
Object.assign(OptionHolder.input, options);
|
|
271
|
-
logger.info(`${
|
|
272
|
-
await
|
|
273
|
-
await parseBinaryVersions();
|
|
377
|
+
logger.info(`${chalk3.inverse("expo-release-it")} Initialization Get Started`);
|
|
378
|
+
await setup();
|
|
274
379
|
await promptInputs();
|
|
275
380
|
await copyTemplates();
|
|
276
|
-
logger.
|
|
381
|
+
logger.success(
|
|
382
|
+
`${chalk3.inverse("expo-release-it")} Initialization Done. Check out generated 'expo-release-it' directory.`
|
|
383
|
+
);
|
|
277
384
|
}
|
|
278
385
|
async function promptInputs() {
|
|
279
|
-
OptionHolder.
|
|
280
|
-
message:
|
|
386
|
+
OptionHolder.keyholderMap.ios_app_identifier = await input({
|
|
387
|
+
message: constructInquirerFormattedMessage({
|
|
388
|
+
name: "iOS Bundle Identifier",
|
|
389
|
+
explanation: `The bundle identifier for your iOS standalone app. You make it up, but it needs to be unique on the App Store.
|
|
390
|
+
This means 'CFBundleIdentifier' in info.plist and 'ios.bundleIdentifier' in expo app config`,
|
|
391
|
+
example: "com.companyname.appname",
|
|
392
|
+
referUrl: "https://docs.expo.dev/versions/latest/config/app/#bundleidentifier"
|
|
393
|
+
}),
|
|
281
394
|
required: true,
|
|
282
|
-
default:
|
|
283
|
-
});
|
|
284
|
-
OptionHolder.keyholderMap.ios_app_store_connect_team_id = await input({
|
|
285
|
-
message: "IOS App Store Connect Team Id",
|
|
286
|
-
default: isDev ? "ios_app_store_connect_team_id" : void 0
|
|
395
|
+
default: OptionHolder.keyholderMap.ios_app_identifier
|
|
287
396
|
});
|
|
288
397
|
OptionHolder.keyholderMap.ios_developer_team_id = await input({
|
|
289
|
-
message:
|
|
290
|
-
|
|
398
|
+
message: constructInquirerFormattedMessage({
|
|
399
|
+
name: "IOS Developer Team Id",
|
|
400
|
+
explanation: `A unique alphanumeric string that identifies your Apple Developer Team`,
|
|
401
|
+
example: "8A51ABC4H7",
|
|
402
|
+
referUrl: "https://developer.apple.com/account"
|
|
403
|
+
}),
|
|
404
|
+
required: true,
|
|
405
|
+
default: OptionHolder.keyholderMap.ios_developer_team_id
|
|
291
406
|
});
|
|
292
407
|
OptionHolder.keyholderMap.ios_match_git_url = await input({
|
|
293
|
-
message:
|
|
294
|
-
|
|
408
|
+
message: constructInquirerFormattedMessage({
|
|
409
|
+
name: "iOS Fastlane Match Github URL",
|
|
410
|
+
explanation: `The URL of the private Git repository where code signing certificates and profiles are stored for Fastlane Match.`,
|
|
411
|
+
example: "https://github.com/yourname/your-certificates-repo.git",
|
|
412
|
+
referUrl: "https://docs.fastlane.tools/actions/match/"
|
|
413
|
+
}),
|
|
414
|
+
required: true,
|
|
415
|
+
default: OptionHolder.keyholderMap.ios_match_git_url
|
|
295
416
|
});
|
|
296
417
|
OptionHolder.keyholderMap.ios_match_password = await input({
|
|
297
|
-
message:
|
|
298
|
-
|
|
418
|
+
message: constructInquirerFormattedMessage({
|
|
419
|
+
name: "iOS Fastlane Match Passphrase",
|
|
420
|
+
explanation: `When running match for the first time on a new machine, it will ask you for the passphrase for the Git repository. This is an additional layer of security: each of the files will be encrypted using openssl. Make sure to remember the password, as you'll need it when you run match on a different machine.`,
|
|
421
|
+
example: "yourpassphrase",
|
|
422
|
+
referUrl: "https://docs.fastlane.tools/actions/match/"
|
|
423
|
+
}),
|
|
424
|
+
required: true,
|
|
425
|
+
default: OptionHolder.keyholderMap.ios_match_password
|
|
299
426
|
});
|
|
300
427
|
OptionHolder.keyholderMap.ios_app_store_connect_api_key_id = await input({
|
|
301
|
-
message:
|
|
302
|
-
|
|
428
|
+
message: constructInquirerFormattedMessage({
|
|
429
|
+
name: "iOS App Store Connect API Key ID",
|
|
430
|
+
explanation: `ID of App Store Connect API key, used for authentication with Apple\u2019s API.
|
|
431
|
+
App Store Connect API Key can be issued in ${chalk3.underline.blue("https://appstoreconnect.apple.com/access/integrations/api")}`,
|
|
432
|
+
example: "1A2BC3DE4F",
|
|
433
|
+
referUrl: "https://appstoreconnect.apple.com/access/integrations/api"
|
|
434
|
+
}),
|
|
435
|
+
required: true,
|
|
436
|
+
default: OptionHolder.keyholderMap.ios_app_store_connect_api_key_id
|
|
303
437
|
});
|
|
304
438
|
OptionHolder.keyholderMap.ios_app_store_connect_api_key_issuer_id = await input({
|
|
305
|
-
message:
|
|
306
|
-
|
|
439
|
+
message: constructInquirerFormattedMessage({
|
|
440
|
+
name: "iOS App Store Connect API Key Issuer ID",
|
|
441
|
+
explanation: `Identifies the issuer who created the authentication token.
|
|
442
|
+
App Store Connect API Key can be issued in ${chalk3.underline.blue("https://appstoreconnect.apple.com/access/integrations/api")}`,
|
|
443
|
+
example: "abcd1234-5678-90ef-ghij-1234567890kl",
|
|
444
|
+
referUrl: "https://appstoreconnect.apple.com/access/integrations/api"
|
|
445
|
+
}),
|
|
446
|
+
required: true,
|
|
447
|
+
default: OptionHolder.keyholderMap.ios_app_store_connect_api_key_issuer_id
|
|
307
448
|
});
|
|
308
449
|
OptionHolder.keyholderMap.ios_xcode_project_target = await input({
|
|
309
|
-
message:
|
|
310
|
-
|
|
450
|
+
message: constructInquirerFormattedMessage({
|
|
451
|
+
name: "iOS App Target Name",
|
|
452
|
+
explanation: `A target in Xcode defines a product to build, including app, extension, or framework, with its own settings and build instructions.
|
|
453
|
+
Usually, this is equivalent to {{name}}.xcodeproj in your ios directory`,
|
|
454
|
+
example: "app"
|
|
455
|
+
}),
|
|
456
|
+
required: true,
|
|
457
|
+
default: OptionHolder.keyholderMap.ios_xcode_project_target || "app"
|
|
311
458
|
});
|
|
312
|
-
OptionHolder.
|
|
313
|
-
message:
|
|
314
|
-
|
|
459
|
+
OptionHolder.keyholderMap.android_package_name = await input({
|
|
460
|
+
message: constructInquirerFormattedMessage({
|
|
461
|
+
name: "Android Package Name",
|
|
462
|
+
explanation: `The package name for your Android standalone app. You make it up, but it needs to be unique on the Play Store.`,
|
|
463
|
+
example: "com.companyname.appname",
|
|
464
|
+
referUrl: "https://docs.expo.dev/versions/latest/config/app/#package"
|
|
465
|
+
}),
|
|
466
|
+
required: true,
|
|
467
|
+
default: OptionHolder.keyholderMap.android_package_name
|
|
315
468
|
});
|
|
316
469
|
OptionHolder.keyholderMap.android_keystore_store_password = await input({
|
|
317
|
-
message:
|
|
318
|
-
|
|
470
|
+
message: constructInquirerFormattedMessage({
|
|
471
|
+
name: "Android Keystore Store Password",
|
|
472
|
+
explanation: `Storage password used in android release keystore
|
|
473
|
+
You can issue your android release keystore following this guide: ${chalk3.underline.blue("https://developer.android.com/studio/publish/app-signing.html#generate-key")}
|
|
474
|
+
Or, you can do same thing with CLI: ${chalk3.underline.blue("https://gist.github.com/henriquemenezes/70feb8fff20a19a65346e48786bedb8f")}`,
|
|
475
|
+
referUrl: "https://developer.android.com/studio/publish/app-signing.html#generate-key"
|
|
476
|
+
}),
|
|
477
|
+
required: true,
|
|
478
|
+
default: OptionHolder.keyholderMap.android_keystore_store_password
|
|
319
479
|
});
|
|
320
480
|
OptionHolder.keyholderMap.android_keystore_key_alias = await input({
|
|
321
|
-
message:
|
|
322
|
-
|
|
481
|
+
message: constructInquirerFormattedMessage({
|
|
482
|
+
name: "Android Keystore Key Alias",
|
|
483
|
+
explanation: `Key alias used in android release keystore
|
|
484
|
+
You can issue your android release keystore following this guide: ${chalk3.underline.blue("https://developer.android.com/studio/publish/app-signing.html#generate-key")}
|
|
485
|
+
Or, you can do same thing with CLI: ${chalk3.underline.blue("https://gist.github.com/henriquemenezes/70feb8fff20a19a65346e48786bedb8f")}`,
|
|
486
|
+
referUrl: "https://developer.android.com/studio/publish/app-signing.html#generate-key"
|
|
487
|
+
}),
|
|
488
|
+
required: true,
|
|
489
|
+
default: OptionHolder.keyholderMap.android_keystore_key_alias
|
|
323
490
|
});
|
|
324
491
|
OptionHolder.keyholderMap.android_keystore_key_password = await input({
|
|
325
|
-
message:
|
|
326
|
-
|
|
492
|
+
message: constructInquirerFormattedMessage({
|
|
493
|
+
name: "Android Keystore Key Password",
|
|
494
|
+
explanation: `Key password used in android release keystore
|
|
495
|
+
You can issue your android release keystore following this guide: ${chalk3.underline.blue("https://developer.android.com/studio/publish/app-signing.html#generate-key")}
|
|
496
|
+
Or, you can do same thing with CLI: ${chalk3.underline.blue("https://gist.github.com/henriquemenezes/70feb8fff20a19a65346e48786bedb8f")}`,
|
|
497
|
+
referUrl: "https://developer.android.com/studio/publish/app-signing.html#generate-key"
|
|
498
|
+
}),
|
|
499
|
+
required: true,
|
|
500
|
+
default: OptionHolder.keyholderMap.android_keystore_key_password
|
|
327
501
|
});
|
|
328
502
|
writeJson(OptionHolder.keyholderFilePath, OptionHolder.keyholderMap);
|
|
329
503
|
}
|
|
@@ -340,19 +514,21 @@ async function copyTemplates() {
|
|
|
340
514
|
|
|
341
515
|
// src/command/bump.ts
|
|
342
516
|
import semver2 from "semver";
|
|
343
|
-
import { $ } from "zx";
|
|
344
|
-
|
|
345
|
-
// src/util/hydrateKeyholderFileValues.ts
|
|
346
|
-
async function hydrateKeyholderFileValues() {
|
|
347
|
-
Object.assign(OptionHolder.keyholderMap, readJsonSlow(OptionHolder.keyholderFilePath));
|
|
348
|
-
}
|
|
349
517
|
|
|
350
|
-
// src/util/setup/
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
518
|
+
// src/util/setup/execShellScript.ts
|
|
519
|
+
import { execa } from "execa";
|
|
520
|
+
var exe = execa({
|
|
521
|
+
stdin: "pipe",
|
|
522
|
+
stdout: "inherit",
|
|
523
|
+
stderr: "inherit",
|
|
524
|
+
shell: false
|
|
525
|
+
});
|
|
526
|
+
var yesShell = (command, args = [], options) => {
|
|
527
|
+
const subProcess = exe(command, args, { ...options, stdin: "pipe" });
|
|
528
|
+
subProcess.stdin?.write("y\n");
|
|
529
|
+
subProcess.stdin?.end();
|
|
530
|
+
return subProcess;
|
|
531
|
+
};
|
|
356
532
|
|
|
357
533
|
// src/command/bump.ts
|
|
358
534
|
async function bump({ options }) {
|
|
@@ -367,11 +543,10 @@ async function bump({ options }) {
|
|
|
367
543
|
logger.success("Binary versions have been bumped in expo config file");
|
|
368
544
|
}
|
|
369
545
|
async function preCheck() {
|
|
370
|
-
if (isDev) return;
|
|
371
546
|
try {
|
|
372
547
|
let hasChange = false;
|
|
373
548
|
try {
|
|
374
|
-
await
|
|
549
|
+
await exe("git", ["diff", "--quiet", "HEAD"]);
|
|
375
550
|
} catch (_) {
|
|
376
551
|
hasChange = true;
|
|
377
552
|
}
|
|
@@ -383,9 +558,6 @@ async function preCheck() {
|
|
|
383
558
|
}
|
|
384
559
|
}
|
|
385
560
|
|
|
386
|
-
// src/util/setup/prepareAndroid.ts
|
|
387
|
-
import { spinner, $ as $2 } from "zx";
|
|
388
|
-
|
|
389
561
|
// src/util/iterateAllFiles.ts
|
|
390
562
|
async function iterateAllFiles(root, callback) {
|
|
391
563
|
await go(root);
|
|
@@ -402,7 +574,7 @@ async function iterateAllFiles(root, callback) {
|
|
|
402
574
|
}
|
|
403
575
|
|
|
404
576
|
// src/util/injectTemplatePlaceHolders.ts
|
|
405
|
-
import
|
|
577
|
+
import * as path2 from "node:path";
|
|
406
578
|
async function injectTemplatePlaceHolders(root) {
|
|
407
579
|
await iterateAllFiles(root, async (filePath) => {
|
|
408
580
|
const dirPath = path2.dirname(filePath);
|
|
@@ -418,25 +590,40 @@ async function injectTemplatePlaceHolders(root) {
|
|
|
418
590
|
});
|
|
419
591
|
}
|
|
420
592
|
|
|
593
|
+
// src/util/spinner.ts
|
|
594
|
+
import { oraPromise } from "ora";
|
|
595
|
+
import chalk4 from "chalk";
|
|
596
|
+
async function spinner(text, promise) {
|
|
597
|
+
return oraPromise(promise, {
|
|
598
|
+
text,
|
|
599
|
+
successText: chalk4.green(`${text} - Done`),
|
|
600
|
+
failText: chalk4.red(`${text} - Failed`)
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
421
604
|
// src/util/setup/prepareAndroid.ts
|
|
422
605
|
async function prepareAndroid() {
|
|
423
606
|
const srcDir = resolve2(OptionHolder.cli.templateDir, "android");
|
|
424
607
|
const destDir = resolve2(OptionHolder.projectDir, "android");
|
|
608
|
+
await yesShell("expo", ["prebuild", "-p", "android", "--no-install"], {
|
|
609
|
+
cwd: OptionHolder.projectDir
|
|
610
|
+
});
|
|
611
|
+
logger.success("Expo Prebuild - Done");
|
|
425
612
|
await spinner(
|
|
426
|
-
"
|
|
427
|
-
() =>
|
|
613
|
+
"Inject Templates",
|
|
614
|
+
iterateDir(srcDir, async (file) => {
|
|
615
|
+
const filePath = resolve2(srcDir, file);
|
|
616
|
+
remove(resolve2(destDir, file));
|
|
617
|
+
copy(filePath, resolve2(destDir, file));
|
|
618
|
+
})
|
|
428
619
|
);
|
|
429
|
-
|
|
430
|
-
await
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
})
|
|
435
|
-
logger.success("
|
|
436
|
-
await replaceAndroidSigningConfig(destDir);
|
|
437
|
-
logger.success("Overwrite Android Release Signing Config");
|
|
438
|
-
await injectTemplatePlaceHolders(resolve2(destDir, "fastlane"));
|
|
439
|
-
logger.success("Hydrate template placeholders");
|
|
620
|
+
await spinner("Overwrite Android Release Signing Config", replaceAndroidSigningConfig(destDir));
|
|
621
|
+
await spinner(
|
|
622
|
+
"Hydrate template placeholders",
|
|
623
|
+
injectTemplatePlaceHolders(resolve2(destDir, "fastlane"))
|
|
624
|
+
);
|
|
625
|
+
await exe({ cwd: destDir })`bundle install`;
|
|
626
|
+
logger.success("Bunder Install - Done");
|
|
440
627
|
}
|
|
441
628
|
async function replaceAndroidSigningConfig(destDir) {
|
|
442
629
|
const buildGradleDirPath = resolve2(destDir, "app");
|
|
@@ -463,28 +650,31 @@ async function replaceAndroidSigningConfig(destDir) {
|
|
|
463
650
|
}
|
|
464
651
|
|
|
465
652
|
// src/util/setup/prepareIos.ts
|
|
466
|
-
import { $ as $3, spinner as spinner2 } from "zx";
|
|
467
653
|
async function prepareIos() {
|
|
468
654
|
const srcDir = resolve2(OptionHolder.cli.templateDir, "ios");
|
|
469
655
|
const destDir = resolve2(OptionHolder.projectDir, "ios");
|
|
470
|
-
await
|
|
471
|
-
|
|
472
|
-
() => $3`cd ${OptionHolder.projectDir} && expo prebuild -p ios --no-install`
|
|
473
|
-
);
|
|
474
|
-
logger.success("expo prebuild");
|
|
475
|
-
await iterateDir(srcDir, async (file) => {
|
|
476
|
-
const filePath = resolve2(srcDir, file);
|
|
477
|
-
remove(resolve2(destDir, file));
|
|
478
|
-
copy(filePath, resolve2(destDir, file));
|
|
656
|
+
await yesShell("expo", ["prebuild", "-p", "ios", "--no-install"], {
|
|
657
|
+
cwd: OptionHolder.projectDir
|
|
479
658
|
});
|
|
480
|
-
logger.success("
|
|
481
|
-
await
|
|
482
|
-
|
|
659
|
+
logger.success("Expo Prebuild - Done");
|
|
660
|
+
await spinner(
|
|
661
|
+
"Inject templates",
|
|
662
|
+
iterateDir(srcDir, async (file) => {
|
|
663
|
+
const filePath = resolve2(srcDir, file);
|
|
664
|
+
remove(resolve2(destDir, file));
|
|
665
|
+
copy(filePath, resolve2(destDir, file));
|
|
666
|
+
})
|
|
667
|
+
);
|
|
668
|
+
await spinner(
|
|
669
|
+
"Hydrate template placeholders",
|
|
670
|
+
injectTemplatePlaceHolders(resolve2(destDir, "fastlane"))
|
|
671
|
+
);
|
|
672
|
+
await exe({ cwd: destDir })`bundle install`;
|
|
673
|
+
logger.success("Bunder Install - Done");
|
|
483
674
|
}
|
|
484
675
|
|
|
485
676
|
// src/command/upload.ts
|
|
486
|
-
import
|
|
487
|
-
import chalk3 from "chalk";
|
|
677
|
+
import chalk5 from "chalk";
|
|
488
678
|
|
|
489
679
|
// src/util/calculateElapsed.ts
|
|
490
680
|
import { padZero } from "@mj-studio/js-util";
|
|
@@ -495,64 +685,12 @@ function calculateElapsed(t) {
|
|
|
495
685
|
return `${minutes}:${padZero(seconds)}`;
|
|
496
686
|
}
|
|
497
687
|
|
|
498
|
-
// src/util/input/InqueryInputs.ts
|
|
499
|
-
import { select } from "@inquirer/prompts";
|
|
500
|
-
import { is } from "@mj-studio/js-util";
|
|
501
|
-
var InqueryInputs = {
|
|
502
|
-
platform: async () => {
|
|
503
|
-
if (!OptionHolder.input.platform) {
|
|
504
|
-
OptionHolder.input.platform = await select({
|
|
505
|
-
message: "Platform to release",
|
|
506
|
-
choices: [
|
|
507
|
-
{ name: "ios", value: "ios", description: "Release ios" },
|
|
508
|
-
{ name: "android", value: "android", description: "Release android" }
|
|
509
|
-
]
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
},
|
|
513
|
-
podinstall: async () => {
|
|
514
|
-
if (OptionHolder.input.platform === "ios") {
|
|
515
|
-
OptionHolder.input.pod = await select({
|
|
516
|
-
message: "Install Cocoapods",
|
|
517
|
-
choices: [
|
|
518
|
-
{ name: "yes", value: true, description: "Install pods before release" },
|
|
519
|
-
{ name: "no", value: false, description: "Skip pods install" }
|
|
520
|
-
]
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
},
|
|
524
|
-
androidBuildOutput: async () => {
|
|
525
|
-
if (OptionHolder.input.platform === "android") {
|
|
526
|
-
if (!OptionHolder.input.androidOutput) {
|
|
527
|
-
OptionHolder.input.androidOutput = await select({
|
|
528
|
-
message: "Android Output",
|
|
529
|
-
choices: [
|
|
530
|
-
{ name: "aab", value: "aab", description: "Build as App bundle" },
|
|
531
|
-
{ name: "apk", value: "apk", description: "Build as APK" }
|
|
532
|
-
]
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
},
|
|
537
|
-
uploadMetadata: async () => {
|
|
538
|
-
if (!is.boolean(OptionHolder.input.uploadMetadata)) {
|
|
539
|
-
OptionHolder.input.uploadMetadata = await select({
|
|
540
|
-
message: "Upload Metadata(screenshot, title, description, ...)",
|
|
541
|
-
choices: [
|
|
542
|
-
{ name: "yes", value: true, description: "Upload" },
|
|
543
|
-
{ name: "no", value: false, description: "Don't Upload" }
|
|
544
|
-
]
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
|
|
550
688
|
// src/util/FastlaneOption.ts
|
|
551
689
|
function getAndroidFastlaneOptions() {
|
|
552
690
|
return [
|
|
691
|
+
...Object.entries(OptionHolder.keyholderMap).map(([k, v]) => `${k}:${v}`),
|
|
553
692
|
`version_name:${OptionHolder.versionName}`,
|
|
554
693
|
`version_code:${OptionHolder.versionCode}`,
|
|
555
|
-
`package_name:${OptionHolder.androidPackageName}`,
|
|
556
694
|
`upload_metadata:${OptionHolder.input.uploadMetadata}`,
|
|
557
695
|
`aab_path:${resolve2(OptionHolder.outputOfInitDir, "output", "android", "aab", "app-release.aab")}`,
|
|
558
696
|
`apk_path:${resolve2(OptionHolder.outputOfInitDir, "output", "android", "apk", "app-release.apk")}`,
|
|
@@ -562,13 +700,9 @@ function getAndroidFastlaneOptions() {
|
|
|
562
700
|
}
|
|
563
701
|
function getIosFastlaneOptions() {
|
|
564
702
|
return [
|
|
703
|
+
...Object.entries(OptionHolder.keyholderMap).map(([k, v]) => `${k}:${v}`),
|
|
565
704
|
`version_name:${OptionHolder.versionName}`,
|
|
566
705
|
`version_code:${OptionHolder.versionCode}`,
|
|
567
|
-
`ios_app_identifier:${OptionHolder.iosBundleIdentifier}`,
|
|
568
|
-
`ios_xcode_project_target:${OptionHolder.keyholderMap.ios_xcode_project_target}`,
|
|
569
|
-
`ios_developer_team_id:${OptionHolder.keyholderMap.ios_developer_team_id}`,
|
|
570
|
-
`ios_app_store_connect_api_key_id:${OptionHolder.keyholderMap.ios_app_store_connect_api_key_id}`,
|
|
571
|
-
`ios_app_store_connect_api_key_issuer_id:${OptionHolder.keyholderMap.ios_app_store_connect_api_key_issuer_id}`,
|
|
572
706
|
`api_key_filepath:${resolve2(OptionHolder.keyDir, "ios_testflight_upload_api_key.p8")}`,
|
|
573
707
|
`upload_metadata:${OptionHolder.input.uploadMetadata}`,
|
|
574
708
|
`ipa_path:${resolve2(OptionHolder.outputOfInitDir, "output", "ios", "app.ipa")}`,
|
|
@@ -587,10 +721,10 @@ async function upload({ options }) {
|
|
|
587
721
|
logger.info(`Version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
588
722
|
if (OptionHolder.input.platform === "ios") {
|
|
589
723
|
await uploadIos();
|
|
590
|
-
logger.success(`iOS Upload ${
|
|
724
|
+
logger.success(`iOS Upload ${chalk5.bold.inverse(calculateElapsed(startTime))}`);
|
|
591
725
|
} else {
|
|
592
726
|
await uploadAndroid();
|
|
593
|
-
logger.success(`Android Upload ${
|
|
727
|
+
logger.success(`Android Upload ${chalk5.bold.inverse(calculateElapsed(startTime))}`);
|
|
594
728
|
}
|
|
595
729
|
}
|
|
596
730
|
async function promptInputs2() {
|
|
@@ -605,45 +739,29 @@ async function promptInputs2() {
|
|
|
605
739
|
}
|
|
606
740
|
async function uploadIos() {
|
|
607
741
|
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
608
|
-
const $$ = $4({
|
|
609
|
-
verbose: false,
|
|
610
|
-
cwd: iosDir,
|
|
611
|
-
env: {
|
|
612
|
-
MATCH_PASSWORD: OptionHolder.keyholderMap.ios_match_password
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
742
|
await prepareIos();
|
|
616
743
|
await fastlane();
|
|
617
744
|
async function fastlane() {
|
|
618
|
-
await
|
|
619
|
-
|
|
620
|
-
|
|
745
|
+
await exe({
|
|
746
|
+
cwd: iosDir,
|
|
747
|
+
env: {
|
|
748
|
+
MATCH_PASSWORD: OptionHolder.keyholderMap.ios_match_password
|
|
749
|
+
}
|
|
750
|
+
})`bundle exec fastlane upload ${getIosFastlaneOptions()}`;
|
|
621
751
|
}
|
|
622
752
|
}
|
|
623
753
|
async function uploadAndroid() {
|
|
624
|
-
const $$ = $4({
|
|
625
|
-
verbose: false,
|
|
626
|
-
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
627
|
-
});
|
|
628
754
|
await prepareAndroid();
|
|
629
755
|
await fastlane();
|
|
630
756
|
async function fastlane() {
|
|
631
|
-
await
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
await spinner3(
|
|
635
|
-
"Fastlane",
|
|
636
|
-
() => $$`bundle exec fastlane upload ${getAndroidFastlaneOptions()}`
|
|
637
|
-
);
|
|
638
|
-
} catch (e) {
|
|
639
|
-
throwError("Fastlane failed");
|
|
640
|
-
}
|
|
757
|
+
await exe({
|
|
758
|
+
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
759
|
+
})`bundle exec fastlane upload ${getAndroidFastlaneOptions()}`;
|
|
641
760
|
}
|
|
642
761
|
}
|
|
643
762
|
|
|
644
763
|
// src/command/submit.ts
|
|
645
|
-
import
|
|
646
|
-
import { spinner as spinner4, $ as $5 } from "zx";
|
|
764
|
+
import chalk6 from "chalk";
|
|
647
765
|
async function submit({ options }) {
|
|
648
766
|
Object.assign(OptionHolder.input, options);
|
|
649
767
|
const startTime = Date.now();
|
|
@@ -653,10 +771,10 @@ async function submit({ options }) {
|
|
|
653
771
|
logger.info(`Version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
654
772
|
if (OptionHolder.input.platform === "ios") {
|
|
655
773
|
await submitIos();
|
|
656
|
-
logger.success(`Appstore Review Submitted ${
|
|
774
|
+
logger.success(`Appstore Review Submitted ${chalk6.bold.inverse(calculateElapsed(startTime))}`);
|
|
657
775
|
} else {
|
|
658
776
|
await submitAndroid();
|
|
659
|
-
logger.success(`Playstore Review Submitted ${
|
|
777
|
+
logger.success(`Playstore Review Submitted ${chalk6.bold.inverse(calculateElapsed(startTime))}`);
|
|
660
778
|
}
|
|
661
779
|
}
|
|
662
780
|
async function promptInputs3() {
|
|
@@ -664,37 +782,28 @@ async function promptInputs3() {
|
|
|
664
782
|
await InqueryInputs.uploadMetadata();
|
|
665
783
|
}
|
|
666
784
|
async function submitAndroid() {
|
|
667
|
-
const $$ = $5({
|
|
668
|
-
verbose: false,
|
|
669
|
-
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
670
|
-
});
|
|
671
785
|
await prepareAndroid();
|
|
672
786
|
await fastlane();
|
|
673
787
|
async function fastlane() {
|
|
674
|
-
await
|
|
675
|
-
|
|
676
|
-
|
|
788
|
+
await exe({
|
|
789
|
+
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
790
|
+
})`bundle exec fastlane sumbit ${getIosFastlaneOptions()}`;
|
|
677
791
|
}
|
|
678
792
|
}
|
|
679
793
|
async function submitIos() {
|
|
680
794
|
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
681
|
-
const $$ = $5({
|
|
682
|
-
verbose: false,
|
|
683
|
-
cwd: iosDir
|
|
684
|
-
});
|
|
685
795
|
await prepareIos();
|
|
686
796
|
await fastlane();
|
|
687
797
|
async function fastlane() {
|
|
688
|
-
await spinner4("Bundler Install", () => $$`bundle install`);
|
|
689
|
-
logger.success("Bundler Install");
|
|
690
798
|
remove(resolve2(iosDir, ".xcode.env.local"));
|
|
691
|
-
await
|
|
799
|
+
await exe({
|
|
800
|
+
cwd: iosDir
|
|
801
|
+
})`bundle exec fastlane submit ${getAndroidFastlaneOptions()}`;
|
|
692
802
|
}
|
|
693
803
|
}
|
|
694
804
|
|
|
695
805
|
// src/command/build.ts
|
|
696
|
-
import
|
|
697
|
-
import chalk5 from "chalk";
|
|
806
|
+
import chalk7 from "chalk";
|
|
698
807
|
async function build({ options }) {
|
|
699
808
|
Object.assign(OptionHolder.input, options);
|
|
700
809
|
const startTime = Date.now();
|
|
@@ -704,10 +813,10 @@ async function build({ options }) {
|
|
|
704
813
|
logger.info(`Version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
705
814
|
if (OptionHolder.input.platform === "ios") {
|
|
706
815
|
await buildIos();
|
|
707
|
-
logger.success(`iOS Build ${
|
|
816
|
+
logger.success(`iOS Build ${chalk7.bold.inverse(calculateElapsed(startTime))}`);
|
|
708
817
|
} else {
|
|
709
818
|
await buildAndroid();
|
|
710
|
-
logger.success(`Android Build ${
|
|
819
|
+
logger.success(`Android Build ${chalk7.bold.inverse(calculateElapsed(startTime))}`);
|
|
711
820
|
}
|
|
712
821
|
}
|
|
713
822
|
async function promptInputs4() {
|
|
@@ -717,8 +826,7 @@ async function promptInputs4() {
|
|
|
717
826
|
}
|
|
718
827
|
async function buildIos() {
|
|
719
828
|
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
720
|
-
const
|
|
721
|
-
verbose: false,
|
|
829
|
+
const exeEnv = exe({
|
|
722
830
|
cwd: iosDir,
|
|
723
831
|
env: {
|
|
724
832
|
MATCH_PASSWORD: OptionHolder.keyholderMap.ios_match_password
|
|
@@ -727,40 +835,38 @@ async function buildIos() {
|
|
|
727
835
|
await prepareIos();
|
|
728
836
|
await fastlane();
|
|
729
837
|
async function fastlane() {
|
|
730
|
-
await spinner5("Bundler Install", () => $$`bundle install`);
|
|
731
|
-
logger.success("Bundler Install");
|
|
732
838
|
if (OptionHolder.input.pod) {
|
|
733
|
-
await
|
|
734
|
-
logger.
|
|
839
|
+
await exeEnv("bundle", ["exec", "pod", "install"]);
|
|
840
|
+
logger.done("Pod Install");
|
|
735
841
|
}
|
|
736
842
|
remove(resolve2(iosDir, ".xcode.env.local"));
|
|
737
|
-
await
|
|
843
|
+
await exeEnv("bundle", ["exec", "fastlane", "build", ...getIosFastlaneOptions()]);
|
|
844
|
+
logger.success(
|
|
845
|
+
`artifact generated: ${relativePath(OptionHolder.projectDir, resolve2(OptionHolder.outputOfInitDir, "output", "ios", "app.ipa"))}`
|
|
846
|
+
);
|
|
738
847
|
}
|
|
739
848
|
}
|
|
740
849
|
async function buildAndroid() {
|
|
741
850
|
const androidDir = resolve2(OptionHolder.projectDir, "android");
|
|
742
|
-
const
|
|
743
|
-
verbose: false,
|
|
851
|
+
const exeEnv = exe({
|
|
744
852
|
cwd: androidDir
|
|
745
853
|
});
|
|
746
854
|
await prepareAndroid();
|
|
747
855
|
await fastlane();
|
|
748
856
|
async function fastlane() {
|
|
749
|
-
await spinner5("Bundler Install", () => $$`bundle install`);
|
|
750
|
-
logger.success("Bundler Install");
|
|
751
857
|
let buildOutputDir;
|
|
752
858
|
let outputDir;
|
|
753
859
|
if (OptionHolder.input.androidOutput === "aab") {
|
|
754
|
-
await
|
|
860
|
+
await spinner(
|
|
755
861
|
"Bundle AAB",
|
|
756
|
-
(
|
|
862
|
+
exeEnv(isWin ? "gradle.bat" : "./gradlew", ["app:bundleRelease"])
|
|
757
863
|
);
|
|
758
864
|
buildOutputDir = resolve2(androidDir, "app", "build", "outputs", "bundle", "release");
|
|
759
865
|
outputDir = resolve2(OptionHolder.outputOfInitDir, "output", "android", "aab");
|
|
760
866
|
} else {
|
|
761
|
-
await
|
|
867
|
+
await spinner(
|
|
762
868
|
"Bundle APK",
|
|
763
|
-
(
|
|
869
|
+
exeEnv(isWin ? "gradle.bat" : "./gradlew", ["app:assembleRelease"])
|
|
764
870
|
);
|
|
765
871
|
buildOutputDir = resolve2(androidDir, "app", "build", "outputs", "apk", "release");
|
|
766
872
|
outputDir = resolve2(OptionHolder.outputOfInitDir, "output", "android", "apk");
|
|
@@ -771,6 +877,72 @@ async function buildAndroid() {
|
|
|
771
877
|
}
|
|
772
878
|
}
|
|
773
879
|
|
|
880
|
+
// src/util/generateAppStoreConnectApiKeyFile.ts
|
|
881
|
+
function generateAppStoreConnectApiKeyFile() {
|
|
882
|
+
const json = {
|
|
883
|
+
key: read(resolve2(OptionHolder.keyDir, "ios_testflight_upload_api_key.p8")),
|
|
884
|
+
key_id: OptionHolder.keyholderMap.ios_app_store_connect_api_key_id,
|
|
885
|
+
issuer_id: OptionHolder.keyholderMap.ios_app_store_connect_api_key_issuer_id
|
|
886
|
+
};
|
|
887
|
+
const file = join2(OptionHolder.tempDir, "app_store_connect_api.json");
|
|
888
|
+
writeJson(file, json);
|
|
889
|
+
return file;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/command/pull.ts
|
|
893
|
+
async function pull({ options }) {
|
|
894
|
+
Object.assign(OptionHolder.input, options);
|
|
895
|
+
await setup();
|
|
896
|
+
await promptInputs5();
|
|
897
|
+
if (OptionHolder.input.platform === "ios") {
|
|
898
|
+
await pullIosMetadata();
|
|
899
|
+
} else {
|
|
900
|
+
await pullAndroidMetadata();
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async function promptInputs5() {
|
|
904
|
+
await InqueryInputs.platform();
|
|
905
|
+
}
|
|
906
|
+
async function pullIosMetadata() {
|
|
907
|
+
await prepareIos();
|
|
908
|
+
const iosDir = join2(OptionHolder.projectDir, "ios");
|
|
909
|
+
const exeEnv = exe({
|
|
910
|
+
cwd: iosDir,
|
|
911
|
+
env: {
|
|
912
|
+
MATCH_PASSWORD: OptionHolder.keyholderMap.ios_match_password
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
await exeEnv("bundle", [
|
|
916
|
+
"exec",
|
|
917
|
+
"fastlane",
|
|
918
|
+
"deliver",
|
|
919
|
+
"download_metadata",
|
|
920
|
+
"-f",
|
|
921
|
+
"--api_key_path",
|
|
922
|
+
generateAppStoreConnectApiKeyFile()
|
|
923
|
+
]);
|
|
924
|
+
await exeEnv("bundle", [
|
|
925
|
+
"exec",
|
|
926
|
+
"fastlane",
|
|
927
|
+
"deliver",
|
|
928
|
+
"download_screenshots",
|
|
929
|
+
"-f",
|
|
930
|
+
"--api_key_path",
|
|
931
|
+
generateAppStoreConnectApiKeyFile()
|
|
932
|
+
]);
|
|
933
|
+
const metadataSrcDir = join2(iosDir, "fastlane/metadata");
|
|
934
|
+
const screenshotsSrcDir = join2(iosDir, "fastlane/screenshots");
|
|
935
|
+
const metadataDestDir = join2(OptionHolder.outputOfInitDir, "metadata/ios/metadata");
|
|
936
|
+
const screenshotsDestDir = join2(OptionHolder.outputOfInitDir, "metadata/ios/screenshots");
|
|
937
|
+
move(metadataSrcDir, metadataDestDir);
|
|
938
|
+
move(screenshotsSrcDir, screenshotsDestDir);
|
|
939
|
+
}
|
|
940
|
+
async function pullAndroidMetadata() {
|
|
941
|
+
await prepareAndroid();
|
|
942
|
+
const androidDir = join2(OptionHolder.projectDir, "android");
|
|
943
|
+
await exe("bundle", ["exec", "fastlane", "supply", "init"], { cwd: androidDir });
|
|
944
|
+
}
|
|
945
|
+
|
|
774
946
|
// src/index.ts
|
|
775
947
|
var platformOption = new Option("-p --platform <platform>", "Platform").choices([
|
|
776
948
|
"android",
|
|
@@ -787,20 +959,43 @@ var run = () => {
|
|
|
787
959
|
try {
|
|
788
960
|
await init({ options });
|
|
789
961
|
} catch (e) {
|
|
790
|
-
|
|
962
|
+
throwError("Command Failed");
|
|
791
963
|
}
|
|
792
964
|
});
|
|
793
965
|
program.command("bump").description("Bump binary release patch version with modifying app.json").action(async (options) => {
|
|
794
|
-
|
|
966
|
+
try {
|
|
967
|
+
await bump({ options });
|
|
968
|
+
} catch (e) {
|
|
969
|
+
throwError("Command Failed");
|
|
970
|
+
}
|
|
795
971
|
});
|
|
796
972
|
program.command("build").description("Build binary").addOption(platformOption).addOption(androidBuildOutputOption).action(async (options) => {
|
|
797
|
-
|
|
973
|
+
try {
|
|
974
|
+
await build({ options });
|
|
975
|
+
} catch (e) {
|
|
976
|
+
throwError("Command Failed", e);
|
|
977
|
+
}
|
|
798
978
|
});
|
|
799
979
|
program.command("upload").description("Upload artifact to play console internal test track(android) and testflight(ios)").addOption(platformOption).addOption(androidBuildOutputOption).option("-m --uploadMetadata", "Upload store metadatas").action(async (options) => {
|
|
800
|
-
|
|
980
|
+
try {
|
|
981
|
+
await upload({ options });
|
|
982
|
+
} catch (e) {
|
|
983
|
+
throwError("Command Failed");
|
|
984
|
+
}
|
|
801
985
|
});
|
|
802
986
|
program.command("submit").description("Submit Google Play Console & App Store Connect Review with latest testing track").addOption(platformOption).action(async (options) => {
|
|
803
|
-
|
|
987
|
+
try {
|
|
988
|
+
await submit({ options });
|
|
989
|
+
} catch (e) {
|
|
990
|
+
throwError("Command Failed");
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
program.command("pull").description("Sync registered metadatas from stores").addOption(platformOption).action(async (options) => {
|
|
994
|
+
try {
|
|
995
|
+
await pull({ options });
|
|
996
|
+
} catch (e) {
|
|
997
|
+
throwError("Command Failed");
|
|
998
|
+
}
|
|
804
999
|
});
|
|
805
1000
|
program.parse();
|
|
806
1001
|
};
|