@better-update/cli 0.16.0 → 0.17.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/index.mjs +148 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -14,12 +14,13 @@ import { createServer } from "node:http";
|
|
|
14
14
|
import { maxBy, uniqBy } from "es-toolkit";
|
|
15
15
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
16
16
|
import forge from "node-forge";
|
|
17
|
-
import { createReadStream, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { createReadStream, existsSync, promises, readFileSync, writeFileSync } from "node:fs";
|
|
18
18
|
import { spawn as spawn$1 } from "node-pty";
|
|
19
19
|
import chalk from "chalk";
|
|
20
20
|
import os from "node:os";
|
|
21
21
|
import plistMod from "@expo/plist";
|
|
22
22
|
import { ExpoRunFormatter } from "@expo/xcpretty";
|
|
23
|
+
import ignore from "ignore";
|
|
23
24
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
24
25
|
import { getFormattedSerialNumber, getX509Certificate, parsePKCS12 } from "@expo/pkcs12";
|
|
25
26
|
import qrcode from "qrcode-terminal";
|
|
@@ -30,7 +31,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
30
31
|
|
|
31
32
|
//#endregion
|
|
32
33
|
//#region package.json
|
|
33
|
-
var version = "0.
|
|
34
|
+
var version = "0.17.0";
|
|
34
35
|
|
|
35
36
|
//#endregion
|
|
36
37
|
//#region src/lib/interactive-mode.ts
|
|
@@ -1665,6 +1666,7 @@ var InvalidArgumentError = class extends Data.TaggedError("InvalidArgumentError"
|
|
|
1665
1666
|
var InteractiveProhibitedError = class extends Data.TaggedError("InteractiveProhibitedError") {};
|
|
1666
1667
|
var CredentialsJsonError = class extends Data.TaggedError("CredentialsJsonError") {};
|
|
1667
1668
|
var DirtyRepoError = class extends Data.TaggedError("DirtyRepoError") {};
|
|
1669
|
+
var StagingError = class extends Data.TaggedError("StagingError") {};
|
|
1668
1670
|
|
|
1669
1671
|
//#endregion
|
|
1670
1672
|
//#region src/lib/format-error.ts
|
|
@@ -6520,6 +6522,131 @@ const detectPlatform = (explicit, config) => Effect.gen(function* () {
|
|
|
6520
6522
|
})));
|
|
6521
6523
|
});
|
|
6522
6524
|
|
|
6525
|
+
//#endregion
|
|
6526
|
+
//#region src/lib/project-staging.ts
|
|
6527
|
+
const LOCKFILES = [
|
|
6528
|
+
["bun.lock", "bun"],
|
|
6529
|
+
["bun.lockb", "bun"],
|
|
6530
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
6531
|
+
["yarn.lock", "yarn"],
|
|
6532
|
+
["package-lock.json", "npm"]
|
|
6533
|
+
];
|
|
6534
|
+
/**
|
|
6535
|
+
* Paths never copied into staging — covers generated native build outputs and
|
|
6536
|
+
* dependency dirs that must be reinstalled fresh in staging.
|
|
6537
|
+
*/
|
|
6538
|
+
const ALWAYS_IGNORE = [
|
|
6539
|
+
"node_modules",
|
|
6540
|
+
".git",
|
|
6541
|
+
"ios/build",
|
|
6542
|
+
"ios/Pods",
|
|
6543
|
+
"ios/DerivedData",
|
|
6544
|
+
"android/build",
|
|
6545
|
+
"android/app/build",
|
|
6546
|
+
"android/.gradle",
|
|
6547
|
+
"android/.kotlin",
|
|
6548
|
+
".expo",
|
|
6549
|
+
".gradle",
|
|
6550
|
+
".turbo",
|
|
6551
|
+
"dist"
|
|
6552
|
+
];
|
|
6553
|
+
const findLockfile = (fs, dir) => Effect.gen(function* () {
|
|
6554
|
+
for (const [name, pm] of LOCKFILES) if (yield* fs.exists(path.join(dir, name)).pipe(Effect.catchAll(() => Effect.succeed(false)))) return pm;
|
|
6555
|
+
});
|
|
6556
|
+
const walkUpForLockfile = (startCwd, dir) => Effect.gen(function* () {
|
|
6557
|
+
const pm = yield* findLockfile(yield* FileSystem.FileSystem, dir);
|
|
6558
|
+
if (pm !== void 0) return {
|
|
6559
|
+
workspaceRoot: dir,
|
|
6560
|
+
packageManager: pm
|
|
6561
|
+
};
|
|
6562
|
+
const parent = path.dirname(dir);
|
|
6563
|
+
if (parent === dir) return {
|
|
6564
|
+
workspaceRoot: startCwd,
|
|
6565
|
+
packageManager: "bun"
|
|
6566
|
+
};
|
|
6567
|
+
return yield* walkUpForLockfile(startCwd, parent);
|
|
6568
|
+
});
|
|
6569
|
+
/**
|
|
6570
|
+
* Walk up from `cwd` to the first ancestor directory containing a lockfile.
|
|
6571
|
+
* That directory is the install root (monorepo workspace root or the app dir
|
|
6572
|
+
* itself in single-app layouts). Defaults to `cwd` + bun when no lockfile is
|
|
6573
|
+
* found anywhere up to the volume root.
|
|
6574
|
+
*/
|
|
6575
|
+
const detectWorkspaceRoot = (cwd) => walkUpForLockfile(cwd, cwd);
|
|
6576
|
+
/**
|
|
6577
|
+
* Build an `Ignore` matcher for the workspace root. `.easignore` REPLACES
|
|
6578
|
+
* `.gitignore` when present (matches EAS semantics); otherwise `.gitignore`
|
|
6579
|
+
* is layered on top of the always-ignore baseline.
|
|
6580
|
+
*/
|
|
6581
|
+
const buildIgnoreInstance = (workspaceRoot) => Effect.gen(function* () {
|
|
6582
|
+
const fs = yield* FileSystem.FileSystem;
|
|
6583
|
+
const ig = ignore();
|
|
6584
|
+
ig.add([...ALWAYS_IGNORE]);
|
|
6585
|
+
const easignorePath = path.join(workspaceRoot, ".easignore");
|
|
6586
|
+
if (yield* fs.exists(easignorePath).pipe(Effect.catchAll(() => Effect.succeed(false)))) {
|
|
6587
|
+
const content = yield* fs.readFileString(easignorePath).pipe(Effect.catchAll(() => Effect.succeed("")));
|
|
6588
|
+
ig.add(content);
|
|
6589
|
+
return ig;
|
|
6590
|
+
}
|
|
6591
|
+
const gitignorePath = path.join(workspaceRoot, ".gitignore");
|
|
6592
|
+
if (yield* fs.exists(gitignorePath).pipe(Effect.catchAll(() => Effect.succeed(false)))) {
|
|
6593
|
+
const content = yield* fs.readFileString(gitignorePath).pipe(Effect.catchAll(() => Effect.succeed("")));
|
|
6594
|
+
ig.add(content);
|
|
6595
|
+
}
|
|
6596
|
+
return ig;
|
|
6597
|
+
});
|
|
6598
|
+
const copyProjectTree = (params) => Effect.tryPromise({
|
|
6599
|
+
try: async () => {
|
|
6600
|
+
await promises.cp(params.source, params.dest, {
|
|
6601
|
+
recursive: true,
|
|
6602
|
+
dereference: false,
|
|
6603
|
+
filter: (src) => {
|
|
6604
|
+
const rel = path.relative(params.source, src);
|
|
6605
|
+
if (rel === "") return true;
|
|
6606
|
+
const posixRel = rel.split(path.sep).join("/");
|
|
6607
|
+
return !params.ig.ignores(posixRel);
|
|
6608
|
+
}
|
|
6609
|
+
});
|
|
6610
|
+
},
|
|
6611
|
+
catch: (cause) => new StagingError({ message: `Failed to copy project to staging dir: ${formatCause(cause)}` })
|
|
6612
|
+
});
|
|
6613
|
+
const runInstall = (params) => runStep({
|
|
6614
|
+
command: params.packageManager,
|
|
6615
|
+
args: ["install"],
|
|
6616
|
+
cwd: params.stagingRoot,
|
|
6617
|
+
env: params.env
|
|
6618
|
+
}, `${params.packageManager} install`);
|
|
6619
|
+
/**
|
|
6620
|
+
* Copy the user's project (or workspace root, for monorepos) into a fresh
|
|
6621
|
+
* directory inside `tempDir`, then run `<pm> install` there. The build then
|
|
6622
|
+
* runs entirely against the staged copy — the user's working tree stays clean
|
|
6623
|
+
* regardless of what `expo prebuild`, `pod install`, or `gradlew` write.
|
|
6624
|
+
*/
|
|
6625
|
+
const prepareStagingProject = (input) => Effect.gen(function* () {
|
|
6626
|
+
const runtime = yield* CliRuntime;
|
|
6627
|
+
const { workspaceRoot, packageManager } = yield* detectWorkspaceRoot(input.userCwd);
|
|
6628
|
+
const relAppPath = path.relative(workspaceRoot, input.userCwd);
|
|
6629
|
+
const stagingRoot = path.join(input.tempDir, "project");
|
|
6630
|
+
const projectRoot = relAppPath === "" ? stagingRoot : path.join(stagingRoot, relAppPath);
|
|
6631
|
+
yield* Console.log(`Staging build into ${stagingRoot}${relAppPath === "" ? "" : ` (app: ${relAppPath})`}`);
|
|
6632
|
+
yield* copyProjectTree({
|
|
6633
|
+
source: workspaceRoot,
|
|
6634
|
+
dest: stagingRoot,
|
|
6635
|
+
ig: yield* buildIgnoreInstance(workspaceRoot)
|
|
6636
|
+
});
|
|
6637
|
+
yield* runInstall({
|
|
6638
|
+
stagingRoot,
|
|
6639
|
+
packageManager,
|
|
6640
|
+
env: yield* runtime.commandEnvironment(input.envVars)
|
|
6641
|
+
});
|
|
6642
|
+
return {
|
|
6643
|
+
stagingRoot,
|
|
6644
|
+
projectRoot,
|
|
6645
|
+
packageManager,
|
|
6646
|
+
relAppPath
|
|
6647
|
+
};
|
|
6648
|
+
});
|
|
6649
|
+
|
|
6523
6650
|
//#endregion
|
|
6524
6651
|
//#region src/lib/repo-clean.ts
|
|
6525
6652
|
const MAX_FILES_SHOWN = 10;
|
|
@@ -6669,35 +6796,40 @@ const resolveProfileName = (projectRoot, requested) => Effect.gen(function* () {
|
|
|
6669
6796
|
});
|
|
6670
6797
|
const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
|
|
6671
6798
|
const api = yield* apiClient;
|
|
6672
|
-
const
|
|
6799
|
+
const userCwd = yield* (yield* CliRuntime).cwd;
|
|
6673
6800
|
yield* ensureRepoClean({
|
|
6674
|
-
projectRoot,
|
|
6801
|
+
projectRoot: userCwd,
|
|
6675
6802
|
allowDirty: options.allowDirty ?? false,
|
|
6676
6803
|
label: "build"
|
|
6677
6804
|
});
|
|
6678
|
-
const baseConfig = yield* readExpoConfig(
|
|
6805
|
+
const baseConfig = yield* readExpoConfig(userCwd);
|
|
6679
6806
|
const projectId = yield* extractProjectId(baseConfig);
|
|
6680
6807
|
const platform = yield* detectPlatform(options.platform, baseConfig);
|
|
6681
|
-
const profile = yield* readBuildProfile(
|
|
6808
|
+
const profile = yield* readBuildProfile(userCwd, yield* resolveProfileName(userCwd, options.profileName));
|
|
6682
6809
|
const envVars = yield* pullEnvVars(api, {
|
|
6683
6810
|
projectId,
|
|
6684
6811
|
environment: profile.environment
|
|
6685
6812
|
});
|
|
6686
6813
|
yield* applyAutoIncrement({
|
|
6687
|
-
projectRoot,
|
|
6814
|
+
projectRoot: userCwd,
|
|
6688
6815
|
platform,
|
|
6689
|
-
config: yield* readExpoConfig(
|
|
6816
|
+
config: yield* readExpoConfig(userCwd, envVars),
|
|
6690
6817
|
...platform === "ios" && profile.ios?.autoIncrement !== void 0 ? { iosMode: profile.ios.autoIncrement } : {},
|
|
6691
6818
|
...platform === "android" && profile.android?.autoIncrement !== void 0 ? { androidMode: profile.android.autoIncrement } : {}
|
|
6692
6819
|
});
|
|
6693
|
-
const appMeta = yield* readAppMeta(yield* readExpoConfig(
|
|
6820
|
+
const appMeta = yield* readAppMeta(yield* readExpoConfig(userCwd, envVars), platform);
|
|
6694
6821
|
const runtimeVersion = yield* resolveRuntimeVersion({
|
|
6695
6822
|
raw: appMeta.rawRuntimeVersion,
|
|
6696
6823
|
appVersion: appMeta.appVersion,
|
|
6697
|
-
projectRoot
|
|
6824
|
+
projectRoot: userCwd
|
|
6698
6825
|
});
|
|
6699
|
-
if (options.clearCache) yield* clearBuildCaches(
|
|
6826
|
+
if (options.clearCache) yield* clearBuildCaches(userCwd);
|
|
6700
6827
|
const tempDir = yield* acquireBuildTempDir;
|
|
6828
|
+
const staging = yield* prepareStagingProject({
|
|
6829
|
+
userCwd,
|
|
6830
|
+
tempDir,
|
|
6831
|
+
envVars
|
|
6832
|
+
});
|
|
6701
6833
|
yield* Console.log(`Building ${platform} artifact for profile "${profile.name}" (runtimeVersion=${runtimeVersion})`);
|
|
6702
6834
|
const { build, target, bundleId } = yield* runPlatformBuild({
|
|
6703
6835
|
api,
|
|
@@ -6707,14 +6839,14 @@ const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
6707
6839
|
appMeta,
|
|
6708
6840
|
envVars,
|
|
6709
6841
|
projectId,
|
|
6710
|
-
projectRoot,
|
|
6842
|
+
projectRoot: staging.projectRoot,
|
|
6711
6843
|
tempDir
|
|
6712
6844
|
});
|
|
6713
6845
|
yield* Console.log(`Artifact produced: ${build.artifactPath}`);
|
|
6714
6846
|
let exportedArtifactPath = void 0;
|
|
6715
6847
|
if (options.output !== void 0) {
|
|
6716
6848
|
const fs = yield* FileSystem.FileSystem;
|
|
6717
|
-
const outputPath = path.resolve(
|
|
6849
|
+
const outputPath = path.resolve(userCwd, options.output);
|
|
6718
6850
|
const outputDir = path.dirname(outputPath);
|
|
6719
6851
|
yield* fs.makeDirectory(outputDir, { recursive: true }).pipe(Effect.mapError((cause) => new BuildProfileError({ message: `Failed to create output directory: ${formatCause(cause)}` })));
|
|
6720
6852
|
yield* fs.copyFile(build.artifactPath, outputPath).pipe(Effect.mapError((cause) => new BuildProfileError({ message: `Failed to copy artifact to ${outputPath}: ${formatCause(cause)}` })));
|
|
@@ -6731,7 +6863,7 @@ const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
6731
6863
|
]);
|
|
6732
6864
|
return;
|
|
6733
6865
|
}
|
|
6734
|
-
const rawGitContext = yield* readGitContext(
|
|
6866
|
+
const rawGitContext = yield* readGitContext(userCwd);
|
|
6735
6867
|
const gitContext = {
|
|
6736
6868
|
...rawGitContext.ref === void 0 ? {} : { ref: rawGitContext.ref },
|
|
6737
6869
|
...rawGitContext.commit === void 0 ? {} : { commit: rawGitContext.commit },
|
|
@@ -6875,7 +7007,8 @@ const BUILD_EXIT_EXTRAS = {
|
|
|
6875
7007
|
PresignedUrlExpiredError: 7,
|
|
6876
7008
|
CompleteError: 7,
|
|
6877
7009
|
EnvExportError: 7,
|
|
6878
|
-
DirtyRepoError: 3
|
|
7010
|
+
DirtyRepoError: 3,
|
|
7011
|
+
StagingError: 6
|
|
6879
7012
|
};
|
|
6880
7013
|
const buildCommand = defineCommand({
|
|
6881
7014
|
meta: {
|