react-native-update-cli 1.41.0 → 1.42.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/lib/app.js +3 -3
- package/lib/bundle.js +6 -6
- package/lib/index.js +4 -15
- package/lib/locales/en.js +13 -1
- package/lib/locales/zh.js +13 -1
- package/lib/package.js +11 -3
- package/lib/utils/app-info-parser/utils.js +1 -1
- package/lib/utils/dep-versions.js +36 -0
- package/lib/utils/git.js +58 -0
- package/lib/utils/i18n.js +28 -0
- package/lib/utils/index.js +7 -29
- package/lib/utils/lock-checker.js +8 -0
- package/lib/versions.js +9 -5
- package/package.json +12 -11
- package/src/api.ts +1 -1
- package/src/app.ts +11 -7
- package/src/bundle.ts +17 -24
- package/src/index.ts +4 -17
- package/src/locales/en.ts +17 -1
- package/src/locales/zh.ts +16 -1
- package/src/package.ts +15 -6
- package/src/utils/app-info-parser/utils.js +1 -1
- package/src/utils/dep-versions.ts +26 -0
- package/src/utils/git.ts +50 -0
- package/src/utils/i18n.ts +28 -0
- package/src/utils/index.ts +9 -36
- package/src/utils/lock-checker.ts +7 -0
- package/src/versions.ts +12 -7
package/lib/app.js
CHANGED
|
@@ -103,7 +103,8 @@ const commands = {
|
|
|
103
103
|
const platform = checkPlatform(options.platform || await (0, _utils.question)('平台(ios/android/harmony):'));
|
|
104
104
|
const { id } = await (0, _api.post)('/app/create', {
|
|
105
105
|
name,
|
|
106
|
-
platform
|
|
106
|
+
platform,
|
|
107
|
+
downloadUrl
|
|
107
108
|
});
|
|
108
109
|
console.log(`已成功创建应用(id: ${id})`);
|
|
109
110
|
await this.selectApp({
|
|
@@ -111,8 +112,7 @@ const commands = {
|
|
|
111
112
|
id
|
|
112
113
|
],
|
|
113
114
|
options: {
|
|
114
|
-
platform
|
|
115
|
-
downloadUrl
|
|
115
|
+
platform
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
},
|
package/lib/bundle.js
CHANGED
|
@@ -28,6 +28,7 @@ const _app = require("./app");
|
|
|
28
28
|
const _nodechild_process = require("node:child_process");
|
|
29
29
|
const _satisfies = /*#__PURE__*/ _interop_require_default(require("semver/functions/satisfies"));
|
|
30
30
|
const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os"));
|
|
31
|
+
const _depversions = require("./utils/dep-versions");
|
|
31
32
|
function _interop_require_default(obj) {
|
|
32
33
|
return obj && obj.__esModule ? obj : {
|
|
33
34
|
default: obj
|
|
@@ -123,15 +124,15 @@ async function runReactNativeBundleCommand({ bundleName, dev, entryFile, outputF
|
|
|
123
124
|
};
|
|
124
125
|
const getRnCli = ()=>{
|
|
125
126
|
try {
|
|
126
|
-
// rn
|
|
127
|
-
cliPath = require.resolve('
|
|
127
|
+
// rn < 0.75
|
|
128
|
+
cliPath = require.resolve('react-native/local-cli/cli.js', {
|
|
128
129
|
paths: [
|
|
129
130
|
process.cwd()
|
|
130
131
|
]
|
|
131
132
|
});
|
|
132
133
|
} catch (e) {
|
|
133
|
-
// rn
|
|
134
|
-
cliPath = require.resolve('react-native/
|
|
134
|
+
// rn >= 0.75
|
|
135
|
+
cliPath = require.resolve('@react-native-community/cli/build/bin.js', {
|
|
135
136
|
paths: [
|
|
136
137
|
process.cwd()
|
|
137
138
|
]
|
|
@@ -766,8 +767,7 @@ const commands = {
|
|
|
766
767
|
if (!platform) {
|
|
767
768
|
throw new Error('Platform must be specified.');
|
|
768
769
|
}
|
|
769
|
-
|
|
770
|
-
console.log(`Bundling with react-native: ${version}`);
|
|
770
|
+
console.log(`Bundling with react-native: ${_depversions.depVersions['react-native']}`);
|
|
771
771
|
await runReactNativeBundleCommand({
|
|
772
772
|
bundleName,
|
|
773
773
|
dev,
|
package/lib/index.js
CHANGED
|
@@ -7,34 +7,23 @@ const _api = require("./api");
|
|
|
7
7
|
const _updatenotifier = /*#__PURE__*/ _interop_require_default(require("update-notifier"));
|
|
8
8
|
const _utils = require("./utils");
|
|
9
9
|
const _packagejson = /*#__PURE__*/ _interop_require_default(require("../package.json"));
|
|
10
|
-
const
|
|
11
|
-
const _en = /*#__PURE__*/ _interop_require_default(require("./locales/en"));
|
|
12
|
-
const _zh = /*#__PURE__*/ _interop_require_default(require("./locales/zh"));
|
|
13
|
-
const _constants = require("./utils/constants");
|
|
10
|
+
const _i18n = require("./utils/i18n");
|
|
14
11
|
function _interop_require_default(obj) {
|
|
15
12
|
return obj && obj.__esModule ? obj : {
|
|
16
13
|
default: obj
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
|
-
_i18next.default.init({
|
|
20
|
-
lng: _constants.IS_CRESC ? 'en' : 'zh',
|
|
21
|
-
// debug: process.env.NODE_ENV !== 'production',
|
|
22
|
-
resources: {
|
|
23
|
-
en: _en.default,
|
|
24
|
-
zh: _zh.default
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
16
|
(0, _updatenotifier.default)({
|
|
28
17
|
pkg: _packagejson.default
|
|
29
18
|
}).notify({
|
|
30
19
|
isGlobal: true,
|
|
31
|
-
message:
|
|
20
|
+
message: (0, _i18n.t)('updateNotifier')
|
|
32
21
|
});
|
|
33
22
|
function printUsage() {
|
|
34
23
|
// const commandName = args[0];
|
|
35
24
|
// TODO: print usage of commandName, or print global usage.
|
|
36
25
|
console.log('Usage is under development now.');
|
|
37
|
-
console.log('Visit `https://github.com/reactnativecn/react-native-
|
|
26
|
+
console.log('Visit `https://github.com/reactnativecn/react-native-update` for document.');
|
|
38
27
|
process.exit(1);
|
|
39
28
|
}
|
|
40
29
|
const commands = {
|
|
@@ -55,7 +44,7 @@ async function run() {
|
|
|
55
44
|
global.USE_ACC_OSS = argv.options.acc;
|
|
56
45
|
(0, _api.loadSession)().then(()=>commands[argv.command](argv)).catch((err)=>{
|
|
57
46
|
if (err.status === 401) {
|
|
58
|
-
console.log(
|
|
47
|
+
console.log((0, _i18n.t)('loginFirst'));
|
|
59
48
|
return;
|
|
60
49
|
}
|
|
61
50
|
console.error(err.stack);
|
package/lib/locales/en.js
CHANGED
|
@@ -8,4 +8,16 @@ Object.defineProperty(exports, "default", {
|
|
|
8
8
|
return _default;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _default = {
|
|
11
|
+
const _default = {
|
|
12
|
+
updateNotifier: 'Run `{updateCommand}` to update the CLI to get continuous improvements in features, performance, and security.',
|
|
13
|
+
loginFirst: 'Not logged in.\nPlease run `cresc login` in the project directory to login.',
|
|
14
|
+
lockNotFound: 'No lock file detected, which may cause inconsistent dependencies and hot-updating issues.',
|
|
15
|
+
multipleLocksFound: 'Multiple lock files detected ({lockFiles}), which may cause inconsistent dependencies and hot-updating issues.',
|
|
16
|
+
lockBestPractice: `
|
|
17
|
+
Best practices for lock files:
|
|
18
|
+
1. All members of the development team should use the same package manager to maintain a single lock file.
|
|
19
|
+
2. Add the lock file to version control (but do not commit multiple lock files of different formats).
|
|
20
|
+
3. Pay attention to changes in the lock file during code review.
|
|
21
|
+
This can reduce the risk of inconsistent dependencies and supply chain attacks.
|
|
22
|
+
`
|
|
23
|
+
};
|
package/lib/locales/zh.js
CHANGED
|
@@ -8,4 +8,16 @@ Object.defineProperty(exports, "default", {
|
|
|
8
8
|
return _default;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _default = {
|
|
11
|
+
const _default = {
|
|
12
|
+
updateNotifier: '建议运行 `{updateCommand}` 来更新命令行工具以获得功能、性能和安全性的持续改进',
|
|
13
|
+
loginFirst: '尚未登录。\n请在项目目录中运行`pushy login`命令来登录',
|
|
14
|
+
lockNotFound: '没有检测到任何 lock 文件,这可能导致依赖关系不一致而使热更异常。',
|
|
15
|
+
lockBestPractice: `
|
|
16
|
+
关于 lock 文件的最佳实践:
|
|
17
|
+
1. 开发团队中的所有成员应该使用相同的包管理器,维护同一份 lock 文件。
|
|
18
|
+
2. 将 lock 文件添加到版本控制中(但不要同时提交多种不同格式的 lock 文件)。
|
|
19
|
+
3. 代码审核时应关注 lock 文件的变化。
|
|
20
|
+
这样可以最大限度避免因依赖关系不一致而导致的热更异常,也降低供应链攻击等安全隐患。
|
|
21
|
+
`,
|
|
22
|
+
multipleLocksFound: '检测到多种不同格式的锁文件({lockFiles}),这可能导致依赖关系不一致而使热更异常。'
|
|
23
|
+
};
|
package/lib/package.js
CHANGED
|
@@ -23,6 +23,8 @@ const _api = require("./api");
|
|
|
23
23
|
const _utils = require("./utils");
|
|
24
24
|
const _app = require("./app");
|
|
25
25
|
const _ttytable = /*#__PURE__*/ _interop_require_default(require("tty-table"));
|
|
26
|
+
const _depversions = require("./utils/dep-versions");
|
|
27
|
+
const _git = require("./utils/git");
|
|
26
28
|
function _interop_require_default(obj) {
|
|
27
29
|
return obj && obj.__esModule ? obj : {
|
|
28
30
|
default: obj
|
|
@@ -92,7 +94,9 @@ const commands = {
|
|
|
92
94
|
const { id } = await (0, _api.post)(`/app/${appId}/package/create`, {
|
|
93
95
|
name: versionName,
|
|
94
96
|
hash,
|
|
95
|
-
buildTime
|
|
97
|
+
buildTime,
|
|
98
|
+
deps: _depversions.depVersions,
|
|
99
|
+
commit: await (0, _git.getCommitInfo)()
|
|
96
100
|
});
|
|
97
101
|
(0, _utils.saveToLocal)(fn, `${appId}/package/${id}.ipa`);
|
|
98
102
|
console.log(`已成功上传ipa原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`);
|
|
@@ -114,7 +118,9 @@ const commands = {
|
|
|
114
118
|
const { id } = await (0, _api.post)(`/app/${appId}/package/create`, {
|
|
115
119
|
name: versionName,
|
|
116
120
|
hash,
|
|
117
|
-
buildTime
|
|
121
|
+
buildTime,
|
|
122
|
+
deps: _depversions.depVersions,
|
|
123
|
+
commit: await (0, _git.getCommitInfo)()
|
|
118
124
|
});
|
|
119
125
|
(0, _utils.saveToLocal)(fn, `${appId}/package/${id}.apk`);
|
|
120
126
|
console.log(`已成功上传apk原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`);
|
|
@@ -136,7 +142,9 @@ const commands = {
|
|
|
136
142
|
const { id } = await (0, _api.post)(`/app/${appId}/package/create`, {
|
|
137
143
|
name: versionName,
|
|
138
144
|
hash,
|
|
139
|
-
buildTime
|
|
145
|
+
buildTime,
|
|
146
|
+
deps: _depversions.depVersions,
|
|
147
|
+
commit: await (0, _git.getCommitInfo)()
|
|
140
148
|
});
|
|
141
149
|
(0, _utils.saveToLocal)(fn, `${appId}/package/${id}.app`);
|
|
142
150
|
console.log(`已成功上传app原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "depVersions", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return depVersions;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const currentPackage = require(`${process.cwd()}/package.json`);
|
|
12
|
+
const depKeys = Object.keys(currentPackage.dependencies);
|
|
13
|
+
const devDepKeys = Object.keys(currentPackage.devDependencies);
|
|
14
|
+
const dedupedDeps = [
|
|
15
|
+
...new Set([
|
|
16
|
+
...depKeys,
|
|
17
|
+
...devDepKeys
|
|
18
|
+
])
|
|
19
|
+
];
|
|
20
|
+
const _depVersions = {};
|
|
21
|
+
for (const dep of dedupedDeps){
|
|
22
|
+
try {
|
|
23
|
+
const packageJsonPath = require.resolve(`${dep}/package.json`, {
|
|
24
|
+
paths: [
|
|
25
|
+
process.cwd()
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
const version = require(packageJsonPath).version;
|
|
29
|
+
_depVersions[dep] = version;
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
}
|
|
32
|
+
const depVersions = Object.keys(_depVersions).sort() // Sort the keys alphabetically
|
|
33
|
+
.reduce((obj, key)=>{
|
|
34
|
+
obj[key] = _depVersions[key]; // Rebuild the object with sorted keys
|
|
35
|
+
return obj;
|
|
36
|
+
}, {}); // console.log({ depVersions });
|
package/lib/utils/git.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "getCommitInfo", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return getCommitInfo;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _isomorphicgit = /*#__PURE__*/ _interop_require_default(require("isomorphic-git"));
|
|
12
|
+
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
13
|
+
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
14
|
+
function _interop_require_default(obj) {
|
|
15
|
+
return obj && obj.__esModule ? obj : {
|
|
16
|
+
default: obj
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function findGitRoot(dir = process.cwd()) {
|
|
20
|
+
const gitRoot = _nodefs.default.readdirSync(dir).find((dir)=>dir === '.git');
|
|
21
|
+
if (gitRoot) {
|
|
22
|
+
// console.log({ gitRoot });
|
|
23
|
+
return _nodepath.default.join(dir, gitRoot);
|
|
24
|
+
}
|
|
25
|
+
const parentDir = _nodepath.default.dirname(dir);
|
|
26
|
+
if (parentDir === dir) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return findGitRoot(parentDir);
|
|
30
|
+
}
|
|
31
|
+
const gitRoot = findGitRoot();
|
|
32
|
+
async function getCommitInfo() {
|
|
33
|
+
if (!gitRoot) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const remotes = await _isomorphicgit.default.listRemotes({
|
|
38
|
+
fs: _nodefs.default,
|
|
39
|
+
gitdir: gitRoot
|
|
40
|
+
});
|
|
41
|
+
const origin = remotes.find((remote)=>remote.remote === 'origin') || remotes[0];
|
|
42
|
+
const { commit, oid } = (await _isomorphicgit.default.log({
|
|
43
|
+
fs: _nodefs.default,
|
|
44
|
+
gitdir: gitRoot,
|
|
45
|
+
depth: 1
|
|
46
|
+
}))[0];
|
|
47
|
+
return {
|
|
48
|
+
hash: oid,
|
|
49
|
+
message: commit.message,
|
|
50
|
+
author: commit.author.name || commit.committer.name,
|
|
51
|
+
timestamp: String(commit.committer.timestamp),
|
|
52
|
+
origin: origin.url
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(error);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "t", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return t;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _i18next = /*#__PURE__*/ _interop_require_default(require("i18next"));
|
|
12
|
+
const _en = /*#__PURE__*/ _interop_require_default(require("../locales/en"));
|
|
13
|
+
const _zh = /*#__PURE__*/ _interop_require_default(require("../locales/zh"));
|
|
14
|
+
const _constants = require("./constants");
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
_i18next.default.init({
|
|
21
|
+
lng: _constants.IS_CRESC ? 'en' : 'zh',
|
|
22
|
+
// debug: process.env.NODE_ENV !== 'production',
|
|
23
|
+
resources: {
|
|
24
|
+
en: _en.default,
|
|
25
|
+
zh: _zh.default
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const t = _i18next.default.t;
|
package/lib/utils/index.js
CHANGED
|
@@ -21,9 +21,6 @@ _export(exports, {
|
|
|
21
21
|
getIpaInfo: function() {
|
|
22
22
|
return getIpaInfo;
|
|
23
23
|
},
|
|
24
|
-
getRNVersion: function() {
|
|
25
|
-
return getRNVersion;
|
|
26
|
-
},
|
|
27
24
|
printVersionCommand: function() {
|
|
28
25
|
return printVersionCommand;
|
|
29
26
|
},
|
|
@@ -48,6 +45,7 @@ const _latestversion = /*#__PURE__*/ _interop_require_default(require("@badisi/l
|
|
|
48
45
|
const _checkplugin = require("./check-plugin");
|
|
49
46
|
const _read = require("read");
|
|
50
47
|
const _constants = require("./constants");
|
|
48
|
+
const _depversions = require("./dep-versions");
|
|
51
49
|
function _interop_require_default(obj) {
|
|
52
50
|
return obj && obj.__esModule ? obj : {
|
|
53
51
|
default: obj
|
|
@@ -75,19 +73,6 @@ function translateOptions(options) {
|
|
|
75
73
|
}
|
|
76
74
|
return ret;
|
|
77
75
|
}
|
|
78
|
-
function getRNVersion() {
|
|
79
|
-
const version = JSON.parse(_fsextra.default.readFileSync(require.resolve('react-native/package.json', {
|
|
80
|
-
paths: [
|
|
81
|
-
process.cwd()
|
|
82
|
-
]
|
|
83
|
-
})).toString()).version;
|
|
84
|
-
const [, major, minor] = /^(\d+)\.(\d+)\./.exec(version) || [];
|
|
85
|
-
return {
|
|
86
|
-
version,
|
|
87
|
-
major: Number(major),
|
|
88
|
-
minor: Number(minor)
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
76
|
async function getApkInfo(fn) {
|
|
92
77
|
const appInfoParser = new _appinfoparser.default(fn);
|
|
93
78
|
const bundleFile = await appInfoParser.parser.getEntry(/assets\/index.android.bundle/);
|
|
@@ -184,7 +169,7 @@ function saveToLocal(originPath, destName) {
|
|
|
184
169
|
}
|
|
185
170
|
async function getLatestVersion(pkgNames) {
|
|
186
171
|
return (0, _latestversion.default)(pkgNames, {
|
|
187
|
-
useCache: true,
|
|
172
|
+
// useCache: true,
|
|
188
173
|
requestOptions: {
|
|
189
174
|
timeout: 2000
|
|
190
175
|
}
|
|
@@ -198,18 +183,9 @@ async function printVersionCommand() {
|
|
|
198
183
|
latestPushyCliVersion = latestPushyCliVersion ? ` (最新:${_chalk.default.green(latestPushyCliVersion)})` : '';
|
|
199
184
|
console.log(`react-native-update-cli: ${_packagejson.default.version}${latestPushyCliVersion}`);
|
|
200
185
|
let pushyVersion = '';
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
process.cwd()
|
|
205
|
-
]
|
|
206
|
-
});
|
|
207
|
-
pushyVersion = require(PACKAGE_JSON_PATH).version;
|
|
208
|
-
latestPushyVersion = latestPushyVersion ? ` (最新:${_chalk.default.green(latestPushyVersion)})` : '';
|
|
209
|
-
console.log(`react-native-update: ${pushyVersion}${latestPushyVersion}`);
|
|
210
|
-
} catch (e) {
|
|
211
|
-
console.log('react-native-update: 无法获取版本号,请在项目目录中运行命令');
|
|
212
|
-
}
|
|
186
|
+
pushyVersion = _depversions.depVersions['react-native-update'];
|
|
187
|
+
latestPushyVersion = latestPushyVersion ? ` (最新:${_chalk.default.green(latestPushyVersion)})` : '';
|
|
188
|
+
console.log(`react-native-update: ${pushyVersion}${latestPushyVersion}`);
|
|
213
189
|
if (pushyVersion) {
|
|
214
190
|
if ((0, _satisfies.default)(pushyVersion, '<8.5.2')) {
|
|
215
191
|
console.warn(`当前版本已不再支持,请至少升级到 v8 的最新小版本后重新打包(代码无需改动): npm i react-native-update@8 .
|
|
@@ -220,5 +196,7 @@ async function printVersionCommand() {
|
|
|
220
196
|
} else if ((0, _satisfies.default)(pushyVersion, '10.0.0 - 10.17.0')) {
|
|
221
197
|
console.warn('当前版本已不再支持,请升级到 v10 的最新小版本(代码无需改动,可直接热更): npm i react-native-update@10');
|
|
222
198
|
}
|
|
199
|
+
} else {
|
|
200
|
+
console.log('react-native-update: 无法获取版本号,请在项目目录中运行命令');
|
|
223
201
|
}
|
|
224
202
|
}
|
package/lib/versions.js
CHANGED
|
@@ -13,16 +13,18 @@ const _utils = require("./utils");
|
|
|
13
13
|
const _app = require("./app");
|
|
14
14
|
const _package = require("./package");
|
|
15
15
|
const _compareversions = require("compare-versions");
|
|
16
|
+
const _depversions = require("./utils/dep-versions");
|
|
17
|
+
const _git = require("./utils/git");
|
|
16
18
|
async function showVersion(appId, offset) {
|
|
17
19
|
const { data, count } = await (0, _api.get)(`/app/${appId}/version/list`);
|
|
18
20
|
console.log(`Offset ${offset}`);
|
|
19
21
|
for (const version of data){
|
|
20
22
|
let packageInfo = version.packages.slice(0, 3).map((v)=>v.name).join(', ');
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
packageInfo += `...and ${
|
|
23
|
+
const pkgCount = version.packages.length;
|
|
24
|
+
if (pkgCount > 3) {
|
|
25
|
+
packageInfo += `...and ${pkgCount - 3} more`;
|
|
24
26
|
}
|
|
25
|
-
if (
|
|
27
|
+
if (pkgCount === 0) {
|
|
26
28
|
packageInfo = 'no package';
|
|
27
29
|
} else {
|
|
28
30
|
packageInfo = `[${packageInfo}]`;
|
|
@@ -91,7 +93,9 @@ const commands = {
|
|
|
91
93
|
name: versionName,
|
|
92
94
|
hash,
|
|
93
95
|
description: description || await (0, _utils.question)('输入版本描述:'),
|
|
94
|
-
metaInfo: metaInfo || await (0, _utils.question)('输入自定义的 meta info:')
|
|
96
|
+
metaInfo: metaInfo || await (0, _utils.question)('输入自定义的 meta info:'),
|
|
97
|
+
deps: _depversions.depVersions,
|
|
98
|
+
commit: await (0, _git.getCommitInfo)()
|
|
95
99
|
});
|
|
96
100
|
// TODO local diff
|
|
97
101
|
(0, _utils.saveToLocal)(fn, `${appId}/ppk/${id}.ppk`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-update-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.1",
|
|
4
4
|
"description": "Command tools for javaScript updater with `pushy` service for react native apps.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,26 +35,27 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@badisi/latest-version": "^7.0.
|
|
38
|
+
"@badisi/latest-version": "^7.0.12",
|
|
39
39
|
"bplist-parser": "^0.3.2",
|
|
40
40
|
"bytebuffer": "^5.0.1",
|
|
41
41
|
"cgbi-to-png": "^1.0.7",
|
|
42
42
|
"chalk": "4",
|
|
43
43
|
"cli-arguments": "^0.2.1",
|
|
44
|
-
"commander": "^
|
|
44
|
+
"commander": "^13.1.0",
|
|
45
45
|
"compare-versions": "^6.1.1",
|
|
46
46
|
"filesize-parser": "^1.5.1",
|
|
47
|
-
"form-data": "^4.0.
|
|
47
|
+
"form-data": "^4.0.2",
|
|
48
48
|
"fs-extra": "8",
|
|
49
49
|
"gradle-to-js": "^2.0.1",
|
|
50
|
-
"i18next": "^24.2.
|
|
50
|
+
"i18next": "^24.2.3",
|
|
51
|
+
"isomorphic-git": "^1.29.0",
|
|
51
52
|
"isomorphic-unzip": "^1.1.5",
|
|
52
53
|
"node-fetch": "^2.6.1",
|
|
53
54
|
"plist": "^3.1.0",
|
|
54
55
|
"progress": "^2.0.3",
|
|
55
56
|
"properties": "^1.2.1",
|
|
56
|
-
"read": "^4.
|
|
57
|
-
"semver": "^7.
|
|
57
|
+
"read": "^4.1.0",
|
|
58
|
+
"semver": "^7.7.1",
|
|
58
59
|
"tcp-ping": "^0.1.1",
|
|
59
60
|
"tty-table": "4.2",
|
|
60
61
|
"update-notifier": "^5.1.0",
|
|
@@ -66,11 +67,11 @@
|
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
69
|
"@biomejs/biome": "^1.9.4",
|
|
69
|
-
"@swc/cli": "^0.
|
|
70
|
-
"@swc/core": "^1.9
|
|
70
|
+
"@swc/cli": "^0.6.0",
|
|
71
|
+
"@swc/core": "^1.11.9",
|
|
71
72
|
"@types/filesize-parser": "^1.5.3",
|
|
72
73
|
"@types/fs-extra": "^11.0.4",
|
|
73
|
-
"@types/node": "^22.
|
|
74
|
+
"@types/node": "^22.13.10",
|
|
74
75
|
"@types/node-fetch": "^2.6.12",
|
|
75
76
|
"@types/progress": "^2.0.7",
|
|
76
77
|
"@types/semver": "^7.5.8",
|
|
@@ -78,6 +79,6 @@
|
|
|
78
79
|
"@types/update-notifier": "^6.0.8",
|
|
79
80
|
"@types/yauzl": "^2.10.3",
|
|
80
81
|
"@types/yazl": "^2.4.6",
|
|
81
|
-
"typescript": "^5.
|
|
82
|
+
"typescript": "^5.8.2"
|
|
82
83
|
}
|
|
83
84
|
}
|
package/src/api.ts
CHANGED
package/src/app.ts
CHANGED
|
@@ -72,20 +72,24 @@ export async function chooseApp(platform: Platform) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export const commands = {
|
|
75
|
-
createApp: async function ({
|
|
75
|
+
createApp: async function ({
|
|
76
|
+
options,
|
|
77
|
+
}: {
|
|
78
|
+
options: { name: string; downloadUrl: string; platform: Platform };
|
|
79
|
+
}) {
|
|
76
80
|
const name = options.name || (await question('应用名称:'));
|
|
77
81
|
const { downloadUrl } = options;
|
|
78
82
|
const platform = checkPlatform(
|
|
79
83
|
options.platform || (await question('平台(ios/android/harmony):')),
|
|
80
84
|
);
|
|
81
|
-
const { id } = await post('/app/create', { name, platform });
|
|
85
|
+
const { id } = await post('/app/create', { name, platform, downloadUrl });
|
|
82
86
|
console.log(`已成功创建应用(id: ${id})`);
|
|
83
87
|
await this.selectApp({
|
|
84
88
|
args: [id],
|
|
85
|
-
options: { platform
|
|
89
|
+
options: { platform },
|
|
86
90
|
});
|
|
87
91
|
},
|
|
88
|
-
deleteApp: async ({ args, options }) => {
|
|
92
|
+
deleteApp: async ({ args, options }: { args: string[]; options: { platform: Platform } }) => {
|
|
89
93
|
const { platform } = options;
|
|
90
94
|
const id = args[0] || chooseApp(platform);
|
|
91
95
|
if (!id) {
|
|
@@ -94,11 +98,11 @@ export const commands = {
|
|
|
94
98
|
await doDelete(`/app/${id}`);
|
|
95
99
|
console.log('操作成功');
|
|
96
100
|
},
|
|
97
|
-
apps: async ({ options }) => {
|
|
101
|
+
apps: async ({ options }: { options: { platform: Platform } }) => {
|
|
98
102
|
const { platform } = options;
|
|
99
103
|
listApp(platform);
|
|
100
104
|
},
|
|
101
|
-
selectApp: async ({ args, options }) => {
|
|
105
|
+
selectApp: async ({ args, options }: { args: string[]; options: { platform: Platform } }) => {
|
|
102
106
|
const platform = checkPlatform(
|
|
103
107
|
options.platform || (await question('平台(ios/android/harmony):')),
|
|
104
108
|
);
|
|
@@ -106,7 +110,7 @@ export const commands = {
|
|
|
106
110
|
? Number.parseInt(args[0])
|
|
107
111
|
: (await chooseApp(platform)).id;
|
|
108
112
|
|
|
109
|
-
let updateInfo = {};
|
|
113
|
+
let updateInfo: Partial<Record<Platform, { appId: number; appKey: string }>> = {};
|
|
110
114
|
if (fs.existsSync('update.json')) {
|
|
111
115
|
try {
|
|
112
116
|
updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8'));
|
package/src/bundle.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { translateOptions } from './utils';
|
|
3
3
|
import * as fs from 'fs-extra';
|
|
4
4
|
import { ZipFile } from 'yazl';
|
|
5
5
|
import { open as openZipFile } from 'yauzl';
|
|
@@ -10,6 +10,7 @@ import semverSatisfies from 'semver/functions/satisfies';
|
|
|
10
10
|
const g2js = require('gradle-to-js/lib/parser');
|
|
11
11
|
import os from 'node:os';
|
|
12
12
|
const properties = require('properties');
|
|
13
|
+
import { depVersions } from './utils/dep-versions';
|
|
13
14
|
|
|
14
15
|
let bsdiff;
|
|
15
16
|
let hdiff;
|
|
@@ -82,11 +83,13 @@ async function runReactNativeBundleCommand({
|
|
|
82
83
|
paths: [process.cwd()],
|
|
83
84
|
});
|
|
84
85
|
const expoCliVersion = JSON.parse(
|
|
85
|
-
fs
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
fs
|
|
87
|
+
.readFileSync(
|
|
88
|
+
require.resolve('@expo/cli/package.json', {
|
|
89
|
+
paths: [process.cwd()],
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
.toString(),
|
|
90
93
|
).version;
|
|
91
94
|
// expo cli 0.10.17 (expo 49) 开始支持 bundle:embed
|
|
92
95
|
if (semverSatisfies(expoCliVersion, '>= 0.10.17')) {
|
|
@@ -99,13 +102,13 @@ async function runReactNativeBundleCommand({
|
|
|
99
102
|
|
|
100
103
|
const getRnCli = () => {
|
|
101
104
|
try {
|
|
102
|
-
// rn
|
|
103
|
-
cliPath = require.resolve('
|
|
105
|
+
// rn < 0.75
|
|
106
|
+
cliPath = require.resolve('react-native/local-cli/cli.js', {
|
|
104
107
|
paths: [process.cwd()],
|
|
105
108
|
});
|
|
106
109
|
} catch (e) {
|
|
107
|
-
// rn
|
|
108
|
-
cliPath = require.resolve('react-native/
|
|
110
|
+
// rn >= 0.75
|
|
111
|
+
cliPath = require.resolve('@react-native-community/cli/build/bin.js', {
|
|
109
112
|
paths: [process.cwd()],
|
|
110
113
|
});
|
|
111
114
|
}
|
|
@@ -175,19 +178,11 @@ async function runReactNativeBundleCommand({
|
|
|
175
178
|
platform,
|
|
176
179
|
'--reset-cache',
|
|
177
180
|
]);
|
|
178
|
-
|
|
181
|
+
|
|
179
182
|
if (cli.taro) {
|
|
180
|
-
reactNativeBundleArgs.push(...[
|
|
181
|
-
'--type',
|
|
182
|
-
'rn',
|
|
183
|
-
])
|
|
183
|
+
reactNativeBundleArgs.push(...['--type', 'rn']);
|
|
184
184
|
} else {
|
|
185
|
-
reactNativeBundleArgs.push(...[
|
|
186
|
-
'--dev',
|
|
187
|
-
dev,
|
|
188
|
-
'--entry-file',
|
|
189
|
-
entryFile,
|
|
190
|
-
])
|
|
185
|
+
reactNativeBundleArgs.push(...['--dev', dev, '--entry-file', entryFile]);
|
|
191
186
|
}
|
|
192
187
|
|
|
193
188
|
if (sourcemapOutput) {
|
|
@@ -927,9 +922,7 @@ export const commands = {
|
|
|
927
922
|
throw new Error('Platform must be specified.');
|
|
928
923
|
}
|
|
929
924
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
console.log(`Bundling with react-native: ${version}`);
|
|
925
|
+
console.log(`Bundling with react-native: ${depVersions['react-native']}`);
|
|
933
926
|
|
|
934
927
|
await runReactNativeBundleCommand({
|
|
935
928
|
bundleName,
|
package/src/index.ts
CHANGED
|
@@ -4,24 +4,11 @@ import { loadSession } from './api';
|
|
|
4
4
|
import updateNotifier from 'update-notifier';
|
|
5
5
|
import { printVersionCommand } from './utils';
|
|
6
6
|
import pkg from '../package.json';
|
|
7
|
-
import
|
|
8
|
-
import en from './locales/en';
|
|
9
|
-
import zh from './locales/zh';
|
|
10
|
-
import { IS_CRESC } from './utils/constants';
|
|
11
|
-
|
|
12
|
-
i18next.init({
|
|
13
|
-
lng: IS_CRESC ? 'en' : 'zh',
|
|
14
|
-
// debug: process.env.NODE_ENV !== 'production',
|
|
15
|
-
resources: {
|
|
16
|
-
en,
|
|
17
|
-
zh,
|
|
18
|
-
},
|
|
19
|
-
});
|
|
7
|
+
import { t } from './utils/i18n';
|
|
20
8
|
|
|
21
9
|
updateNotifier({ pkg }).notify({
|
|
22
10
|
isGlobal: true,
|
|
23
|
-
message:
|
|
24
|
-
'建议运行 `{updateCommand}` 来更新命令行工具以获得功能、性能和安全性的持续改进',
|
|
11
|
+
message: t('updateNotifier'),
|
|
25
12
|
});
|
|
26
13
|
|
|
27
14
|
function printUsage() {
|
|
@@ -30,7 +17,7 @@ function printUsage() {
|
|
|
30
17
|
|
|
31
18
|
console.log('Usage is under development now.');
|
|
32
19
|
console.log(
|
|
33
|
-
'Visit `https://github.com/reactnativecn/react-native-
|
|
20
|
+
'Visit `https://github.com/reactnativecn/react-native-update` for document.',
|
|
34
21
|
);
|
|
35
22
|
process.exit(1);
|
|
36
23
|
}
|
|
@@ -58,7 +45,7 @@ async function run() {
|
|
|
58
45
|
.then(() => commands[argv.command](argv))
|
|
59
46
|
.catch((err) => {
|
|
60
47
|
if (err.status === 401) {
|
|
61
|
-
console.log('
|
|
48
|
+
console.log(t('loginFirst'));
|
|
62
49
|
return;
|
|
63
50
|
}
|
|
64
51
|
console.error(err.stack);
|
package/src/locales/en.ts
CHANGED
|
@@ -1 +1,17 @@
|
|
|
1
|
-
export default {
|
|
1
|
+
export default {
|
|
2
|
+
updateNotifier:
|
|
3
|
+
'Run `{updateCommand}` to update the CLI to get continuous improvements in features, performance, and security.',
|
|
4
|
+
loginFirst:
|
|
5
|
+
'Not logged in.\nPlease run `cresc login` in the project directory to login.',
|
|
6
|
+
lockNotFound:
|
|
7
|
+
'No lock file detected, which may cause inconsistent dependencies and hot-updating issues.',
|
|
8
|
+
multipleLocksFound:
|
|
9
|
+
'Multiple lock files detected ({lockFiles}), which may cause inconsistent dependencies and hot-updating issues.',
|
|
10
|
+
lockBestPractice: `
|
|
11
|
+
Best practices for lock files:
|
|
12
|
+
1. All members of the development team should use the same package manager to maintain a single lock file.
|
|
13
|
+
2. Add the lock file to version control (but do not commit multiple lock files of different formats).
|
|
14
|
+
3. Pay attention to changes in the lock file during code review.
|
|
15
|
+
This can reduce the risk of inconsistent dependencies and supply chain attacks.
|
|
16
|
+
`,
|
|
17
|
+
};
|
package/src/locales/zh.ts
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
export default {
|
|
1
|
+
export default {
|
|
2
|
+
updateNotifier:
|
|
3
|
+
'建议运行 `{updateCommand}` 来更新命令行工具以获得功能、性能和安全性的持续改进',
|
|
4
|
+
loginFirst: '尚未登录。\n请在项目目录中运行`pushy login`命令来登录',
|
|
5
|
+
lockNotFound:
|
|
6
|
+
'没有检测到任何 lock 文件,这可能导致依赖关系不一致而使热更异常。',
|
|
7
|
+
lockBestPractice: `
|
|
8
|
+
关于 lock 文件的最佳实践:
|
|
9
|
+
1. 开发团队中的所有成员应该使用相同的包管理器,维护同一份 lock 文件。
|
|
10
|
+
2. 将 lock 文件添加到版本控制中(但不要同时提交多种不同格式的 lock 文件)。
|
|
11
|
+
3. 代码审核时应关注 lock 文件的变化。
|
|
12
|
+
这样可以最大限度避免因依赖关系不一致而导致的热更异常,也降低供应链攻击等安全隐患。
|
|
13
|
+
`,
|
|
14
|
+
multipleLocksFound:
|
|
15
|
+
'检测到多种不同格式的锁文件({lockFiles}),这可能导致依赖关系不一致而使热更异常。',
|
|
16
|
+
};
|
package/src/package.ts
CHANGED
|
@@ -5,6 +5,9 @@ import { checkPlatform, getSelectedApp } from './app';
|
|
|
5
5
|
|
|
6
6
|
import { getApkInfo, getIpaInfo, getAppInfo } from './utils';
|
|
7
7
|
import Table from 'tty-table';
|
|
8
|
+
import { depVersions } from './utils/dep-versions';
|
|
9
|
+
import { getCommitInfo } from './utils/git';
|
|
10
|
+
import type { Platform } from 'types';
|
|
8
11
|
|
|
9
12
|
export async function listPackage(appId: string) {
|
|
10
13
|
const { data } = await get(`/app/${appId}/package/list?limit=1000`);
|
|
@@ -79,13 +82,15 @@ export const commands = {
|
|
|
79
82
|
name: versionName,
|
|
80
83
|
hash,
|
|
81
84
|
buildTime,
|
|
85
|
+
deps: depVersions,
|
|
86
|
+
commit: await getCommitInfo(),
|
|
82
87
|
});
|
|
83
88
|
saveToLocal(fn, `${appId}/package/${id}.ipa`);
|
|
84
89
|
console.log(
|
|
85
90
|
`已成功上传ipa原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`,
|
|
86
91
|
);
|
|
87
92
|
},
|
|
88
|
-
uploadApk: async ({ args }) => {
|
|
93
|
+
uploadApk: async ({ args }: { args: string[] }) => {
|
|
89
94
|
const fn = args[0];
|
|
90
95
|
if (!fn || !fn.endsWith('.apk')) {
|
|
91
96
|
throw new Error('使用方法: pushy uploadApk apk后缀文件');
|
|
@@ -116,13 +121,15 @@ export const commands = {
|
|
|
116
121
|
name: versionName,
|
|
117
122
|
hash,
|
|
118
123
|
buildTime,
|
|
124
|
+
deps: depVersions,
|
|
125
|
+
commit: await getCommitInfo(),
|
|
119
126
|
});
|
|
120
127
|
saveToLocal(fn, `${appId}/package/${id}.apk`);
|
|
121
128
|
console.log(
|
|
122
129
|
`已成功上传apk原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`,
|
|
123
130
|
);
|
|
124
131
|
},
|
|
125
|
-
uploadApp: async ({ args }) => {
|
|
132
|
+
uploadApp: async ({ args }: { args: string[] }) => {
|
|
126
133
|
const fn = args[0];
|
|
127
134
|
if (!fn || !fn.endsWith('.app')) {
|
|
128
135
|
throw new Error('使用方法: pushy uploadApp app后缀文件');
|
|
@@ -153,34 +160,36 @@ export const commands = {
|
|
|
153
160
|
name: versionName,
|
|
154
161
|
hash,
|
|
155
162
|
buildTime,
|
|
163
|
+
deps: depVersions,
|
|
164
|
+
commit: await getCommitInfo(),
|
|
156
165
|
});
|
|
157
166
|
saveToLocal(fn, `${appId}/package/${id}.app`);
|
|
158
167
|
console.log(
|
|
159
168
|
`已成功上传app原生包(id: ${id}, version: ${versionName}, buildTime: ${buildTime})`,
|
|
160
169
|
);
|
|
161
170
|
},
|
|
162
|
-
parseApp: async ({ args }) => {
|
|
171
|
+
parseApp: async ({ args }: { args: string[] }) => {
|
|
163
172
|
const fn = args[0];
|
|
164
173
|
if (!fn || !fn.endsWith('.app')) {
|
|
165
174
|
throw new Error('使用方法: pushy parseApp app后缀文件');
|
|
166
175
|
}
|
|
167
176
|
console.log(await getAppInfo(fn));
|
|
168
177
|
},
|
|
169
|
-
parseIpa: async ({ args }) => {
|
|
178
|
+
parseIpa: async ({ args }: { args: string[] }) => {
|
|
170
179
|
const fn = args[0];
|
|
171
180
|
if (!fn || !fn.endsWith('.ipa')) {
|
|
172
181
|
throw new Error('使用方法: pushy parseIpa ipa后缀文件');
|
|
173
182
|
}
|
|
174
183
|
console.log(await getIpaInfo(fn));
|
|
175
184
|
},
|
|
176
|
-
parseApk: async ({ args }) => {
|
|
185
|
+
parseApk: async ({ args }: { args: string[] }) => {
|
|
177
186
|
const fn = args[0];
|
|
178
187
|
if (!fn || !fn.endsWith('.apk')) {
|
|
179
188
|
throw new Error('使用方法: pushy parseApk apk后缀文件');
|
|
180
189
|
}
|
|
181
190
|
console.log(await getApkInfo(fn));
|
|
182
191
|
},
|
|
183
|
-
packages: async ({ options }) => {
|
|
192
|
+
packages: async ({ options }: { options: { platform: Platform } }) => {
|
|
184
193
|
const platform = checkPlatform(
|
|
185
194
|
options.platform || (await question('平台(ios/android/harmony):')),
|
|
186
195
|
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const currentPackage = require(`${process.cwd()}/package.json`);
|
|
2
|
+
|
|
3
|
+
const depKeys = Object.keys(currentPackage.dependencies);
|
|
4
|
+
const devDepKeys = Object.keys(currentPackage.devDependencies);
|
|
5
|
+
const dedupedDeps = [...new Set([...depKeys, ...devDepKeys])];
|
|
6
|
+
|
|
7
|
+
const _depVersions: Record<string, string> = {};
|
|
8
|
+
|
|
9
|
+
for (const dep of dedupedDeps) {
|
|
10
|
+
try {
|
|
11
|
+
const packageJsonPath = require.resolve(`${dep}/package.json`, {
|
|
12
|
+
paths: [process.cwd()],
|
|
13
|
+
});
|
|
14
|
+
const version = require(packageJsonPath).version;
|
|
15
|
+
_depVersions[dep] = version;
|
|
16
|
+
} catch (e) {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const depVersions = Object.keys(_depVersions)
|
|
20
|
+
.sort() // Sort the keys alphabetically
|
|
21
|
+
.reduce((obj, key) => {
|
|
22
|
+
obj[key] = _depVersions[key]; // Rebuild the object with sorted keys
|
|
23
|
+
return obj;
|
|
24
|
+
}, {} as Record<string, string>);
|
|
25
|
+
|
|
26
|
+
// console.log({ depVersions });
|
package/src/utils/git.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import git from 'isomorphic-git';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface CommitInfo {
|
|
6
|
+
hash: string;
|
|
7
|
+
message: string;
|
|
8
|
+
author: string;
|
|
9
|
+
timestamp: string;
|
|
10
|
+
origin: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function findGitRoot(dir = process.cwd()) {
|
|
14
|
+
const gitRoot = fs.readdirSync(dir).find((dir) => dir === '.git');
|
|
15
|
+
if (gitRoot) {
|
|
16
|
+
// console.log({ gitRoot });
|
|
17
|
+
return path.join(dir, gitRoot);
|
|
18
|
+
}
|
|
19
|
+
const parentDir = path.dirname(dir);
|
|
20
|
+
if (parentDir === dir) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return findGitRoot(parentDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const gitRoot = findGitRoot();
|
|
27
|
+
|
|
28
|
+
export async function getCommitInfo(): Promise<CommitInfo | undefined> {
|
|
29
|
+
if (!gitRoot) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const remotes = await git.listRemotes({ fs, gitdir: gitRoot });
|
|
34
|
+
const origin =
|
|
35
|
+
remotes.find((remote) => remote.remote === 'origin') || remotes[0];
|
|
36
|
+
const { commit, oid } = (
|
|
37
|
+
await git.log({ fs, gitdir: gitRoot, depth: 1 })
|
|
38
|
+
)[0];
|
|
39
|
+
return {
|
|
40
|
+
hash: oid,
|
|
41
|
+
message: commit.message,
|
|
42
|
+
author: commit.author.name || commit.committer.name,
|
|
43
|
+
timestamp: String(commit.committer.timestamp),
|
|
44
|
+
origin: origin.url,
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import en from '../locales/en';
|
|
3
|
+
import zh from '../locales/zh';
|
|
4
|
+
import { IS_CRESC } from './constants';
|
|
5
|
+
i18next.init({
|
|
6
|
+
lng: IS_CRESC ? 'en' : 'zh',
|
|
7
|
+
// debug: process.env.NODE_ENV !== 'production',
|
|
8
|
+
resources: {
|
|
9
|
+
en,
|
|
10
|
+
zh,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
declare module 'i18next' {
|
|
15
|
+
// Extend CustomTypeOptions
|
|
16
|
+
interface CustomTypeOptions {
|
|
17
|
+
// custom namespace type, if you changed it
|
|
18
|
+
defaultNS: 'en';
|
|
19
|
+
// custom resources type
|
|
20
|
+
resources: {
|
|
21
|
+
en: typeof en;
|
|
22
|
+
zh: typeof zh;
|
|
23
|
+
};
|
|
24
|
+
// other
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const t = i18next.t;
|
package/src/utils/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { checkPlugins } from './check-plugin';
|
|
|
10
10
|
|
|
11
11
|
import { read } from 'read';
|
|
12
12
|
import { tempDir } from './constants';
|
|
13
|
+
import { depVersions } from './dep-versions';
|
|
13
14
|
|
|
14
15
|
export async function question(query: string, password?: boolean) {
|
|
15
16
|
if (NO_INTERACTIVE) {
|
|
@@ -38,26 +39,6 @@ export function translateOptions(options: Record<string, string>) {
|
|
|
38
39
|
return ret;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export function getRNVersion() {
|
|
42
|
-
const version = JSON.parse(
|
|
43
|
-
fs
|
|
44
|
-
.readFileSync(
|
|
45
|
-
require.resolve('react-native/package.json', {
|
|
46
|
-
paths: [process.cwd()],
|
|
47
|
-
}),
|
|
48
|
-
)
|
|
49
|
-
.toString(),
|
|
50
|
-
).version;
|
|
51
|
-
|
|
52
|
-
const [, major, minor] = /^(\d+)\.(\d+)\./.exec(version) || [];
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
version,
|
|
56
|
-
major: Number(major),
|
|
57
|
-
minor: Number(minor),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
42
|
export async function getApkInfo(fn: string) {
|
|
62
43
|
const appInfoParser = new AppInfoParser(fn);
|
|
63
44
|
const bundleFile = await appInfoParser.parser.getEntry(
|
|
@@ -177,7 +158,7 @@ export function saveToLocal(originPath: string, destName: string) {
|
|
|
177
158
|
|
|
178
159
|
async function getLatestVersion(pkgNames: string[]) {
|
|
179
160
|
return latestVersion(pkgNames, {
|
|
180
|
-
useCache: true,
|
|
161
|
+
// useCache: true,
|
|
181
162
|
requestOptions: {
|
|
182
163
|
timeout: 2000,
|
|
183
164
|
},
|
|
@@ -198,21 +179,11 @@ export async function printVersionCommand() {
|
|
|
198
179
|
`react-native-update-cli: ${pkg.version}${latestPushyCliVersion}`,
|
|
199
180
|
);
|
|
200
181
|
let pushyVersion = '';
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
},
|
|
207
|
-
);
|
|
208
|
-
pushyVersion = require(PACKAGE_JSON_PATH).version;
|
|
209
|
-
latestPushyVersion = latestPushyVersion
|
|
210
|
-
? ` (最新:${chalk.green(latestPushyVersion)})`
|
|
211
|
-
: '';
|
|
212
|
-
console.log(`react-native-update: ${pushyVersion}${latestPushyVersion}`);
|
|
213
|
-
} catch (e) {
|
|
214
|
-
console.log('react-native-update: 无法获取版本号,请在项目目录中运行命令');
|
|
215
|
-
}
|
|
182
|
+
pushyVersion = depVersions['react-native-update'];
|
|
183
|
+
latestPushyVersion = latestPushyVersion
|
|
184
|
+
? ` (最新:${chalk.green(latestPushyVersion)})`
|
|
185
|
+
: '';
|
|
186
|
+
console.log(`react-native-update: ${pushyVersion}${latestPushyVersion}`);
|
|
216
187
|
if (pushyVersion) {
|
|
217
188
|
if (semverSatisfies(pushyVersion, '<8.5.2')) {
|
|
218
189
|
console.warn(
|
|
@@ -229,6 +200,8 @@ export async function printVersionCommand() {
|
|
|
229
200
|
'当前版本已不再支持,请升级到 v10 的最新小版本(代码无需改动,可直接热更): npm i react-native-update@10',
|
|
230
201
|
);
|
|
231
202
|
}
|
|
203
|
+
} else {
|
|
204
|
+
console.log('react-native-update: 无法获取版本号,请在项目目录中运行命令');
|
|
232
205
|
}
|
|
233
206
|
}
|
|
234
207
|
|
package/src/versions.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { question, saveToLocal } from './utils';
|
|
|
4
4
|
import { checkPlatform, getSelectedApp } from './app';
|
|
5
5
|
import { choosePackage } from './package';
|
|
6
6
|
import { compare } from 'compare-versions';
|
|
7
|
+
import { depVersions } from './utils/dep-versions';
|
|
8
|
+
import { getCommitInfo } from './utils/git';
|
|
7
9
|
|
|
8
10
|
async function showVersion(appId: string, offset: number) {
|
|
9
11
|
const { data, count } = await get(`/app/${appId}/version/list`);
|
|
@@ -13,11 +15,11 @@ async function showVersion(appId: string, offset: number) {
|
|
|
13
15
|
.slice(0, 3)
|
|
14
16
|
.map((v) => v.name)
|
|
15
17
|
.join(', ');
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
packageInfo += `...and ${
|
|
18
|
+
const pkgCount = version.packages.length;
|
|
19
|
+
if (pkgCount > 3) {
|
|
20
|
+
packageInfo += `...and ${pkgCount - 3} more`;
|
|
19
21
|
}
|
|
20
|
-
if (
|
|
22
|
+
if (pkgCount === 0) {
|
|
21
23
|
packageInfo = 'no package';
|
|
22
24
|
} else {
|
|
23
25
|
packageInfo = `[${packageInfo}]`;
|
|
@@ -31,7 +33,7 @@ async function showVersion(appId: string, offset: number) {
|
|
|
31
33
|
return data;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
async function listVersions(appId) {
|
|
36
|
+
async function listVersions(appId: string) {
|
|
35
37
|
let offset = 0;
|
|
36
38
|
while (true) {
|
|
37
39
|
await showVersion(appId, offset);
|
|
@@ -52,7 +54,7 @@ async function listVersions(appId) {
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
async function chooseVersion(appId) {
|
|
57
|
+
async function chooseVersion(appId: string) {
|
|
56
58
|
let offset = 0;
|
|
57
59
|
while (true) {
|
|
58
60
|
const data = await showVersion(appId, offset);
|
|
@@ -97,12 +99,15 @@ export const commands = {
|
|
|
97
99
|
|
|
98
100
|
const { hash } = await uploadFile(fn);
|
|
99
101
|
|
|
100
|
-
const versionName =
|
|
102
|
+
const versionName =
|
|
103
|
+
name || (await question('输入版本名称: ')) || '(未命名)';
|
|
101
104
|
const { id } = await post(`/app/${appId}/version/create`, {
|
|
102
105
|
name: versionName,
|
|
103
106
|
hash,
|
|
104
107
|
description: description || (await question('输入版本描述:')),
|
|
105
108
|
metaInfo: metaInfo || (await question('输入自定义的 meta info:')),
|
|
109
|
+
deps: depVersions,
|
|
110
|
+
commit: await getCommitInfo(),
|
|
106
111
|
});
|
|
107
112
|
// TODO local diff
|
|
108
113
|
saveToLocal(fn, `${appId}/ppk/${id}.ppk`);
|