|
20 | 20 | * - Once enabled, any upload/reconcile/manifest failure THROWS and fails the |
21 | 21 | * release step — re-running the workflow is idempotent (uploads overwrite). |
22 | 22 | * |
23 | | - * Environment variables (all injected from GitHub repo Settings — Secrets for |
24 | | - * credentials, Variables for the rest; never hardcode values in the workflow): |
| 23 | + * Environment variables (all injected from GitHub repo Settings → Secrets; |
| 24 | + * no OSS defaults are hardcoded in this repo): |
25 | 25 | * BAILIAN_OSS_AK / BAILIAN_OSS_SK —— RAM AccessKey; needs oss:PutObject and |
26 | | - * oss:GetObject on `<prefix>/*` |
27 | | - * BAILIAN_OSS_BUCKET —— target bucket, default bailian-wiki |
28 | | - * BAILIAN_OSS_REGION —— region, default oss-cn-hangzhou |
29 | | - * BAILIAN_OSS_ENDPOINT —— optional request endpoint override, |
30 | | - * e.g. oss-accelerate.aliyuncs.com; public |
31 | | - * manifest URLs always use the region endpoint |
32 | | - * BAILIAN_RELEASE_PREFIX —— object prefix, default release |
| 26 | + * oss:GetObject on the release prefix |
| 27 | + * BAILIAN_OSS_BUCKET / BAILIAN_OSS_REGION / BAILIAN_RELEASE_PREFIX |
| 28 | + * —— required once the channel is enabled |
| 29 | + * BAILIAN_OSS_ENDPOINT —— optional request endpoint override; |
| 30 | + * public manifest URLs always use the |
| 31 | + * region endpoint |
33 | 32 | */ |
34 | 33 | import { createHash, createHmac } from "node:crypto"; |
35 | 34 | import { readFileSync, statSync } from "node:fs"; |
36 | 35 | import { basename } from "node:path"; |
37 | 36 |
|
38 | | -const OSS_CONFIG = { |
39 | | - bucket: process.env.BAILIAN_OSS_BUCKET || "bailian-wiki", |
40 | | - region: process.env.BAILIAN_OSS_REGION || "oss-cn-hangzhou", |
41 | | - endpoint: process.env.BAILIAN_OSS_ENDPOINT || "", |
42 | | - prefix: process.env.BAILIAN_RELEASE_PREFIX || "release", |
43 | | -}; |
44 | | - |
45 | | -function ossCredentials() { |
| 37 | +/** |
| 38 | + * Resolve the OSS context from the environment. Returns null when the channel |
| 39 | + * is disabled (no credentials). Throws when credentials are present but the |
| 40 | + * non-credential configuration is incomplete — a misconfigured release must |
| 41 | + * fail loudly instead of uploading to a guessed location. |
| 42 | + */ |
| 43 | +function ossContext() { |
46 | 44 | const ak = process.env.BAILIAN_OSS_AK?.trim(); |
47 | 45 | const sk = process.env.BAILIAN_OSS_SK?.trim(); |
48 | | - return ak && sk ? { ak, sk } : null; |
| 46 | + if (!ak || !sk) return null; |
| 47 | + const cfg = { |
| 48 | + bucket: process.env.BAILIAN_OSS_BUCKET?.trim() || "", |
| 49 | + region: process.env.BAILIAN_OSS_REGION?.trim() || "", |
| 50 | + endpoint: process.env.BAILIAN_OSS_ENDPOINT?.trim() || "", |
| 51 | + prefix: process.env.BAILIAN_RELEASE_PREFIX?.trim() || "", |
| 52 | + }; |
| 53 | + const missing = [ |
| 54 | + ["BAILIAN_OSS_BUCKET", cfg.bucket], |
| 55 | + ["BAILIAN_OSS_REGION", cfg.region], |
| 56 | + ["BAILIAN_RELEASE_PREFIX", cfg.prefix], |
| 57 | + ] |
| 58 | + .filter(([, value]) => !value) |
| 59 | + .map(([name]) => name); |
| 60 | + if (missing.length > 0) { |
| 61 | + throw new Error(`OSS channel misconfigured; missing env: ${missing.join(", ")}`); |
| 62 | + } |
| 63 | + return { creds: { ak, sk }, cfg }; |
49 | 64 | } |
50 | 65 |
|
51 | 66 | /** Virtual-hosted-style request host: <bucket>.<endpoint-or-region>. */ |
@@ -186,20 +201,19 @@ async function getObjectJson(key, creds, cfg) { |
186 | 201 | * @returns {Promise<{ uploaded: number, skipped: boolean }>} |
187 | 202 | */ |
188 | 203 | export async function mirrorReleaseAssetsToOss({ plans, dryRun = false }) { |
189 | | - const creds = ossCredentials(); |
190 | | - const cfg = OSS_CONFIG; |
191 | | - |
192 | | - const jobs = plans.flatMap(({ tag, paths }) => |
193 | | - paths.map((path) => ({ path, key: `${cfg.prefix}/${tag}/${basename(path)}` })), |
194 | | - ); |
195 | | - if (jobs.length === 0) return { uploaded: 0, skipped: true }; |
196 | | - |
197 | | - if (!creds) { |
| 204 | + const ctx = ossContext(); |
| 205 | + if (!ctx) { |
198 | 206 | process.stdout.write( |
199 | 207 | "\n[warn] BAILIAN_OSS_AK/SK unset; skip the OSS release channel entirely\n", |
200 | 208 | ); |
201 | 209 | return { uploaded: 0, skipped: true }; |
202 | 210 | } |
| 211 | + const { creds, cfg } = ctx; |
| 212 | + |
| 213 | + const jobs = plans.flatMap(({ tag, paths }) => |
| 214 | + paths.map((path) => ({ path, key: `${cfg.prefix}/${tag}/${basename(path)}` })), |
| 215 | + ); |
| 216 | + if (jobs.length === 0) return { uploaded: 0, skipped: true }; |
203 | 217 |
|
204 | 218 | process.stdout.write( |
205 | 219 | `\n==> OSS upload: ${jobs.length} object(s) → ${cfg.bucket} (${cfg.endpoint || cfg.region})\n`, |
@@ -267,14 +281,13 @@ export async function mirrorReleaseAssetsToOss({ plans, dryRun = false }) { |
267 | 281 | * @returns {Promise<{ updated: boolean, latest: string | null }>} |
268 | 282 | */ |
269 | 283 | export async function maintainReleaseManifest({ tag, assetNames, dryRun = false }) { |
270 | | - const creds = ossCredentials(); |
271 | | - const cfg = OSS_CONFIG; |
272 | | - const key = `${cfg.prefix}/manifest.json`; |
273 | | - |
274 | | - if (!creds) { |
| 284 | + const ctx = ossContext(); |
| 285 | + if (!ctx) { |
275 | 286 | process.stdout.write("[info] BAILIAN_OSS_AK/SK unset; skip manifest.json maintenance\n"); |
276 | 287 | return { updated: false, latest: null }; |
277 | 288 | } |
| 289 | + const { creds, cfg } = ctx; |
| 290 | + const key = `${cfg.prefix}/manifest.json`; |
278 | 291 |
|
279 | 292 | if (dryRun) { |
280 | 293 | process.stdout.write( |
|
0 commit comments