hot-updater 0.0.4 → 0.1.0
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/dist/config.cjs +30 -0
- package/dist/config.d.cts +5 -0
- package/dist/config.js +0 -2
- package/dist/index.cjs +386 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.js +41 -2020
- package/package.json +11 -10
- package/dist/chunk-JSBRDJBE.js +0 -30
- package/dist/chunk-QGM4M3NI.js +0 -37
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/config.ts
|
|
21
|
+
var config_exports = {};
|
|
22
|
+
__export(config_exports, {
|
|
23
|
+
defineConfig: () => defineConfig
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(config_exports);
|
|
26
|
+
var defineConfig = (config) => typeof config === "function" ? config() : config;
|
|
27
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
28
|
+
0 && (module.exports = {
|
|
29
|
+
defineConfig
|
|
30
|
+
});
|
package/dist/config.js
CHANGED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/commands/console.ts
|
|
27
|
+
var import_node_server = require("@hono/node-server");
|
|
28
|
+
var import_console = __toESM(require("@hot-updater/console"), 1);
|
|
29
|
+
var openConsole = () => {
|
|
30
|
+
(0, import_node_server.serve)(
|
|
31
|
+
{
|
|
32
|
+
fetch: import_console.default.fetch,
|
|
33
|
+
port: 1422
|
|
34
|
+
},
|
|
35
|
+
(info) => {
|
|
36
|
+
console.log(`Server running on port ${info.port}`);
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/commands/deploy.ts
|
|
42
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
43
|
+
var import_prompts = require("@clack/prompts");
|
|
44
|
+
|
|
45
|
+
// src/utils/createZip.ts
|
|
46
|
+
var import_path = __toESM(require("path"), 1);
|
|
47
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
48
|
+
var import_jszip = __toESM(require("jszip"), 1);
|
|
49
|
+
var createZip = async (dirPath, filename) => {
|
|
50
|
+
const zip = new import_jszip.default();
|
|
51
|
+
async function addFiles(dir, zipFolder) {
|
|
52
|
+
const files = await import_promises.default.readdir(dir);
|
|
53
|
+
files.sort();
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const fullPath = import_path.default.join(dir, file);
|
|
56
|
+
const stats = await import_promises.default.stat(fullPath);
|
|
57
|
+
if (stats.isDirectory()) {
|
|
58
|
+
const folder = zipFolder.folder(file);
|
|
59
|
+
if (!folder) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
await addFiles(fullPath, folder);
|
|
63
|
+
} else {
|
|
64
|
+
const data = await import_promises.default.readFile(fullPath);
|
|
65
|
+
zipFolder.file(file, data);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await addFiles(dirPath, zip);
|
|
70
|
+
zip.forEach((_, file) => {
|
|
71
|
+
file.date = /* @__PURE__ */ new Date(0);
|
|
72
|
+
});
|
|
73
|
+
const content = await zip.generateAsync({
|
|
74
|
+
type: "nodebuffer",
|
|
75
|
+
compression: "DEFLATE",
|
|
76
|
+
compressionOptions: {
|
|
77
|
+
level: 9
|
|
78
|
+
},
|
|
79
|
+
platform: "UNIX"
|
|
80
|
+
});
|
|
81
|
+
await import_promises.default.writeFile(filename, content);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/utils/formatDate.ts
|
|
85
|
+
var formatDate = (date) => {
|
|
86
|
+
const year = date.getFullYear();
|
|
87
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
88
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
89
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
90
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
91
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
92
|
+
return Number(`${year}${month}${day}${hours}${minutes}${seconds}`);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/utils/getDefaultTargetVersion.ts
|
|
96
|
+
var import_child_process = require("child_process");
|
|
97
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
98
|
+
var import_util = __toESM(require("util"), 1);
|
|
99
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
100
|
+
var findXCodeProjectFilename = async (cwd) => {
|
|
101
|
+
try {
|
|
102
|
+
const iosDirPath = import_path2.default.join(cwd, "ios");
|
|
103
|
+
const dirContent = await import_promises2.default.readdir(iosDirPath);
|
|
104
|
+
for (const item of dirContent) {
|
|
105
|
+
const itemPath = import_path2.default.join(iosDirPath, item);
|
|
106
|
+
const stats = await import_promises2.default.stat(itemPath);
|
|
107
|
+
if (stats.isDirectory()) {
|
|
108
|
+
const pbxprojPath = import_path2.default.join(itemPath, "project.pbxproj");
|
|
109
|
+
try {
|
|
110
|
+
await import_promises2.default.access(pbxprojPath);
|
|
111
|
+
return item;
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var getIOSVersion = async (cwd) => {
|
|
122
|
+
const filename = await findXCodeProjectFilename(cwd);
|
|
123
|
+
if (!filename) return null;
|
|
124
|
+
const projectPath = import_path2.default.join(cwd, "ios", filename);
|
|
125
|
+
try {
|
|
126
|
+
const execPromise = import_util.default.promisify(import_child_process.exec);
|
|
127
|
+
const { stdout } = await execPromise(
|
|
128
|
+
`xcodebuild -project ${projectPath} -showBuildSettings | grep MARKETING_VERSION`
|
|
129
|
+
);
|
|
130
|
+
const versionMatch = stdout.match(/MARKETING_VERSION = ([\d.]+)/);
|
|
131
|
+
return versionMatch?.[1] ? versionMatch[1] : null;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var getAndroidVersion = async (cwd) => {
|
|
137
|
+
const buildGradlePath = import_path2.default.join(cwd, "android", "app", "build.gradle");
|
|
138
|
+
try {
|
|
139
|
+
const buildGradleContent = await import_promises2.default.readFile(buildGradlePath, "utf8");
|
|
140
|
+
const versionNameMatch = buildGradleContent.match(
|
|
141
|
+
/versionName\s+"([\d.]+)"/
|
|
142
|
+
);
|
|
143
|
+
return versionNameMatch?.[1] ? versionNameMatch[1] : null;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var getDefaultTargetVersion = async (cwd, platform) => {
|
|
149
|
+
switch (platform) {
|
|
150
|
+
case "ios":
|
|
151
|
+
return getIOSVersion(cwd);
|
|
152
|
+
case "android":
|
|
153
|
+
return getAndroidVersion(cwd);
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/utils/getFileHash.ts
|
|
159
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
160
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
161
|
+
var getFileHashFromFile = async (filepath) => {
|
|
162
|
+
try {
|
|
163
|
+
const fileBuffer = await import_promises3.default.readFile(filepath).catch((error) => {
|
|
164
|
+
console.error("Error reading the file:", error);
|
|
165
|
+
throw error;
|
|
166
|
+
});
|
|
167
|
+
const hash = import_crypto.default.createHash("sha256");
|
|
168
|
+
hash.update(fileBuffer);
|
|
169
|
+
const fileHash = hash.digest("hex");
|
|
170
|
+
return fileHash;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error("Error fetching or processing the file:", error);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/commands/deploy.ts
|
|
178
|
+
var import_plugin_core = require("@hot-updater/plugin-core");
|
|
179
|
+
var import_utils = require("@hot-updater/utils");
|
|
180
|
+
var deploy = async (options) => {
|
|
181
|
+
const s = (0, import_prompts.spinner)();
|
|
182
|
+
const config = await (0, import_plugin_core.loadConfig)();
|
|
183
|
+
if (!config) {
|
|
184
|
+
console.error("No config found. Please run `hot-updater init` first.");
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
(0, import_prompts.intro)("Please provide a description for the bundle.");
|
|
188
|
+
const description = await (0, import_prompts.text)({ message: "Description" });
|
|
189
|
+
const cwd = (0, import_plugin_core.getCwd)();
|
|
190
|
+
const targetVersion = options.targetVersion ?? await getDefaultTargetVersion(cwd, options.platform);
|
|
191
|
+
if (!targetVersion) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
"Target version not found. Please provide a target version."
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
s.start("Build in progress");
|
|
197
|
+
const { buildPath } = await config.build({
|
|
198
|
+
cwd,
|
|
199
|
+
platform: options.platform
|
|
200
|
+
});
|
|
201
|
+
s.message("Checking existing updates...");
|
|
202
|
+
await createZip(buildPath, "build.zip");
|
|
203
|
+
const bundlePath = buildPath.concat(".zip");
|
|
204
|
+
const hash = await getFileHashFromFile(bundlePath);
|
|
205
|
+
const newBundleVersion = formatDate(/* @__PURE__ */ new Date());
|
|
206
|
+
const deployPlugin = config.deploy({
|
|
207
|
+
cwd
|
|
208
|
+
});
|
|
209
|
+
const updateSources = await deployPlugin.getUpdateSources();
|
|
210
|
+
const targetVersions = (0, import_utils.filterTargetVersion)(
|
|
211
|
+
updateSources ?? [],
|
|
212
|
+
targetVersion,
|
|
213
|
+
options.platform
|
|
214
|
+
);
|
|
215
|
+
if (targetVersions.length > 0) {
|
|
216
|
+
const recentVersion = targetVersions[0];
|
|
217
|
+
const recentHash = recentVersion?.hash;
|
|
218
|
+
if (recentHash === hash) {
|
|
219
|
+
s.stop("The update already exists.", -1);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
s.message("Uploading bundle...");
|
|
224
|
+
const { file } = await deployPlugin.uploadBundle(
|
|
225
|
+
options.platform,
|
|
226
|
+
newBundleVersion,
|
|
227
|
+
bundlePath
|
|
228
|
+
);
|
|
229
|
+
await deployPlugin.appendUpdateSource({
|
|
230
|
+
forceUpdate: options.forceUpdate,
|
|
231
|
+
platform: options.platform,
|
|
232
|
+
file,
|
|
233
|
+
hash,
|
|
234
|
+
description: String(description),
|
|
235
|
+
targetVersion,
|
|
236
|
+
bundleVersion: newBundleVersion,
|
|
237
|
+
enabled: true
|
|
238
|
+
});
|
|
239
|
+
await deployPlugin.commitUpdateSource();
|
|
240
|
+
await import_promises4.default.rm(bundlePath);
|
|
241
|
+
s.stop("Uploading Success !", 0);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/commands/generateSecretKey.ts
|
|
245
|
+
var import_crypto2 = __toESM(require("crypto"), 1);
|
|
246
|
+
var import_prompts2 = require("@clack/prompts");
|
|
247
|
+
var generateSecretKey = async () => {
|
|
248
|
+
const secretKey = import_crypto2.default.randomBytes(32).toString("hex");
|
|
249
|
+
await (0, import_prompts2.text)({
|
|
250
|
+
message: "Secret Key: ",
|
|
251
|
+
initialValue: secretKey
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/commands/prune.ts
|
|
256
|
+
var p = __toESM(require("@clack/prompts"), 1);
|
|
257
|
+
var import_plugin_core2 = require("@hot-updater/plugin-core");
|
|
258
|
+
var import_plugin_core3 = require("@hot-updater/plugin-core");
|
|
259
|
+
var prune = async (options) => {
|
|
260
|
+
const s = p.spinner();
|
|
261
|
+
const config = await (0, import_plugin_core3.loadConfig)();
|
|
262
|
+
if (!config) {
|
|
263
|
+
console.error("No config found. Please run `hot-updater init` first.");
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
const cwd = (0, import_plugin_core2.getCwd)();
|
|
267
|
+
const deployPlugin = config.deploy({
|
|
268
|
+
cwd
|
|
269
|
+
// spinner: s,
|
|
270
|
+
});
|
|
271
|
+
s.start("Checking existing updates");
|
|
272
|
+
const updateSources = await deployPlugin.getUpdateSources();
|
|
273
|
+
const activeSources = updateSources.filter((source) => source.enabled);
|
|
274
|
+
const inactiveSources = updateSources.filter((source) => !source.enabled);
|
|
275
|
+
if (inactiveSources.length === 0) {
|
|
276
|
+
s.stop("No inactive versions found", -1);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
s.message("Pruning updates");
|
|
280
|
+
await deployPlugin.setUpdateSources(activeSources);
|
|
281
|
+
await deployPlugin.commitUpdateSource();
|
|
282
|
+
for (const source of inactiveSources) {
|
|
283
|
+
const key = await deployPlugin.deleteBundle(
|
|
284
|
+
options.platform,
|
|
285
|
+
source.bundleVersion
|
|
286
|
+
);
|
|
287
|
+
p.log.info(`deleting: ${key}`);
|
|
288
|
+
}
|
|
289
|
+
s.stop("Done");
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/packageJson.ts
|
|
293
|
+
var import_url = require("url");
|
|
294
|
+
var import_read_package_up = require("read-package-up");
|
|
295
|
+
var import_meta = {};
|
|
296
|
+
var __dirname = (0, import_url.fileURLToPath)(new URL(".", import_meta.url));
|
|
297
|
+
var packageJsonData = (0, import_read_package_up.readPackageUpSync)({
|
|
298
|
+
cwd: __dirname
|
|
299
|
+
});
|
|
300
|
+
var version = packageJsonData?.packageJson.version;
|
|
301
|
+
|
|
302
|
+
// src/components/banner.tsx
|
|
303
|
+
var import_boxen = __toESM(require("boxen"), 1);
|
|
304
|
+
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
305
|
+
var banner = (0, import_boxen.default)(
|
|
306
|
+
[
|
|
307
|
+
`${import_picocolors.default.bold("Hot Updater - React Native OTA Solution")} v${version}`,
|
|
308
|
+
"",
|
|
309
|
+
`Github: ${import_picocolors.default.magenta(
|
|
310
|
+
import_picocolors.default.underline("https://github.com/gronxb/hot-updater")
|
|
311
|
+
)}`,
|
|
312
|
+
"Give a \u2B50\uFE0F if you like it!"
|
|
313
|
+
].join("\n"),
|
|
314
|
+
{
|
|
315
|
+
padding: 1,
|
|
316
|
+
borderStyle: "round",
|
|
317
|
+
borderColor: "redBright",
|
|
318
|
+
textAlignment: "center"
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// src/prompts/getPlatform.ts
|
|
323
|
+
var import_prompts3 = require("@clack/prompts");
|
|
324
|
+
var getPlatform = async (message) => {
|
|
325
|
+
const platform = await (0, import_prompts3.select)({
|
|
326
|
+
message,
|
|
327
|
+
initialValue: "ios",
|
|
328
|
+
options: [
|
|
329
|
+
{ label: "ios", value: "ios" },
|
|
330
|
+
{ label: "android", value: "android" }
|
|
331
|
+
]
|
|
332
|
+
});
|
|
333
|
+
if (typeof platform !== "string") {
|
|
334
|
+
throw new Error("Invalid platform");
|
|
335
|
+
}
|
|
336
|
+
return platform;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// src/index.ts
|
|
340
|
+
var import_plugin_core4 = require("@hot-updater/plugin-core");
|
|
341
|
+
var import_commander = require("commander");
|
|
342
|
+
var program = new import_commander.Command();
|
|
343
|
+
program.name("hot-updater").description(banner).version(version);
|
|
344
|
+
program.command("init").description("Initialize Hot Updater").action(() => {
|
|
345
|
+
console.log("Initializing Hot Updater");
|
|
346
|
+
});
|
|
347
|
+
program.command("deploy").description("deploy a new version").addOption(
|
|
348
|
+
new import_commander.Option("-p, --platform <platform>", "specify the platform").choices([
|
|
349
|
+
"ios",
|
|
350
|
+
"android"
|
|
351
|
+
])
|
|
352
|
+
).addOption(
|
|
353
|
+
new import_commander.Option("-t, --target-version <targetVersion>", "specify the platform")
|
|
354
|
+
).addOption(
|
|
355
|
+
new import_commander.Option("-f, --force-update", "force update the app").default(false)
|
|
356
|
+
).action(async (options) => {
|
|
357
|
+
if (!options.platform) {
|
|
358
|
+
options.platform = await getPlatform(
|
|
359
|
+
"Which platform do you want to deploy?"
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
deploy(options);
|
|
363
|
+
});
|
|
364
|
+
program.command("console").description("open the console").action(openConsole);
|
|
365
|
+
program.command("generate-secret-key").description("generate a new secret key").action(generateSecretKey);
|
|
366
|
+
program.command("app-version").description("get the current app version").action(async () => {
|
|
367
|
+
const path3 = (0, import_plugin_core4.getCwd)();
|
|
368
|
+
const androidVersion = await getDefaultTargetVersion(path3, "android");
|
|
369
|
+
const iosVersion = await getDefaultTargetVersion(path3, "ios");
|
|
370
|
+
import_plugin_core4.log.info(`Android version: ${androidVersion}`);
|
|
371
|
+
import_plugin_core4.log.info(`iOS version: ${iosVersion}`);
|
|
372
|
+
});
|
|
373
|
+
program.command("prune").description("prune all the inactive versions").addOption(
|
|
374
|
+
new import_commander.Option("-p, --platform <platform>", "specify the platform").choices([
|
|
375
|
+
"ios",
|
|
376
|
+
"android"
|
|
377
|
+
])
|
|
378
|
+
).action(async (options) => {
|
|
379
|
+
if (!options.platform) {
|
|
380
|
+
options.platform = await getPlatform(
|
|
381
|
+
"Which platform do you want to prune?"
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
prune(options);
|
|
385
|
+
});
|
|
386
|
+
program.parse(process.argv);
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|