@freecodecamp/universe-cli 0.5.0 → 0.5.1

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/README.md CHANGED
@@ -22,27 +22,27 @@ universe <command>
22
22
  Download the latest binary from [Releases](../../releases):
23
23
  </summary>
24
24
 
25
- ```sh
26
- # macOS (Apple Silicon)
27
- gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-darwin-arm64"
28
- chmod +x universe-darwin-arm64
29
- sudo mv universe-darwin-arm64 /usr/local/bin/universe
30
-
31
- # macOS (Intel)
32
- gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-darwin-amd64"
33
- chmod +x universe-darwin-amd64
34
- sudo mv universe-darwin-amd64 /usr/local/bin/universe
35
-
36
- # Linux x64
37
- gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-linux-amd64"
38
- chmod +x universe-linux-amd64
39
- sudo mv universe-linux-amd64 /usr/local/bin/universe
40
-
41
- # Linux ARM64
42
- gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-linux-arm64"
43
- chmod +x universe-linux-arm64
44
- sudo mv universe-linux-arm64 /usr/local/bin/universe
45
- ```
25
+ ```sh
26
+ # macOS (Apple Silicon)
27
+ gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-darwin-arm64"
28
+ chmod +x universe-darwin-arm64
29
+ sudo mv universe-darwin-arm64 /usr/local/bin/universe
30
+
31
+ # macOS (Intel)
32
+ gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-darwin-amd64"
33
+ chmod +x universe-darwin-amd64
34
+ sudo mv universe-darwin-amd64 /usr/local/bin/universe
35
+
36
+ # Linux x64
37
+ gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-linux-amd64"
38
+ chmod +x universe-linux-amd64
39
+ sudo mv universe-linux-amd64 /usr/local/bin/universe
40
+
41
+ # Linux ARM64
42
+ gh release download --repo freeCodeCamp-Universe/universe-cli --pattern "universe-linux-arm64"
43
+ chmod +x universe-linux-arm64
44
+ sudo mv universe-linux-arm64 /usr/local/bin/universe
45
+ ```
46
46
 
47
47
  </details>
48
48
 
@@ -88,16 +88,12 @@ All commands support `--json` for CI integration.
88
88
  The CLI resolves a GitHub identity in this order — first match wins:
89
89
 
90
90
  1. `$GITHUB_TOKEN` / `$GH_TOKEN` env (CI explicit)
91
- 2. GHA OIDC (`$ACTIONS_ID_TOKEN_REQUEST_URL` + `$ACTIONS_ID_TOKEN_REQUEST_TOKEN`)
92
- 3. Woodpecker OIDC env (deferred — placeholder slot)
93
- 4. `gh auth token` shell-out (laptop with `gh` installed)
94
- 5. Device-flow stored token at `~/.config/universe-cli/token`
91
+ 1. `gh auth token` shell-out (laptop with `gh` installed)
92
+ 1. Device-flow stored token at `~/.config/universe-cli/token`
93
+
94
+ GHA OIDC and Woodpecker OIDC slots were dropped in v0.4: artemis validates bearers via GitHub `GET /user`, which only accepts user-scoped tokens. CI runners must export `$GITHUB_TOKEN` explicitly. The slots will return once artemis grows an OIDC verifier.
95
95
 
96
- The proxy validates whatever it receives via GitHub `GET /user`, then
97
- authorizes against the registry server-side (Valkey-backed; the
98
- `sites_yaml` backend was retired in artemis `f115198`). Run
99
- `universe whoami` to see which slot resolved; inspect the sites you
100
- can deploy to with `universe sites ls --mine`.
96
+ The proxy validates whatever it receives via GitHub `GET /user`, then authorizes against the registry server-side (Valkey-backed; the `sites_yaml` backend was retired in artemis `f115198`). Run `universe whoami` to see which slot resolved; inspect the sites you can deploy to with `universe sites ls --mine`.
101
97
 
102
98
  ## Configuration (`platform.yaml`)
103
99
 
@@ -107,11 +103,9 @@ Every site has a `platform.yaml` at its repo root. Minimal valid file:
107
103
  site: my-site
108
104
  ```
109
105
 
110
- Full schema reference (every field, defaults, validation rules):
111
- [`docs/platform-yaml.md`](docs/platform-yaml.md).
106
+ Full schema reference (every field, defaults, validation rules): [`docs/platform-yaml.md`](docs/platform-yaml.md).
112
107
 
113
- No credential fields. The proxy holds the R2 admin key; the CLI never
114
- reads or writes one.
108
+ No credential fields. The proxy holds the R2 admin key; the CLI never reads or writes one.
115
109
 
116
110
  ## Common flows
117
111
 
@@ -140,8 +134,7 @@ universe static promote
140
134
  universe static rollback --to 20260427-141522-abc1234
141
135
  ```
142
136
 
143
- Registry admin (staff team only — gated server-side by artemis's
144
- `REGISTRY_AUTHZ_TEAM`, default `staff`):
137
+ Registry admin (staff team only — gated server-side by artemis's `REGISTRY_AUTHZ_TEAM`, default `staff`):
145
138
 
146
139
  ```sh
147
140
  # Register a new site (defaults --team to staff)
@@ -154,10 +147,7 @@ universe sites update hello-universe --team bots,staff,curriculum
154
147
  universe sites rm hello-universe
155
148
  ```
156
149
 
157
- CI (GitHub Actions) — set `permissions: id-token: write` and rely on
158
- slot 2 (GHA OIDC) of the identity chain, or pass `$GITHUB_TOKEN`
159
- explicitly. Full operator walkthrough:
160
- [`docs/STAFF-GUIDE.md`](docs/STAFF-GUIDE.md).
150
+ CI (GitHub Actions) — pass `$GITHUB_TOKEN` explicitly (slot 1). The OIDC slot will return once artemis grows an OIDC verifier. Full operator walkthrough: [`docs/STAFF-GUIDE.md`](docs/STAFF-GUIDE.md).
161
151
 
162
152
  ## Environment overrides
163
153
 
@@ -167,8 +157,4 @@ explicitly. Full operator walkthrough:
167
157
  | `UNIVERSE_GH_CLIENT_ID` | _baked-in freeCodeCamp OAuth id_ | Override GitHub OAuth App id (fork tenants, `login` only) |
168
158
  | `GITHUB_TOKEN` / `GH_TOKEN` | — | Slot 1 of identity chain |
169
159
 
170
- The shipped binary embeds the `freeCodeCamp` GitHub OAuth App client id
171
- (public; device flow uses no `client_secret`), so `universe login` works
172
- out of the box for staff. Fork operators and self-hosted mirror tenants
173
- set `UNIVERSE_GH_CLIENT_ID` to their own OAuth App's id — env value
174
- wins when set.
160
+ The shipped binary embeds the `freeCodeCamp` GitHub OAuth App client id (public; device flow uses no `client_secret`), so `universe login` works out of the box for staff. Fork operators and self-hosted mirror tenants set `UNIVERSE_GH_CLIENT_ID` to their own OAuth App's id — env value wins when set.
package/dist/index.cjs CHANGED
@@ -22777,7 +22777,7 @@ async function deploy(options, deps = {}) {
22777
22777
  );
22778
22778
  }
22779
22779
  const git = gitState();
22780
- if (git.dirty) {
22780
+ if (git.dirty && !options.json) {
22781
22781
  warn(
22782
22782
  "git working tree is dirty \u2014 uncommitted changes will not be reflected."
22783
22783
  );
@@ -22789,7 +22789,7 @@ async function deploy(options, deps = {}) {
22789
22789
  cwd,
22790
22790
  outputDir
22791
22791
  });
22792
- if (buildResult.skipped) {
22792
+ if (buildResult.skipped && !options.json) {
22793
22793
  info("build.command not set \u2014 using pre-built output.");
22794
22794
  }
22795
22795
  const resolvedOutputDir = buildResult.outputDir;
@@ -22848,7 +22848,7 @@ async function deploy(options, deps = {}) {
22848
22848
  );
22849
22849
  } else {
22850
22850
  const sizeKB = (uploadResult.totalSize / 1024).toFixed(1);
22851
- const nextLine = mode === "preview" ? "Next: universe static promote" : "Promoted to production.";
22851
+ const nextLine = mode === "preview" ? `Next: universe static promote --from ${finalizeResult.deployId}` : "Promoted to production.\nPreview alias unchanged.";
22852
22852
  success2(
22853
22853
  [
22854
22854
  `Deployed ${finalizeResult.deployId}`,
@@ -23146,7 +23146,10 @@ async function ls(options, deps = {}) {
23146
23146
  getAuthToken: () => identity.token
23147
23147
  });
23148
23148
  const raw = await client.siteDeploys({ site });
23149
- const deploys = raw.map((d2) => parseDeployId(d2.deployId));
23149
+ const sorted = [...raw].sort(
23150
+ (a, b) => b.deployId.localeCompare(a.deployId)
23151
+ );
23152
+ const deploys = sorted.map((d2) => parseDeployId(d2.deployId));
23150
23153
  if (options.json) {
23151
23154
  emitJson4(
23152
23155
  buildEnvelope("ls", true, {
@@ -23239,15 +23242,17 @@ async function promote(options, deps = {}) {
23239
23242
  })
23240
23243
  );
23241
23244
  } else {
23242
- success2(
23243
- [
23244
- `Promoted ${result.deployId} to production`,
23245
- ``,
23246
- ` Site: ${config2.site}`,
23247
- ` Deploy: ${result.deployId}`,
23248
- ` Production: ${result.url}`
23249
- ].join("\n")
23250
- );
23245
+ const lines = [
23246
+ `Promoted ${result.deployId} to production`,
23247
+ ``,
23248
+ ` Site: ${config2.site}`,
23249
+ ` Deploy: ${result.deployId}`,
23250
+ ` Production: ${result.url}`
23251
+ ];
23252
+ if (options.from) {
23253
+ lines.push(``, "Preview alias unchanged.");
23254
+ }
23255
+ success2(lines.join("\n"));
23251
23256
  }
23252
23257
  } catch (err) {
23253
23258
  const { code, message } = wrapProxyError("promote", err);
@@ -23689,7 +23694,7 @@ function outputError(ctx, code, message, issues) {
23689
23694
  }
23690
23695
 
23691
23696
  // src/cli.ts
23692
- var version2 = true ? "0.5.0" : "0.0.0";
23697
+ var version2 = true ? "0.5.1" : "0.0.0";
23693
23698
  function handleActionError(command, json2, err) {
23694
23699
  const ctx = { json: json2, command };
23695
23700
  const message = err instanceof Error ? err.message : "unknown error";
package/dist/index.js CHANGED
@@ -920,7 +920,7 @@ async function deploy(options, deps = {}) {
920
920
  );
921
921
  }
922
922
  const git = gitState();
923
- if (git.dirty) {
923
+ if (git.dirty && !options.json) {
924
924
  warn(
925
925
  "git working tree is dirty \u2014 uncommitted changes will not be reflected."
926
926
  );
@@ -932,7 +932,7 @@ async function deploy(options, deps = {}) {
932
932
  cwd,
933
933
  outputDir
934
934
  });
935
- if (buildResult.skipped) {
935
+ if (buildResult.skipped && !options.json) {
936
936
  info("build.command not set \u2014 using pre-built output.");
937
937
  }
938
938
  const resolvedOutputDir = buildResult.outputDir;
@@ -991,7 +991,7 @@ async function deploy(options, deps = {}) {
991
991
  );
992
992
  } else {
993
993
  const sizeKB = (uploadResult.totalSize / 1024).toFixed(1);
994
- const nextLine = mode === "preview" ? "Next: universe static promote" : "Promoted to production.";
994
+ const nextLine = mode === "preview" ? `Next: universe static promote --from ${finalizeResult.deployId}` : "Promoted to production.\nPreview alias unchanged.";
995
995
  success(
996
996
  [
997
997
  `Deployed ${finalizeResult.deployId}`,
@@ -1294,7 +1294,10 @@ async function ls(options, deps = {}) {
1294
1294
  getAuthToken: () => identity.token
1295
1295
  });
1296
1296
  const raw = await client.siteDeploys({ site });
1297
- const deploys = raw.map((d) => parseDeployId(d.deployId));
1297
+ const sorted = [...raw].sort(
1298
+ (a, b) => b.deployId.localeCompare(a.deployId)
1299
+ );
1300
+ const deploys = sorted.map((d) => parseDeployId(d.deployId));
1298
1301
  if (options.json) {
1299
1302
  emitJson4(
1300
1303
  buildEnvelope("ls", true, {
@@ -1388,15 +1391,17 @@ async function promote(options, deps = {}) {
1388
1391
  })
1389
1392
  );
1390
1393
  } else {
1391
- success(
1392
- [
1393
- `Promoted ${result.deployId} to production`,
1394
- ``,
1395
- ` Site: ${config.site}`,
1396
- ` Deploy: ${result.deployId}`,
1397
- ` Production: ${result.url}`
1398
- ].join("\n")
1399
- );
1394
+ const lines = [
1395
+ `Promoted ${result.deployId} to production`,
1396
+ ``,
1397
+ ` Site: ${config.site}`,
1398
+ ` Deploy: ${result.deployId}`,
1399
+ ` Production: ${result.url}`
1400
+ ];
1401
+ if (options.from) {
1402
+ lines.push(``, "Preview alias unchanged.");
1403
+ }
1404
+ success(lines.join("\n"));
1400
1405
  }
1401
1406
  } catch (err) {
1402
1407
  const { code, message } = wrapProxyError("promote", err);
@@ -1849,7 +1854,7 @@ function outputError(ctx, code, message, issues) {
1849
1854
  }
1850
1855
 
1851
1856
  // src/cli.ts
1852
- var version = true ? "0.5.0" : "0.0.0";
1857
+ var version = true ? "0.5.1" : "0.0.0";
1853
1858
  function handleActionError(command, json, err) {
1854
1859
  const ctx = { json, command };
1855
1860
  const message = err instanceof Error ? err.message : "unknown error";
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@freecodecamp/universe-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "test": "vitest run",
7
+ "test:smoke": "UNIVERSE_E2E_REAL=1 vitest run tests/e2e/smoke-real-artemis.test.ts",
7
8
  "build": "tsup",
8
9
  "lint": "oxlint src tests",
9
10
  "typecheck": "tsc --noEmit",