swiftpatch-cli 1.1.3 → 1.1.5
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/index.js +274 -267
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -57,16 +57,26 @@ __export(api_exports, {
|
|
|
57
57
|
api: () => api
|
|
58
58
|
});
|
|
59
59
|
import axios from "axios";
|
|
60
|
-
|
|
60
|
+
import { readFileSync } from "fs";
|
|
61
|
+
import { dirname, join } from "path";
|
|
62
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
63
|
+
function rejectIfInsecure(url) {
|
|
61
64
|
if (url.startsWith("http://") && !url.includes("localhost") && !url.includes("127.0.0.1")) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
if (process.env.SWIFTPATCH_ALLOW_INSECURE === "1") {
|
|
66
|
+
console.warn(
|
|
67
|
+
"\x1B[33m\u26A0 Warning: API URL uses HTTP instead of HTTPS. Credentials will be sent in plaintext.\x1B[0m"
|
|
68
|
+
);
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"API URL uses HTTP instead of HTTPS. This is insecure and credentials would be sent in plaintext.\nSet SWIFTPATCH_ALLOW_INSECURE=1 to bypass this check (not recommended for production)."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
|
-
function normalizeIds(obj) {
|
|
76
|
+
function normalizeIds(obj, depth = 0) {
|
|
77
|
+
if (depth > 20) return obj;
|
|
68
78
|
if (obj === null || obj === void 0) return obj;
|
|
69
|
-
if (Array.isArray(obj)) return obj.map(normalizeIds);
|
|
79
|
+
if (Array.isArray(obj)) return obj.map((item) => normalizeIds(item, depth + 1));
|
|
70
80
|
if (typeof obj === "object") {
|
|
71
81
|
const result = {};
|
|
72
82
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -75,14 +85,14 @@ function normalizeIds(obj) {
|
|
|
75
85
|
} else if (key === "__v") {
|
|
76
86
|
continue;
|
|
77
87
|
} else {
|
|
78
|
-
result[key] = normalizeIds(value);
|
|
88
|
+
result[key] = normalizeIds(value, depth + 1);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
91
|
return result;
|
|
82
92
|
}
|
|
83
93
|
return obj;
|
|
84
94
|
}
|
|
85
|
-
var API_BASE_URL, ApiClient, api;
|
|
95
|
+
var API_BASE_URL, __dirname2, CLI_VERSION, ApiClient, api;
|
|
86
96
|
var init_api = __esm({
|
|
87
97
|
"src/lib/api.ts"() {
|
|
88
98
|
"use strict";
|
|
@@ -90,17 +100,24 @@ var init_api = __esm({
|
|
|
90
100
|
init_auth();
|
|
91
101
|
init_config();
|
|
92
102
|
API_BASE_URL = process.env.SWIFTPATCH_API_URL || "https://api.blendapp.ae/api/v1";
|
|
103
|
+
__dirname2 = dirname(fileURLToPath2(import.meta.url));
|
|
104
|
+
CLI_VERSION = "1.0.0";
|
|
105
|
+
try {
|
|
106
|
+
const pkg = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
|
|
107
|
+
CLI_VERSION = pkg.version;
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
93
110
|
ApiClient = class {
|
|
94
111
|
client;
|
|
95
112
|
constructor() {
|
|
96
113
|
const baseURL = config.get("apiUrl") || API_BASE_URL;
|
|
97
|
-
|
|
114
|
+
rejectIfInsecure(baseURL);
|
|
98
115
|
this.client = axios.create({
|
|
99
116
|
baseURL,
|
|
100
117
|
timeout: 3e4,
|
|
101
118
|
headers: {
|
|
102
119
|
"Content-Type": "application/json",
|
|
103
|
-
"User-Agent":
|
|
120
|
+
"User-Agent": `swiftpatch-cli/${CLI_VERSION}`
|
|
104
121
|
}
|
|
105
122
|
});
|
|
106
123
|
this.client.interceptors.request.use((reqConfig) => {
|
|
@@ -476,8 +493,8 @@ init_esm_shims();
|
|
|
476
493
|
import { Command as Command47 } from "commander";
|
|
477
494
|
import chalk45 from "chalk";
|
|
478
495
|
import { readFileSync as readFileSync3 } from "fs";
|
|
479
|
-
import { fileURLToPath as
|
|
480
|
-
import { dirname as
|
|
496
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
497
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
481
498
|
|
|
482
499
|
// src/commands/login.ts
|
|
483
500
|
init_esm_shims();
|
|
@@ -520,7 +537,7 @@ var logger = {
|
|
|
520
537
|
};
|
|
521
538
|
|
|
522
539
|
// src/commands/login.ts
|
|
523
|
-
var loginCommand = new Command("login").description("Authenticate with SwiftPatch").option("-k, --api-key <key>", "Login with API key (for CI/CD)").option("-e, --email <email>", "Login with email and password").option("-
|
|
540
|
+
var loginCommand = new Command("login").description("Authenticate with SwiftPatch").option("-k, --api-key <key>", "Login with API key (for CI/CD)").option("-e, --email <email>", "Login with email and password").option("-i, --interactive", "Force interactive login").action(async (options) => {
|
|
524
541
|
const existingToken = auth.getToken();
|
|
525
542
|
if (existingToken && !options.interactive && !options.apiKey && !options.email) {
|
|
526
543
|
const user = await api.getCurrentUser().catch(() => null);
|
|
@@ -556,7 +573,7 @@ var loginCommand = new Command("login").description("Authenticate with SwiftPatc
|
|
|
556
573
|
return;
|
|
557
574
|
}
|
|
558
575
|
if (options.email) {
|
|
559
|
-
await emailLogin(options.email
|
|
576
|
+
await emailLogin(options.email);
|
|
560
577
|
return;
|
|
561
578
|
}
|
|
562
579
|
const { method } = await inquirer.prompt([
|
|
@@ -579,19 +596,17 @@ var loginCommand = new Command("login").description("Authenticate with SwiftPatc
|
|
|
579
596
|
await apiKeyLogin();
|
|
580
597
|
}
|
|
581
598
|
});
|
|
582
|
-
async function emailLogin(email
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
password = answers.password;
|
|
594
|
-
}
|
|
599
|
+
async function emailLogin(email) {
|
|
600
|
+
const answers = await inquirer.prompt([
|
|
601
|
+
{
|
|
602
|
+
type: "password",
|
|
603
|
+
name: "password",
|
|
604
|
+
message: "Password:",
|
|
605
|
+
mask: "*",
|
|
606
|
+
validate: (input) => input ? true : "Password is required"
|
|
607
|
+
}
|
|
608
|
+
]);
|
|
609
|
+
const password = answers.password;
|
|
595
610
|
const spinner = ora("Logging in...").start();
|
|
596
611
|
try {
|
|
597
612
|
const result = await api.loginWithCredentials(email, password);
|
|
@@ -615,26 +630,19 @@ async function emailLogin(email, password) {
|
|
|
615
630
|
}
|
|
616
631
|
}
|
|
617
632
|
async function emailLoginInteractive() {
|
|
618
|
-
const { email
|
|
633
|
+
const { email } = await inquirer.prompt([
|
|
619
634
|
{
|
|
620
635
|
type: "input",
|
|
621
636
|
name: "email",
|
|
622
637
|
message: "Email:",
|
|
623
638
|
validate: (input) => {
|
|
624
639
|
if (!input) return "Email is required";
|
|
625
|
-
if (
|
|
640
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)) return "Invalid email format";
|
|
626
641
|
return true;
|
|
627
642
|
}
|
|
628
|
-
},
|
|
629
|
-
{
|
|
630
|
-
type: "password",
|
|
631
|
-
name: "password",
|
|
632
|
-
message: "Password:",
|
|
633
|
-
mask: "*",
|
|
634
|
-
validate: (input) => input ? true : "Password is required"
|
|
635
643
|
}
|
|
636
644
|
]);
|
|
637
|
-
await emailLogin(email
|
|
645
|
+
await emailLogin(email);
|
|
638
646
|
}
|
|
639
647
|
async function browserLogin() {
|
|
640
648
|
const spinner = ora("Opening browser...").start();
|
|
@@ -1150,7 +1158,6 @@ import { Command as Command10 } from "commander";
|
|
|
1150
1158
|
import chalk12 from "chalk";
|
|
1151
1159
|
import inquirer6 from "inquirer";
|
|
1152
1160
|
import ora7 from "ora";
|
|
1153
|
-
import path4 from "path";
|
|
1154
1161
|
import fs6 from "fs-extra";
|
|
1155
1162
|
|
|
1156
1163
|
// src/lib/bundler.ts
|
|
@@ -1168,8 +1175,7 @@ var bundler = {
|
|
|
1168
1175
|
minify = true,
|
|
1169
1176
|
sourcemap = false
|
|
1170
1177
|
} = options;
|
|
1171
|
-
const tempDir = path2.join(os2.tmpdir(),
|
|
1172
|
-
await fs.ensureDir(tempDir);
|
|
1178
|
+
const tempDir = await fs.mkdtemp(path2.join(os2.tmpdir(), "swiftpatch-bundle-"));
|
|
1173
1179
|
const bundlePath = path2.join(tempDir, `index.${platform}.bundle`);
|
|
1174
1180
|
const sourcemapPath = sourcemap ? path2.join(tempDir, `index.${platform}.bundle.map`) : void 0;
|
|
1175
1181
|
const args = [
|
|
@@ -1206,8 +1212,7 @@ var bundler = {
|
|
|
1206
1212
|
function runCommand(command, args) {
|
|
1207
1213
|
return new Promise((resolve, reject) => {
|
|
1208
1214
|
const child = spawn(command, args, {
|
|
1209
|
-
stdio: ["inherit", "pipe", "pipe"]
|
|
1210
|
-
shell: true
|
|
1215
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
1211
1216
|
});
|
|
1212
1217
|
let stderr = "";
|
|
1213
1218
|
child.stdout?.on("data", () => {
|
|
@@ -1232,9 +1237,13 @@ function runCommand(command, args) {
|
|
|
1232
1237
|
init_esm_shims();
|
|
1233
1238
|
import axios2 from "axios";
|
|
1234
1239
|
import fs2 from "fs-extra";
|
|
1240
|
+
var MAX_UPLOAD_SIZE = 500 * 1024 * 1024;
|
|
1235
1241
|
var uploader = {
|
|
1236
1242
|
async uploadBundle(uploadUrl, bundlePath, onProgress) {
|
|
1237
1243
|
const fileSize = (await fs2.stat(bundlePath)).size;
|
|
1244
|
+
if (fileSize > MAX_UPLOAD_SIZE) {
|
|
1245
|
+
throw new Error(`Bundle size (${(fileSize / 1024 / 1024).toFixed(1)}MB) exceeds maximum upload size of ${MAX_UPLOAD_SIZE / 1024 / 1024}MB`);
|
|
1246
|
+
}
|
|
1238
1247
|
const fileStream = fs2.createReadStream(bundlePath);
|
|
1239
1248
|
let uploadedBytes = 0;
|
|
1240
1249
|
fileStream.on("data", (chunk) => {
|
|
@@ -1256,6 +1265,9 @@ var uploader = {
|
|
|
1256
1265
|
*/
|
|
1257
1266
|
async uploadZip(uploadUrl, zipPath, onProgress) {
|
|
1258
1267
|
const fileSize = (await fs2.stat(zipPath)).size;
|
|
1268
|
+
if (fileSize > MAX_UPLOAD_SIZE) {
|
|
1269
|
+
throw new Error(`ZIP size (${(fileSize / 1024 / 1024).toFixed(1)}MB) exceeds maximum upload size of ${MAX_UPLOAD_SIZE / 1024 / 1024}MB`);
|
|
1270
|
+
}
|
|
1259
1271
|
const fileStream = fs2.createReadStream(zipPath);
|
|
1260
1272
|
let uploadedBytes = 0;
|
|
1261
1273
|
fileStream.on("data", (chunk) => {
|
|
@@ -1296,7 +1308,6 @@ function sleep2(ms) {
|
|
|
1296
1308
|
init_esm_shims();
|
|
1297
1309
|
import crypto2 from "crypto";
|
|
1298
1310
|
import fs3 from "fs-extra";
|
|
1299
|
-
import { readFileSync } from "fs";
|
|
1300
1311
|
async function hashBundle(bundlePath) {
|
|
1301
1312
|
return new Promise((resolve, reject) => {
|
|
1302
1313
|
const hash = crypto2.createHash("sha256");
|
|
@@ -1306,14 +1317,6 @@ async function hashBundle(bundlePath) {
|
|
|
1306
1317
|
stream.on("error", reject);
|
|
1307
1318
|
});
|
|
1308
1319
|
}
|
|
1309
|
-
function calculateZipHash(filePath) {
|
|
1310
|
-
try {
|
|
1311
|
-
const fileBuffer = readFileSync(filePath);
|
|
1312
|
-
return crypto2.createHash("sha256").update(fileBuffer).digest("hex");
|
|
1313
|
-
} catch {
|
|
1314
|
-
return null;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
1320
|
async function hashFile(filePath) {
|
|
1318
1321
|
return new Promise((resolve, reject) => {
|
|
1319
1322
|
const hash = crypto2.createHash("sha256");
|
|
@@ -1677,14 +1680,13 @@ var releaseCommand = new Command10("release").description("Bundle and publish a
|
|
|
1677
1680
|
});
|
|
1678
1681
|
async function detectAppFromConfig() {
|
|
1679
1682
|
const configPaths = [
|
|
1680
|
-
"./swiftpatch.config.js",
|
|
1681
1683
|
"./swiftpatch.config.json",
|
|
1682
1684
|
"./.swiftpatchrc"
|
|
1683
1685
|
];
|
|
1684
1686
|
for (const configPath of configPaths) {
|
|
1685
1687
|
if (await fs6.pathExists(configPath)) {
|
|
1686
1688
|
try {
|
|
1687
|
-
const config2 =
|
|
1689
|
+
const config2 = await fs6.readJson(configPath);
|
|
1688
1690
|
return config2.appId || config2.app;
|
|
1689
1691
|
} catch {
|
|
1690
1692
|
continue;
|
|
@@ -2794,7 +2796,7 @@ init_esm_shims();
|
|
|
2794
2796
|
import { Command as Command34 } from "commander";
|
|
2795
2797
|
import chalk32 from "chalk";
|
|
2796
2798
|
import ora27 from "ora";
|
|
2797
|
-
import
|
|
2799
|
+
import path4 from "path";
|
|
2798
2800
|
import fs7 from "fs-extra";
|
|
2799
2801
|
|
|
2800
2802
|
// src/lib/claude.ts
|
|
@@ -2954,7 +2956,7 @@ async function collectDiagnostics(projectRoot) {
|
|
|
2954
2956
|
gradleDeps: [],
|
|
2955
2957
|
warnings: []
|
|
2956
2958
|
};
|
|
2957
|
-
const pkgPath =
|
|
2959
|
+
const pkgPath = path4.join(projectRoot, "package.json");
|
|
2958
2960
|
if (await fs7.pathExists(pkgPath)) {
|
|
2959
2961
|
try {
|
|
2960
2962
|
diag.packageJson = await fs7.readJson(pkgPath);
|
|
@@ -2985,13 +2987,13 @@ async function collectDiagnostics(projectRoot) {
|
|
|
2985
2987
|
}
|
|
2986
2988
|
const assetDirs = ["assets", "src/assets", "app/assets", "src/images"];
|
|
2987
2989
|
for (const dir of assetDirs) {
|
|
2988
|
-
const fullDir =
|
|
2990
|
+
const fullDir = path4.join(projectRoot, dir);
|
|
2989
2991
|
if (await fs7.pathExists(fullDir)) {
|
|
2990
2992
|
try {
|
|
2991
2993
|
const files = await fs7.readdir(fullDir);
|
|
2992
2994
|
for (const file of files.slice(0, 50)) {
|
|
2993
2995
|
try {
|
|
2994
|
-
const stat = await fs7.stat(
|
|
2996
|
+
const stat = await fs7.stat(path4.join(fullDir, file));
|
|
2995
2997
|
if (stat.isFile() && stat.size > 100 * 1024) {
|
|
2996
2998
|
diag.assets.push({ name: `${dir}/${file}`, sizeKB: Math.round(stat.size / 1024) });
|
|
2997
2999
|
}
|
|
@@ -3002,7 +3004,7 @@ async function collectDiagnostics(projectRoot) {
|
|
|
3002
3004
|
}
|
|
3003
3005
|
}
|
|
3004
3006
|
}
|
|
3005
|
-
const gradlePath =
|
|
3007
|
+
const gradlePath = path4.join(projectRoot, "android/app/build.gradle");
|
|
3006
3008
|
if (await fs7.pathExists(gradlePath)) {
|
|
3007
3009
|
try {
|
|
3008
3010
|
const gradle = await fs7.readFile(gradlePath, "utf-8");
|
|
@@ -3014,7 +3016,7 @@ async function collectDiagnostics(projectRoot) {
|
|
|
3014
3016
|
} catch {
|
|
3015
3017
|
}
|
|
3016
3018
|
}
|
|
3017
|
-
const gradlePropsPath =
|
|
3019
|
+
const gradlePropsPath = path4.join(projectRoot, "android/gradle.properties");
|
|
3018
3020
|
if (await fs7.pathExists(gradlePropsPath)) {
|
|
3019
3021
|
try {
|
|
3020
3022
|
const props = await fs7.readFile(gradlePropsPath, "utf-8");
|
|
@@ -3025,9 +3027,9 @@ async function collectDiagnostics(projectRoot) {
|
|
|
3025
3027
|
}
|
|
3026
3028
|
}
|
|
3027
3029
|
const signingPaths = [
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3030
|
+
path4.join(projectRoot, "swiftpatch-private.pem"),
|
|
3031
|
+
path4.join(projectRoot, ".swiftpatch/private.pem"),
|
|
3032
|
+
path4.join(projectRoot, "keys/swiftpatch-private.pem")
|
|
3031
3033
|
];
|
|
3032
3034
|
for (const p of signingPaths) {
|
|
3033
3035
|
if (await fs7.pathExists(p)) {
|
|
@@ -3035,7 +3037,7 @@ async function collectDiagnostics(projectRoot) {
|
|
|
3035
3037
|
break;
|
|
3036
3038
|
}
|
|
3037
3039
|
}
|
|
3038
|
-
const podfileLock =
|
|
3040
|
+
const podfileLock = path4.join(projectRoot, "ios/Podfile.lock");
|
|
3039
3041
|
if (await fs7.pathExists(podfileLock)) {
|
|
3040
3042
|
try {
|
|
3041
3043
|
const stat = await fs7.stat(podfileLock);
|
|
@@ -3046,8 +3048,8 @@ async function collectDiagnostics(projectRoot) {
|
|
|
3046
3048
|
return diag;
|
|
3047
3049
|
}
|
|
3048
3050
|
var aiDoctorCommand = new Command34("doctor").description("AI-powered pre-publish diagnostics for your React Native project").option("-d, --dir <path>", "Project root directory", process.cwd()).option("-y, --yes", "Skip consent prompt (for CI/CD)").option("--json", "Output raw diagnostics as JSON").action(async (options) => {
|
|
3049
|
-
const projectRoot =
|
|
3050
|
-
if (!await fs7.pathExists(
|
|
3051
|
+
const projectRoot = path4.resolve(options.dir);
|
|
3052
|
+
if (!await fs7.pathExists(path4.join(projectRoot, "package.json"))) {
|
|
3051
3053
|
logger.error("No package.json found. Run this from your React Native project root.");
|
|
3052
3054
|
process.exit(1);
|
|
3053
3055
|
}
|
|
@@ -3809,83 +3811,65 @@ init_api();
|
|
|
3809
3811
|
import { Command as Command39 } from "commander";
|
|
3810
3812
|
import chalk37 from "chalk";
|
|
3811
3813
|
import ora31 from "ora";
|
|
3812
|
-
import
|
|
3813
|
-
import
|
|
3814
|
+
import path7 from "path";
|
|
3815
|
+
import fs10 from "fs-extra";
|
|
3814
3816
|
import os3 from "os";
|
|
3815
3817
|
|
|
3816
|
-
// src/lib/archive.ts
|
|
3817
|
-
init_esm_shims();
|
|
3818
|
-
import archiver from "archiver";
|
|
3819
|
-
import fs8 from "fs";
|
|
3820
|
-
import path6 from "path";
|
|
3821
|
-
function createZip(inputPath, outputPath) {
|
|
3822
|
-
return new Promise((resolve, reject) => {
|
|
3823
|
-
const zipPath = path6.join(outputPath, "build.zip");
|
|
3824
|
-
const output = fs8.createWriteStream(zipPath);
|
|
3825
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
3826
|
-
output.on("close", () => resolve());
|
|
3827
|
-
archive.on("error", (err) => reject(err));
|
|
3828
|
-
archive.pipe(output);
|
|
3829
|
-
archive.directory(inputPath, "build");
|
|
3830
|
-
archive.finalize();
|
|
3831
|
-
});
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
3818
|
// src/lib/artifacts.ts
|
|
3835
3819
|
init_esm_shims();
|
|
3836
|
-
import
|
|
3837
|
-
import
|
|
3820
|
+
import fs8 from "fs-extra";
|
|
3821
|
+
import path5 from "path";
|
|
3838
3822
|
async function keepArtifacts(contentRootPath, platform, variant) {
|
|
3839
|
-
const artifactsDir =
|
|
3840
|
-
await
|
|
3841
|
-
const bundlesDir =
|
|
3842
|
-
const sourcemapsDir =
|
|
3843
|
-
if (await
|
|
3844
|
-
await copyDirRecursive(bundlesDir,
|
|
3823
|
+
const artifactsDir = path5.join(process.cwd(), "swiftpatch-artifacts", platform, variant);
|
|
3824
|
+
await fs8.ensureDir(artifactsDir);
|
|
3825
|
+
const bundlesDir = path5.join(contentRootPath, "bundles");
|
|
3826
|
+
const sourcemapsDir = path5.join(contentRootPath, "sourcemaps");
|
|
3827
|
+
if (await fs8.pathExists(bundlesDir)) {
|
|
3828
|
+
await copyDirRecursive(bundlesDir, path5.join(artifactsDir, "bundles"));
|
|
3845
3829
|
}
|
|
3846
|
-
if (await
|
|
3847
|
-
await copyDirRecursive(sourcemapsDir,
|
|
3830
|
+
if (await fs8.pathExists(sourcemapsDir)) {
|
|
3831
|
+
await copyDirRecursive(sourcemapsDir, path5.join(artifactsDir, "sourcemaps"));
|
|
3848
3832
|
}
|
|
3849
3833
|
}
|
|
3850
3834
|
async function copyDirRecursive(srcDir, destDir) {
|
|
3851
|
-
await
|
|
3852
|
-
const entries = await
|
|
3835
|
+
await fs8.ensureDir(destDir);
|
|
3836
|
+
const entries = await fs8.readdir(srcDir);
|
|
3853
3837
|
for (const entry of entries) {
|
|
3854
|
-
const srcPath =
|
|
3855
|
-
const destPath =
|
|
3856
|
-
const stat = await
|
|
3838
|
+
const srcPath = path5.join(srcDir, entry);
|
|
3839
|
+
const destPath = path5.join(destDir, entry);
|
|
3840
|
+
const stat = await fs8.stat(srcPath);
|
|
3857
3841
|
if (stat.isDirectory()) {
|
|
3858
3842
|
await copyDirRecursive(srcPath, destPath);
|
|
3859
3843
|
} else {
|
|
3860
|
-
await
|
|
3844
|
+
await fs8.copyFile(srcPath, destPath);
|
|
3861
3845
|
}
|
|
3862
3846
|
}
|
|
3863
3847
|
}
|
|
3864
3848
|
|
|
3865
3849
|
// src/lib/react-native-utils.ts
|
|
3866
3850
|
init_esm_shims();
|
|
3867
|
-
import * as
|
|
3868
|
-
import * as
|
|
3851
|
+
import * as path6 from "path";
|
|
3852
|
+
import * as fs9 from "fs";
|
|
3869
3853
|
import * as childProcess from "child_process";
|
|
3870
3854
|
import { coerce, compare } from "semver";
|
|
3871
3855
|
function findUpwardReactNativePackageJson(startDir = process.cwd()) {
|
|
3872
3856
|
let current = startDir;
|
|
3873
|
-
while (current !==
|
|
3874
|
-
const candidate =
|
|
3875
|
-
if (
|
|
3876
|
-
current =
|
|
3857
|
+
while (current !== path6.parse(current).root) {
|
|
3858
|
+
const candidate = path6.join(current, "node_modules", "react-native", "package.json");
|
|
3859
|
+
if (fs9.existsSync(candidate)) return candidate;
|
|
3860
|
+
current = path6.dirname(current);
|
|
3877
3861
|
}
|
|
3878
3862
|
return null;
|
|
3879
3863
|
}
|
|
3880
3864
|
function getReactNativeVersion() {
|
|
3881
3865
|
const rnPackageJsonPath = findUpwardReactNativePackageJson();
|
|
3882
3866
|
if (!rnPackageJsonPath) return null;
|
|
3883
|
-
const rnPackageJson = JSON.parse(
|
|
3867
|
+
const rnPackageJson = JSON.parse(fs9.readFileSync(rnPackageJsonPath, "utf-8"));
|
|
3884
3868
|
return rnPackageJson.version;
|
|
3885
3869
|
}
|
|
3886
|
-
function directoryExistsSync(
|
|
3870
|
+
function directoryExistsSync(dirname4) {
|
|
3887
3871
|
try {
|
|
3888
|
-
return
|
|
3872
|
+
return fs9.statSync(dirname4).isDirectory();
|
|
3889
3873
|
} catch (err) {
|
|
3890
3874
|
if (err.code !== "ENOENT") throw err;
|
|
3891
3875
|
}
|
|
@@ -3893,14 +3877,14 @@ function directoryExistsSync(dirname3) {
|
|
|
3893
3877
|
}
|
|
3894
3878
|
function getReactNativePackagePath() {
|
|
3895
3879
|
const rnPackageJsonPath = findUpwardReactNativePackageJson();
|
|
3896
|
-
if (rnPackageJsonPath) return
|
|
3880
|
+
if (rnPackageJsonPath) return path6.dirname(rnPackageJsonPath);
|
|
3897
3881
|
const result = childProcess.spawnSync("node", [
|
|
3898
3882
|
"--print",
|
|
3899
3883
|
"require.resolve('react-native/package.json')"
|
|
3900
3884
|
]);
|
|
3901
|
-
const packagePath =
|
|
3885
|
+
const packagePath = path6.dirname(result.stdout.toString().trim());
|
|
3902
3886
|
if (result.status === 0 && directoryExistsSync(packagePath)) return packagePath;
|
|
3903
|
-
return
|
|
3887
|
+
return path6.join("node_modules", "react-native");
|
|
3904
3888
|
}
|
|
3905
3889
|
function resolvePackageDirFromCwd(packageName) {
|
|
3906
3890
|
const result = childProcess.spawnSync("node", [
|
|
@@ -3910,7 +3894,7 @@ function resolvePackageDirFromCwd(packageName) {
|
|
|
3910
3894
|
if (result.status !== 0) return null;
|
|
3911
3895
|
const resolved = result.stdout.toString().trim();
|
|
3912
3896
|
if (!resolved) return null;
|
|
3913
|
-
const packageDir =
|
|
3897
|
+
const packageDir = path6.dirname(resolved);
|
|
3914
3898
|
return directoryExistsSync(packageDir) ? packageDir : null;
|
|
3915
3899
|
}
|
|
3916
3900
|
function isValidPlatform(platform) {
|
|
@@ -3918,18 +3902,18 @@ function isValidPlatform(platform) {
|
|
|
3918
3902
|
}
|
|
3919
3903
|
function fileExists(filePath) {
|
|
3920
3904
|
try {
|
|
3921
|
-
return
|
|
3905
|
+
return fs9.statSync(filePath).isFile();
|
|
3922
3906
|
} catch {
|
|
3923
3907
|
return false;
|
|
3924
3908
|
}
|
|
3925
3909
|
}
|
|
3926
3910
|
function getCliPath() {
|
|
3927
|
-
return
|
|
3911
|
+
return path6.join(getReactNativePackagePath(), "cli.js");
|
|
3928
3912
|
}
|
|
3929
3913
|
async function runReactNativeBundleCommand(bundleName, entryFile, outputFolder, platform, sourcemap, devMode) {
|
|
3930
|
-
|
|
3914
|
+
fs9.mkdirSync(path6.join(outputFolder, "bundles"), { recursive: true });
|
|
3931
3915
|
if (sourcemap) {
|
|
3932
|
-
|
|
3916
|
+
fs9.mkdirSync(path6.join(outputFolder, "sourcemaps"), { recursive: true });
|
|
3933
3917
|
}
|
|
3934
3918
|
const reactNativeBundleArgs = [
|
|
3935
3919
|
getCliPath(),
|
|
@@ -3937,14 +3921,14 @@ async function runReactNativeBundleCommand(bundleName, entryFile, outputFolder,
|
|
|
3937
3921
|
"--dev",
|
|
3938
3922
|
devMode,
|
|
3939
3923
|
"--assets-dest",
|
|
3940
|
-
|
|
3924
|
+
path6.join(outputFolder, "bundles"),
|
|
3941
3925
|
"--bundle-output",
|
|
3942
|
-
|
|
3926
|
+
path6.join(outputFolder, "bundles", bundleName),
|
|
3943
3927
|
"--entry-file",
|
|
3944
3928
|
entryFile,
|
|
3945
3929
|
"--platform",
|
|
3946
3930
|
platform,
|
|
3947
|
-
...sourcemap ? ["--sourcemap-output",
|
|
3931
|
+
...sourcemap ? ["--sourcemap-output", path6.join(outputFolder, "sourcemaps", bundleName + ".map")] : []
|
|
3948
3932
|
];
|
|
3949
3933
|
logger.info('Running "react-native bundle" command');
|
|
3950
3934
|
const reactNativeBundleProcess = childProcess.spawn("node", reactNativeBundleArgs);
|
|
@@ -3970,17 +3954,27 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, hermesLogs =
|
|
|
3970
3954
|
...sourcemap ? ["--output-source-map"] : [],
|
|
3971
3955
|
"--emit-binary",
|
|
3972
3956
|
"--out",
|
|
3973
|
-
|
|
3974
|
-
|
|
3957
|
+
path6.join(outputFolder, bundleName + ".hbc"),
|
|
3958
|
+
path6.join(outputFolder, "bundles", bundleName)
|
|
3975
3959
|
];
|
|
3976
3960
|
logger.info("Converting JS bundle to Hermes bytecode");
|
|
3977
3961
|
const hermesCommand = await getHermesCommand();
|
|
3978
|
-
|
|
3979
|
-
|
|
3962
|
+
const resolvedHermesPath = hermescPath || hermesCommand;
|
|
3963
|
+
if (hermescPath) {
|
|
3964
|
+
const binaryName = path6.basename(hermescPath).replace(/\.exe$/, "");
|
|
3965
|
+
if (!["hermesc", "hermes"].includes(binaryName)) {
|
|
3966
|
+
throw new Error(
|
|
3967
|
+
`Invalid hermesc path: binary must be named "hermesc" or "hermes", got "${path6.basename(hermescPath)}"`
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
logger.info(`Hermesc path: ${resolvedHermesPath}`);
|
|
3972
|
+
const hermesProcess = childProcess.spawn(resolvedHermesPath, hermesArgs);
|
|
3980
3973
|
let logFile = null;
|
|
3974
|
+
const logFilePath = path6.join(outputFolder, "hermes-output.log");
|
|
3981
3975
|
let isWarned = false;
|
|
3982
3976
|
if (hermesLogs) {
|
|
3983
|
-
logFile =
|
|
3977
|
+
logFile = fs9.createWriteStream(logFilePath, { flags: "a" });
|
|
3984
3978
|
}
|
|
3985
3979
|
return new Promise((resolve, reject) => {
|
|
3986
3980
|
hermesProcess.stdout.on("data", (data) => {
|
|
@@ -3996,21 +3990,21 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, hermesLogs =
|
|
|
3996
3990
|
});
|
|
3997
3991
|
hermesProcess.on("close", (exitCode, signal) => {
|
|
3998
3992
|
if (hermesLogs && logFile) {
|
|
3999
|
-
logger.success(
|
|
3993
|
+
logger.success(`Done writing logs to ${logFilePath}`);
|
|
4000
3994
|
logFile.end();
|
|
4001
3995
|
}
|
|
4002
3996
|
if (exitCode !== 0) {
|
|
4003
3997
|
reject(new Error(`"hermes" command failed (exitCode=${exitCode}, signal=${signal}).`));
|
|
4004
3998
|
return;
|
|
4005
3999
|
}
|
|
4006
|
-
const source =
|
|
4007
|
-
const destination =
|
|
4008
|
-
|
|
4000
|
+
const source = path6.join(outputFolder, bundleName + ".hbc");
|
|
4001
|
+
const destination = path6.join(outputFolder, "bundles", bundleName);
|
|
4002
|
+
fs9.copyFile(source, destination, (err) => {
|
|
4009
4003
|
if (err) {
|
|
4010
4004
|
reject(new Error(`Copying Hermes output failed: ${err.message}`));
|
|
4011
4005
|
return;
|
|
4012
4006
|
}
|
|
4013
|
-
|
|
4007
|
+
fs9.unlink(source, (unlinkErr) => {
|
|
4014
4008
|
if (unlinkErr) {
|
|
4015
4009
|
reject(unlinkErr);
|
|
4016
4010
|
return;
|
|
@@ -4041,7 +4035,7 @@ function getHermesOSExe() {
|
|
|
4041
4035
|
return process.platform === "win32" ? hermesExecutableName + ".exe" : hermesExecutableName;
|
|
4042
4036
|
}
|
|
4043
4037
|
async function getHermesCommand() {
|
|
4044
|
-
const bundledHermesEngine =
|
|
4038
|
+
const bundledHermesEngine = path6.join(
|
|
4045
4039
|
getReactNativePackagePath(),
|
|
4046
4040
|
"sdks",
|
|
4047
4041
|
"hermesc",
|
|
@@ -4051,23 +4045,23 @@ async function getHermesCommand() {
|
|
|
4051
4045
|
if (fileExists(bundledHermesEngine)) return bundledHermesEngine;
|
|
4052
4046
|
const hermesEnginePackageDir = resolvePackageDirFromCwd("hermes-engine");
|
|
4053
4047
|
if (hermesEnginePackageDir) {
|
|
4054
|
-
const hermesEngine2 =
|
|
4048
|
+
const hermesEngine2 = path6.join(hermesEnginePackageDir, getHermesOSBin(), getHermesOSExe());
|
|
4055
4049
|
if (fileExists(hermesEngine2)) return hermesEngine2;
|
|
4056
4050
|
}
|
|
4057
|
-
const hermesEngine =
|
|
4051
|
+
const hermesEngine = path6.join("node_modules", "hermes-engine", getHermesOSBin(), getHermesOSExe());
|
|
4058
4052
|
if (fileExists(hermesEngine)) return hermesEngine;
|
|
4059
4053
|
const hermesCompilerPackageDir = resolvePackageDirFromCwd("hermes-compiler");
|
|
4060
4054
|
if (hermesCompilerPackageDir) {
|
|
4061
|
-
const hermesCompiler2 =
|
|
4055
|
+
const hermesCompiler2 = path6.join(hermesCompilerPackageDir, "hermesc", getHermesOSBin(), getHermesOSExe());
|
|
4062
4056
|
if (fileExists(hermesCompiler2)) return hermesCompiler2;
|
|
4063
4057
|
}
|
|
4064
|
-
const hermesCompiler =
|
|
4058
|
+
const hermesCompiler = path6.join("node_modules", "hermes-compiler", "hermesc", getHermesOSBin(), getHermesOSExe());
|
|
4065
4059
|
if (fileExists(hermesCompiler)) return hermesCompiler;
|
|
4066
4060
|
const hermesVmPackageDir = resolvePackageDirFromCwd("hermesvm");
|
|
4067
4061
|
if (hermesVmPackageDir) {
|
|
4068
|
-
return
|
|
4062
|
+
return path6.join(hermesVmPackageDir, getHermesOSBin(), "hermes");
|
|
4069
4063
|
}
|
|
4070
|
-
return
|
|
4064
|
+
return path6.join("node_modules", "hermesvm", getHermesOSBin(), "hermes");
|
|
4071
4065
|
}
|
|
4072
4066
|
|
|
4073
4067
|
// src/commands/publish-bundle.ts
|
|
@@ -4116,8 +4110,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
|
|
|
4116
4110
|
const uploadPath = appSlug || resolvedAppId || "";
|
|
4117
4111
|
const entryFile = options.entryFile || "index.js";
|
|
4118
4112
|
const bundleName = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
|
|
4119
|
-
const contentRootPath =
|
|
4120
|
-
await fs11.ensureDir(contentRootPath);
|
|
4113
|
+
const contentRootPath = await fs10.mkdtemp(path7.join(os3.tmpdir(), "swiftpatch-publish-"));
|
|
4121
4114
|
try {
|
|
4122
4115
|
const spinner = ora31("Bundling JavaScript...").start();
|
|
4123
4116
|
try {
|
|
@@ -4159,7 +4152,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
|
|
|
4159
4152
|
if (options.privateKey) {
|
|
4160
4153
|
const signSpinner = ora31("Signing bundle...").start();
|
|
4161
4154
|
try {
|
|
4162
|
-
const bundlesDir2 =
|
|
4155
|
+
const bundlesDir2 = path7.join(contentRootPath, "bundles");
|
|
4163
4156
|
await signBundleDirectory(bundlesDir2, options.privateKey);
|
|
4164
4157
|
signSpinner.succeed("Bundle signed with JWT RS256");
|
|
4165
4158
|
} catch (error) {
|
|
@@ -4167,26 +4160,19 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
|
|
|
4167
4160
|
process.exit(1);
|
|
4168
4161
|
}
|
|
4169
4162
|
}
|
|
4170
|
-
const
|
|
4171
|
-
const
|
|
4172
|
-
|
|
4173
|
-
const
|
|
4174
|
-
if (!await fs11.pathExists(zipPath)) {
|
|
4175
|
-
zipSpinner.fail("ZIP creation failed: build.zip not found");
|
|
4176
|
-
process.exit(1);
|
|
4177
|
-
}
|
|
4178
|
-
const zipSize = (await fs11.stat(zipPath)).size;
|
|
4179
|
-
zipSpinner.succeed(`ZIP archive created: ${chalk37.gray(formatBytes4(zipSize))}`);
|
|
4180
|
-
const hashSpinner = ora31("Computing ZIP hash...").start();
|
|
4181
|
-
const hash = calculateZipHash(zipPath);
|
|
4163
|
+
const bundlesDir = path7.join(contentRootPath, "bundles");
|
|
4164
|
+
const rawBundlePath = path7.join(bundlesDir, bundleName);
|
|
4165
|
+
const hashSpinner = ora31("Computing bundle hash...").start();
|
|
4166
|
+
const hash = await hashBundle(rawBundlePath);
|
|
4182
4167
|
if (!hash) {
|
|
4183
|
-
hashSpinner.fail("Failed to compute
|
|
4168
|
+
hashSpinner.fail("Failed to compute bundle hash");
|
|
4184
4169
|
process.exit(1);
|
|
4185
4170
|
}
|
|
4186
|
-
|
|
4171
|
+
const bundleSize = (await fs10.stat(rawBundlePath)).size;
|
|
4172
|
+
hashSpinner.succeed(`Bundle hash: ${chalk37.gray(hash.slice(0, 16))}... (${formatBytes4(bundleSize)})`);
|
|
4187
4173
|
let bundleSignature;
|
|
4188
4174
|
if (options.privateKey) {
|
|
4189
|
-
const bundlesDir2 =
|
|
4175
|
+
const bundlesDir2 = path7.join(contentRootPath, "bundles");
|
|
4190
4176
|
const sig = await readBundleSignature(bundlesDir2);
|
|
4191
4177
|
if (sig) bundleSignature = sig;
|
|
4192
4178
|
}
|
|
@@ -4204,7 +4190,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
|
|
|
4204
4190
|
);
|
|
4205
4191
|
urlSpinner.succeed("Upload URL received");
|
|
4206
4192
|
const uploadSpinner = ora31("Uploading bundle...").start();
|
|
4207
|
-
await uploader.
|
|
4193
|
+
await uploader.uploadBundle(url, rawBundlePath, (percent) => {
|
|
4208
4194
|
uploadSpinner.text = `Uploading bundle... ${percent}%`;
|
|
4209
4195
|
});
|
|
4210
4196
|
uploadSpinner.succeed("Bundle uploaded");
|
|
@@ -4233,7 +4219,8 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
|
|
|
4233
4219
|
process.exit(1);
|
|
4234
4220
|
}
|
|
4235
4221
|
} finally {
|
|
4236
|
-
await
|
|
4222
|
+
await fs10.remove(contentRootPath).catch(() => {
|
|
4223
|
+
logger.warning(`Failed to clean up temp directory: ${contentRootPath}`);
|
|
4237
4224
|
});
|
|
4238
4225
|
}
|
|
4239
4226
|
});
|
|
@@ -4359,12 +4346,12 @@ init_esm_shims();
|
|
|
4359
4346
|
import { Command as Command42 } from "commander";
|
|
4360
4347
|
import chalk40 from "chalk";
|
|
4361
4348
|
import ora34 from "ora";
|
|
4362
|
-
import
|
|
4349
|
+
import path8 from "path";
|
|
4363
4350
|
var generateKeyPairCommand = new Command42("generate-key-pair").description("Generate RSA 2048-bit key pair for bundle signing").option("-o, --output <dir>", "Output directory for key files", ".").action(async (options) => {
|
|
4364
4351
|
console.log("");
|
|
4365
4352
|
logger.info(chalk40.bold("SwiftPatch Generate Key Pair"));
|
|
4366
4353
|
console.log("");
|
|
4367
|
-
const outputDir =
|
|
4354
|
+
const outputDir = path8.resolve(options.output || ".");
|
|
4368
4355
|
const spinner = ora34("Generating RSA 2048-bit key pair...").start();
|
|
4369
4356
|
try {
|
|
4370
4357
|
const { publicKeyPath, privateKeyPath } = await generateKeyPair(outputDir);
|
|
@@ -4397,8 +4384,9 @@ import { Command as Command43 } from "commander";
|
|
|
4397
4384
|
import chalk41 from "chalk";
|
|
4398
4385
|
import inquirer21 from "inquirer";
|
|
4399
4386
|
import ora35 from "ora";
|
|
4400
|
-
import
|
|
4401
|
-
import
|
|
4387
|
+
import fs11 from "fs-extra";
|
|
4388
|
+
import path9 from "path";
|
|
4389
|
+
import os4 from "os";
|
|
4402
4390
|
var deployCommand = new Command43("deploy").description("Bundle, publish, and release in one step").option("-p, --platform <platform>", "Target platform (ios or android)").option("-a, --app <app-id>", "App ID or slug").option("-o, --org <org-id>", "Organization ID").option("--app-version <version>", "Target app version (semver)").option("-n, --release-note <note>", "Release notes").option("-m, --mandatory", "Mark as mandatory update").option("--paused", "Create release in paused state").option("--hermes", "Compile to Hermes bytecode").option("--ci-token <token>", "CI token for authentication").option("-e, --entry-file <path>", "Entry file (default: index.js)").option("--dev", "Dev bundle").option("--sourcemap", "Generate sourcemaps").option("-k, --private-key <path>", "Private key path for signing").option("-y, --yes", "Skip confirmations").action(async (options) => {
|
|
4403
4391
|
const ciToken = options.ciToken || process.env.SWIFTPATCH_CI_TOKEN;
|
|
4404
4392
|
if (!ciToken) {
|
|
@@ -4407,8 +4395,8 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
|
|
|
4407
4395
|
const orgId = ciToken ? void 0 : await resolveOrgId(options.org);
|
|
4408
4396
|
let platform = options.platform;
|
|
4409
4397
|
if (!platform) {
|
|
4410
|
-
const hasIos = await
|
|
4411
|
-
const hasAndroid = await
|
|
4398
|
+
const hasIos = await fs11.pathExists("./ios");
|
|
4399
|
+
const hasAndroid = await fs11.pathExists("./android");
|
|
4412
4400
|
if (hasIos && !hasAndroid) {
|
|
4413
4401
|
platform = "ios";
|
|
4414
4402
|
} else if (hasAndroid && !hasIos) {
|
|
@@ -4428,7 +4416,7 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
|
|
|
4428
4416
|
let appVersion = options.appVersion;
|
|
4429
4417
|
if (!appVersion) {
|
|
4430
4418
|
try {
|
|
4431
|
-
const pkg = await
|
|
4419
|
+
const pkg = await fs11.readJson("./package.json");
|
|
4432
4420
|
appVersion = pkg.version;
|
|
4433
4421
|
} catch {
|
|
4434
4422
|
}
|
|
@@ -4469,94 +4457,96 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
|
|
|
4469
4457
|
console.log(chalk41.bold(" Deploy"));
|
|
4470
4458
|
console.log(chalk41.gray(` Platform: ${platform} \xB7 Version: ${appVersion}`));
|
|
4471
4459
|
console.log("");
|
|
4472
|
-
const
|
|
4460
|
+
const tmpRoot = await fs11.mkdtemp(path9.join(os4.tmpdir(), "swiftpatch-deploy-"));
|
|
4473
4461
|
try {
|
|
4474
|
-
const
|
|
4475
|
-
|
|
4476
|
-
|
|
4462
|
+
const bundleSpinner = ora35("Bundling JavaScript...").start();
|
|
4463
|
+
try {
|
|
4464
|
+
const entryFile = options.entryFile || "index.js";
|
|
4465
|
+
const outputDir2 = path9.join(tmpRoot, platform);
|
|
4466
|
+
await fs11.ensureDir(outputDir2);
|
|
4467
|
+
const bundleName2 = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
|
|
4468
|
+
await runReactNativeBundleCommand(
|
|
4469
|
+
bundleName2,
|
|
4470
|
+
entryFile,
|
|
4471
|
+
outputDir2,
|
|
4472
|
+
platform,
|
|
4473
|
+
options.sourcemap || false,
|
|
4474
|
+
options.dev || false
|
|
4475
|
+
);
|
|
4476
|
+
if (options.hermes) {
|
|
4477
|
+
bundleSpinner.text = "Compiling to Hermes bytecode...";
|
|
4478
|
+
}
|
|
4479
|
+
bundleSpinner.succeed("Bundle created");
|
|
4480
|
+
} catch (error) {
|
|
4481
|
+
bundleSpinner.fail(`Bundle failed: ${error.message}`);
|
|
4482
|
+
process.exit(1);
|
|
4483
|
+
}
|
|
4484
|
+
const hashSpinner = ora35("Computing bundle hash...").start();
|
|
4477
4485
|
const bundleName = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
)
|
|
4486
|
-
|
|
4487
|
-
|
|
4486
|
+
const outputDir = path9.join(tmpRoot, platform);
|
|
4487
|
+
const rawBundlePath = path9.join(outputDir, bundleName);
|
|
4488
|
+
let bundleHash;
|
|
4489
|
+
try {
|
|
4490
|
+
bundleHash = await hashBundle(rawBundlePath);
|
|
4491
|
+
if (!bundleHash) throw new Error("Failed to compute bundle hash");
|
|
4492
|
+
hashSpinner.succeed(`Bundle hashed (${bundleHash.slice(0, 12)}...)`);
|
|
4493
|
+
} catch (error) {
|
|
4494
|
+
hashSpinner.fail(`Hash failed: ${error.message}`);
|
|
4495
|
+
process.exit(1);
|
|
4488
4496
|
}
|
|
4489
|
-
|
|
4490
|
-
} catch (error) {
|
|
4491
|
-
bundleSpinner.fail(`Bundle failed: ${error.message}`);
|
|
4492
|
-
process.exit(1);
|
|
4493
|
-
}
|
|
4494
|
-
const archiveSpinner = ora35("Creating archive...").start();
|
|
4495
|
-
let zipPath;
|
|
4496
|
-
let bundleHash;
|
|
4497
|
-
try {
|
|
4498
|
-
const outputDir = path11.join(".swiftpatch-tmp", platform);
|
|
4499
|
-
const zipOutputDir = path11.join(".swiftpatch-tmp", "zip");
|
|
4500
|
-
await fs12.ensureDir(zipOutputDir);
|
|
4501
|
-
await createZip(outputDir, zipOutputDir);
|
|
4502
|
-
zipPath = path11.join(zipOutputDir, "build.zip");
|
|
4503
|
-
bundleHash = calculateZipHash(zipPath);
|
|
4504
|
-
if (!bundleHash) throw new Error("Failed to compute bundle hash");
|
|
4505
|
-
archiveSpinner.succeed(`Archive created (hash: ${bundleHash.slice(0, 12)}...)`);
|
|
4506
|
-
} catch (error) {
|
|
4507
|
-
archiveSpinner.fail(`Archive failed: ${error.message}`);
|
|
4508
|
-
process.exit(1);
|
|
4509
|
-
}
|
|
4510
|
-
const uploadSpinner = ora35("Uploading bundle...").start();
|
|
4511
|
-
try {
|
|
4512
|
-
const { url } = await api.generateSignedUrl(
|
|
4513
|
-
{ hash: bundleHash, uploadPath, platform, releaseNote },
|
|
4514
|
-
ciToken
|
|
4515
|
-
);
|
|
4516
|
-
await uploader.uploadZip(url, zipPath, (progress) => {
|
|
4517
|
-
uploadSpinner.text = `Uploading bundle... ${progress}%`;
|
|
4518
|
-
});
|
|
4519
|
-
uploadSpinner.succeed("Bundle uploaded");
|
|
4520
|
-
} catch (error) {
|
|
4521
|
-
uploadSpinner.fail(`Upload failed: ${error.message}`);
|
|
4522
|
-
process.exit(1);
|
|
4523
|
-
}
|
|
4524
|
-
if (!ciToken) {
|
|
4525
|
-
console.log("");
|
|
4526
|
-
logger.warning("Deploy requires a CI token to create the release.");
|
|
4527
|
-
console.log(chalk41.gray(" Bundle was uploaded. Promote it manually:"));
|
|
4528
|
-
console.log(chalk41.cyan(` $ swiftpatch release-bundle --hash ${bundleHash} --app-version ${appVersion} --ci-token <token>`));
|
|
4529
|
-
console.log("");
|
|
4530
|
-
} else {
|
|
4531
|
-
const releaseSpinner = ora35("Creating release...").start();
|
|
4497
|
+
const uploadSpinner = ora35("Uploading bundle...").start();
|
|
4532
4498
|
try {
|
|
4533
|
-
await api.
|
|
4534
|
-
{
|
|
4535
|
-
projectId: uploadPath,
|
|
4536
|
-
hash: bundleHash,
|
|
4537
|
-
appVersion,
|
|
4538
|
-
releaseNote,
|
|
4539
|
-
isMandatory: options.mandatory || false,
|
|
4540
|
-
isPaused: options.paused || false
|
|
4541
|
-
},
|
|
4499
|
+
const { url } = await api.generateSignedUrl(
|
|
4500
|
+
{ hash: bundleHash, uploadPath, platform, releaseNote },
|
|
4542
4501
|
ciToken
|
|
4543
4502
|
);
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
console.log(` Version: ${chalk41.cyan(appVersion)}`);
|
|
4549
|
-
console.log(` Platform: ${platform}`);
|
|
4550
|
-
console.log(` Mandatory: ${options.mandatory ? chalk41.yellow("Yes") : "No"}`);
|
|
4551
|
-
console.log(` Status: ${options.paused ? chalk41.yellow("Paused") : chalk41.green("Released")}`);
|
|
4552
|
-
console.log("");
|
|
4503
|
+
await uploader.uploadBundle(url, rawBundlePath, (progress) => {
|
|
4504
|
+
uploadSpinner.text = `Uploading bundle... ${progress}%`;
|
|
4505
|
+
});
|
|
4506
|
+
uploadSpinner.succeed("Bundle uploaded");
|
|
4553
4507
|
} catch (error) {
|
|
4554
|
-
|
|
4508
|
+
uploadSpinner.fail(`Upload failed: ${error.message}`);
|
|
4555
4509
|
process.exit(1);
|
|
4556
4510
|
}
|
|
4511
|
+
if (!ciToken) {
|
|
4512
|
+
console.log("");
|
|
4513
|
+
logger.warning("Deploy requires a CI token to create the release.");
|
|
4514
|
+
console.log(chalk41.gray(" Bundle was uploaded. Promote it manually:"));
|
|
4515
|
+
console.log(chalk41.cyan(` $ swiftpatch release-bundle --hash ${bundleHash} --app-version ${appVersion} --ci-token <token>`));
|
|
4516
|
+
console.log("");
|
|
4517
|
+
} else {
|
|
4518
|
+
const releaseSpinner = ora35("Creating release...").start();
|
|
4519
|
+
try {
|
|
4520
|
+
await api.promoteBundle(
|
|
4521
|
+
{
|
|
4522
|
+
projectId: uploadPath,
|
|
4523
|
+
hash: bundleHash,
|
|
4524
|
+
appVersion,
|
|
4525
|
+
releaseNote,
|
|
4526
|
+
isMandatory: options.mandatory || false,
|
|
4527
|
+
isPaused: options.paused || false
|
|
4528
|
+
},
|
|
4529
|
+
ciToken
|
|
4530
|
+
);
|
|
4531
|
+
releaseSpinner.succeed("Release created!");
|
|
4532
|
+
console.log("");
|
|
4533
|
+
console.log(chalk41.bold(" Deploy Complete"));
|
|
4534
|
+
console.log(` Hash: ${chalk41.gray(bundleHash)}`);
|
|
4535
|
+
console.log(` Version: ${chalk41.cyan(appVersion)}`);
|
|
4536
|
+
console.log(` Platform: ${platform}`);
|
|
4537
|
+
console.log(` Mandatory: ${options.mandatory ? chalk41.yellow("Yes") : "No"}`);
|
|
4538
|
+
console.log(` Status: ${options.paused ? chalk41.yellow("Paused") : chalk41.green("Released")}`);
|
|
4539
|
+
console.log("");
|
|
4540
|
+
} catch (error) {
|
|
4541
|
+
releaseSpinner.fail(`Release failed: ${error.message}`);
|
|
4542
|
+
process.exit(1);
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
} finally {
|
|
4546
|
+
await fs11.remove(tmpRoot).catch((err) => {
|
|
4547
|
+
logger.warning(`Failed to clean up temp directory: ${tmpRoot}`);
|
|
4548
|
+
});
|
|
4557
4549
|
}
|
|
4558
|
-
await fs12.remove(".swiftpatch-tmp").catch(() => {
|
|
4559
|
-
});
|
|
4560
4550
|
});
|
|
4561
4551
|
|
|
4562
4552
|
// src/commands/status.ts
|
|
@@ -4653,18 +4643,18 @@ import { Command as Command45 } from "commander";
|
|
|
4653
4643
|
import chalk43 from "chalk";
|
|
4654
4644
|
import inquirer23 from "inquirer";
|
|
4655
4645
|
import ora37 from "ora";
|
|
4656
|
-
import
|
|
4657
|
-
import
|
|
4646
|
+
import fs12 from "fs-extra";
|
|
4647
|
+
import path10 from "path";
|
|
4658
4648
|
var initCommand = new Command45("init").description("Initialize SwiftPatch in your React Native project").option("-y, --yes", "Accept defaults").action(async (options) => {
|
|
4659
4649
|
console.log("");
|
|
4660
4650
|
console.log(chalk43.bold(" SwiftPatch Setup"));
|
|
4661
4651
|
console.log("");
|
|
4662
|
-
const hasPackageJson = await
|
|
4652
|
+
const hasPackageJson = await fs12.pathExists("./package.json");
|
|
4663
4653
|
if (!hasPackageJson) {
|
|
4664
4654
|
logger.error("No package.json found. Run this command in your React Native project root.");
|
|
4665
4655
|
process.exit(1);
|
|
4666
4656
|
}
|
|
4667
|
-
const pkg = await
|
|
4657
|
+
const pkg = await fs12.readJson("./package.json");
|
|
4668
4658
|
const hasRN = pkg.dependencies?.["react-native"] || pkg.devDependencies?.["react-native"];
|
|
4669
4659
|
if (!hasRN) {
|
|
4670
4660
|
logger.warning("react-native not found in dependencies. Is this a React Native project?");
|
|
@@ -4693,8 +4683,8 @@ var initCommand = new Command45("init").description("Initialize SwiftPatch in yo
|
|
|
4693
4683
|
}
|
|
4694
4684
|
logger.success("Authenticated");
|
|
4695
4685
|
const orgId = await resolveOrgId();
|
|
4696
|
-
const hasIos = await
|
|
4697
|
-
const hasAndroid = await
|
|
4686
|
+
const hasIos = await fs12.pathExists("./ios");
|
|
4687
|
+
const hasAndroid = await fs12.pathExists("./android");
|
|
4698
4688
|
const platforms = [];
|
|
4699
4689
|
if (hasIos) platforms.push("ios");
|
|
4700
4690
|
if (hasAndroid) platforms.push("android");
|
|
@@ -4745,13 +4735,30 @@ var initCommand = new Command45("init").description("Initialize SwiftPatch in yo
|
|
|
4745
4735
|
platform: defaultPlatform || (selectedApp.platform === "BOTH" ? void 0 : selectedApp.platform.toLowerCase()),
|
|
4746
4736
|
deploymentKey: selectedApp.deploymentKey
|
|
4747
4737
|
};
|
|
4748
|
-
const rcPath =
|
|
4749
|
-
await
|
|
4738
|
+
const rcPath = path10.resolve(".swiftpatchrc");
|
|
4739
|
+
await fs12.writeJson(rcPath, rcConfig, { spaces: 2 });
|
|
4750
4740
|
logger.success("Created .swiftpatchrc");
|
|
4751
4741
|
config.set("defaultOrg", orgId);
|
|
4752
4742
|
config.set("defaultApp", selectedApp.id);
|
|
4753
4743
|
if (defaultPlatform) config.set("defaultPlatform", defaultPlatform);
|
|
4754
4744
|
logger.success("Saved defaults to CLI config");
|
|
4745
|
+
const gitignorePath = path10.resolve(".gitignore");
|
|
4746
|
+
const swiftpatchIgnores = [
|
|
4747
|
+
"# SwiftPatch",
|
|
4748
|
+
".swiftpatch-tmp/",
|
|
4749
|
+
"swiftpatch-artifacts/",
|
|
4750
|
+
"swiftpatch-private.pem"
|
|
4751
|
+
];
|
|
4752
|
+
try {
|
|
4753
|
+
let gitignore = await fs12.pathExists(gitignorePath) ? await fs12.readFile(gitignorePath, "utf-8") : "";
|
|
4754
|
+
const missing = swiftpatchIgnores.filter((line) => !gitignore.includes(line));
|
|
4755
|
+
if (missing.length > 0) {
|
|
4756
|
+
const addition = (gitignore.endsWith("\n") || gitignore === "" ? "" : "\n") + missing.join("\n") + "\n";
|
|
4757
|
+
await fs12.appendFile(gitignorePath, addition);
|
|
4758
|
+
logger.success("Updated .gitignore with SwiftPatch entries");
|
|
4759
|
+
}
|
|
4760
|
+
} catch {
|
|
4761
|
+
}
|
|
4755
4762
|
console.log("");
|
|
4756
4763
|
console.log(chalk43.bold(" Setup Complete"));
|
|
4757
4764
|
console.log("");
|
|
@@ -4772,7 +4779,7 @@ async function createApp(orgId, pkg, platforms) {
|
|
|
4772
4779
|
type: "input",
|
|
4773
4780
|
name: "name",
|
|
4774
4781
|
message: "App name:",
|
|
4775
|
-
default: pkg.name ||
|
|
4782
|
+
default: pkg.name || path10.basename(process.cwd())
|
|
4776
4783
|
}
|
|
4777
4784
|
]);
|
|
4778
4785
|
let platform;
|
|
@@ -4808,7 +4815,7 @@ init_auth();
|
|
|
4808
4815
|
init_config();
|
|
4809
4816
|
import { Command as Command46 } from "commander";
|
|
4810
4817
|
import chalk44 from "chalk";
|
|
4811
|
-
import
|
|
4818
|
+
import fs13 from "fs-extra";
|
|
4812
4819
|
var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatch setup").action(async () => {
|
|
4813
4820
|
console.log("");
|
|
4814
4821
|
console.log(chalk44.bold(" SwiftPatch Doctor"));
|
|
@@ -4822,9 +4829,9 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
|
|
|
4822
4829
|
logger.error(`Node.js ${nodeVersion} \u2014 requires >= 18.0.0`);
|
|
4823
4830
|
issues++;
|
|
4824
4831
|
}
|
|
4825
|
-
const hasPackageJson = await
|
|
4832
|
+
const hasPackageJson = await fs13.pathExists("./package.json");
|
|
4826
4833
|
if (hasPackageJson) {
|
|
4827
|
-
const pkg = await
|
|
4834
|
+
const pkg = await fs13.readJson("./package.json");
|
|
4828
4835
|
const rnVersion = pkg.dependencies?.["react-native"] || pkg.devDependencies?.["react-native"];
|
|
4829
4836
|
if (rnVersion) {
|
|
4830
4837
|
logger.success(`React Native ${rnVersion}`);
|
|
@@ -4843,8 +4850,8 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
|
|
|
4843
4850
|
} else {
|
|
4844
4851
|
logger.warning("No package.json found (not in a project directory?)");
|
|
4845
4852
|
}
|
|
4846
|
-
const hasIos = await
|
|
4847
|
-
const hasAndroid = await
|
|
4853
|
+
const hasIos = await fs13.pathExists("./ios");
|
|
4854
|
+
const hasAndroid = await fs13.pathExists("./android");
|
|
4848
4855
|
if (hasIos || hasAndroid) {
|
|
4849
4856
|
const platforms = [hasIos && "iOS", hasAndroid && "Android"].filter(Boolean).join(", ");
|
|
4850
4857
|
logger.success(`Platforms: ${platforms}`);
|
|
@@ -4888,10 +4895,10 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
|
|
|
4888
4895
|
} else {
|
|
4889
4896
|
logger.info("No default app set");
|
|
4890
4897
|
}
|
|
4891
|
-
const hasRc = await
|
|
4898
|
+
const hasRc = await fs13.pathExists("./.swiftpatchrc");
|
|
4892
4899
|
if (hasRc) {
|
|
4893
4900
|
try {
|
|
4894
|
-
const rc = await
|
|
4901
|
+
const rc = await fs13.readJson("./.swiftpatchrc");
|
|
4895
4902
|
logger.success(`.swiftpatchrc found (app: ${rc.app || rc.appId || "unknown"})`);
|
|
4896
4903
|
} catch {
|
|
4897
4904
|
logger.warning(".swiftpatchrc exists but is invalid JSON");
|
|
@@ -4902,7 +4909,7 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
|
|
|
4902
4909
|
}
|
|
4903
4910
|
if (hasPackageJson) {
|
|
4904
4911
|
try {
|
|
4905
|
-
const pkg = await
|
|
4912
|
+
const pkg = await fs13.readJson("./package.json");
|
|
4906
4913
|
const rnVersion = pkg.dependencies?.["react-native"];
|
|
4907
4914
|
if (rnVersion) {
|
|
4908
4915
|
const cleanVersion = rnVersion.replace(/[\^~>=<]/g, "");
|
|
@@ -4926,10 +4933,10 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
|
|
|
4926
4933
|
});
|
|
4927
4934
|
|
|
4928
4935
|
// src/cli.ts
|
|
4929
|
-
var
|
|
4936
|
+
var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
4930
4937
|
var version = "1.0.0";
|
|
4931
4938
|
try {
|
|
4932
|
-
const pkg = JSON.parse(readFileSync3(
|
|
4939
|
+
const pkg = JSON.parse(readFileSync3(join3(__dirname3, "../package.json"), "utf-8"));
|
|
4933
4940
|
version = pkg.version;
|
|
4934
4941
|
} catch {
|
|
4935
4942
|
}
|
|
@@ -5002,13 +5009,13 @@ init_esm_shims();
|
|
|
5002
5009
|
function checkForUpdates() {
|
|
5003
5010
|
try {
|
|
5004
5011
|
import("update-notifier").then(({ default: updateNotifier }) => {
|
|
5005
|
-
import("fs-extra").then(({ default:
|
|
5006
|
-
import("url").then(({ fileURLToPath:
|
|
5007
|
-
import("path").then(({ dirname:
|
|
5012
|
+
import("fs-extra").then(({ default: fs14 }) => {
|
|
5013
|
+
import("url").then(({ fileURLToPath: fileURLToPath4 }) => {
|
|
5014
|
+
import("path").then(({ dirname: dirname4, join: join4 }) => {
|
|
5008
5015
|
try {
|
|
5009
|
-
const
|
|
5016
|
+
const __dirname4 = dirname4(fileURLToPath4(import.meta.url));
|
|
5010
5017
|
const pkg = JSON.parse(
|
|
5011
|
-
|
|
5018
|
+
fs14.readFileSync(join4(__dirname4, "../../package.json"), "utf-8")
|
|
5012
5019
|
);
|
|
5013
5020
|
const notifier = updateNotifier({ pkg, updateCheckInterval: 1e3 * 60 * 60 * 24 });
|
|
5014
5021
|
notifier.notify();
|