liftbay 0.5.0 → 0.5.2
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 +47 -30
- package/package.json +2 -2
- package/skills/liftbay/SKILL.md +2 -2
package/dist/index.js
CHANGED
|
@@ -25969,7 +25969,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync }
|
|
|
25969
25969
|
import { homedir } from "os";
|
|
25970
25970
|
import { join } from "path";
|
|
25971
25971
|
|
|
25972
|
-
//
|
|
25972
|
+
// node_modules/openapi-fetch/dist/index.mjs
|
|
25973
25973
|
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
25974
25974
|
var supportsRequestInitExt = () => {
|
|
25975
25975
|
return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
|
|
@@ -26445,7 +26445,7 @@ function removeTrailingSlash(url) {
|
|
|
26445
26445
|
return url;
|
|
26446
26446
|
}
|
|
26447
26447
|
|
|
26448
|
-
//
|
|
26448
|
+
// node_modules/@liftbay/sdk/dist/index.js
|
|
26449
26449
|
var LiftbayApiError = class extends Error {
|
|
26450
26450
|
constructor(status2, code, message, fields = {}) {
|
|
26451
26451
|
super(message);
|
|
@@ -28582,6 +28582,11 @@ function writeAppJson(data, cwd = process.cwd()) {
|
|
|
28582
28582
|
|
|
28583
28583
|
// src/prompts.ts
|
|
28584
28584
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
28585
|
+
function fit(text2, reserve = 8) {
|
|
28586
|
+
const width = Math.max(20, (process.stdout.columns || 80) - reserve);
|
|
28587
|
+
const plain = text2.replace(/\x1b\[[0-9;]*m/g, "");
|
|
28588
|
+
return plain.length <= width ? text2 : `${plain.slice(0, width - 1)}\u2026`;
|
|
28589
|
+
}
|
|
28585
28590
|
var symbol2 = (state) => state === "submit" ? import_picocolors3.default.green("\u25C7") : state === "cancel" ? import_picocolors3.default.red("\u25A0") : state === "error" ? import_picocolors3.default.yellow("\u25B2") : import_picocolors3.default.cyan("\u25C6");
|
|
28586
28591
|
function checkboxes(opts) {
|
|
28587
28592
|
const required = opts.required ?? true;
|
|
@@ -28983,7 +28988,7 @@ async function status(lb, config, refresh = true) {
|
|
|
28983
28988
|
}
|
|
28984
28989
|
async function waitFor(lb, config, message, done) {
|
|
28985
28990
|
const spin = spinner();
|
|
28986
|
-
spin.start(
|
|
28991
|
+
spin.start(fit(message));
|
|
28987
28992
|
const until = Date.now() + WAIT_MINUTES * 6e4;
|
|
28988
28993
|
while (Date.now() < until) {
|
|
28989
28994
|
await sleep2(4e3);
|
|
@@ -28998,7 +29003,13 @@ async function waitFor(lb, config, message, done) {
|
|
|
28998
29003
|
}
|
|
28999
29004
|
async function waitForInstall(lb, config, attempt) {
|
|
29000
29005
|
const spin = spinner();
|
|
29001
|
-
spin.start(
|
|
29006
|
+
spin.start(fit("Waiting for the GitHub App to be installed"));
|
|
29007
|
+
const explain = (text2, spinnerText) => {
|
|
29008
|
+
spin.stop(import_picocolors6.default.dim("\u2026"));
|
|
29009
|
+
log.info(text2);
|
|
29010
|
+
spin.start(fit(spinnerText));
|
|
29011
|
+
};
|
|
29012
|
+
let noRepoAccessShown = false;
|
|
29002
29013
|
const until = Date.now() + WAIT_MINUTES * 6e4;
|
|
29003
29014
|
let last = "waiting";
|
|
29004
29015
|
while (Date.now() < until) {
|
|
@@ -29006,7 +29017,7 @@ async function waitForInstall(lb, config, attempt) {
|
|
|
29006
29017
|
const a2 = attempt ? (await lb.GET("/v1/orgs/{org}/github/install/{attempt}", { params: { path: { org: config.org, attempt } } })).data : void 0;
|
|
29007
29018
|
if (a2 && a2.status !== last) {
|
|
29008
29019
|
last = a2.status;
|
|
29009
|
-
if (a2.status === "needs_sign_in")
|
|
29020
|
+
if (a2.status === "needs_sign_in") explain("GitHub is done. Sign in to Liftbay in that browser to finish linking.", "Waiting for you to sign in to Liftbay in the browser");
|
|
29010
29021
|
else if (a2.status !== "waiting" && a2.status !== "connected") {
|
|
29011
29022
|
spin.error("GitHub didn't connect");
|
|
29012
29023
|
return { ok: false, message: a2.message };
|
|
@@ -29018,7 +29029,10 @@ async function waitForInstall(lb, config, attempt) {
|
|
|
29018
29029
|
return { ok: true, status: s };
|
|
29019
29030
|
}
|
|
29020
29031
|
if (a2?.status === "connected") {
|
|
29021
|
-
|
|
29032
|
+
if (!noRepoAccessShown) {
|
|
29033
|
+
noRepoAccessShown = true;
|
|
29034
|
+
explain(`Installed on ${s.repo.split("/")[0]}, but without access to ${s.repo}. On GitHub, Configure the app and select the repository.`, "Waiting for access to the repository");
|
|
29035
|
+
}
|
|
29022
29036
|
}
|
|
29023
29037
|
}
|
|
29024
29038
|
spin.error("Stopped waiting");
|
|
@@ -29206,7 +29220,7 @@ async function readyToBuild(branch, interactive) {
|
|
|
29206
29220
|
}
|
|
29207
29221
|
}
|
|
29208
29222
|
async function build(opts) {
|
|
29209
|
-
|
|
29223
|
+
const config = requireConfig();
|
|
29210
29224
|
const lb = client();
|
|
29211
29225
|
const interactive = isInteractive();
|
|
29212
29226
|
const path2 = { org: config.org, project: config.project };
|
|
@@ -29247,25 +29261,26 @@ async function build(opts) {
|
|
|
29247
29261
|
platforms = choice === "all" ? profile.platforms : [choice];
|
|
29248
29262
|
} else platforms = profile.platforms;
|
|
29249
29263
|
const storeIos = platforms.includes("ios") && !opts.simulator && profile.distribution === "store";
|
|
29250
|
-
let iosEngine = opts.engine ??
|
|
29264
|
+
let iosEngine = opts.engine ?? "auto";
|
|
29251
29265
|
if (!storeIos && iosEngine === "xcode-cloud") {
|
|
29252
|
-
|
|
29253
|
-
iosEngine = "github-actions";
|
|
29266
|
+
fail(`Xcode Cloud makes iOS store builds only; profile "${profileName}" is ${opts.simulator ? "a simulator" : `an ${profile.distribution}`} build. Use a store profile, or --engine github-actions.`);
|
|
29254
29267
|
}
|
|
29255
29268
|
if (storeIos) {
|
|
29256
|
-
if (!opts.engine &&
|
|
29269
|
+
if (!opts.engine && interactive) {
|
|
29270
|
+
const setting = (await unwrap(lb.GET("/v1/orgs/{org}/projects/{project}", { params: { path: path2 } }))).iosEngine ?? "github-actions";
|
|
29271
|
+
const label = (e) => e === "xcode-cloud" ? "Xcode Cloud" : "GitHub Actions";
|
|
29257
29272
|
iosEngine = cancelled(
|
|
29258
29273
|
await select({
|
|
29259
|
-
message: "Where should iOS
|
|
29260
|
-
options: [
|
|
29261
|
-
|
|
29262
|
-
|
|
29263
|
-
|
|
29274
|
+
message: "Where should this iOS build run?",
|
|
29275
|
+
options: ["github-actions", "xcode-cloud"].map((e) => ({
|
|
29276
|
+
value: e,
|
|
29277
|
+
label: label(e),
|
|
29278
|
+
hint: [e === setting ? "project default" : "", e === "xcode-cloud" ? "25 free hours a month with the Apple Developer Program" : "macOS runners on your GitHub minutes"].filter(Boolean).join(" \xB7 ")
|
|
29279
|
+
})),
|
|
29280
|
+
initialValue: setting
|
|
29264
29281
|
})
|
|
29265
29282
|
);
|
|
29266
|
-
|
|
29267
|
-
writeConfig(config);
|
|
29268
|
-
log.info(import_picocolors7.default.dim("Saved in liftbay.json (commit it). Override once with --engine."));
|
|
29283
|
+
if (iosEngine !== setting) log.info(import_picocolors7.default.dim(`Just this build. Change the project default in the dashboard's project settings, or with ${cmd("liftbay ci init --ios-engine " + iosEngine)}.`));
|
|
29269
29284
|
}
|
|
29270
29285
|
if (iosEngine === "xcode-cloud") {
|
|
29271
29286
|
const xc = await unwrap(lb.GET("/v1/orgs/{org}/projects/{project}/xcode-cloud", { params: { path: path2 } }));
|
|
@@ -29543,7 +29558,7 @@ async function xcodeCloudConnect(opts) {
|
|
|
29543
29558
|
last = s.step;
|
|
29544
29559
|
if (!wait || s.step === "apple_key") fail(s.step === "apple_key" ? s.message : "Not connected yet. Run this again once the step above is done.");
|
|
29545
29560
|
const spin = spinner();
|
|
29546
|
-
spin.start("Waiting for that step (checking every 10 seconds
|
|
29561
|
+
spin.start(fit("Waiting for that step (checking every 10 seconds)"));
|
|
29547
29562
|
await sleep5(1e4);
|
|
29548
29563
|
spin.stop("Checking again");
|
|
29549
29564
|
}
|
|
@@ -30108,16 +30123,17 @@ async function ciInit(app, config, opts) {
|
|
|
30108
30123
|
let iosEngine = opts.iosEngine;
|
|
30109
30124
|
if (iosEngine && iosEngine !== "github-actions" && iosEngine !== "xcode-cloud") fail("--ios-engine must be github-actions or xcode-cloud");
|
|
30110
30125
|
if (platforms.includes("ios") && !iosEngine) {
|
|
30111
|
-
|
|
30126
|
+
const current = (await unwrap(lb.GET("/v1/orgs/{org}/projects/{project}", { params: { path: { org: config.org, project: config.project } } }))).iosEngine;
|
|
30127
|
+
iosEngine = opts.yes || !isInteractive() ? current ?? "github-actions" : cancelled(
|
|
30112
30128
|
await select({
|
|
30113
|
-
message: "Where should iOS builds run?",
|
|
30129
|
+
message: "Where should iOS store builds run? (you can pick for each build too)",
|
|
30114
30130
|
options: [
|
|
30115
30131
|
{ value: "github-actions", label: "GitHub Actions", hint: "macOS runners on your GitHub minutes; ready now" },
|
|
30116
30132
|
{ value: "xcode-cloud", label: "Xcode Cloud", hint: "store builds, 25 free hours a month with the Apple Developer Program; one-time setup after iOS signing" }
|
|
30117
30133
|
],
|
|
30118
|
-
initialValue: "github-actions"
|
|
30134
|
+
initialValue: current ?? "github-actions"
|
|
30119
30135
|
})
|
|
30120
|
-
)
|
|
30136
|
+
);
|
|
30121
30137
|
}
|
|
30122
30138
|
if (platforms.includes("ios") && !app.bundleId) log.warn(`No iOS bundle identifier found. Set ${import_picocolors13.default.cyan("expo.ios.bundleIdentifier")} in app.json before the first iOS build.`);
|
|
30123
30139
|
if (platforms.includes("android") && !app.androidPackage) log.warn(`No Android package found. Set ${import_picocolors13.default.cyan("expo.android.package")} in app.json before the first Android build.`);
|
|
@@ -30155,12 +30171,13 @@ async function ciInit(app, config, opts) {
|
|
|
30155
30171
|
log.info(import_picocolors13.default.dim(`Variables kept on expo.dev (EAS environment variables) aren't in eas.json: ${import_picocolors13.default.cyan("npx eas env:pull --environment production")}, then ${import_picocolors13.default.cyan(cmd("liftbay env import .env.local --environment production"))}.`));
|
|
30156
30172
|
}
|
|
30157
30173
|
const profiles = Object.fromEntries(Object.entries(base).map(([name, prof]) => [name, { ...prof, platforms }]));
|
|
30158
|
-
|
|
30174
|
+
if (iosEngine) await unwrap(lb.PATCH("/v1/orgs/{org}/projects/{project}", { params: { path: path2 }, body: { iosEngine } }));
|
|
30175
|
+
const next = { ...config, build: { profiles, triggers: config.build?.triggers ?? [] } };
|
|
30159
30176
|
writeConfig(next);
|
|
30160
30177
|
note(
|
|
30161
30178
|
Object.entries(profiles).map(([name, prof]) => `${import_picocolors13.default.bold(name.padEnd(12))} env ${prof.environment.padEnd(12)} ${prof.distribution === "store" ? "store" : "internal"} \xB7 ${prof.platforms.join(", ")}`).join("\n") + (iosEngine ? `
|
|
30162
30179
|
|
|
30163
|
-
iOS builds run on ${iosEngine === "xcode-cloud" ? "Xcode Cloud" : "GitHub Actions"}` : ""),
|
|
30180
|
+
iOS store builds run on ${iosEngine === "xcode-cloud" ? "Xcode Cloud" : "GitHub Actions"} (project setting)` : ""),
|
|
30164
30181
|
"Build profiles"
|
|
30165
30182
|
);
|
|
30166
30183
|
if (iosEngine === "xcode-cloud") {
|
|
@@ -30549,7 +30566,7 @@ async function publish(opts) {
|
|
|
30549
30566
|
let done = 0;
|
|
30550
30567
|
for (const m of prep.missing) {
|
|
30551
30568
|
const f = bySha.get(m.sha256);
|
|
30552
|
-
spin.message(`Uploading ${++done}/${prep.missing.length}: ${f.path}`);
|
|
30569
|
+
spin.message(fit(`Uploading ${++done}/${prep.missing.length}: ${f.path}`));
|
|
30553
30570
|
const res = await fetch(m.uploadUrl, { method: "PUT", body: readFileSync10(f.abs), headers: { "Content-Type": f.contentType } });
|
|
30554
30571
|
if (!res.ok) fail(`Upload failed for ${f.path} (${res.status})`);
|
|
30555
30572
|
}
|
|
@@ -30635,7 +30652,7 @@ async function channels() {
|
|
|
30635
30652
|
}
|
|
30636
30653
|
|
|
30637
30654
|
// src/index.ts
|
|
30638
|
-
var program2 = new Command("liftbay").description("Build, ship and update React Native apps").version("0.5.
|
|
30655
|
+
var program2 = new Command("liftbay").description("Build, ship and update React Native apps").version("0.5.2").option("--json", "print the result as JSON on stdout (progress goes to stderr; never prompts). Also LIFTBAY_JSON=1").hook("preAction", () => {
|
|
30639
30656
|
if (program2.opts().json || process.env.LIFTBAY_JSON === "1") enableJson();
|
|
30640
30657
|
});
|
|
30641
30658
|
var wrap = (fn) => async (...args) => {
|
|
@@ -30645,14 +30662,14 @@ var wrap = (fn) => async (...args) => {
|
|
|
30645
30662
|
reportError(e);
|
|
30646
30663
|
}
|
|
30647
30664
|
};
|
|
30648
|
-
var setupOptions = (cmd2) => cmd2.option("--org <slug>", "organization").option("--project <slug>", "project slug (created if it doesn't exist)").option("--platforms <list>", "ios, android or all").option("--runtime-policy <policy>", "appVersion or fingerprint").option("--ios-engine <engine>", "where iOS builds run
|
|
30665
|
+
var setupOptions = (cmd2) => cmd2.option("--org <slug>", "organization").option("--project <slug>", "project slug (created if it doesn't exist)").option("--platforms <list>", "ios, android or all").option("--runtime-policy <policy>", "appVersion or fingerprint").option("--ios-engine <engine>", "project setting: where iOS store builds run, github-actions or xcode-cloud").option("--no-eas", "don't use eas.json's build profiles and env, even if there is one").option("-y, --yes", "accept defaults, no prompts").option("--api-url <url>", "API URL (for self-hosted or local development)").option("--dashboard-url <url>", "dashboard URL");
|
|
30649
30666
|
program2.command("login").description("Sign in through the browser").option("--api-url <url>", "API URL").option("--dashboard-url <url>", "dashboard URL").option("--no-open", "don't open the browser").option("--no-wait", "start the sign-in and exit; run `liftbay login` again after approving to finish (for agents)").action(wrap(login));
|
|
30650
30667
|
program2.command("logout").description("Sign out and remove the saved token").action(wrap(logout));
|
|
30651
30668
|
program2.command("whoami").description("Show the signed-in user and organizations").action(wrap(whoami));
|
|
30652
30669
|
setupOptions(program2.command("init").description("Set up this app: link the project, builds, then OTA updates").option("--no-ota", "skip OTA updates")).action(wrap(initCommand));
|
|
30653
30670
|
var ci = program2.command("ci").description("Builds");
|
|
30654
30671
|
setupOptions(ci.command("init").description("Set up build profiles, environments and app identifiers")).action(wrap(ciInitCommand));
|
|
30655
|
-
program2.command("build").description("Build on your GitHub Actions (Android on Linux, iOS on macOS) and follow the logs").option("--platform <platform>", "ios, android or all (asked in a terminal; otherwise the profile's platforms)").option("--profile <name>", "build profile from liftbay.json (asked in a terminal; otherwise production)").option("--ref <ref>", "branch, tag or commit to build (default: current branch)").option("-m, --message <text>", "build message").option("--simulator", "iOS: build for the simulator instead of a signed .ipa").option("--no-wait", "queue and exit without following the logs").option("--submit", "submit store builds when they succeed: TestFlight, or a Google Play track").option("--track <track>", "Play track for --submit: internal, alpha, beta or production").option("--engine <engine>", "where iOS
|
|
30672
|
+
program2.command("build").description("Build on your GitHub Actions (Android on Linux, iOS on macOS) and follow the logs").option("--platform <platform>", "ios, android or all (asked in a terminal; otherwise the profile's platforms)").option("--profile <name>", "build profile from liftbay.json (asked in a terminal; otherwise production)").option("--ref <ref>", "branch, tag or commit to build (default: current branch)").option("-m, --message <text>", "build message").option("--simulator", "iOS: build for the simulator instead of a signed .ipa").option("--no-wait", "queue and exit without following the logs").option("--submit", "submit store builds when they succeed: TestFlight, or a Google Play track").option("--track <track>", "Play track for --submit: internal, alpha, beta or production").option("--engine <engine>", "where this iOS store build runs: github-actions or xcode-cloud (asked in a terminal; otherwise the project's setting)").action(wrap(build));
|
|
30656
30673
|
var builds = program2.command("builds").description("Look at builds: list, status, logs, artifacts");
|
|
30657
30674
|
builds.command("list").description("Recent builds, newest first").option("--platform <platform>", "ios or android").option("--status <list>", "comma-separated: queued, preparing, running, uploading, succeeded, failed, cancelled").option("--limit <n>", "how many", "20").action(wrap(buildsList));
|
|
30658
30675
|
builds.command("view <number>").description("Status, phases and artifacts of one build").action(wrap(buildsView));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liftbay",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Build, sign, submit and update React Native apps from your own CI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@clack/core": "1.5.1",
|
|
51
51
|
"@clack/prompts": "^1.8.1",
|
|
52
|
-
"@liftbay/sdk": "^0.1.
|
|
52
|
+
"@liftbay/sdk": "^0.1.2",
|
|
53
53
|
"@types/node": "^26.6.4",
|
|
54
54
|
"@types/qrcode": "^1.5.6",
|
|
55
55
|
"@types/node-forge": "^1.3.14",
|
package/skills/liftbay/SKILL.md
CHANGED
|
@@ -60,7 +60,7 @@ Useful flags:
|
|
|
60
60
|
|
|
61
61
|
To set up one part on its own, use `liftbay ci init --yes --json` (builds) or `liftbay ota init --yes --json` (OTA).
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
`--ios-engine github-actions|xcode-cloud` sets a **project setting**: where iOS **store** builds run unless a build picks. Builds you start without `--engine`, and every build started by a git push, use it. It's also in the dashboard's project settings. Xcode Cloud uses the Apple team's free hours, and needs iOS signing and `liftbay xcode-cloud connect` first; until it's connected (or when it's out of hours), those builds fall back to GitHub Actions and say so in the log. Internal (ad hoc) and simulator builds always run on GitHub Actions.
|
|
64
64
|
|
|
65
65
|
Then get GitHub ready. Builds start the workflow through the Liftbay GitHub App:
|
|
66
66
|
|
|
@@ -159,7 +159,7 @@ Statuses: `queued` → `preparing` → `running` → `uploading` → `succeeded`
|
|
|
159
159
|
Other flags:
|
|
160
160
|
- `--ref <branch|tag|sha>`: defaults to the current branch, which must be pushed.
|
|
161
161
|
- `--simulator`: unsigned iOS simulator build; needs no Apple account.
|
|
162
|
-
- `--engine github-actions|xcode-cloud`:
|
|
162
|
+
- `--engine github-actions|xcode-cloud`: for this build only, instead of the project's setting (Xcode Cloud: iOS store builds only)
|
|
163
163
|
- `-m "message"`
|
|
164
164
|
|
|
165
165
|
**Xcode Cloud (optional):** `liftbay xcode-cloud connect --json`. Its error says which one-time step the user has to do in App Store Connect.
|