diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 44a1521..76bf32c 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -1,36 +1,58 @@ # ============================================================================= # 用途: calculate-version 專案的 CI workflow。 -# 在 Pull Request 開啟或更新時,觸發 OpenCode AI 程式碼審查。 -# 更新日期: 2026/06/30 12:12:59 +# 在 Pull Request 開啟或更新 (且目標分支非 master) 時,先以 composite +# action 釋出並標註 beta 成品版本,再用剛標註出的版本呼叫 +# calculate-version action 計算版本號。 +# 更新日期: 2026/06/30 16:31:09 # ============================================================================= # workflow 名稱,顯示於 Gitea Actions 介面。 name: CI -# 觸發條件設定。 +# 觸發條件設定,定義此 workflow 在哪些事件下被啟動。 on: # 於 Pull Request 事件觸發。 pull_request: - # 忽略目標分支為 master 的 PR (master 走 CD 流程,不在此審查)。 + # 忽略目標分支為 master 的 PR (master 走 CD 流程,不在此 CI 審查)。 branches-ignore: + # 排除清單的單一項目: master 分支。 - master - # 僅在 PR 開啟 (opened) 或有新 commit 推送 (synchronize) 時觸發。 + # 僅在 PR 開啟 (opened) 或有新 commit 推送 (synchronize) 時觸發,避免其他 PR 事件 (如關閉) 重複觸發。 types: [opened, synchronize] +# 定義此 workflow 要執行的所有 job。 jobs: + # 第一個 job: 釋出並標註成品版本,並將版本號往外拋給後續 job 使用。 release-tag-version: + # job 顯示名稱。 name: Release Tag Version + # 指定執行環境 (runner) 標籤為 ubuntu。 runs-on: ubuntu + # 宣告此 job 的輸出,供後續 needs 此 job 的其他 job 取用。 outputs: + # 將下方 id 為 release-tag-version 的 step 所輸出的 version,設為此 job 的對外 version 輸出 (關鍵點: 透過 outputs 把標註出的版本往外拋)。 version: ${{ steps.release-tag-version.outputs.version }} + # 此 job 依序執行的步驟。 steps: + # 步驟: 呼叫 composite action 進行釋出並標註成品版本。 - name: 釋出並標註成品版本 + # 設定此 step 的 id,讓上方 outputs 可用 steps.release-tag-version.outputs.version 取得其輸出。 id: release-tag-version + # 引用 release-tag-version composite action,版本以變數 vars.ACTION_RELEASE_TAG_VERSION 釘選 (集中於 repo/org 變數管理,便於統一升版)。 uses: https://gitea.jsc.idv.tw/composite-actions/release-tag-version@${{ vars.ACTION_RELEASE_TAG_VERSION }} + # 傳遞給該 action 的輸入參數。 with: + # is_beta 設為 true 表示走 beta 標版流程 (標註的是 beta 成品版本,而非正式版)。 is_beta: true + # 第二個 job: 以前一個 job 標註出的版本來計算版本號。 calculate-version: + # job 顯示名稱。 name: Calculate Version + # 指定執行環境 (runner) 標籤為 ubuntu。 runs-on: ubuntu + # 宣告相依於 release-tag-version job: 需等其成功後才執行,並可透過 needs 取得其 outputs (關鍵點: 以此取得剛標註出的版本)。 needs: release-tag-version + # 此 job 依序執行的步驟。 steps: + # 步驟: 呼叫 calculate-version action 計算版本號。 - name: 計算版本號 + # 引用本專案的 calculate-version action,並以 @v 釘選自身 action 版本; 其中 version 來自上一個 job 透過 needs 取得的 outputs.version (關鍵點: 用剛標註的版本釘選自身)。 uses: https://gitea.jsc.idv.tw/docker-actions/calculate-version@v${{ needs.release-tag-version.outputs.version }} diff --git a/README.md b/README.md index 8d0b1a4..2f520f7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # calculate-version -> 更新時間:2026/06/30 12:32:40 +計算版本號的 Gitea Action:依儲存庫現有的 release,推算下一個穩定版或 beta 版本號,並寫出為 Action output 供後續步驟取用。以 Docker 容器執行,容器內由 Node.js 主程式實作。 -計算版本號的 Gitea Action。依現有 release 推算下一個穩定版或 beta 版本號,並將結果寫入 Action output `version`。核心邏輯以 Node.js 實作,置於 `app/`,由 `entrypoint.sh` 作為容器進入點啟動;容器映像採多階段建置(build 階段 `node:latest`、runtime 階段 `node:slim`)。 - -版本進位規則:`patch + 1`;當 `patch` 達 10 進位至 `minor`,`minor` 達 10 進位至 `major`。beta 版本號形如 `-beta.`,其中 `` 為該版本既有 beta 標籤的最大序號加 1。 +> 更新時間:2026/06/30 16:42:27(Asia/Taipei) ## 專案列表 @@ -12,7 +10,7 @@ | 專案名稱 | 專案描述 | | --- | --- | -| [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) | 計算版本號的 Gitea Action:載入並驗證環境變數設定、分頁取得 Gitea release、解析最新穩定版並推算下一個穩定版或 beta 版本號,最後將結果寫入 Action output。 | ### 參考專案 @@ -24,196 +22,195 @@ | 專案名稱 | NuGet 套件列表 | | --- | --- | -| [calculate-version](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app) | 無(zero-dependency;僅使用 Node.js 內建模組與全域 fetch) | +| [calculate-version](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app) | 無(本專案為純 Node.js,package.json 未宣告任何 npm 相依) | ## 功能列表 -### index(app/index.js) +### calculate-version | 功能名稱 | 功能描述 | | --- | --- | -| [index.main](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/index.js#L27) | [Action 進入點:協調設定載入、release 取得、版本計算與輸出(支援相依注入)。](#indexmain) | - -### logger(app/logger.js) - -| 功能名稱 | 功能描述 | -| --- | --- | -| [logger.section](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L45) | [輸出帶標題的區塊段落至標準輸出,標題前後以分隔線包夾。](#loggersection) | -| [logger.info](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L60) | [輸出 INF 層級 log 訊息,格式為 `[INF][時間]: 訊息`。](#loggerinfo) | -| [logger.error](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L77) | [輸出 ERR 層級訊息至 stderr(格式為 `[ERR][時間]: 訊息`,僅輸出不終止行程)。](#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.isUnset](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L11) | [判斷環境變數值是否視為「未設定」(undefined/null/空字串/字面 "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 旗標正規化為布林值。](#confignormalizebetaflag) | -| [config.loadConfig](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/config.js#L80) | [從環境變數載入並驗證執行所需的設定。](#configloadconfig) | - -### version(app/version.js) - -| 功能名稱 | 功能描述 | -| --- | --- | +| [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.section](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L45) | [輸出帶分隔線的區塊標題,用於在 log 中分隔處理階段。](#loggersection) | +| [logger.info](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L60) | [輸出 `[INF][時間]` 格式的資訊訊息至標準輸出。](#loggerinfo) | +| [logger.error](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/logger.js#L77) | [輸出 `[ERR][時間]` 格式的錯誤訊息至標準錯誤輸出。](#loggererror) | +| [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#L14) | [逐區段比較兩個版本號數值陣列,較短者視為較小。](#versioncompareversionarrays) | | [version.parseStableVersions](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L38) | [從 release 清單解析出所有穩定版的版本號數值陣列。](#versionparsestableversions) | -| [version.latestStableVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L57) | [取得 release 清單中最新的穩定版版本號字串。](#versionlateststableversion) | -| [version.nextReleaseVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L78) | [依最新穩定版計算下一個發行版本號。](#versionnextreleaseversion) | +| [version.latestStableVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L57) | [取得最新(最大)的穩定版版本號字串。](#versionlateststableversion) | +| [version.nextReleaseVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L78) | [依最新穩定版計算下一個發行版本號(逢 10 進位)。](#versionnextreleaseversion) | | [version.nextBetaNumber](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L101) | [計算指定版本號的下一個 beta 流水號。](#versionnextbetanumber) | -| [version.calculateVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L126) | [計算最新穩定版與下一個版本號(支援 beta)。](#versioncalculateversion) | - -### releases(app/releases.js) - -| 功能名稱 | 功能描述 | -| --- | --- | -| [releases.fetchReleases](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/releases.js#L24) | [以分頁方式取得指定 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#L16) | [將一行 `name=value` 附加寫入 Action 的輸出檔。](#outputwriteoutput) | +| [version.nextVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L130) | [依已知的最新穩定版計算本次要使用的版本號(含 beta)。](#versionnextversion) | +| [version.calculateVersion](https://gitea.jsc.idv.tw/docker-actions/calculate-version/src/branch/develop/app/version.js#L146) | [計算最新穩定版與下一個版本號。](#versioncalculateversion) | ## 使用範例 - -### 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 - -輸出一般資訊(INF)層級訊息至標準輸出,格式統一為 `[INF][{時間}]: {訊息}`,時間使用台灣時區(Asia/Taipei)、格式 `yyyy/MM/dd HH:mm:ss`,並於結尾換行;不會結束行程。 - -```js -const logger = require('./logger'); - -logger.info('IS_BETA=false'); -// 輸出:[INF][2026/06/30 12:00:00]: IS_BETA=false -``` - - -### logger.error - -輸出錯誤(ERR)層級訊息至標準錯誤輸出(stderr),格式統一為 `[ERR][{時間}]: {訊息}`(時間為台灣時區)。僅負責輸出,**不終止行程**;是否結束由呼叫端(進入點)決定,以利測試與錯誤復原。 - -```js -const logger = require('./logger'); - -logger.error('GITEA_SERVER_URL 未設定'); -// 對 stderr 輸出:[ERR][2026/06/30 12:00:00]: GITEA_SERVER_URL 未設定 -``` - ### config.isUnset -判斷環境變數值是否視為「未設定」。`undefined`、`null`、空字串、字面字串 `"null"` 皆視為未設定。 +判斷單一環境變數值是否應視為「未設定」。下列任一情況回傳 `true`:`undefined`、`null`、空字串、字面字串 `"null"`;其餘回傳 `false`。常用於 `loadConfig` 內判斷必填與選填設定。 ```js -const { isUnset } = require('./config'); +const { isUnset } = require('./app/config'); isUnset(undefined); // true isUnset('null'); // true -isUnset('false'); // false +isUnset(''); // true +isUnset('abc'); // false ``` ### config.requireEnv -驗證必填環境變數;未設定時拋出 `Error`(訊息為 `${name} 未設定`),已設定時原樣回傳值。 +驗證必填環境變數;值被視為未設定時拋出 `Error`(訊息為 `${name} 未設定`),否則原樣回傳該值。 ```js -const { requireEnv } = require('./config'); +const { requireEnv } = require('./app/config'); const url = requireEnv('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL); -// 未設定時拋出:Error: GITEA_SERVER_URL 未設定 +// 未設定時 → throw Error('GITEA_SERVER_URL 未設定') ``` ### config.normalizeBetaFlag -將 beta 旗標正規化為布林值。未設定時預設為 `false`;僅當值嚴格等於字面字串 `"true"` 時回傳 `true`。 +將 beta 旗標環境變數正規化為布林值。未設定時預設為 `false`;僅當值嚴格等於字面字串 `"true"` 時回傳 `true`。 ```js -const { normalizeBetaFlag } = require('./config'); +const { normalizeBetaFlag } = require('./app/config'); -normalizeBetaFlag('true'); // true -normalizeBetaFlag('TRUE'); // false(嚴格比較,不做大小寫轉換) +normalizeBetaFlag('true'); // true +normalizeBetaFlag('false'); // false normalizeBetaFlag(undefined); // false ``` ### config.loadConfig -從環境變數載入並驗證執行所需的設定。`GITEA_SERVER_URL` 與 `GITEA_REPOSITORY` 為必填(未設定即拋錯),且 `GITEA_SERVER_URL` 須為合法的 http/https URL;`GITEA_TOKEN` 非必填(未設定為 `null`);`IS_BETA` 會正規化為布林值。 +從環境變數載入並驗證執行所需設定。`GITEA_SERVER_URL`(須為合法 http/https URL)與 `GITEA_REPOSITORY`(須為 `owner/repo` 形式、排除路徑穿越)為必填;`GITEA_TOKEN` 選填(未設定為 `null`);`IS_BETA` 正規化為布林值。驗證失敗會拋出 `Error`。 ```js -const { loadConfig } = require('./config'); +const { loadConfig } = require('./app/config'); -const config = loadConfig({ - GITEA_SERVER_URL: 'https://gitea.example.com', - GITEA_REPOSITORY: 'owner/repo', - IS_BETA: 'true', +const config = loadConfig(process.env); +// → { serverUrl, repository, token, isBeta } +``` + + +### 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: () => {}, }); -// => { serverUrl: 'https://gitea.example.com', repository: 'owner/repo', token: null, isBeta: true } +// → 回傳 '1.2.4' +``` + + +### logger.section + +輸出帶標題的區塊段落至標準輸出,標題前後以分隔線包夾,用於在 log 中分隔各處理階段(例如「參數檢查」「取得舊版本」)。此為結構性段落標題,不套用 `[等級][時間]` 前綴。 + +```js +const logger = require('./app/logger'); + +logger.section('參數檢查'); +// 輸出:換行 + 50 個 = + 標題 + 50 個 - +``` + + +### logger.info + +輸出一般資訊(INF)層級的 log 訊息至標準輸出,格式統一為 `[INF][{台灣時間}]: {訊息}`(時間為 Asia/Taipei,格式 `yyyy/MM/dd HH:mm:ss`),結尾換行。 + +```js +const logger = require('./app/logger'); + +logger.info('NEW_VERSION=1.2.4'); +// 輸出:[INF][2026/06/30 16:42:27]: NEW_VERSION=1.2.4 +``` + + +### logger.error + +輸出錯誤(ERR)層級的 log 訊息至標準錯誤輸出(stderr),格式統一為 `[ERR][{台灣時間}]: {訊息}`。僅負責輸出,不終止行程,是否結束由呼叫端決定。 + +```js +const logger = require('./app/logger'); + +logger.error('GITEA_SERVER_URL 未設定'); +// stderr 輸出:[ERR][2026/06/30 16:42:27]: GITEA_SERVER_URL 未設定 +``` + + +### 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 +``` + + +### releases.fetchReleases + +以分頁方式(每頁 10 筆)透過全域 `fetch` 取得指定 Gitea repo 的所有 release,回傳合併後的陣列。有 token 時以 `Authorization: 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 }, +); ``` ### version.compareVersionArrays -逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。回傳值 `> 0` 表示 a 大於 b、`< 0` 表示 a 小於 b、`0` 表示相等;相異區段回傳的是該段差值(非固定 ±1)。 +逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。回傳大於 0 表示 `a` 大於 `b`、小於 0 表示 `a` 小於 `b`、0 表示相等。 ```js -const { compareVersionArrays } = require('./version'); +const { compareVersionArrays } = require('./app/version'); -compareVersionArrays([1, 10], [1, 2, 3]); // > 0(1.10 > 1.2.3) -compareVersionArrays([1, 2], [1, 2, 0]); // < 0(較短者較小) +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`。傳入非陣列時回傳空陣列。 +從 release 清單解析出所有穩定版(排除含 `-beta.` 與格式不合法者)的版本號數值陣列;會去除 tag 開頭的 `v`。輸入非陣列時回傳空陣列。 ```js -const { parseStableVersions } = require('./version'); +const { parseStableVersions } = require('./app/version'); parseStableVersions([ { tag_name: 'v1.2.3' }, { tag_name: '2.0.0' }, - { tag_name: 'v1.0.0-beta.1' }, // 被排除 + { tag_name: 'v1.3.0-beta.1' }, ]); -// => [[1, 2, 3], [2, 0, 0]] +// → [[1, 2, 3], [2, 0, 0]] ``` @@ -222,76 +219,63 @@ parseStableVersions([ 取得 release 清單中最新(最大)的穩定版版本號字串,固定格式化為三段 `major.minor.patch`;查無穩定版時回傳 `"0.0.0"`。 ```js -const { latestStableVersion } = require('./version'); +const { latestStableVersion } = require('./app/version'); -latestStableVersion([{ tag_name: 'v1.2.3' }, { tag_name: 'v1.9.9' }]); // "1.9.9" -latestStableVersion([]); // "0.0.0" +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)。 +依最新穩定版字串計算下一個發行版本號:patch 加 1,patch 達 10 進位至 minor,minor 達 10 進位至 major(逢 10 進位為本專案自訂約定,非標準 SemVer)。 ```js -const { nextReleaseVersion } = require('./version'); +const { nextReleaseVersion } = require('./app/version'); -nextReleaseVersion('1.2.8'); // "1.2.9" -nextReleaseVersion('1.2.9'); // "1.3.0" -nextReleaseVersion('1.9.9'); // "2.0.0" +nextReleaseVersion('1.2.3'); // '1.2.4' +nextReleaseVersion('1.2.9'); // '1.3.0' +nextReleaseVersion('1.9.9'); // '2.0.0' ``` ### version.nextBetaNumber -計算指定版本號的下一個 beta 流水號:取現有相符 beta 標籤(`v-beta.`)的最大序號加 1,查無時回傳 `1`。`version` 參數不應含前綴 `v`。 +計算指定版本號的下一個 beta 流水號:取現有相符 `v-beta.` 標籤的最大序號加 1,查無時回傳 1。 ```js -const { nextBetaNumber } = require('./version'); +const { nextBetaNumber } = require('./app/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 +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` 時回傳 `-beta.`。 + +```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' ``` ### version.calculateVersion -計算最新穩定版與下一個版本號;`isBeta` 為 `true` 時產生 beta 版本號。回傳物件 `{ latest, version }`。 +計算最新穩定版與下一個版本號;組合 `latestStableVersion` 與 `nextVersion`。回傳物件含 `latest`(現有最新穩定版,查無為 `"0.0.0"`)與 `version`(本次版本號)。 ```js -const { calculateVersion } = require('./version'); +const { calculateVersion } = require('./app/version'); calculateVersion([{ tag_name: 'v1.2.3' }], false); -// => { latest: '1.2.3', version: '1.2.4' } +// → { 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`(Node.js 18+)逐頁請求(每頁 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`)。以 append 方式寫入、不覆蓋既有內容;輸出檔路徑為 falsy 時拋出 `Error`。 - -```js -const { writeOutput } = require('./output'); - -writeOutput('version', '1.2.4'); -// 對 $GITHUB_OUTPUT 附加一行: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' } ``` diff --git a/action.yaml b/action.yaml index c1140b8..268e625 100644 --- a/action.yaml +++ b/action.yaml @@ -1,18 +1,42 @@ +# ============================================================================= +# 用途:此檔為 calculate-version Gitea Action 的定義,宣告其 inputs(is_beta)、 +# outputs(version)與以 docker 方式執行時注入給容器的環境變數。 +# 更新日期:2026/06/30 16:31:09 +# ============================================================================= + +# Action 名稱,會顯示於 Gitea workflow 執行畫面上以利辨識 name: 'Calculate Version' +# Action 簡述,說明此 Action 的功能為計算版本號 description: '計算版本號' +# Action 作者標示,供維護與聯絡用 author: 'Jeffery' +# inputs 區塊:宣告呼叫此 Action 時可傳入的參數 inputs: + # is_beta:控制此次要計算穩定版還是 beta 版本號的旗標 is_beta: + # 參數說明:是否為 beta 版本 description: '是否為 beta 版本' + # 預設值為 false,表示未指定時預設計算穩定版本號 default: false +# outputs 區塊:宣告此 Action 執行後對外提供的輸出值 outputs: + # version:容器內主程式計算完成後寫出的版本號,供後續步驟取用 version: + # 輸出說明:計算出的版本號 description: '計算出的版本號' +# runs 區塊:定義此 Action 的執行方式與相關設定 runs: + # 以 docker 容器方式執行此 Action using: 'docker' + # 指定容器映像來源為同目錄的 Dockerfile,會即時建置成映像後執行 image: 'Dockerfile' + # env 區塊:把 Gitea 平台 context 與 inputs 對應成容器內環境變數,供 Node.js 主程式(app/index.js)讀取 env: + # 注入 Gitea 伺服器網址,供主程式組出 release API 端點時使用 GITEA_SERVER_URL: ${{ gitea.server_url }} + # 注入目前 repository(owner/repo),供主程式定位要查詢 release 的儲存庫 GITEA_REPOSITORY: ${{ gitea.repository }} + # 注入 Gitea token,供主程式呼叫 release API 時通過身分驗證(屬敏感憑證,勿外洩) GITEA_TOKEN: ${{ gitea.token }} + # 將 inputs.is_beta 對應成 IS_BETA 環境變數,供主程式判斷要推算穩定版或 beta 版本號 IS_BETA: ${{ inputs.is_beta }} diff --git a/app/logger.js b/app/logger.js index 5aba8fb..ba516ef 100644 --- a/app/logger.js +++ b/app/logger.js @@ -39,7 +39,7 @@ function taipeiTimestamp() { * * @param {string} title - 區塊標題文字;會原樣輸出於兩條分隔線之間。 * @returns {void} - * @remarks 通常在進入一個處理階段前呼叫(例如「參數檢查」「取得版本資料」), + * @remarks 通常在進入一個處理階段前呼叫(例如「參數檢查」「取得舊版本」), * 用以在 CI/容器 log 中分隔各階段輸出,便於閱讀與定位。 */ function section(title) {