@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 +31 -45
- package/dist/index.cjs +19 -14
- package/dist/index.js +19 -14
- package/package.json +2 -1
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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) —
|
|
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" ?
|
|
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
|
|
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
|
-
|
|
23243
|
-
|
|
23244
|
-
|
|
23245
|
-
|
|
23246
|
-
|
|
23247
|
-
|
|
23248
|
-
|
|
23249
|
-
|
|
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.
|
|
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" ?
|
|
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
|
|
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
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
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.
|
|
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.
|
|
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",
|