@expo-up/cli 0.1.5 → 0.1.6

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.
Files changed (3) hide show
  1. package/README.md +26 -1
  2. package/dist/index.js +63 -39
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -17,12 +17,28 @@ CLI package for managing self-hosted Expo OTA workflows.
17
17
 
18
18
  - `--debug`: enable verbose logs for API calls, release diffing, and error context.
19
19
 
20
+ ## CI/CD Token Auth
21
+
22
+ For non-interactive environments, use:
23
+ - `EXPO_UP_CLI_GITHUB_TOKEN` (recommended)
24
+ - or `--token` per command
25
+ - token must have **write access** to your GitHub storage repository (contents write).
26
+
27
+ Example:
28
+
29
+ ```bash
30
+ EXPO_UP_CLI_GITHUB_TOKEN=ghp_xxx expo-up release --channel main --platform all
31
+ ```
32
+
20
33
  ## Command Reference
21
34
 
22
35
  ### `expo-up login`
23
36
 
24
37
  Authenticate and store local token/config for subsequent commands.
25
38
 
39
+ Options:
40
+ - `-t, --token <token>` save token directly (skip OAuth browser flow)
41
+
26
42
  ### `expo-up logout`
27
43
 
28
44
  Clear locally stored token/config session.
@@ -51,6 +67,9 @@ expo-up set-channel staging
51
67
 
52
68
  List available channels from the storage repository.
53
69
 
70
+ Options:
71
+ - `-t, --token <token>` use GitHub token directly (CI-friendly)
72
+
54
73
  ### `expo-up release`
55
74
 
56
75
  Build and upload a new OTA build for a channel/runtime.
@@ -58,6 +77,7 @@ Build and upload a new OTA build for a channel/runtime.
58
77
  Options:
59
78
  - `--platform <ios|android|all>` default: `all`
60
79
  - `--channel <name>` optional channel override
80
+ - `-t, --token <token>` use GitHub token directly (CI-friendly)
61
81
  - channel defaults to `main` when no saved/override channel is provided
62
82
 
63
83
  Behavior:
@@ -71,6 +91,7 @@ Examples:
71
91
 
72
92
  ```bash
73
93
  expo-up release --platform all --channel main
94
+ expo-up release -t "$EXPO_UP_CLI_GITHUB_TOKEN" --platform all --channel main
74
95
  expo-up --debug release --platform ios --channel feat/new-home
75
96
  ```
76
97
 
@@ -83,12 +104,14 @@ Options:
83
104
  - `--delete <buildIds...>` non-interactive delete mode (CI friendly)
84
105
  - `--yes` skip confirmation for delete mode
85
106
  - `--no-interactive-delete` disable TUI selection mode
107
+ - `-t, --token <token>` use GitHub token directly (CI-friendly)
86
108
  - channel defaults to `main` when no saved/override channel is provided
87
109
 
88
110
  Examples:
89
111
 
90
112
  ```bash
91
113
  expo-up history --channel main
114
+ expo-up history -t "$EXPO_UP_CLI_GITHUB_TOKEN" --channel main
92
115
  expo-up history --channel main --delete 10 11 --yes
93
116
  ```
94
117
 
@@ -98,7 +121,8 @@ Rollback channel to previous build or embedded app update.
98
121
 
99
122
  Options:
100
123
  - `--channel <name>` optional channel override
101
- - `--to <buildId>` rollback target build
124
+ - `-b, --to <buildId>` rollback target build
125
+ - `-t, --token <token>` use GitHub token directly (CI-friendly)
102
126
  - `--embedded` rollback to embedded/native update
103
127
  - channel defaults to `main` when no saved/override channel is provided
104
128
 
@@ -106,6 +130,7 @@ Examples:
106
130
 
107
131
  ```bash
108
132
  expo-up rollback --channel main --to 10
133
+ expo-up rollback -t "$EXPO_UP_CLI_GITHUB_TOKEN" --channel main -b 10
109
134
  expo-up rollback --channel main --embedded
110
135
  ```
111
136
 
package/dist/index.js CHANGED
@@ -334081,22 +334081,28 @@ function writeConfig(data) {
334081
334081
  fs7.writeFileSync(configPath, JSON.stringify(merged, null, 2));
334082
334082
  ensureConfigIgnored(EXPO_UP_CONFIG_RELATIVE_PATH);
334083
334083
  }
334084
- async function login() {
334084
+ async function login(token) {
334085
+ if (token?.trim()) {
334086
+ writeConfig({ github_token: token.trim() });
334087
+ console.log(`
334088
+ ${import_picocolors.default.green("✔")} Token saved.`);
334089
+ return token.trim();
334090
+ }
334085
334091
  const { serverUrl } = getAutoConfig();
334086
334092
  if (!serverUrl)
334087
334093
  throw new Error(`Server URL not found in expo config.`);
334088
334094
  return new Promise((resolve, reject) => {
334089
334095
  const localServer = http.createServer((req, res) => {
334090
334096
  const url3 = new URL(req.url ?? "/", `http://${req.headers.host}`);
334091
- const token = url3.searchParams.get("token");
334092
- if (token) {
334093
- writeConfig({ github_token: token });
334097
+ const token2 = url3.searchParams.get("token");
334098
+ if (token2) {
334099
+ writeConfig({ github_token: token2 });
334094
334100
  res.writeHead(200, { "Content-Type": "text/html" });
334095
334101
  res.end(`<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body style="font-family: sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background: #0f172a; color: white;"><h1 style="color: #22d3ee;">\uD83D\uDE80 expo-up</h1><p>Login successful! You can close this tab and return to your terminal.</p></body></html>`);
334096
334102
  console.log(`
334097
334103
  ${import_picocolors.default.green("✔")} Successfully authenticated! Token saved.`);
334098
334104
  localServer.close();
334099
- resolve(token);
334105
+ resolve(token2);
334100
334106
  } else {
334101
334107
  res.writeHead(400);
334102
334108
  res.end("Token missing");
@@ -334115,6 +334121,15 @@ ${import_picocolors.default.cyan("\uD83D\uDE80")} Opening browser for GitHub Aut
334115
334121
  function getStoredToken() {
334116
334122
  return readConfig().github_token;
334117
334123
  }
334124
+ function resolveGithubToken(token) {
334125
+ const explicit = token?.trim();
334126
+ if (explicit)
334127
+ return explicit;
334128
+ const envToken = process.env.EXPO_UP_CLI_GITHUB_TOKEN?.trim();
334129
+ if (envToken)
334130
+ return envToken;
334131
+ return getStoredToken();
334132
+ }
334118
334133
  function getStoredChannel() {
334119
334134
  return readConfig().channel || DEFAULT_CHANNEL;
334120
334135
  }
@@ -336689,7 +336704,8 @@ function toErrorMessage2(error48) {
336689
336704
  var Release = ({
336690
336705
  channel = "main",
336691
336706
  platform: platform3,
336692
- debug = false
336707
+ debug = false,
336708
+ token
336693
336709
  }) => {
336694
336710
  const [status, setStatus] = import_react23.useState("idle");
336695
336711
  const [logs, setLogs] = import_react23.useState([]);
@@ -336720,9 +336736,9 @@ var Release = ({
336720
336736
  const run = async () => {
336721
336737
  try {
336722
336738
  const config2 = getAutoConfig();
336723
- const token = getStoredToken();
336724
- if (!token)
336725
- throw new Error('Not logged in. Run "login" first.');
336739
+ const resolvedToken = resolveGithubToken(token);
336740
+ if (!resolvedToken)
336741
+ throw new Error('Missing GitHub token. Use "login", --token, or EXPO_UP_CLI_GITHUB_TOKEN.');
336726
336742
  if (!config2.serverUrl || !config2.projectId || !config2.runtimeVersion) {
336727
336743
  throw new Error("Missing Expo updates configuration. Check Expo config updates.url and version.");
336728
336744
  }
@@ -336730,7 +336746,7 @@ var Release = ({
336730
336746
  if (!projectRes.ok)
336731
336747
  throw new Error(`Project "${config2.projectId}" not found on server.`);
336732
336748
  const { owner, repo } = parseProjectDescriptor(await projectRes.json());
336733
- const octokit = new Octokit({ auth: token });
336749
+ const octokit = new Octokit({ auth: resolvedToken });
336734
336750
  setStatus("exporting");
336735
336751
  appendLog(`${import_picocolors2.default.blue("ℹ")} Exporting project with Metro (${platform3})...`);
336736
336752
  if (debug)
@@ -336967,7 +336983,7 @@ createdAt=${new Date().toISOString()}
336967
336983
  }
336968
336984
  };
336969
336985
  run();
336970
- }, [channel, platform3]);
336986
+ }, [channel, debug, platform3, token]);
336971
336987
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
336972
336988
  flexDirection: "column",
336973
336989
  padding: 1,
@@ -339662,7 +339678,8 @@ var Rollback = ({
339662
339678
  channel,
339663
339679
  to,
339664
339680
  embedded,
339665
- debug = false
339681
+ debug = false,
339682
+ token
339666
339683
  }) => {
339667
339684
  const [logs, setLogs] = React12.useState([]);
339668
339685
  const [debugLogs, setDebugLogs] = React12.useState([]);
@@ -339674,13 +339691,13 @@ var Rollback = ({
339674
339691
  try {
339675
339692
  const appendLog = (message) => setLogs((prev) => [...prev, message]);
339676
339693
  const appendDebug = (message) => setDebugLogs((prev) => [...prev, message]);
339677
- const token = getStoredToken();
339694
+ const resolvedToken = resolveGithubToken(token);
339678
339695
  const { serverUrl, projectId, runtimeVersion } = getAutoConfig();
339679
- if (!token || !serverUrl || !projectId || !runtimeVersion)
339680
- throw new Error("Missing configuration.");
339696
+ if (!resolvedToken || !serverUrl || !projectId || !runtimeVersion)
339697
+ throw new Error('Missing configuration. Use "login", --token, or EXPO_UP_CLI_GITHUB_TOKEN.');
339681
339698
  if (debug)
339682
339699
  appendDebug(`Resolved config: server=${serverUrl}, project=${projectId}, runtime=${runtimeVersion}`);
339683
- const octokit = new Octokit2({ auth: token });
339700
+ const octokit = new Octokit2({ auth: resolvedToken });
339684
339701
  setStatus("running");
339685
339702
  const projRes = await fetch(`${serverUrl}/projects/${projectId}`);
339686
339703
  if (!projRes.ok)
@@ -339798,7 +339815,7 @@ var Rollback = ({
339798
339815
  }
339799
339816
  };
339800
339817
  run();
339801
- }, [channel, debug, embedded, to]);
339818
+ }, [channel, debug, embedded, to, token]);
339802
339819
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
339803
339820
  flexDirection: "column",
339804
339821
  padding: 1,
@@ -339953,7 +339970,8 @@ var History = ({
339953
339970
  debug = false,
339954
339971
  deleteBuildIds,
339955
339972
  interactiveDelete = true,
339956
- yes = false
339973
+ yes = false,
339974
+ token
339957
339975
  }) => {
339958
339976
  const [items, setItems] = React13.useState([]);
339959
339977
  const [status, setStatus] = React13.useState("loading");
@@ -339989,15 +340007,15 @@ var History = ({
339989
340007
  setPendingDeleteIds(null);
339990
340008
  setSelectedBuilds(new Set);
339991
340009
  setCursor(0);
339992
- const token = getStoredToken();
340010
+ const resolvedToken = resolveGithubToken(token);
339993
340011
  const { serverUrl, projectId, runtimeVersion } = getAutoConfig();
339994
- if (!token || !serverUrl || !projectId || !runtimeVersion) {
339995
- throw new Error("Missing configuration. Are you logged in?");
340012
+ if (!resolvedToken || !serverUrl || !projectId || !runtimeVersion) {
340013
+ throw new Error('Missing configuration. Use "login", --token, or EXPO_UP_CLI_GITHUB_TOKEN.');
339996
340014
  }
339997
340015
  if (debug) {
339998
340016
  appendDebug(`Resolved config: server=${serverUrl}, project=${projectId}, runtime=${runtimeVersion}, channel=${channel}`);
339999
340017
  }
340000
- const octokit = new Octokit2({ auth: token });
340018
+ const octokit = new Octokit2({ auth: resolvedToken });
340001
340019
  const projRes = await fetch(`${serverUrl}/projects/${projectId}`);
340002
340020
  if (!projRes.ok)
340003
340021
  throw new Error(`Project "${projectId}" not found.`);
@@ -340064,7 +340082,7 @@ var History = ({
340064
340082
  if (debug)
340065
340083
  appendDebug(`Failure: ${message}`);
340066
340084
  }
340067
- }, [appendDebug, channel, debug]);
340085
+ }, [appendDebug, channel, debug, token]);
340068
340086
  const deleteBuilds = React13.useCallback(async (buildIds) => {
340069
340087
  const context = ctxRef.current;
340070
340088
  if (!context) {
@@ -340443,7 +340461,8 @@ var History = ({
340443
340461
  var React14 = __toESM(require_react(), 1);
340444
340462
  var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
340445
340463
  var ListChannels = ({
340446
- debug = false
340464
+ debug = false,
340465
+ token
340447
340466
  }) => {
340448
340467
  const [channels, setChannels] = React14.useState([]);
340449
340468
  const [loading, setLoading] = React14.useState(true);
@@ -340453,9 +340472,9 @@ var ListChannels = ({
340453
340472
  const run = async () => {
340454
340473
  try {
340455
340474
  const appendDebug = (message) => setDebugLogs((prev) => [...prev, message]);
340456
- const token = getStoredToken();
340475
+ const resolvedToken = resolveGithubToken(token);
340457
340476
  const { serverUrl, projectId } = getAutoConfig();
340458
- if (!token || !serverUrl || !projectId)
340477
+ if (!resolvedToken || !serverUrl || !projectId)
340459
340478
  throw new Error("Missing configuration. Are you logged in?");
340460
340479
  if (debug)
340461
340480
  appendDebug(`Resolved config: server=${serverUrl}, project=${projectId}`);
@@ -340463,7 +340482,7 @@ var ListChannels = ({
340463
340482
  if (!projRes.ok)
340464
340483
  throw new Error(`Project "${projectId}" not found.`);
340465
340484
  const { owner, repo } = parseProjectDescriptor(await projRes.json());
340466
- const octokit = new Octokit2({ auth: token });
340485
+ const octokit = new Octokit2({ auth: resolvedToken });
340467
340486
  const { data: branches } = await octokit.repos.listBranches({
340468
340487
  owner,
340469
340488
  repo
@@ -340481,7 +340500,7 @@ var ListChannels = ({
340481
340500
  }
340482
340501
  };
340483
340502
  run();
340484
- }, [debug]);
340503
+ }, [debug, token]);
340485
340504
  const currentChannel = getStoredChannel();
340486
340505
  return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
340487
340506
  flexDirection: "column",
@@ -340759,14 +340778,16 @@ program.command("set-channel <name>").description("Set your active channel (e.g.
340759
340778
  writeConfig({ channel: name });
340760
340779
  console.log(`${import_picocolors4.default.green("✔")} Active channel set to: ${import_picocolors4.default.cyan(name)}`);
340761
340780
  });
340762
- program.command("list-channels").description("List available channels in your storage repository").action(() => {
340781
+ program.command("list-channels").description("List available channels in your storage repository").option("-t, --token <token>", "Use GitHub token directly (CI-friendly)").action((options) => {
340763
340782
  const debug = program.opts().debug;
340764
340783
  render_default(/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ListChannels, {
340765
- debug
340784
+ debug,
340785
+ token: options.token
340766
340786
  }, undefined, false, undefined, this));
340767
340787
  });
340768
- program.command("login").description("Authenticate with GitHub").action(withCommandErrorBoundary(async () => {
340769
- await login();
340788
+ program.command("login").description("Authenticate with GitHub").option("-t, --token <token>", "Save GitHub token without OAuth browser flow").action(withCommandErrorBoundary(async (options) => {
340789
+ const token = typeof options.token === "string" ? options.token : "";
340790
+ await login(token);
340770
340791
  process.exit(0);
340771
340792
  }));
340772
340793
  program.command("logout").description("Clear local session").action(() => {
@@ -340774,7 +340795,7 @@ program.command("logout").description("Clear local session").action(() => {
340774
340795
  process.exit(0);
340775
340796
  });
340776
340797
  program.command("whoami").description("Check currently logged in session and project info").action(() => {
340777
- const token = getStoredToken();
340798
+ const token = resolveGithubToken();
340778
340799
  const { serverUrl, projectId } = getAutoConfig();
340779
340800
  const channel = getStoredChannel();
340780
340801
  console.log(`${import_picocolors4.default.blue("ℹ")} ${import_picocolors4.default.bold("Project ID:")} ${projectId || import_picocolors4.default.red("Not found in Expo config")}`);
@@ -340787,27 +340808,29 @@ program.command("whoami").description("Check currently logged in session and pro
340787
340808
  }
340788
340809
  process.exit(0);
340789
340810
  });
340790
- program.command("release").description("Bundle and upload an update").option("-p, --platform <platform>", "ios, android, or all", "all").option("-c, --channel <channel>", "Override active channel").action((options) => {
340811
+ program.command("release").description("Bundle and upload an update").option("-p, --platform <platform>", "ios, android, or all", "all").option("-c, --channel <channel>", "Override active channel").option("-t, --token <token>", "Use GitHub token directly (CI-friendly)").action((options) => {
340791
340812
  const channel = options.channel || getStoredChannel() || DEFAULT_CHANNEL;
340792
340813
  const platform3 = parsePlatform(options.platform);
340793
340814
  const debug = program.opts().debug;
340794
340815
  render_default(/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Release, {
340795
340816
  channel,
340796
340817
  platform: platform3,
340797
- debug
340818
+ debug,
340819
+ token: options.token
340798
340820
  }, undefined, false, undefined, this));
340799
340821
  });
340800
- program.command("rollback").description("Rollback to a previous build").option("-c, --channel <channel>", "Override active channel").option("-t, --to <build>", "Specific build ID").option("-e, --embedded", "Rollback to native build").action((options) => {
340822
+ program.command("rollback").description("Rollback to a previous build").option("-c, --channel <channel>", "Override active channel").option("-b, --to <build>", "Specific build ID").option("-t, --token <token>", "Use GitHub token directly (CI-friendly)").option("-e, --embedded", "Rollback to native build").action((options) => {
340801
340823
  const channel = options.channel || getStoredChannel() || DEFAULT_CHANNEL;
340802
340824
  const debug = program.opts().debug;
340803
340825
  render_default(/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Rollback, {
340804
340826
  channel,
340805
340827
  to: options.to,
340806
340828
  embedded: options.embedded,
340807
- debug
340829
+ debug,
340830
+ token: options.token
340808
340831
  }, undefined, false, undefined, this));
340809
340832
  });
340810
- program.command("history").description("View build history for a channel").option("-c, --channel <channel>", "Override active channel").option("--delete <buildIds...>", "Delete one or more build IDs (CI-friendly, supports comma or space-separated values)").option("--yes", "Skip confirmation prompt for delete actions", false).option("--no-interactive-delete", "Disable interactive multi-select delete mode for build history").action((options) => {
340833
+ program.command("history").description("View build history for a channel").option("-c, --channel <channel>", "Override active channel").option("--delete <buildIds...>", "Delete one or more build IDs (CI-friendly, supports comma or space-separated values)").option("--yes", "Skip confirmation prompt for delete actions", false).option("--no-interactive-delete", "Disable interactive multi-select delete mode for build history").option("-t, --token <token>", "Use GitHub token directly (CI-friendly)").action((options) => {
340811
340834
  const channel = options.channel || getStoredChannel() || DEFAULT_CHANNEL;
340812
340835
  const debug = program.opts().debug;
340813
340836
  render_default(/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(History, {
@@ -340815,7 +340838,8 @@ program.command("history").description("View build history for a channel").optio
340815
340838
  debug,
340816
340839
  deleteBuildIds: options.delete,
340817
340840
  interactiveDelete: options.interactiveDelete,
340818
- yes: options.yes
340841
+ yes: options.yes,
340842
+ token: options.token
340819
340843
  }, undefined, false, undefined, this));
340820
340844
  });
340821
340845
  program.command("codesigning:generate").description("Generate code signing keys/certificate and configure Expo config").option("-o, --organization <organization>", "Organization name for certificate issuer (O)").option("--certificate-validity-duration-years <years>", "Certificate validity in years", (value2) => Number.parseInt(value2, 10)).option("--key-id <id>", "Key ID to write into Expo config (default: main)", "main").option("-p, --project-root <path>", "Expo app root containing app.json or app.config.* (defaults to auto-detect)").option("--key-output-directory <path>", "Directory for private/public key output (default: codesigning-keys)", "codesigning-keys").option("--certificate-output-directory <path>", "Directory for certificate output (default: certs)", "certs").option("--force", "Overwrite existing codesigning-keys directory", false).action(withCommandErrorBoundary(async (options) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo-up/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",