expo-release-it 0.0.11 → 0.0.13
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/bin/expo-release-it.mjs +3 -1
- package/build/command/build.d.ts +10 -0
- package/build/command/build.js +422 -69
- package/build/command/build.js.map +1 -0
- package/build/command/bump.d.ts +6 -0
- package/build/command/bump.js +291 -32
- package/build/command/bump.js.map +1 -0
- package/build/command/init.d.ts +6 -0
- package/build/command/init.js +334 -92
- package/build/command/init.js.map +1 -0
- package/build/command/submit.d.ts +8 -0
- package/build/command/submit.js +376 -56
- package/build/command/submit.js.map +1 -0
- package/build/command/upload.d.ts +8 -0
- package/build/command/upload.js +379 -57
- package/build/command/upload.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.js +670 -53
- package/build/index.js.map +1 -0
- package/build/util/EnvUtil.d.ts +4 -0
- package/build/util/EnvUtil.js +8 -2
- package/build/util/EnvUtil.js.map +1 -0
- package/build/util/FileUtil.d.ts +18 -0
- package/build/util/FileUtil.js +73 -55
- package/build/util/FileUtil.js.map +1 -0
- package/build/util/OptionHolder.d.ts +50 -0
- package/build/util/OptionHolder.js +83 -28
- package/build/util/OptionHolder.js.map +1 -0
- package/build/util/VersionUtil.d.ts +7 -0
- package/build/util/VersionUtil.js +217 -80
- package/build/util/VersionUtil.js.map +1 -0
- package/build/util/calculateElapsed.d.ts +3 -0
- package/build/util/calculateElapsed.js +11 -6
- package/build/util/calculateElapsed.js.map +1 -0
- package/build/util/hydrateKeyholderFileValues.d.ts +3 -0
- package/build/util/hydrateKeyholderFileValues.js +91 -4
- package/build/util/hydrateKeyholderFileValues.js.map +1 -0
- package/build/util/iterateAllFilesInGeneratedTemplate.d.ts +3 -0
- package/build/util/iterateAllFilesInGeneratedTemplate.js +112 -16
- package/build/util/iterateAllFilesInGeneratedTemplate.js.map +1 -0
- package/build/util/logger.d.ts +10 -0
- package/build/util/logger.js +15 -0
- package/build/util/logger.js.map +1 -0
- package/build/util/setup/prepareAndroid.d.ts +3 -0
- package/build/util/setup/prepareAndroid.js +170 -26
- package/build/util/setup/prepareAndroid.js.map +1 -0
- package/build/util/setup/prepareIos.d.ts +3 -0
- package/build/util/setup/prepareIos.js +143 -16
- package/build/util/setup/prepareIos.js.map +1 -0
- package/build/util/setup/promptCommonInputs.d.ts +3 -0
- package/build/util/setup/promptCommonInputs.js +113 -21
- package/build/util/setup/promptCommonInputs.js.map +1 -0
- package/build/util/setup/setup.d.ts +3 -0
- package/build/util/setup/setup.js +213 -7
- package/build/util/setup/setup.js.map +1 -0
- package/build/util/throwError.d.ts +3 -0
- package/build/util/throwError.js +24 -9
- package/build/util/throwError.js.map +1 -0
- package/package.json +5 -3
- package/build/command/release.js +0 -86
- package/build/util/EnvironmentPreparationUtil.js +0 -41
- package/build/util/Log.js +0 -9
- package/build/util/Logger.js +0 -9
- package/build/util/logutil.js +0 -9
- package/build/util/parseBinaryVersion.js +0 -59
- package/build/util/pathfinder.js +0 -5
- package/build/util/prepareAndroid.js +0 -34
- package/build/util/prepareIos.js +0 -17
- package/build/util/promptCommonInputs.js +0 -27
- package/build/util/setup.js +0 -8
package/build/index.js
CHANGED
|
@@ -1,58 +1,675 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
|
+
|
|
4
|
+
// package.json
|
|
5
|
+
var package_default = {
|
|
6
|
+
name: "expo-release-it",
|
|
7
|
+
module: "src/index.ts",
|
|
8
|
+
type: "module",
|
|
9
|
+
version: "0.0.12",
|
|
10
|
+
license: "MIT",
|
|
11
|
+
scripts: {
|
|
12
|
+
build: "tsup",
|
|
13
|
+
dev: "bun run build && NODE_ENV=development node bin/expo-release-it.mjs",
|
|
14
|
+
release: "yarn build && release-it",
|
|
15
|
+
android: "expo run:android",
|
|
16
|
+
ios: "expo run:ios",
|
|
17
|
+
t: "tsc --noEmit -p ."
|
|
18
|
+
},
|
|
19
|
+
dependencies: {
|
|
20
|
+
"@inquirer/prompts": "^7.4.1",
|
|
21
|
+
"@mj-studio/js-util": "^1.1.20",
|
|
22
|
+
chalk: "^5.4.1",
|
|
23
|
+
commander: "^13.1.0",
|
|
24
|
+
"fs-extra": "^11.3.0",
|
|
25
|
+
react: "18.3.1",
|
|
26
|
+
"react-native": "0.76.9",
|
|
27
|
+
semver: "^7.7.1",
|
|
28
|
+
url: "^0.11.4",
|
|
29
|
+
zx: "^8.5.3"
|
|
30
|
+
},
|
|
31
|
+
devDependencies: {
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"@types/fs-extra": "^11.0.4",
|
|
34
|
+
"@types/semver": "^7.7.0",
|
|
35
|
+
prettier: "3.5.3",
|
|
36
|
+
"release-it": "^19.0.1",
|
|
37
|
+
"tiny-glob": "^0.2.9",
|
|
38
|
+
tsup: "^8.4.0",
|
|
39
|
+
typescript: "^5.8.3"
|
|
40
|
+
},
|
|
41
|
+
workspaces: [
|
|
42
|
+
"example"
|
|
43
|
+
],
|
|
44
|
+
bin: "./bin/expo-release-it.mjs",
|
|
45
|
+
files: [
|
|
46
|
+
"build",
|
|
47
|
+
"package.json",
|
|
48
|
+
"bin",
|
|
49
|
+
"template"
|
|
50
|
+
],
|
|
51
|
+
packageManager: "yarn@4.9.1"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/util/FileUtil.ts
|
|
55
|
+
import fs from "fs-extra";
|
|
56
|
+
import * as path from "path";
|
|
57
|
+
var join2 = path.join;
|
|
58
|
+
var resolve2 = path.resolve;
|
|
59
|
+
function exist(path4) {
|
|
60
|
+
return fs.existsSync(path4);
|
|
61
|
+
}
|
|
62
|
+
function isDir(path4) {
|
|
63
|
+
return exist(path4) && fs.lstatSync(path4).isDirectory();
|
|
64
|
+
}
|
|
65
|
+
function isFile(path4) {
|
|
66
|
+
return exist(path4) && fs.lstatSync(path4).isFile();
|
|
67
|
+
}
|
|
68
|
+
function read(path4) {
|
|
69
|
+
return fs.readFileSync(path4, { encoding: "utf8" });
|
|
70
|
+
}
|
|
71
|
+
function readdir(path4) {
|
|
72
|
+
return fs.readdirSync(path4);
|
|
73
|
+
}
|
|
74
|
+
function readJsonSlow(path4) {
|
|
75
|
+
return fs.readJSONSync(path4);
|
|
76
|
+
}
|
|
77
|
+
function write(p, content) {
|
|
78
|
+
const dir = path.dirname(p);
|
|
79
|
+
if (!exist(dir)) {
|
|
80
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
return fs.writeFileSync(p, content);
|
|
83
|
+
}
|
|
84
|
+
function copy(src, dest, options) {
|
|
85
|
+
fs.copySync(src, dest, options);
|
|
86
|
+
}
|
|
87
|
+
function writeJson(path4, json) {
|
|
88
|
+
return write(path4, JSON.stringify(json, null, 2));
|
|
89
|
+
}
|
|
90
|
+
function remove(path4) {
|
|
91
|
+
if (!exist(path4)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (fs.lstatSync(path4).isDirectory()) {
|
|
95
|
+
return fs.rmSync(path4, { force: true, recursive: true });
|
|
96
|
+
} else {
|
|
97
|
+
return fs.rmSync(path4, { force: true });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function iterateDir(path4, fn) {
|
|
101
|
+
if (!isDir(path4)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const file of fs.readdirSync(path4)) {
|
|
105
|
+
await fn(file);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/util/OptionHolder.ts
|
|
110
|
+
import { fileURLToPath } from "url";
|
|
111
|
+
var OptionHolder = {
|
|
112
|
+
cli: {
|
|
113
|
+
version: package_default.version,
|
|
114
|
+
rootDir: resolve2(fileURLToPath(import.meta.url), "../.."),
|
|
115
|
+
templateDir: resolve2(fileURLToPath(import.meta.url), "../../template")
|
|
116
|
+
},
|
|
117
|
+
init: {},
|
|
118
|
+
bump: {},
|
|
119
|
+
build: {},
|
|
120
|
+
upload: {},
|
|
121
|
+
submit: {},
|
|
122
|
+
templateValuePlaceholderMap: {},
|
|
123
|
+
keyholderFileValueMap: {},
|
|
124
|
+
templateDirNames: ["fastlane-android", "fastlane-ios", "key"],
|
|
125
|
+
iosBundleIdentifier: "",
|
|
126
|
+
androidPackageName: "",
|
|
127
|
+
projectDir: "",
|
|
128
|
+
outputOfInitDir: "",
|
|
129
|
+
keyDir: "",
|
|
130
|
+
keyholderFilePath: "",
|
|
131
|
+
versionName: "",
|
|
132
|
+
versionCode: ""
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/util/logger.ts
|
|
136
|
+
import chalk from "chalk";
|
|
137
|
+
var logger = {
|
|
138
|
+
success: (...msg) => console.log(chalk.green(`\u2714 ${msg.join(", ")}`)),
|
|
139
|
+
error: (...msg) => console.log(chalk.red(`\u2716 ${msg.join(", ")}`)),
|
|
140
|
+
warn: (...msg) => console.log(chalk.yellow(`\u26A0 ${msg.join(", ")}`)),
|
|
141
|
+
info: (...msg) => console.log(chalk.cyan(`\u2139 ${msg.join(", ")}`)),
|
|
142
|
+
title: (...msg) => console.log(chalk.bold.blue(`
|
|
143
|
+
${msg.join(", ")}`)),
|
|
144
|
+
dim: (...msg) => console.log(chalk.dim(msg.join(", ")))
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/command/init.ts
|
|
148
|
+
import chalk2 from "chalk";
|
|
149
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
150
|
+
|
|
151
|
+
// src/util/iterateAllFilesInGeneratedTemplate.ts
|
|
152
|
+
async function iterateAllFilesInGeneratedTemplate(callback) {
|
|
153
|
+
for (const dir of OptionHolder.templateDirNames) {
|
|
154
|
+
await go(join2(OptionHolder.outputOfInitDir, dir));
|
|
155
|
+
}
|
|
156
|
+
async function go(dirPath) {
|
|
157
|
+
const paths = readdir(dirPath).map((f) => join2(dirPath, f));
|
|
158
|
+
for (const p of paths) {
|
|
159
|
+
if (isFile(p)) {
|
|
160
|
+
await callback(p);
|
|
161
|
+
} else if (isDir(p)) {
|
|
162
|
+
await go(p);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/util/EnvUtil.ts
|
|
169
|
+
var isDev = process.env.NODE_ENV === "development";
|
|
170
|
+
var isWin = process.platform.startsWith("win");
|
|
171
|
+
|
|
172
|
+
// src/util/throwError.ts
|
|
173
|
+
function throwError(message, e) {
|
|
174
|
+
if (e) {
|
|
175
|
+
logger.error(message, e);
|
|
176
|
+
} else {
|
|
177
|
+
logger.error(message);
|
|
178
|
+
}
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/util/VersionUtil.ts
|
|
183
|
+
import semver from "semver";
|
|
184
|
+
async function parseBinaryVersions() {
|
|
185
|
+
const files = readdir(OptionHolder.projectDir);
|
|
186
|
+
const jsConfigFiles = ["app.config.ts", "app.config.js", "app.config.cjs", "app.config.mjs"];
|
|
187
|
+
const jsonConfigFile = "app.json";
|
|
188
|
+
for (const jsConfigFile of jsConfigFiles) {
|
|
189
|
+
if (files.includes(jsConfigFile)) {
|
|
190
|
+
await parseFromJsConfigFile(jsConfigFile);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (files.includes(jsonConfigFile)) {
|
|
195
|
+
await parseFromJsonConfigFile(jsonConfigFile);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
throwError("expo config file not found");
|
|
199
|
+
async function parseFromJsConfigFile(filename) {
|
|
200
|
+
const filePath = resolve2(OptionHolder.projectDir, filename);
|
|
201
|
+
const content = read(filePath);
|
|
202
|
+
const VERSION_NAME = /const VERSION_NAME = '(.*?)';/.exec(content)?.[1];
|
|
203
|
+
const VERSION_CODE = /const VERSION_CODE = (.*?);/.exec(content)?.[1];
|
|
204
|
+
if (!VERSION_NAME) {
|
|
205
|
+
throwError("cannot parse version name");
|
|
206
|
+
}
|
|
207
|
+
if (!VERSION_CODE) {
|
|
208
|
+
throwError("cannot parse version code");
|
|
209
|
+
}
|
|
210
|
+
if (!semver.valid(VERSION_NAME)) {
|
|
211
|
+
throwError("VERSION_NAME is not a valid semver format");
|
|
212
|
+
}
|
|
213
|
+
OptionHolder.versionName = VERSION_NAME;
|
|
214
|
+
OptionHolder.versionCode = VERSION_CODE;
|
|
215
|
+
}
|
|
216
|
+
async function parseFromJsonConfigFile(filename) {
|
|
217
|
+
const filePath = resolve2(OptionHolder.projectDir, filename);
|
|
218
|
+
const json = readJsonSlow(filePath);
|
|
219
|
+
const versionName = json.expo?.version + "";
|
|
220
|
+
const iosVersionCode = json.expo?.ios?.buildNumber + "";
|
|
221
|
+
const androidVersionCode = json.expo?.android?.versionCode + "";
|
|
222
|
+
if (!/\d+/.test(iosVersionCode)) {
|
|
223
|
+
throwError("app.json expo.ios.buildNumber is not an integer");
|
|
224
|
+
}
|
|
225
|
+
if (!/\d+/.test(androidVersionCode)) {
|
|
226
|
+
throwError("app.json expo.android.versionCode is not an integer");
|
|
227
|
+
}
|
|
228
|
+
if (iosVersionCode !== androidVersionCode) {
|
|
229
|
+
throwError("app.json expo.ios.buildNumber is not equal with expo.android.versionCode");
|
|
230
|
+
}
|
|
231
|
+
if (!semver.valid(versionName)) {
|
|
232
|
+
throwError("VERSION_NAME is not a valid semver format");
|
|
233
|
+
}
|
|
234
|
+
OptionHolder.versionName = versionName;
|
|
235
|
+
OptionHolder.versionCode = iosVersionCode;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function injectBinaryVersions({
|
|
239
|
+
versionCode,
|
|
240
|
+
versionName
|
|
241
|
+
}) {
|
|
242
|
+
const files = readdir(OptionHolder.projectDir);
|
|
243
|
+
const jsConfigFiles = ["app.config.ts", "app.config.js", "app.config.cjs", "app.config.mjs"];
|
|
244
|
+
const jsonConfigFile = "app.json";
|
|
245
|
+
for (const jsConfigFile of jsConfigFiles) {
|
|
246
|
+
if (files.includes(jsConfigFile)) {
|
|
247
|
+
await injectToJsConfigFile(jsConfigFile);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (files.includes(jsonConfigFile)) {
|
|
252
|
+
await injectToJsonConfigFile(jsonConfigFile);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
throwError("expo config file not found");
|
|
256
|
+
async function injectToJsConfigFile(filename) {
|
|
257
|
+
const filePath = resolve2(OptionHolder.projectDir, filename);
|
|
258
|
+
let content = read(filePath);
|
|
259
|
+
content = content.replace(
|
|
260
|
+
/const VERSION_NAME = '(.*?)';/,
|
|
261
|
+
`const VERSION_NAME = '${versionName}';`
|
|
262
|
+
);
|
|
263
|
+
content = content.replace(
|
|
264
|
+
/const VERSION_CODE = (.*?);/,
|
|
265
|
+
`const VERSION_CODE = ${versionCode};`
|
|
266
|
+
);
|
|
267
|
+
write(filePath, content);
|
|
268
|
+
}
|
|
269
|
+
async function injectToJsonConfigFile(filename) {
|
|
270
|
+
const filePath = resolve2(OptionHolder.projectDir, filename);
|
|
271
|
+
const json = readJsonSlow(filePath);
|
|
272
|
+
json.expo.version = versionName;
|
|
273
|
+
json.expo.ios.buildNumber = versionCode;
|
|
274
|
+
json.expo.android.versionCode = +versionCode;
|
|
275
|
+
writeJson(filePath, json);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/util/setup/promptCommonInputs.ts
|
|
280
|
+
import { input } from "@inquirer/prompts";
|
|
281
|
+
async function promptCommonInputs() {
|
|
282
|
+
OptionHolder.projectDir = isDev ? resolve2("example") : resolve2(
|
|
283
|
+
await input({
|
|
284
|
+
message: "react native project root directory path",
|
|
285
|
+
default: isDev ? "example" : ".",
|
|
286
|
+
validate: (value) => {
|
|
287
|
+
const p = resolve2(value, "package.json");
|
|
288
|
+
if (!exist(p)) {
|
|
289
|
+
return "package.json hasn't been detected. provider valid project root.";
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
);
|
|
295
|
+
OptionHolder.outputOfInitDir = resolve2(OptionHolder.projectDir, "expo-local-build");
|
|
296
|
+
OptionHolder.keyDir = resolve2(OptionHolder.outputOfInitDir, "key");
|
|
297
|
+
OptionHolder.keyholderFilePath = resolve2(OptionHolder.outputOfInitDir, "keyholder.json");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/command/init.ts
|
|
301
|
+
import { path as path2 } from "zx";
|
|
302
|
+
async function init({ options }) {
|
|
303
|
+
Object.assign(OptionHolder.init, options);
|
|
304
|
+
logger.info(`${chalk2.inverse("expo-local-build")}`);
|
|
305
|
+
await promptCommonInputs();
|
|
306
|
+
await parseBinaryVersions();
|
|
307
|
+
await promptInputs();
|
|
308
|
+
await copyTemplates();
|
|
309
|
+
await injectPlaceHolders();
|
|
310
|
+
logger.success("expo-local-build init success");
|
|
311
|
+
}
|
|
312
|
+
async function promptInputs() {
|
|
313
|
+
OptionHolder.iosBundleIdentifier = OptionHolder.templateValuePlaceholderMap.ios_app_identifier = await input2({
|
|
314
|
+
message: "IOS Bundle Identifier",
|
|
315
|
+
required: true,
|
|
316
|
+
default: isDev ? "ios_app_identifier" : void 0
|
|
317
|
+
});
|
|
318
|
+
OptionHolder.templateValuePlaceholderMap.ios_app_store_connect_team_id = await input2({
|
|
319
|
+
message: "IOS App Store Connect Team Id",
|
|
320
|
+
default: isDev ? "ios_app_store_connect_team_id" : void 0
|
|
321
|
+
});
|
|
322
|
+
OptionHolder.templateValuePlaceholderMap.ios_developer_team_id = await input2({
|
|
323
|
+
message: "IOS Developer Portal Team Id",
|
|
324
|
+
default: isDev ? "ios_developer_team_id" : void 0
|
|
325
|
+
});
|
|
326
|
+
OptionHolder.templateValuePlaceholderMap.ios_match_git_url = await input2({
|
|
327
|
+
message: "IOS Match Key Storage Github URL",
|
|
328
|
+
default: isDev ? "ios_match_git_url" : void 0
|
|
329
|
+
});
|
|
330
|
+
OptionHolder.keyholderFileValueMap.ios_match_password = await input2({
|
|
331
|
+
message: "IOS Match Password",
|
|
332
|
+
default: isDev ? "ios_match_password" : void 0
|
|
333
|
+
});
|
|
334
|
+
OptionHolder.templateValuePlaceholderMap.ios_app_store_connect_api_key_id = await input2({
|
|
335
|
+
message: "IOS App Store Connect Api Key Id",
|
|
336
|
+
default: isDev ? "ios_app_store_connect_api_key_id" : void 0
|
|
337
|
+
});
|
|
338
|
+
OptionHolder.templateValuePlaceholderMap.ios_app_store_connect_api_key_issuer_id = await input2({
|
|
339
|
+
message: "IOS App Store Connect Api Key Issuer Id",
|
|
340
|
+
default: isDev ? "ios_app_store_connect_api_key_issuer_id" : void 0
|
|
341
|
+
});
|
|
342
|
+
OptionHolder.templateValuePlaceholderMap.ios_xcode_project_target = await input2({
|
|
343
|
+
message: "IOS Xcode Target Name",
|
|
344
|
+
default: "app"
|
|
345
|
+
});
|
|
346
|
+
OptionHolder.androidPackageName = OptionHolder.templateValuePlaceholderMap.android_package_name = await input2({
|
|
347
|
+
message: "Android Package Name",
|
|
348
|
+
default: isDev ? "android_package_name" : void 0
|
|
349
|
+
});
|
|
350
|
+
OptionHolder.keyholderFileValueMap.android_keystore_store_password = await input2({
|
|
351
|
+
message: "Android Keystore Store Password",
|
|
352
|
+
default: isDev ? "android_keystore_store_password" : void 0
|
|
353
|
+
});
|
|
354
|
+
OptionHolder.keyholderFileValueMap.android_keystore_key_alias = await input2({
|
|
355
|
+
message: "Android Keystore Key Alias",
|
|
356
|
+
default: isDev ? "android_keystore_key_alias" : void 0
|
|
357
|
+
});
|
|
358
|
+
OptionHolder.keyholderFileValueMap.android_keystore_key_password = await input2({
|
|
359
|
+
message: "Android Keystore Key Password",
|
|
360
|
+
default: isDev ? "android_keystore_key_password" : void 0
|
|
361
|
+
});
|
|
362
|
+
writeJson(OptionHolder.keyholderFilePath, OptionHolder.keyholderFileValueMap);
|
|
363
|
+
}
|
|
364
|
+
async function copyTemplates() {
|
|
365
|
+
for (const dir of OptionHolder.templateDirNames) {
|
|
366
|
+
await copyDirRecursively(dir);
|
|
367
|
+
}
|
|
368
|
+
async function copyDirRecursively(dir) {
|
|
369
|
+
const sourceDirPath = join2(OptionHolder.cli.templateDir, dir);
|
|
370
|
+
const destDirPath = join2(OptionHolder.outputOfInitDir, dir);
|
|
371
|
+
if (exist(destDirPath) && dir !== "key") {
|
|
372
|
+
remove(destDirPath);
|
|
373
|
+
}
|
|
374
|
+
copy(sourceDirPath, destDirPath, { overwrite: dir !== "key" });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function injectPlaceHolders() {
|
|
378
|
+
await iterateAllFilesInGeneratedTemplate(async (filePath) => {
|
|
379
|
+
let content = read(filePath);
|
|
380
|
+
for (const [key, value] of Object.entries(OptionHolder.templateValuePlaceholderMap)) {
|
|
381
|
+
content = content.replaceAll(`{{${key}}}`, value);
|
|
382
|
+
}
|
|
383
|
+
content = content.replaceAll(
|
|
384
|
+
"{{key_dir_relative_path}}",
|
|
385
|
+
path2.relative(filePath, OptionHolder.keyDir)
|
|
386
|
+
);
|
|
387
|
+
write(filePath, content);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/command/bump.ts
|
|
392
|
+
import semver2 from "semver";
|
|
393
|
+
import { $ } from "zx";
|
|
394
|
+
|
|
395
|
+
// src/util/hydrateKeyholderFileValues.ts
|
|
396
|
+
async function hydrateKeyholderFileValues() {
|
|
397
|
+
Object.assign(OptionHolder.keyholderFileValueMap, readJsonSlow(OptionHolder.keyholderFilePath));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/util/setup/setup.ts
|
|
401
|
+
async function setup() {
|
|
402
|
+
await promptCommonInputs();
|
|
403
|
+
await hydrateKeyholderFileValues();
|
|
404
|
+
await parseBinaryVersions();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/command/bump.ts
|
|
408
|
+
async function bump({ options }) {
|
|
409
|
+
Object.assign(OptionHolder.bump, options);
|
|
410
|
+
await preCheck();
|
|
411
|
+
await setup();
|
|
412
|
+
logger.info(`current version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
413
|
+
const nextVersionName = semver2.inc(OptionHolder.versionName, "patch");
|
|
414
|
+
const nextVersionCode = Number(OptionHolder.versionCode) + 1 + "";
|
|
415
|
+
logger.info(`next version: ${nextVersionName}(${nextVersionCode})`);
|
|
416
|
+
await injectBinaryVersions({ versionName: nextVersionName, versionCode: nextVersionCode });
|
|
417
|
+
logger.success("Binary versions have been bumped in expo config file");
|
|
418
|
+
}
|
|
419
|
+
async function preCheck() {
|
|
420
|
+
if (isDev) return;
|
|
421
|
+
try {
|
|
422
|
+
let hasChange = false;
|
|
21
423
|
try {
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
424
|
+
await $`git diff --quiet HEAD`;
|
|
425
|
+
} catch (_) {
|
|
426
|
+
hasChange = true;
|
|
427
|
+
}
|
|
428
|
+
if (hasChange) {
|
|
429
|
+
throwError("Workspace is not clean. Before bump version, you should commit all changes.");
|
|
430
|
+
}
|
|
431
|
+
} catch (e) {
|
|
432
|
+
throwError("Precheck Failed", e);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/command/upload.ts
|
|
437
|
+
import { select } from "@inquirer/prompts";
|
|
438
|
+
|
|
439
|
+
// src/util/setup/prepareAndroid.ts
|
|
440
|
+
import { spinner, $ as $2, path as path3 } from "zx";
|
|
441
|
+
async function prepareAndroid() {
|
|
442
|
+
const androidDir = resolve2(OptionHolder.projectDir, "android");
|
|
443
|
+
const fastlaneSrcDir = resolve2(OptionHolder.outputOfInitDir, "fastlane-android");
|
|
444
|
+
const fastlaneDestDir = resolve2(OptionHolder.projectDir, "android", "fastlane");
|
|
445
|
+
await spinner(
|
|
446
|
+
"Prebuild Android",
|
|
447
|
+
() => $2`cd ${OptionHolder.projectDir} && expo prebuild -p android --no-install`
|
|
448
|
+
);
|
|
449
|
+
remove(fastlaneDestDir);
|
|
450
|
+
await iterateDir(fastlaneSrcDir, async (file) => {
|
|
451
|
+
const filePath = resolve2(fastlaneSrcDir, file);
|
|
452
|
+
remove(resolve2(androidDir, file));
|
|
453
|
+
copy(filePath, resolve2(androidDir, file));
|
|
454
|
+
});
|
|
455
|
+
logger.success("Inject Android Fastlane files");
|
|
456
|
+
await replaceAndroidSigningConfig();
|
|
457
|
+
async function replaceAndroidSigningConfig() {
|
|
458
|
+
const buildGradlePath = resolve2(androidDir, "app", "build.gradle");
|
|
459
|
+
const {
|
|
460
|
+
android_keystore_store_password,
|
|
461
|
+
android_keystore_key_alias,
|
|
462
|
+
android_keystore_key_password
|
|
463
|
+
} = OptionHolder.keyholderFileValueMap;
|
|
464
|
+
const content = read(buildGradlePath).replace(
|
|
465
|
+
/signingConfigs.*?{.*?debug.*?{.*?}.*?}/s,
|
|
466
|
+
`
|
|
467
|
+
signingConfigs {
|
|
468
|
+
debug {
|
|
469
|
+
storeFile file('${path3.relative(buildGradlePath, resolve2(OptionHolder.outputOfInitDir, "key", "android_release_keystore.jks"))}')
|
|
470
|
+
storePassword '${android_keystore_store_password}'
|
|
471
|
+
keyAlias '${android_keystore_key_alias}'
|
|
472
|
+
keyPassword '${android_keystore_key_password}'
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
`
|
|
476
|
+
);
|
|
477
|
+
write(buildGradlePath, content);
|
|
478
|
+
logger.success("Replace Android Signing Config");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/util/setup/prepareIos.ts
|
|
483
|
+
import { $ as $3, spinner as spinner2 } from "zx";
|
|
484
|
+
async function prepareIos() {
|
|
485
|
+
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
486
|
+
const fastlaneSrcDir = resolve2(OptionHolder.outputOfInitDir, "fastlane-ios");
|
|
487
|
+
const fastlaneDestDir = resolve2(OptionHolder.projectDir, "ios", "fastlane");
|
|
488
|
+
await spinner2(
|
|
489
|
+
"Prebuild iOS",
|
|
490
|
+
() => $3`cd ${OptionHolder.projectDir} && expo prebuild -p ios --no-install`
|
|
491
|
+
);
|
|
492
|
+
remove(fastlaneDestDir);
|
|
493
|
+
await iterateDir(fastlaneSrcDir, async (file) => {
|
|
494
|
+
const filePath = resolve2(fastlaneSrcDir, file);
|
|
495
|
+
remove(resolve2(iosDir, file));
|
|
496
|
+
copy(filePath, resolve2(iosDir, file));
|
|
497
|
+
});
|
|
498
|
+
logger.success("Inject iOS Fastlane files");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/command/upload.ts
|
|
502
|
+
import { spinner as spinner3, $ as $4 } from "zx";
|
|
503
|
+
import chalk3 from "chalk";
|
|
504
|
+
|
|
505
|
+
// src/util/calculateElapsed.ts
|
|
506
|
+
import { padZero } from "@mj-studio/js-util";
|
|
507
|
+
function calculateElapsed(t) {
|
|
508
|
+
let seconds = Math.floor((Date.now() - t) / 1e3);
|
|
509
|
+
const minutes = Math.floor(seconds / 60);
|
|
510
|
+
seconds = seconds % 60;
|
|
511
|
+
return `${minutes}:${padZero(seconds)}`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/command/upload.ts
|
|
515
|
+
async function upload({ options }) {
|
|
516
|
+
Object.assign(OptionHolder.upload, options);
|
|
517
|
+
const startTime = Date.now();
|
|
518
|
+
await setup();
|
|
519
|
+
await promptInputs2();
|
|
520
|
+
logger.info("Build & Upload Started");
|
|
521
|
+
logger.info(`Version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
522
|
+
if (OptionHolder.upload.platform === "ios") {
|
|
523
|
+
await uploadIos();
|
|
524
|
+
logger.success(`iOS Upload ${chalk3.bold.inverse(calculateElapsed(startTime))}`);
|
|
525
|
+
} else {
|
|
526
|
+
await uploadAndroid();
|
|
527
|
+
logger.success(`Android Upload ${chalk3.bold.inverse(calculateElapsed(startTime))}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function promptInputs2() {
|
|
531
|
+
if (!OptionHolder.upload.platform) {
|
|
532
|
+
OptionHolder.upload.platform = await select({
|
|
533
|
+
message: "Platform to release",
|
|
534
|
+
choices: [
|
|
535
|
+
{ name: "ios", value: "ios", description: "Release ios" },
|
|
536
|
+
{ name: "android", value: "android", description: "Release android" }
|
|
537
|
+
]
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function uploadIos() {
|
|
542
|
+
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
543
|
+
const $$ = $4({
|
|
544
|
+
verbose: false,
|
|
545
|
+
cwd: iosDir,
|
|
546
|
+
env: {
|
|
547
|
+
MATCH_PASSWORD: OptionHolder.keyholderFileValueMap.ios_match_password
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
await prepareIos();
|
|
551
|
+
await fastlane();
|
|
552
|
+
async function fastlane() {
|
|
553
|
+
await spinner3("Bundler Install", () => $$`bundle install`);
|
|
554
|
+
logger.success("Bundler Install");
|
|
555
|
+
await spinner3(
|
|
556
|
+
"Fastlane",
|
|
557
|
+
() => $$`bundle exec fastlane upload version_name:${OptionHolder.versionName} version_code:${OptionHolder.versionCode}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async function uploadAndroid() {
|
|
562
|
+
const $$ = $4({
|
|
563
|
+
verbose: false,
|
|
564
|
+
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
565
|
+
});
|
|
566
|
+
await prepareAndroid();
|
|
567
|
+
await fastlane();
|
|
568
|
+
async function fastlane() {
|
|
569
|
+
await spinner3("Bundler Install", () => $$`bundle install`);
|
|
570
|
+
logger.success("Bundler Install");
|
|
571
|
+
await spinner3(
|
|
572
|
+
"Fastlane Supply",
|
|
573
|
+
() => $$`bundle exec fastlane deploy version_name:${OptionHolder.versionName} version_code:${OptionHolder.versionCode}`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/command/submit.ts
|
|
579
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
580
|
+
import chalk4 from "chalk";
|
|
581
|
+
import { spinner as spinner4, $ as $5 } from "zx";
|
|
582
|
+
async function submit({ options }) {
|
|
583
|
+
Object.assign(OptionHolder.submit, options);
|
|
584
|
+
const startTime = Date.now();
|
|
585
|
+
await setup();
|
|
586
|
+
await promptInputs3();
|
|
587
|
+
logger.info("Submit Started");
|
|
588
|
+
logger.info(`Version: ${OptionHolder.versionName}(${OptionHolder.versionCode})`);
|
|
589
|
+
if (OptionHolder.submit.platform === "ios") {
|
|
590
|
+
await submitIos();
|
|
591
|
+
logger.success(`Appstore Review Submitted ${chalk4.bold.inverse(calculateElapsed(startTime))}`);
|
|
592
|
+
} else {
|
|
593
|
+
await submitAndroid();
|
|
594
|
+
logger.success(`Playstore Review Submitted ${chalk4.bold.inverse(calculateElapsed(startTime))}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function promptInputs3() {
|
|
598
|
+
if (!OptionHolder.submit.platform) {
|
|
599
|
+
OptionHolder.submit.platform = await select2({
|
|
600
|
+
message: "Platform to submit",
|
|
601
|
+
choices: [
|
|
602
|
+
{ name: "ios", value: "ios", description: "Release ios" },
|
|
603
|
+
{ name: "android", value: "android", description: "Release android" }
|
|
604
|
+
]
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function submitAndroid() {
|
|
609
|
+
const $$ = $5({
|
|
610
|
+
verbose: false,
|
|
611
|
+
cwd: resolve2(OptionHolder.projectDir, "android")
|
|
612
|
+
});
|
|
613
|
+
await prepareAndroid();
|
|
614
|
+
await fastlane();
|
|
615
|
+
async function fastlane() {
|
|
616
|
+
await spinner4("Bundler Install", () => $$`bundle install`);
|
|
617
|
+
logger.success("Bundler Install");
|
|
618
|
+
await spinner4(
|
|
619
|
+
"Fastlane",
|
|
620
|
+
() => $$`bundle exec fastlane submit_playstore_review version_name:${OptionHolder.versionName} version_code:${OptionHolder.versionCode}`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function submitIos() {
|
|
625
|
+
const iosDir = resolve2(OptionHolder.projectDir, "ios");
|
|
626
|
+
const $$ = $5({
|
|
627
|
+
verbose: false,
|
|
628
|
+
cwd: iosDir
|
|
629
|
+
});
|
|
630
|
+
await prepareIos();
|
|
631
|
+
await fastlane();
|
|
632
|
+
async function fastlane() {
|
|
633
|
+
await spinner4("Bundler Install", () => $$`bundle install`);
|
|
634
|
+
logger.success("Bundler Install");
|
|
635
|
+
remove(resolve2(iosDir, ".xcode.env.local"));
|
|
636
|
+
await spinner4(
|
|
637
|
+
"Fastlane",
|
|
638
|
+
() => $$`bundle exec fastlane submit_appstore_review version_name:${OptionHolder.versionName} version_code:${OptionHolder.versionCode}`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/index.ts
|
|
644
|
+
var run = () => {
|
|
645
|
+
const platformOption = new Option("-p --platform <platform>", "Platform").choices([
|
|
646
|
+
"android",
|
|
647
|
+
"ios"
|
|
648
|
+
]);
|
|
649
|
+
const program = new Command();
|
|
650
|
+
program.name("expo-release-it").description("Build & Upload & Submit locally for Expo project").version(OptionHolder.cli.version);
|
|
651
|
+
program.command("init").description("Initialize CLI utilities").action(async (options) => {
|
|
652
|
+
try {
|
|
653
|
+
await init({ options });
|
|
654
|
+
} catch (e) {
|
|
655
|
+
logger.error(e);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
program.command("bump").description("Bump binary release patch version with modifying app.json").action(async (options) => {
|
|
35
659
|
await bump({ options });
|
|
36
|
-
});
|
|
37
|
-
program
|
|
38
|
-
.command('build')
|
|
39
|
-
.description('Build binary')
|
|
40
|
-
.addOption(platformOption)
|
|
41
|
-
.action(async (options) => {
|
|
660
|
+
});
|
|
661
|
+
program.command("build").description("Build binary").addOption(platformOption).action(async (options) => {
|
|
42
662
|
await upload({ options });
|
|
43
|
-
});
|
|
44
|
-
program
|
|
45
|
-
.command('upload')
|
|
46
|
-
.description('Upload artifact to play console internal test track(android) and testflight(ios)')
|
|
47
|
-
.addOption(platformOption)
|
|
48
|
-
.action(async (options) => {
|
|
663
|
+
});
|
|
664
|
+
program.command("upload").description("Upload artifact to play console internal test track(android) and testflight(ios)").addOption(platformOption).action(async (options) => {
|
|
49
665
|
await upload({ options });
|
|
50
|
-
});
|
|
51
|
-
program
|
|
52
|
-
.command('submit')
|
|
53
|
-
.description('Submit Google Play Console & App Store Connect Review with latest testing track')
|
|
54
|
-
.addOption(platformOption)
|
|
55
|
-
.action(async (options) => {
|
|
666
|
+
});
|
|
667
|
+
program.command("submit").description("Submit Google Play Console & App Store Connect Review with latest testing track").addOption(platformOption).action(async (options) => {
|
|
56
668
|
await submit({ options });
|
|
57
|
-
});
|
|
58
|
-
program.parse();
|
|
669
|
+
});
|
|
670
|
+
program.parse();
|
|
671
|
+
};
|
|
672
|
+
export {
|
|
673
|
+
run
|
|
674
|
+
};
|
|
675
|
+
//# sourceMappingURL=index.js.map
|