Files
Jeffery 4b450ae2d8
CI / Release Tag Version (pull_request) Successful in 4s
CI / Calculate Version (pull_request) Successful in 21s
docs(README): 同步 logger 功能列表與使用範例(移除 section、新增 forStage)
2026-06-30 18:22:27 +08:00

286 lines
13 KiB
Markdown
Raw Permalink 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.
# calculate-version
計算版本號的 Gitea Action:依儲存庫現有的 release,推算下一個穩定版或 beta 版本號,並寫出為 Action output 供後續步驟取用。以 Docker 容器執行,容器內由 Node.js 主程式實作。
> 更新時間:2026/06/30 18:18:13Asia/Taipei
## 專案列表
### 專案描述
| 專案名稱 | 專案描述 |
| --- | --- |
| [calculate-version](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app) | 計算版本號的 Gitea Action:載入並驗證環境變數設定、分頁取得 Gitea release、解析最新穩定版並推算下一個穩定版或 beta 版本號,最後將結果寫入 Action output。 |
### 參考專案
| 專案名稱 | 參考專案列表 |
| --- | --- |
| [calculate-version](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app) | 無 |
### NuGet 套件
| 專案名稱 | NuGet 套件列表 |
| --- | --- |
| [calculate-version](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app) | 無(本專案為純 Node.jspackage.json 未宣告任何 npm 相依) |
## 功能列表
### calculate-version
| 功能名稱 | 功能描述 |
| --- | --- |
| [config.isUnset](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L11) | [判斷環境變數值是否視為「未設定」(undefinednull/空字串/字面 "null")。](#configisunset) |
| [config.requireEnv](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L23) | [驗證必填環境變數,未設定時拋出錯誤。](#configrequireenv) |
| [config.normalizeBetaFlag](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L38) | [將 beta 旗標正規化為布林值(僅字面 "true" 為真)。](#confignormalizebetaflag) |
| [config.loadConfig](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L101) | [從環境變數載入並驗證執行所需的設定。](#configloadconfig) |
| [index.main](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/index.js#L30) | [Action 進入點:依序執行參數檢查、取得舊版本、計算版本號並寫出 output。](#indexmain) |
| [logger.info](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L58) | [輸出 `[階段][INF][時間]` 格式的資訊訊息至標準輸出(階段選填)。](#loggerinfo) |
| [logger.error](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L76) | [輸出 `[階段][ERR][時間]` 格式的錯誤訊息至標準錯誤輸出(階段選填)。](#loggererror) |
| [logger.forStage](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L89) | [建立綁定特定階段的子記錄器,自動為每則訊息帶入階段前綴。](#loggerforstage) |
| [output.writeOutput](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/output.js#L16) | [將一行 `name=value` 附加寫入 Action 的輸出檔。](#outputwriteoutput) |
| [releases.fetchReleases](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/releases.js#L24) | [以分頁方式取得指定 Gitea repo 的所有 release。](#releasesfetchreleases) |
| [version.compareVersionArrays](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L15) | [逐區段比較兩個版本號數值陣列,較短者視為較小。](#versioncompareversionarrays) |
| [version.parseStableVersions](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L39) | [從 release 清單解析出所有穩定版的版本號數值陣列。](#versionparsestableversions) |
| [version.latestStableVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L58) | [取得最新(最大)的穩定版版本號字串。](#versionlateststableversion) |
| [version.nextReleaseVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L79) | [依最新穩定版計算下一個發行版本號(逢 10 進位)。](#versionnextreleaseversion) |
| [version.nextBetaNumber](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L102) | [計算指定版本號的下一個 beta 流水號。](#versionnextbetanumber) |
| [version.nextVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L131) | [依已知的最新穩定版計算本次要使用的版本號(含 beta)。](#versionnextversion) |
| [version.calculateVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L147) | [計算最新穩定版與下一個版本號。](#versioncalculateversion) |
## 使用範例
<a id="configisunset"></a>
### config.isUnset
判斷單一環境變數值是否應視為「未設定」。下列任一情況回傳 `true``undefined``null`、空字串、字面字串 `"null"`;其餘回傳 `false`。常用於 `loadConfig` 內判斷必填與選填設定。
```js
const { isUnset } = require('./app/config');
isUnset(undefined); // true
isUnset('null'); // true
isUnset(''); // true
isUnset('abc'); // false
```
<a id="configrequireenv"></a>
### config.requireEnv
驗證必填環境變數;值被視為未設定時拋出 `Error`(訊息為 `${name} 未設定`),否則原樣回傳該值。
```js
const { requireEnv } = require('./app/config');
const url = requireEnv('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL);
// 未設定時 → throw Error('GITEA_SERVER_URL 未設定')
```
<a id="confignormalizebetaflag"></a>
### config.normalizeBetaFlag
將 beta 旗標環境變數正規化為布林值。未設定時預設為 `false`;僅當值嚴格等於字面字串 `"true"` 時回傳 `true`
```js
const { normalizeBetaFlag } = require('./app/config');
normalizeBetaFlag('true'); // true
normalizeBetaFlag('false'); // false
normalizeBetaFlag(undefined); // false
```
<a id="configloadconfig"></a>
### config.loadConfig
從環境變數載入並驗證執行所需設定。`GITEA_SERVER_URL`(須為合法 http/https URL)與 `GITEA_REPOSITORY`(須為 `owner/repo` 形式、排除路徑穿越)為必填;`GITEA_TOKEN` 選填(未設定為 `null`);`IS_BETA` 正規化為布林值。驗證失敗會拋出 `Error`
```js
const { loadConfig } = require('./app/config');
const config = loadConfig(process.env);
// → { serverUrl, repository, token, isBeta }
```
<a id="indexmain"></a>
### index.main
Action 進入點,依序執行三個階段:1. 參數檢查(`loadConfig`)、2. 取得舊版本(`fetchReleases` + `latestStableVersion`)、3. 計算版本號(`nextVersion`)並以 `writeOutput` 寫出。相依模組可透過 `deps` 注入以利測試;失敗時向外拋出例外。
```js
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'
```
<a id="loggerinfo"></a>
### logger.info
輸出一般資訊(INF)層級的 log 訊息至標準輸出,格式統一為 `[{階段}][INF][{台灣時間}]: {訊息}`(時間為 Asia/Taipei,格式 `yyyy/MM/dd HH:mm:ss`),結尾換行。階段(`stage`)為選填,省略時略過 `[{階段}]` 區塊。
```js
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.info('NEW_VERSION=1.2.4', '計算版本號');
// 輸出:[計算版本號][INF][2026/06/30 17:38:51]: NEW_VERSION=1.2.4
```
<a id="loggererror"></a>
### logger.error
輸出錯誤(ERR)層級的 log 訊息至標準錯誤輸出(stderr),格式統一為 `[{階段}][ERR][{台灣時間}]: {訊息}`。階段為選填。僅負責輸出,不終止行程,是否結束由呼叫端決定。
```js
const logger = require('./app/logger');
logger.error('GITEA_SERVER_URL 未設定');
// stderr 輸出:[ERR][2026/06/30 17:38:51]: GITEA_SERVER_URL 未設定
```
<a id="loggerforstage"></a>
### logger.forStage
建立綁定特定階段(stage)的子記錄器,回傳 `{ info, error }`;其方法只需傳入 `message`,即會自動在每則訊息開頭帶入 `[{階段}]` 前綴。進入點以此為每個處理階段建立子記錄器,使該階段(含其呼叫的 `fetchReleases` 等模組)輸出的訊息都帶一致的階段前綴。
```js
const logger = require('./app/logger');
const stageLog = logger.forStage('取得舊版本');
stageLog.info('使用授權 token 取得 release');
// 輸出:[取得舊版本][INF][2026/06/30 17:38:51]: 使用授權 token 取得 release
```
<a id="outputwriteoutput"></a>
### output.writeOutput
將一行 `name=value` 以 append 方式寫入 GitHub/Gitea Action 的輸出檔(預設取自環境變數 `GITHUB_OUTPUT`)。輸出檔路徑為 falsy 時拋出 `Error`。value 僅支援單行字串。
```js
const { writeOutput } = require('./app/output');
writeOutput('version', '1.2.4');
// 對 process.env.GITHUB_OUTPUT 指向的檔案附加一行:version=1.2.4
```
<a id="releasesfetchreleases"></a>
### releases.fetchReleases
以分頁方式(每頁 10 筆)透過全域 `fetch` 取得指定 Gitea repo 的所有 release,回傳合併後的陣列。有 token 時以 `Authorization: token <token>` 授權,否則匿名請求;遇空頁或不足一頁即停止。請求失敗、非 2xx、無法解析或非陣列時拋出 `Error`
```js
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 },
);
```
<a id="versioncompareversionarrays"></a>
### version.compareVersionArrays
逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。回傳大於 0 表示 `a` 大於 `b`、小於 0 表示 `a` 小於 `b`、0 表示相等;相異區段回傳的是該段差值(非固定 ±1)。傳入 `null``undefined` 會在讀取 `.length` 時拋 `TypeError`
```js
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
```
<a id="versionparsestableversions"></a>
### version.parseStableVersions
從 release 清單解析出所有穩定版(排除含 `-beta.` 與格式不合法者)的版本號數值陣列;會去除 tag 開頭的 `v`。輸入非陣列時回傳空陣列。
```js
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]]
```
<a id="versionlateststableversion"></a>
### version.latestStableVersion
取得 release 清單中最新(最大)的穩定版版本號字串,固定格式化為三段 `major.minor.patch`;查無穩定版時回傳 `"0.0.0"`
```js
const { latestStableVersion } = require('./app/version');
latestStableVersion([{ tag_name: 'v1.9.0' }, { tag_name: 'v1.10.0' }]); // '1.10.0'
latestStableVersion([]); // '0.0.0'
```
<a id="versionnextreleaseversion"></a>
### version.nextReleaseVersion
依最新穩定版字串計算下一個發行版本號:patch 加 1,patch 達 10 進位至 minorminor 達 10 進位至 major(逢 10 進位為本專案自訂約定,非標準 SemVer)。
```js
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'
```
<a id="versionnextbetanumber"></a>
### version.nextBetaNumber
計算指定版本號的下一個 beta 流水號:取現有相符 `v<version>-beta.<n>` 標籤的最大序號加 1,查無時回傳 1。
```js
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
```
<a id="versionnextversion"></a>
### version.nextVersion
依「已知的最新穩定版」計算本次要使用的版本號(不負責取得舊版本)。`isBeta``false` 時回傳下一個發行版;為 `true` 時回傳 `<next>-beta.<n>`
```js
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'
```
<a id="versioncalculateversion"></a>
### version.calculateVersion
計算最新穩定版與下一個版本號;組合 `latestStableVersion``nextVersion`。回傳物件含 `latest`(現有最新穩定版,查無為 `"0.0.0"`)與 `version`(本次版本號)。
```js
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' }
```