diff --git a/action.yml b/action.yml index 599c4bb..adc4075 100644 --- a/action.yml +++ b/action.yml @@ -3,7 +3,7 @@ # metadata 定義檔。宣告此 action 的名稱、輸入(is_beta)、輸出(value) # 與執行方式(runner 於執行時就地以 Dockerfile 建置映像並以 entrypoint.sh 啟動)。 # 由 workflow 以 `uses:` 引用本 repo 時,runner 讀取此檔決定如何執行。 -# 更新時間:2026/07/16 09:17:01 +# 更新時間:2026/07/16 11:18:57 # ============================================================================ # action 的顯示名稱:出現在 workflow log 與 marketplace/action 清單中, @@ -33,6 +33,18 @@ inputs: # action 輸入一律為字串,布林判斷由主程式自行解析 default: 'false' + # sha:要檢查的 commit SHA,建議由 workflow 傳入 ${{ gitea.sha }}; + # runner 會以 INPUT_SHA 環境變數傳入容器,該 commit 已有版號 tag 時 + # 直接輸出該版號(忽略 is_beta)、不再計算下一版 + sha: + # 參數說明:未提供時主程式自動改用 runner 內建的 GITHUB_SHA; + # 兩者皆空才略過已標記檢查、直接走一般計算流程 + description: '要檢查的 commit SHA(建議傳入 ${{ gitea.sha }});該 commit 已有版號 tag 時直接輸出該版號。未提供時自動改用 GITHUB_SHA' + # 非必填:省略時依上述順序自動回退 + required: false + # 預設空字串,表示交由主程式依 INPUT_SHA → GITHUB_SHA 順序解析 + default: '' + # 輸出參數區:供 workflow 後續步驟以 steps..outputs.value 取用 outputs: # value:計算出的下一版號;由主程式(src/index.js)將 diff --git a/src/index.js b/src/index.js index f88ae1a..646e032 100644 --- a/src/index.js +++ b/src/index.js @@ -63,6 +63,86 @@ function readIsBeta() { process.exit(1); } +/** + * 讀取要檢查的 commit SHA。 + * + * 依序取 INPUT_SHA(action 的 sha 輸入,建議由 workflow 傳入 `${{ gitea.sha }}`) + * 與 GITHUB_SHA(runner 自動提供的觸發 commit)環境變數,trim 後回傳; + * 兩者皆空時回傳 null(呼叫端略過「已標記檢查」,走一般計算流程)。 + * 純讀取、無副作用,僅輸出一行日誌記錄取得的值或略過原因。 + * + * @returns {?string} commit SHA 字串;無可用來源時回傳 null + * + * @example + * // INPUT_SHA=abc1234 時 + * const sha = readSha(); // => 'abc1234' + * // [讀取輸入][INF][時間]: 收到 commit sha="abc1234" + */ +function readSha() { + const stage = '讀取輸入'; + const sha = (process.env.INPUT_SHA || process.env.GITHUB_SHA || '').trim(); + if (sha === '') { + logger.dbg('未提供 commit sha(INPUT_SHA 與 GITHUB_SHA 皆空),略過已標記檢查', stage); + return null; + } + logger.inf(`收到 commit sha="${sha}"`, stage); + return sha; +} + +/** + * 檢查指定 commit 是否已有符合格式的版號 tag。 + * + * 以 GITHUB_WORKSPACE(無則目前工作目錄)為目標 repo,先設定 git safe.directory, + * 再以 `git tag --points-at ` 列出指向該 commit 的所有 tag, + * 逐一以 parse() 解析並以 compare() 取最大版號;不符合版號格式的 tag 以 DBG 記錄後略過。 + * git 指令失敗(例如 sha 不存在、workspace 不是 git repository)時不中止程序: + * 輸出 WRN 後回傳 null,讓呼叫端改走一般計算流程。 + * + * @param {string} sha - 要檢查的 commit SHA(完整或短 hash) + * @returns {?{major: number, minor: number, patch: number, beta: ?number}} + * 該 commit 上最大的版號物件;無符合格式的 tag 或檢查失敗時回傳 null + * + * @example + * const tagged = findVersionAtCommit('abc1234'); + * // commit 有 tag 1.2.4 時 => { major: 1, minor: 2, patch: 4, beta: null } + * // [檢查已標記][INF][時間]: commit abc1234 已有版號 tag:1.2.4 + */ +function findVersionAtCommit(sha) { + const stage = '檢查已標記'; + const workspace = process.env.GITHUB_WORKSPACE || process.cwd(); + + let output; + try { + git(['config', '--global', '--add', 'safe.directory', workspace], stage); + output = git(['-C', workspace, 'tag', '--points-at', sha], stage); + } catch (error) { + const detail = (error.stderr || error.message || String(error)).toString().trim().replace(/\s*\n\s*/g, ';'); + logger.wrn(`無法檢查 commit ${sha} 的 tag:${detail};改走一般計算流程`, stage); + return null; + } + + const tags = output === '' ? [] : output.split('\n'); + let best = null; + for (const tag of tags) { + const version = parse(tag); + if (version === null) { + logger.dbg(`略過不符合版號格式的 tag:${tag}`, stage); + continue; + } + logger.trc(`解析到版號 tag:${tag}`, stage); + if (best === null || compare(version, best) > 0) { + best = version; + } + } + + if (best === null) { + logger.inf(`commit ${sha} 沒有版號 tag,將計算下一版號`, stage); + } else { + logger.inf(`commit ${sha} 已有版號 tag:${stringify(best)}`, stage); + } + return best; +} + /** * 從 git tag 找出目前最新(最大)的版號。 * @@ -150,7 +230,9 @@ function writeOutput(value) { /** * 主流程:計算並輸出下一版號。 * - * 依序執行:讀取 is_beta 輸入 → 從 git tag 找最新版號 → 以 next() 計算下一版 + * 依序執行:讀取 is_beta 輸入 → 讀取 commit sha(INPUT_SHA/GITHUB_SHA)並檢查 + * 該 commit 是否已有版號 tag(已標記時直接輸出該版號、忽略 is_beta,流程結束) + * → 從 git tag 找最新版號 → 以 next() 計算下一版 *(進位超過 9.9.9 時輸出 ERR 並 exit(1))→ 寫出 value 輸出。 * 檔案頂部的 process.on('uncaughtException') / process.on('unhandledRejection') * 會攔截未捕捉例外,以 ERR 格式輸出後以非零 exit code 結束。 @@ -162,6 +244,19 @@ function main() { logger.inf('開始計算下一版號'); const isBeta = readIsBeta(); + + const sha = readSha(); + if (sha !== null) { + const tagged = findVersionAtCommit(sha); + if (tagged !== null) { + const value = stringify(tagged); + logger.inf(`commit 已標記,直接輸出既有版號:${value}(忽略 is_beta=${isBeta})`, '計算版號'); + writeOutput(value); + logger.inf('計算完成'); + return; + } + } + const latest = findLatestVersion(); const stage = '計算版號';