# calculate-version > 更新時間:2026/06/26 10:49:46 計算版本號的 Gitea Action。依現有 release 推算下一個穩定版或 beta 版本號,並將結果寫入 Action output `version`。核心邏輯以 Node.js 實作,置於 `app/`,由 `entrypoint.sh` 作為容器進入點啟動。 版本進位規則:`patch + 1`;當 `patch` 達 10 進位至 `minor`,`minor` 達 10 進位至 `major`。beta 版本號形如 `-beta.`,其中 `` 為該版本既有 beta 標籤的最大序號加 1。 ## 專案列表 ### 專案描述 | 專案名稱 | 專案描述 | | --- | --- | | [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) | 無(zero-dependency;僅使用 Node.js 內建模組與全域 fetch) | ## 功能列表 ### index(app/index.js) | 功能名稱 | 功能描述 | | --- | --- | | [index.main](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/index.js#L23) | [Action 進入點:協調設定載入、release 取得、版本計算與輸出(支援相依注入)。](#indexmain) | ### logger(app/logger.js) | 功能名稱 | 功能描述 | | --- | --- | | [logger.section](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L16) | [輸出帶標題的區塊段落至標準輸出,標題前後以分隔線包夾。](#loggersection) | | [logger.info](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L26) | [輸出一般資訊層級的 log 訊息,自動加上 `[info]` 前綴。](#loggerinfo) | | [logger.error](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L38) | [輸出錯誤訊息至 stderr(僅輸出,不終止行程)。](#loggererror) | ### config(app/config.js) | 功能名稱 | 功能描述 | | --- | --- | | [config.isUnset](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L11) | [判斷環境變數值是否視為「未設定」。](#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 旗標正規化為布林值。](#confignormalizebetaflag) | | [config.loadConfig](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L68) | [從環境變數載入並驗證執行所需的設定。](#configloadconfig) | ### version(app/version.js) | 功能名稱 | 功能描述 | | --- | --- | | [version.compareVersionArrays](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L12) | [逐區段比較兩個版本號數值陣列,較短者視為較小。](#versioncompareversionarrays) | | [version.parseStableVersions](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L36) | [從 release 清單解析出所有穩定版的版本號數值陣列。](#versionparsestableversions) | | [version.latestStableVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L55) | [取得 release 清單中最新的穩定版版本號字串。](#versionlateststableversion) | | [version.nextReleaseVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L74) | [依最新穩定版計算下一個發行版本號。](#versionnextreleaseversion) | | [version.nextBetaNumber](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L97) | [計算指定版本號的下一個 beta 流水號。](#versionnextbetanumber) | | [version.calculateVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L122) | [計算最新穩定版與下一個版本號(支援 beta)。](#versioncalculateversion) | ### releases(app/releases.js) | 功能名稱 | 功能描述 | | --- | --- | | [releases.fetchReleases](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/releases.js#L22) | [以分頁方式取得指定 Gitea repo 的所有 release。](#releasesfetchreleases) | ### output(app/output.js) | 功能名稱 | 功能描述 | | --- | --- | | [output.writeOutput](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/output.js#L14) | [將一行 `name=value` 附加寫入 Action 的輸出檔。](#outputwriteoutput) | ## 使用範例 ### index.main Action 進入點:依序協調設定載入、release 取得、版本計算與輸出寫出,回傳計算出的版本號。失敗時向外拋出例外(由進入點頂層攔截並以狀態碼 1 結束)。相依模組可透過 `deps` 注入,便於測試。模組僅在被直接執行(`node app/index.js`)時自動啟動。 ```js const { main } = require('./index'); // 測試或自訂情境:注入假的相依 const version = await main({ loadConfig: () => ({ serverUrl: 'https://gitea.example.com', repository: 'owner/repo', token: null, isBeta: false }), fetchReleases: async () => ([{ tag_name: 'v1.2.3' }]), writeOutput: () => {}, log: { section() {}, info() {}, error() {} }, }); // version === '1.2.4' ``` ### logger.section 輸出帶標題的區塊段落至標準輸出:先一個換行,接著 50 個 `=` 的主分隔線、標題文字,最後 50 個 `-` 的次分隔線,用於在 log 中建立可視段落區隔。 ```js const logger = require('./logger'); logger.section('參數檢查'); // 輸出: // // ================================================== // 參數檢查 // -------------------------------------------------- ``` ### logger.info 輸出一般資訊層級訊息至標準輸出,自動加上 `[info]` 前綴與換行;不會結束行程。 ```js const logger = require('./logger'); logger.info('IS_BETA=false'); // 輸出:[info] IS_BETA=false ``` ### logger.error 輸出錯誤訊息至標準錯誤輸出(stderr),自動加上 `[error]` 前綴與換行。僅負責輸出,**不終止行程**;是否結束由呼叫端(進入點)決定,以利測試與錯誤復原。 ```js const logger = require('./logger'); logger.error('GITEA_SERVER_URL 未設定'); // 對 stderr 輸出:[error] GITEA_SERVER_URL 未設定 ``` ### config.isUnset 判斷環境變數值是否視為「未設定」。`undefined`、`null`、空字串、字面字串 `"null"` 皆視為未設定。 ```js const { isUnset } = require('./config'); isUnset(undefined); // true isUnset('null'); // true isUnset('false'); // false ``` ### config.requireEnv 驗證必填環境變數;未設定時拋出 `Error`(訊息為 `${name} 未設定`),已設定時原樣回傳值。 ```js const { requireEnv } = require('./config'); const url = requireEnv('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL); // 未設定時拋出:Error: GITEA_SERVER_URL 未設定 ``` ### config.normalizeBetaFlag 將 beta 旗標正規化為布林值。未設定時預設為 `false`;僅當值嚴格等於字面字串 `"true"` 時回傳 `true`。 ```js const { normalizeBetaFlag } = require('./config'); normalizeBetaFlag('true'); // true normalizeBetaFlag('TRUE'); // false(嚴格比較,不做大小寫轉換) normalizeBetaFlag(undefined); // false ``` ### config.loadConfig 從環境變數載入並驗證執行所需的設定。`GITEA_SERVER_URL` 與 `GITEA_REPOSITORY` 為必填(未設定即拋錯),且 `GITEA_SERVER_URL` 須為合法的 http/https URL;`GITEA_TOKEN` 非必填(未設定為 `null`);`IS_BETA` 會正規化為布林值。 ```js const { loadConfig } = require('./config'); const config = loadConfig({ GITEA_SERVER_URL: 'https://gitea.example.com', GITEA_REPOSITORY: 'owner/repo', IS_BETA: 'true', }); // => { serverUrl: 'https://gitea.example.com', repository: 'owner/repo', token: null, isBeta: true } ``` ### version.compareVersionArrays 逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。回傳值 `> 0` 表示 a 大於 b、`< 0` 表示 a 小於 b、`0` 表示相等。 ```js const { compareVersionArrays } = require('./version'); compareVersionArrays([1, 10], [1, 2, 3]); // > 0(1.10 > 1.2.3) compareVersionArrays([1, 2], [1, 2, 0]); // < 0(較短者較小) ``` ### version.parseStableVersions 從 release 清單解析出所有穩定版(排除 beta、排除格式不合法者)的版本號數值陣列;會去除 tag 開頭的 `v`。 ```js const { parseStableVersions } = require('./version'); parseStableVersions([ { tag_name: 'v1.2.3' }, { tag_name: '2.0.0' }, { tag_name: 'v1.0.0-beta.1' }, // 被排除 ]); // => [[1, 2, 3], [2, 0, 0]] ``` ### version.latestStableVersion 取得 release 清單中最新(最大)的穩定版版本號字串,固定格式化為三段 `major.minor.patch`;查無穩定版時回傳 `"0.0.0"`。 ```js const { latestStableVersion } = require('./version'); latestStableVersion([{ tag_name: 'v1.2.3' }, { tag_name: 'v1.9.9' }]); // "1.9.9" latestStableVersion([]); // "0.0.0" ``` ### version.nextReleaseVersion 依最新穩定版字串計算下一個發行版本號:`patch + 1`,`patch` 達 10 進位至 `minor`,`minor` 達 10 進位至 `major`。 ```js const { nextReleaseVersion } = require('./version'); nextReleaseVersion('1.2.8'); // "1.2.9" nextReleaseVersion('1.2.9'); // "1.3.0" nextReleaseVersion('1.9.9'); // "2.0.0" ``` ### version.nextBetaNumber 計算指定版本號的下一個 beta 流水號:取現有相符 beta 標籤(`v-beta.`)的最大序號加 1,查無時回傳 `1`。`version` 參數不應含前綴 `v`。 ```js const { nextBetaNumber } = require('./version'); nextBetaNumber([{ tag_name: 'v1.3.0-beta.1' }, { tag_name: 'v1.3.0-beta.3' }], '1.3.0'); // 4 nextBetaNumber([], '1.3.0'); // 1 ``` ### version.calculateVersion 計算最新穩定版與下一個版本號;`isBeta` 為 `true` 時產生 beta 版本號。回傳物件 `{ latest, version }`。 ```js const { calculateVersion } = require('./version'); calculateVersion([{ tag_name: 'v1.2.3' }], false); // => { latest: '1.2.3', version: '1.2.4' } calculateVersion([{ tag_name: 'v1.2.3' }], true); // => { latest: '1.2.3', version: '1.2.4-beta.1' } ``` ### releases.fetchReleases 以分頁方式取得指定 Gitea repo 的所有 release,並回傳合併後的陣列。使用全域 `fetch` 逐頁請求(每頁 limit 為 10);當某頁回傳空資料、`null` 或筆數少於上限時即停止。提供 `options.token` 時以授權方式請求。網路失敗、回應讀取失敗、非 2xx、無法解析或非陣列回應時拋出 `Error`(含頁碼)。 ```js const { fetchReleases } = require('./releases'); const logger = require('./logger'); const releases = await fetchReleases( 'https://gitea.example.com/api/v1/repos/owner/repo/releases', { token: process.env.GITEA_TOKEN, logger }, ); // => 所有 release 物件合併後的陣列 ``` ### output.writeOutput 將一行 `name=value` 附加寫入 GitHub/Gitea Action 的輸出檔(預設取 `process.env.GITHUB_OUTPUT`)。輸出檔路徑為 falsy 時拋出 `Error`。 ```js const { writeOutput } = require('./output'); writeOutput('version', '1.2.4'); // 對 $GITHUB_OUTPUT 附加一行:version=1.2.4 ```