Files
calculate-version/app/config.js
T
JefferyandClaude Opus 4.8 d8a86e90ea refactor(calculate-version): 將驗證常數抽離為具名常數
- config 將每段長度上限與允許字元抽為 MAX_REPO_NAME_LENGTH / REPO_SEGMENT_PATTERN(regex 提升至模組層級,避免每次重編譯)
- releases 將片段截斷長度抽為 API_ERROR_SNIPPET_LENGTH

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:11:36 +08:00

97 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* 判斷環境變數值是否視為「未設定」。
*
* 下列任一情況皆視為未設定:undefined、null、空字串、字面字串 "null"。
*
* @param {*} value - 欲檢查的值(通常為環境變數字串)。
* @returns {boolean} 視為未設定時回傳 true,否則回傳 false。
*/
function isUnset(value) {
return value === undefined || value === null || value === '' || value === 'null';
}
/**
* 驗證必填環境變數;未設定時拋出錯誤。
*
* @param {string} name - 環境變數名稱,用於組出錯誤訊息。
* @param {*} value - 環境變數的值。
* @returns {*} 驗證通過後原樣回傳的 value。
* @throws {Error} 當 value 被視為未設定(undefined/null/空字串/"null")時拋出,訊息為 `${name} 未設定`。
*/
function requireEnv(name, value) {
if (isUnset(value)) {
throw new Error(`${name} 未設定`);
}
return value;
}
/**
* 將 beta 旗標正規化為布林值。
*
* 未設定時預設為 false;僅當值嚴格等於字面字串 "true" 時回傳 true。
*
* @param {*} value - beta 旗標環境變數的值。
* @returns {boolean} 啟用 beta 時回傳 true,否則回傳 false。
*/
function normalizeBetaFlag(value) {
if (isUnset(value)) {
return false;
}
return value === 'true';
}
// 驗證字串為合法的 http/https URL,否則拋出錯誤(避免指向非預期協定或格式錯誤的位址)
function assertHttpUrl(name, value) {
let parsed;
try {
parsed = new URL(value);
} catch {
throw new Error(`${name} 格式錯誤,必須為合法的 URL`);
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
throw new Error(`${name} 必須使用 http 或 https 協定`);
}
}
// repository 每段名稱的最大長度與允許字元(字母數字與 . _ -)
const MAX_REPO_NAME_LENGTH = 100;
const REPO_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
// 驗證 repository 為 owner/repo 格式(拒絕 . 與 .. 路徑穿越段、空段、過長或含非法字元的段)
function assertRepository(name, value) {
const parts = value.split('/');
const isRepoFormatValid = parts.length === 2
&& parts.every((part) => part.length > 0 && part.length <= MAX_REPO_NAME_LENGTH
&& REPO_SEGMENT_PATTERN.test(part)
&& part !== '.' && part !== '..');
if (!isRepoFormatValid) {
throw new Error(`${name} 格式錯誤,必須為 owner/repo`);
}
}
/**
* 從環境變數載入並驗證執行所需的設定。
*
* GITEA_SERVER_URL 與 GITEA_REPOSITORY 為必填,未設定時會拋出錯誤;GITEA_SERVER_URL
* 另需為合法的 http/https URLGITEA_REPOSITORY 另需為 owner/repo 格式;
* GITEA_TOKEN 為非必填,未設定時為 null;IS_BETA 會被正規化為布林值。
*
* @param {Object} [env=process.env] - 環境變數來源物件,預設為 process.env。
* @returns {{ serverUrl: string, repository: string, token: (string|null), isBeta: boolean }} 已驗證的設定物件。
* @throws {Error} 當必填項未設定、GITEA_SERVER_URL 非合法 http/https URL,或 GITEA_REPOSITORY 非 owner/repo 格式時拋出。
*/
function loadConfig(env = process.env) {
const serverUrl = requireEnv('GITEA_SERVER_URL', env.GITEA_SERVER_URL);
assertHttpUrl('GITEA_SERVER_URL', serverUrl);
const repository = requireEnv('GITEA_REPOSITORY', env.GITEA_REPOSITORY);
assertRepository('GITEA_REPOSITORY', repository);
const token = isUnset(env.GITEA_TOKEN) ? null : env.GITEA_TOKEN;
const isBeta = normalizeBetaFlag(env.IS_BETA);
return { serverUrl, repository, token, isBeta };
}
module.exports = { isUnset, requireEnv, normalizeBetaFlag, loadConfig };