13 KiB
calculate-version
計算版本號的 Gitea Action:依儲存庫現有的 release,推算下一個穩定版或 beta 版本號,並寫出為 Action output 供後續步驟取用。以 Docker 容器執行,容器內由 Node.js 主程式實作。
更新時間:2026/06/30 17:38:51(Asia/Taipei)
專案列表
專案描述
| 專案名稱 | 專案描述 |
|---|---|
| calculate-version | 計算版本號的 Gitea Action:載入並驗證環境變數設定、分頁取得 Gitea release、解析最新穩定版並推算下一個穩定版或 beta 版本號,最後將結果寫入 Action output。 |
參考專案
| 專案名稱 | 參考專案列表 |
|---|---|
| calculate-version | 無 |
NuGet 套件
| 專案名稱 | NuGet 套件列表 |
|---|---|
| calculate-version | 無(本專案為純 Node.js,package.json 未宣告任何 npm 相依) |
功能列表
calculate-version
使用範例
config.isUnset
判斷單一環境變數值是否應視為「未設定」。下列任一情況回傳 true:undefined、null、空字串、字面字串 "null";其餘回傳 false。常用於 loadConfig 內判斷必填與選填設定。
const { isUnset } = require('./app/config');
isUnset(undefined); // true
isUnset('null'); // true
isUnset(''); // true
isUnset('abc'); // false
config.requireEnv
驗證必填環境變數;值被視為未設定時拋出 Error(訊息為 ${name} 未設定),否則原樣回傳該值。
const { requireEnv } = require('./app/config');
const url = requireEnv('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL);
// 未設定時 → throw Error('GITEA_SERVER_URL 未設定')
config.normalizeBetaFlag
將 beta 旗標環境變數正規化為布林值。未設定時預設為 false;僅當值嚴格等於字面字串 "true" 時回傳 true。
const { normalizeBetaFlag } = require('./app/config');
normalizeBetaFlag('true'); // true
normalizeBetaFlag('false'); // false
normalizeBetaFlag(undefined); // false
config.loadConfig
從環境變數載入並驗證執行所需設定。GITEA_SERVER_URL(須為合法 http/https URL)與 GITEA_REPOSITORY(須為 owner/repo 形式、排除路徑穿越)為必填;GITEA_TOKEN 選填(未設定為 null);IS_BETA 正規化為布林值。驗證失敗會拋出 Error。
const { loadConfig } = require('./app/config');
const config = loadConfig(process.env);
// → { serverUrl, repository, token, isBeta }
index.main
Action 進入點,依序執行三個階段:1. 參數檢查(loadConfig)、2. 取得舊版本(fetchReleases + latestStableVersion)、3. 計算版本號(nextVersion)並以 writeOutput 寫出。相依模組可透過 deps 注入以利測試;失敗時向外拋出例外。
const { main } = require('./app/index');
// 以預設相依(讀環境變數、實際打 API)執行
await main();
// 測試時注入替身
await main({
loadConfig: () => ({ serverUrl: 'https://gitea.example.com', repository: 'owner/repo', token: null, isBeta: false }),
fetchReleases: async () => [{ tag_name: 'v1.2.3' }],
writeOutput: () => {},
});
// → 回傳 '1.2.4'
logger.section
輸出帶標題的區塊段落至標準輸出,標題前後以分隔線包夾,用於在 log 中分隔各處理階段(例如「參數檢查」「取得舊版本」)。此為結構性段落標題,不套用 [等級][時間] 前綴。
const logger = require('./app/logger');
logger.section('參數檢查');
// 輸出:換行 + 50 個 = + 標題 + 50 個 -
logger.info
輸出一般資訊(INF)層級的 log 訊息至標準輸出,格式統一為 [INF][{台灣時間}]: {訊息}(時間為 Asia/Taipei,格式 yyyy/MM/dd HH:mm:ss),結尾換行。
const logger = require('./app/logger');
logger.info('NEW_VERSION=1.2.4');
// 輸出:[INF][2026/06/30 17:38:51]: NEW_VERSION=1.2.4
logger.error
輸出錯誤(ERR)層級的 log 訊息至標準錯誤輸出(stderr),格式統一為 [ERR][{台灣時間}]: {訊息}。僅負責輸出,不終止行程,是否結束由呼叫端決定。
const logger = require('./app/logger');
logger.error('GITEA_SERVER_URL 未設定');
// stderr 輸出:[ERR][2026/06/30 17:38:51]: GITEA_SERVER_URL 未設定
output.writeOutput
將一行 name=value 以 append 方式寫入 GitHub/Gitea Action 的輸出檔(預設取自環境變數 GITHUB_OUTPUT)。輸出檔路徑為 falsy 時拋出 Error。value 僅支援單行字串。
const { writeOutput } = require('./app/output');
writeOutput('version', '1.2.4');
// 對 process.env.GITHUB_OUTPUT 指向的檔案附加一行:version=1.2.4
releases.fetchReleases
以分頁方式(每頁 10 筆)透過全域 fetch 取得指定 Gitea repo 的所有 release,回傳合併後的陣列。有 token 時以 Authorization: token <token> 授權,否則匿名請求;遇空頁或不足一頁即停止。請求失敗、非 2xx、無法解析或非陣列時拋出 Error。
const { fetchReleases } = require('./app/releases');
const logger = require('./app/logger');
const releases = await fetchReleases(
'https://gitea.example.com/api/v1/repos/owner/repo/releases',
{ token: process.env.GITEA_TOKEN, logger },
);
version.compareVersionArrays
逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。回傳大於 0 表示 a 大於 b、小於 0 表示 a 小於 b、0 表示相等;相異區段回傳的是該段差值(非固定 ±1)。傳入 null/undefined 會在讀取 .length 時拋 TypeError。
const { compareVersionArrays } = require('./app/version');
compareVersionArrays([1, 10, 0], [1, 9, 0]); // > 0
compareVersionArrays([1, 2], [1, 2, 0]); // < 0
compareVersionArrays([1, 2, 3], [1, 2, 3]); // 0
version.parseStableVersions
從 release 清單解析出所有穩定版(排除含 -beta. 與格式不合法者)的版本號數值陣列;會去除 tag 開頭的 v。輸入非陣列時回傳空陣列。
const { parseStableVersions } = require('./app/version');
parseStableVersions([
{ tag_name: 'v1.2.3' },
{ tag_name: '2.0.0' },
{ tag_name: 'v1.3.0-beta.1' },
]);
// → [[1, 2, 3], [2, 0, 0]]
version.latestStableVersion
取得 release 清單中最新(最大)的穩定版版本號字串,固定格式化為三段 major.minor.patch;查無穩定版時回傳 "0.0.0"。
const { latestStableVersion } = require('./app/version');
latestStableVersion([{ tag_name: 'v1.9.0' }, { tag_name: 'v1.10.0' }]); // '1.10.0'
latestStableVersion([]); // '0.0.0'
version.nextReleaseVersion
依最新穩定版字串計算下一個發行版本號:patch 加 1,patch 達 10 進位至 minor,minor 達 10 進位至 major(逢 10 進位為本專案自訂約定,非標準 SemVer)。
const { nextReleaseVersion } = require('./app/version');
nextReleaseVersion('1.2.3'); // '1.2.4'
nextReleaseVersion('1.2.9'); // '1.3.0'
nextReleaseVersion('1.9.9'); // '2.0.0'
version.nextBetaNumber
計算指定版本號的下一個 beta 流水號:取現有相符 v<version>-beta.<n> 標籤的最大序號加 1,查無時回傳 1。
const { nextBetaNumber } = require('./app/version');
nextBetaNumber([
{ tag_name: 'v1.2.4-beta.1' },
{ tag_name: 'v1.2.4-beta.3' },
], '1.2.4'); // 4
nextBetaNumber([], '1.2.4'); // 1
version.nextVersion
依「已知的最新穩定版」計算本次要使用的版本號(不負責取得舊版本)。isBeta 為 false 時回傳下一個發行版;為 true 時回傳 <next>-beta.<n>。
const { nextVersion } = require('./app/version');
nextVersion('1.2.3', [], false); // '1.2.4'
nextVersion('1.2.3', [{ tag_name: 'v1.2.4-beta.2' }], true); // '1.2.4-beta.3'
version.calculateVersion
計算最新穩定版與下一個版本號;組合 latestStableVersion 與 nextVersion。回傳物件含 latest(現有最新穩定版,查無為 "0.0.0")與 version(本次版本號)。
const { calculateVersion } = require('./app/version');
calculateVersion([{ tag_name: 'v1.2.3' }], false);
// → { latest: '1.2.3', version: '1.2.4' }
calculateVersion([{ tag_name: 'v1.2.3' }, { tag_name: 'v1.2.4-beta.2' }], true);
// → { latest: '1.2.3', version: '1.2.4-beta.3' }