@freecodecamp/universe-cli 0.2.0 → 0.3.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.cjs +14896 -5077
- package/dist/index.js +52 -8
- package/package.json +21 -5
package/dist/index.js
CHANGED
|
@@ -53,7 +53,7 @@ var staticSchema = z.object({
|
|
|
53
53
|
bucket: z.string().min(1).default("gxy-static-1"),
|
|
54
54
|
rclone_remote: z.string().min(1).default("gxy-static"),
|
|
55
55
|
region: z.string().min(1).default("auto")
|
|
56
|
-
}).
|
|
56
|
+
}).prefault({});
|
|
57
57
|
var domainSchema = z.object({
|
|
58
58
|
production: z.string().min(1),
|
|
59
59
|
preview: z.string().min(1)
|
|
@@ -125,7 +125,19 @@ function loadConfig(options = {}) {
|
|
|
125
125
|
throw err;
|
|
126
126
|
}
|
|
127
127
|
const parsed = parseYaml(raw);
|
|
128
|
-
const
|
|
128
|
+
const parseResult = platformSchema.safeParse(parsed);
|
|
129
|
+
if (!parseResult.success) {
|
|
130
|
+
const issues = parseResult.error.issues.map((issue) => {
|
|
131
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
132
|
+
return ` - ${path}: ${issue.message}`;
|
|
133
|
+
}).join("\n");
|
|
134
|
+
throw new ConfigError(
|
|
135
|
+
`platform.yaml is invalid:
|
|
136
|
+
${issues}
|
|
137
|
+
See STAFF-GUIDE.md for the required format.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const yamlValidated = parseResult.data;
|
|
129
141
|
const envOverrides = readEnvOverrides();
|
|
130
142
|
const flagOverrides = readFlagOverrides(options.flags);
|
|
131
143
|
const merged = {
|
|
@@ -144,11 +156,13 @@ function loadConfig(options = {}) {
|
|
|
144
156
|
import { execSync } from "child_process";
|
|
145
157
|
|
|
146
158
|
// src/output/redact.ts
|
|
147
|
-
var
|
|
159
|
+
var AWS_KEY_PREFIX_PATTERN = /(?:AKIA|ASIA|AROA|AIDA|ACCA|ANPA|ABIA|AGPA)[A-Z0-9]{12,}/g;
|
|
148
160
|
var URL_CREDS_PATTERN = /https?:\/\/[^@\s]+@/g;
|
|
149
|
-
var CREDENTIAL_CONTEXT_PATTERN = /(?:secret|password|token|key|credential|auth)[=:]\s*([A-Za-z0-9/+=]{21,})/gi;
|
|
150
|
-
var HEX_CREDENTIAL_CONTEXT_PATTERN = /(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key)[=:]\s*([a-f0-9]{32,})/gi;
|
|
151
|
-
|
|
161
|
+
var CREDENTIAL_CONTEXT_PATTERN = /(?:access_key_id|secret_access_key|accessKeyId|secretAccessKey|secret|password|token|key|credential|auth)\s*[=:]\s*([A-Za-z0-9/+=]{21,})/gi;
|
|
162
|
+
var HEX_CREDENTIAL_CONTEXT_PATTERN = /(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key)\s*[=:]\s*([a-f0-9]{32,})/gi;
|
|
163
|
+
var JSON_CREDENTIAL_PATTERN = /"(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key|accessKeyId|secretAccessKey)"\s*:\s*"[^"]+"/gi;
|
|
164
|
+
var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
165
|
+
function maskAwsKey(match) {
|
|
152
166
|
return match.slice(0, 4) + "****" + match.slice(-4);
|
|
153
167
|
}
|
|
154
168
|
function maskUrlCreds(match) {
|
|
@@ -159,7 +173,12 @@ function maskUrlCreds(match) {
|
|
|
159
173
|
function redact(value) {
|
|
160
174
|
let result = value;
|
|
161
175
|
result = result.replace(URL_CREDS_PATTERN, maskUrlCreds);
|
|
162
|
-
result = result.replace(
|
|
176
|
+
result = result.replace(AWS_KEY_PREFIX_PATTERN, maskAwsKey);
|
|
177
|
+
result = result.replace(BEARER_PATTERN, "Bearer ****");
|
|
178
|
+
result = result.replace(JSON_CREDENTIAL_PATTERN, (match) => {
|
|
179
|
+
const colonIndex = match.indexOf(":");
|
|
180
|
+
return match.slice(0, colonIndex + 1) + '"****"';
|
|
181
|
+
});
|
|
163
182
|
result = result.replace(
|
|
164
183
|
CREDENTIAL_CONTEXT_PATTERN,
|
|
165
184
|
(_match, _secret, _offset, _full) => {
|
|
@@ -181,6 +200,30 @@ function redact(value) {
|
|
|
181
200
|
}
|
|
182
201
|
|
|
183
202
|
// src/credentials/resolver.ts
|
|
203
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
204
|
+
function validateEndpoint(endpoint) {
|
|
205
|
+
let url;
|
|
206
|
+
try {
|
|
207
|
+
url = new URL(endpoint);
|
|
208
|
+
} catch {
|
|
209
|
+
throw new CredentialError(`S3_ENDPOINT is not a valid URL: ${endpoint}`);
|
|
210
|
+
}
|
|
211
|
+
if (url.username !== "" || url.password !== "") {
|
|
212
|
+
throw new CredentialError(
|
|
213
|
+
"S3_ENDPOINT must not contain credentials in the URL (user:pass@host). Use S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY instead."
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
217
|
+
throw new CredentialError(
|
|
218
|
+
`S3_ENDPOINT must use http or https, got: ${url.protocol}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
if (url.protocol === "http:" && !LOCAL_HOSTS.has(url.hostname)) {
|
|
222
|
+
throw new CredentialError(
|
|
223
|
+
`S3_ENDPOINT must use https for non-localhost hosts. Plaintext http is only allowed for localhost/127.0.0.1. Got: ${endpoint}`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
184
227
|
function tryEnvCredentials() {
|
|
185
228
|
const key = process.env.S3_ACCESS_KEY_ID;
|
|
186
229
|
const secret = process.env.S3_SECRET_ACCESS_KEY;
|
|
@@ -189,6 +232,7 @@ function tryEnvCredentials() {
|
|
|
189
232
|
const present = [key, secret, endpoint].filter(Boolean);
|
|
190
233
|
if (present.length === 0) return "none";
|
|
191
234
|
if (present.length < 3) return "partial";
|
|
235
|
+
validateEndpoint(endpoint);
|
|
192
236
|
const creds = {
|
|
193
237
|
accessKeyId: key,
|
|
194
238
|
secretAccessKey: secret,
|
|
@@ -872,7 +916,7 @@ async function rollback(options) {
|
|
|
872
916
|
}
|
|
873
917
|
|
|
874
918
|
// src/cli.ts
|
|
875
|
-
var version = true ? "0.
|
|
919
|
+
var version = true ? "0.3.0" : "0.0.0";
|
|
876
920
|
function handleActionError(command, json, err) {
|
|
877
921
|
const ctx = { json, command };
|
|
878
922
|
const message = err instanceof Error ? err.message : "unknown error";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freecodecamp/universe-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest run",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@aws-sdk/client-s3": "^3.1029.0",
|
|
29
29
|
"@clack/prompts": "^1.2.0",
|
|
30
|
-
"cac": "^
|
|
30
|
+
"cac": "^7.0.0",
|
|
31
31
|
"mrmime": "^2.0.1",
|
|
32
|
-
"p-limit": "^
|
|
32
|
+
"p-limit": "^7.3.0",
|
|
33
33
|
"yaml": "^2.8.3",
|
|
34
|
-
"zod": "^3.
|
|
34
|
+
"zod": "^4.3.6"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@smithy/util-stream": "^4.5.22",
|
|
@@ -43,5 +43,21 @@
|
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"vitest": "^3.2.4"
|
|
45
45
|
},
|
|
46
|
-
"packageManager": "pnpm@10.
|
|
46
|
+
"packageManager": "pnpm@10.33.0",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22.11.0"
|
|
49
|
+
},
|
|
50
|
+
"description": "Static site deployment CLI for the freeCodeCamp Universe platform",
|
|
51
|
+
"homepage": "https://github.com/freeCodeCamp-Universe/universe-cli#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/freeCodeCamp-Universe/universe-cli/issues"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"cli",
|
|
57
|
+
"s3",
|
|
58
|
+
"cloudflare-r2",
|
|
59
|
+
"static-site",
|
|
60
|
+
"deployment",
|
|
61
|
+
"freecodecamp"
|
|
62
|
+
]
|
|
47
63
|
}
|