Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5b2739b7f | ||
|
|
98a447beb1 | ||
|
|
9f6c2a2556 | ||
|
|
ea75d1adcd | ||
|
|
19a8667962 |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
從 git tag 取得最新版號,依 `is_beta` 計算下一個正式版或 beta 版號(各號碼滿 9 進位)並輸出為 `value` 的 Gitea/GitHub Docker container action。
|
從 git tag 取得最新版號,依 `is_beta` 計算下一個正式版或 beta 版號(各號碼滿 9 進位)並輸出為 `value` 的 Gitea/GitHub Docker container action。
|
||||||
|
|
||||||
- 更新時間:2026/07/16 11:08:39
|
- 更新時間:2026/07/16 11:26:21
|
||||||
|
|
||||||
## action 使用方式
|
## action 使用方式
|
||||||
|
|
||||||
@@ -18,17 +18,20 @@ jobs:
|
|||||||
uses: docker-actions/calculate-next-version@master
|
uses: docker-actions/calculate-next-version@master
|
||||||
with:
|
with:
|
||||||
is_beta: 'false'
|
is_beta: 'false'
|
||||||
|
sha: ${{ gitea.sha }} # 該 commit 已有版號 tag 時直接輸出該版號
|
||||||
- run: echo "下一版號:${{ steps.version.outputs.value }}"
|
- run: echo "下一版號:${{ steps.version.outputs.value }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
| 類型 | 名稱 | 必填 | 預設值 | 說明 |
|
| 類型 | 名稱 | 必填 | 預設值 | 說明 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| input | `is_beta` | 否 | `false` | 是否為 beta 版(true 時產生 X.Y.Z-beta.N 版號) |
|
| input | `is_beta` | 否 | `false` | 是否為 beta 版(true 時產生 X.Y.Z-beta.N 版號) |
|
||||||
|
| input | `sha` | 否 | (空) | 要檢查的 commit SHA(建議傳入 `${{ gitea.sha }}`);該 commit 已有版號 tag 時直接輸出該版號。未提供時自動改用 `GITHUB_SHA` |
|
||||||
| output | `value` | — | — | 計算出的下一版號 |
|
| output | `value` | — | — | 計算出的下一版號 |
|
||||||
|
|
||||||
版號規則:
|
版號規則:
|
||||||
|
|
||||||
- 版號來源為 repo 的 git tag(格式 `X.Y.Z` 或 `X.Y.Z-beta.N`,不帶前綴),取最大版號;無任何版號 tag 時從 `0.0.1`(beta 為 `0.0.1-beta.1`)起算。
|
- 已標記檢查:先以 `sha` 輸入(未提供則用 `GITHUB_SHA`)確認該 commit 是否已有符合格式的版號 tag;已有時直接輸出該 tag 內容(多個取最大、忽略 `is_beta`),不再計算下一版。檢查失敗(如 sha 不存在)輸出 WRN 後改走一般計算流程。
|
||||||
|
- 版號來源為 repo 的 git tag(格式 `X.Y.Z` 或 `X.Y.Z-beta.N`,可帶小寫 `v` 前綴如 `v1.2.3`,解析時只取 `v` 之後的版本號),取最大版號;無任何版號 tag 時從 `0.0.1`(beta 為 `0.0.1-beta.1`)起算。輸出的 `value` 一律不帶前綴。
|
||||||
- git tag 讀取失敗(例如 workspace 不是 git repository、workflow 未先執行 `actions/checkout`)時不會中止:輸出 ERR 說明原因後,改以起始版號 `0.0.1`(beta 為 `0.0.1-beta.1`)作為預設值繼續輸出。
|
- git tag 讀取失敗(例如 workspace 不是 git repository、workflow 未先執行 `actions/checkout`)時不會中止:輸出 ERR 說明原因後,改以起始版號 `0.0.1`(beta 為 `0.0.1-beta.1`)作為預設值繼續輸出。
|
||||||
- `is_beta=false`:最新為正式版 → patch +1(`1.2.3` → `1.2.4`);最新為 beta → 去掉 beta 尾碼轉正式(`1.2.4-beta.3` → `1.2.4`)。
|
- `is_beta=false`:最新為正式版 → patch +1(`1.2.3` → `1.2.4`);最新為 beta → 去掉 beta 尾碼轉正式(`1.2.4-beta.3` → `1.2.4`)。
|
||||||
- `is_beta=true`:最新為正式版 → patch +1 加 `-beta.1`;最新為 beta → beta 號 +1(`beta.9` → `beta.10`,無上限)。
|
- `is_beta=true`:最新為正式版 → patch +1 加 `-beta.1`;最新為 beta → beta 號 +1(`beta.9` → `beta.10`,無上限)。
|
||||||
@@ -67,10 +70,10 @@ jobs:
|
|||||||
| [logger.err](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L116) | [輸出 ERR(錯誤)等級的日誌訊息到 stderr](#loggererr) |
|
| [logger.err](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L116) | [輸出 ERR(錯誤)等級的日誌訊息到 stderr](#loggererr) |
|
||||||
| [logger.trc](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L131) | [輸出 TRC(細部追蹤)等級的日誌訊息到 stdout](#loggertrc) |
|
| [logger.trc](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L131) | [輸出 TRC(細部追蹤)等級的日誌訊息到 stdout](#loggertrc) |
|
||||||
| [logger.dbg](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L146) | [輸出 DBG(除錯)等級的日誌訊息到 stdout](#loggerdbg) |
|
| [logger.dbg](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/logger.js#L146) | [輸出 DBG(除錯)等級的日誌訊息到 stdout](#loggerdbg) |
|
||||||
| [version.parse](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L26) | [解析版號字串為版號物件,不符格式回傳 null](#versionparse) |
|
| [version.parse](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L28) | [解析版號字串為版號物件(可帶 v 前綴),不符格式回傳 null](#versionparse) |
|
||||||
| [version.stringify](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L52) | [將版號物件轉換為版號字串](#versionstringify) |
|
| [version.stringify](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L54) | [將版號物件轉換為版號字串](#versionstringify) |
|
||||||
| [version.compare](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L72) | [比較兩個版號物件的大小,正式版大於同號 beta](#versioncompare) |
|
| [version.compare](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L74) | [比較兩個版號物件的大小,正式版大於同號 beta](#versioncompare) |
|
||||||
| [version.next](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L136) | [依最新版號與 is_beta 旗標計算下一版號](#versionnext) |
|
| [version.next](https://gitea.jsc.idv.tw/docker-actions/calculate-next-version/src/branch/master/src/version.js#L138) | [依最新版號與 is_beta 旗標計算下一版號](#versionnext) |
|
||||||
|
|
||||||
## 使用範例
|
## 使用範例
|
||||||
|
|
||||||
@@ -141,12 +144,13 @@ logger.dbg('略過不符合版號格式的 tag:not-a-version', '讀取版號')
|
|||||||
<a id="versionparse"></a>
|
<a id="versionparse"></a>
|
||||||
### version.parse
|
### version.parse
|
||||||
|
|
||||||
解析版號字串為版號物件 `{ major, minor, patch, beta }`;支援正式版(`X.Y.Z`)與 beta 版(`X.Y.Z-beta.N`)兩種格式,`beta` 為 `null` 表示正式版。不符合格式(含 `null`、非字串轉出的值)時回傳 `null`、不拋出例外,適合逐一解析 git tag 並過濾非版號 tag。
|
解析版號字串為版號物件 `{ major, minor, patch, beta }`;支援正式版(`X.Y.Z`)與 beta 版(`X.Y.Z-beta.N`)兩種格式,皆可帶小寫 `v` 前綴(解析時忽略前綴、只取 `v` 之後的版本號),`beta` 為 `null` 表示正式版。不符合格式(含 `null`、非字串轉出的值)時回傳 `null`、不拋出例外,適合逐一解析 git tag 並過濾非版號 tag。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { parse } = require('./version');
|
const { parse } = require('./version');
|
||||||
|
|
||||||
parse('1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }
|
parse('1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }
|
||||||
|
parse('v1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }(忽略 v 前綴)
|
||||||
parse('1.2.3-beta.5'); // => { major: 1, minor: 2, patch: 3, beta: 5 }
|
parse('1.2.3-beta.5'); // => { major: 1, minor: 2, patch: 3, beta: 5 }
|
||||||
parse('v1.2'); // => null(不符合格式)
|
parse('v1.2'); // => null(不符合格式)
|
||||||
```
|
```
|
||||||
|
|||||||
+13
-1
@@ -3,7 +3,7 @@
|
|||||||
# metadata 定義檔。宣告此 action 的名稱、輸入(is_beta)、輸出(value)
|
# metadata 定義檔。宣告此 action 的名稱、輸入(is_beta)、輸出(value)
|
||||||
# 與執行方式(runner 於執行時就地以 Dockerfile 建置映像並以 entrypoint.sh 啟動)。
|
# 與執行方式(runner 於執行時就地以 Dockerfile 建置映像並以 entrypoint.sh 啟動)。
|
||||||
# 由 workflow 以 `uses:` 引用本 repo 時,runner 讀取此檔決定如何執行。
|
# 由 workflow 以 `uses:` 引用本 repo 時,runner 讀取此檔決定如何執行。
|
||||||
# 更新時間:2026/07/16 09:17:01
|
# 更新時間:2026/07/16 11:18:57
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# action 的顯示名稱:出現在 workflow log 與 marketplace/action 清單中,
|
# action 的顯示名稱:出現在 workflow log 與 marketplace/action 清單中,
|
||||||
@@ -33,6 +33,18 @@ inputs:
|
|||||||
# action 輸入一律為字串,布林判斷由主程式自行解析
|
# action 輸入一律為字串,布林判斷由主程式自行解析
|
||||||
default: 'false'
|
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.<id>.outputs.value 取用
|
# 輸出參數區:供 workflow 後續步驟以 steps.<id>.outputs.value 取用
|
||||||
outputs:
|
outputs:
|
||||||
# value:計算出的下一版號;由主程式(src/index.js)將
|
# value:計算出的下一版號;由主程式(src/index.js)將
|
||||||
|
|||||||
+96
-1
@@ -63,6 +63,86 @@ function readIsBeta() {
|
|||||||
process.exit(1);
|
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 <sha>` 列出指向該 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 找出目前最新(最大)的版號。
|
* 從 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 輸出。
|
*(進位超過 9.9.9 時輸出 ERR 並 exit(1))→ 寫出 value 輸出。
|
||||||
* 檔案頂部的 process.on('uncaughtException') / process.on('unhandledRejection')
|
* 檔案頂部的 process.on('uncaughtException') / process.on('unhandledRejection')
|
||||||
* 會攔截未捕捉例外,以 ERR 格式輸出後以非零 exit code 結束。
|
* 會攔截未捕捉例外,以 ERR 格式輸出後以非零 exit code 結束。
|
||||||
@@ -162,6 +244,19 @@ function main() {
|
|||||||
logger.inf('開始計算下一版號');
|
logger.inf('開始計算下一版號');
|
||||||
|
|
||||||
const isBeta = readIsBeta();
|
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 latest = findLatestVersion();
|
||||||
|
|
||||||
const stage = '計算版號';
|
const stage = '計算版號';
|
||||||
|
|||||||
+7
-5
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// 版號格式:X.Y.Z 或 X.Y.Z-beta.N(不帶前綴)
|
// 版號格式:X.Y.Z 或 X.Y.Z-beta.N(可帶小寫 v 前綴,如 v1.2.3;解析時忽略前綴)
|
||||||
const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-beta\.(\d+))?$/;
|
const VERSION_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-beta\.(\d+))?$/;
|
||||||
|
|
||||||
// major / minor / patch 各位數上限;beta 號碼無上限
|
// major / minor / patch 各位數上限;beta 號碼無上限
|
||||||
const MAX_DIGIT = 9;
|
const MAX_DIGIT = 9;
|
||||||
@@ -9,17 +9,19 @@ const MAX_DIGIT = 9;
|
|||||||
/**
|
/**
|
||||||
* 解析版號字串為版號物件。
|
* 解析版號字串為版號物件。
|
||||||
*
|
*
|
||||||
* 支援正式版(X.Y.Z)與 beta 版(X.Y.Z-beta.N)兩種格式;
|
* 支援正式版(X.Y.Z)與 beta 版(X.Y.Z-beta.N)兩種格式,
|
||||||
|
* 皆可帶小寫 v 前綴(如 v1.2.3),解析時忽略前綴、只取 v 之後的版本號;
|
||||||
* 用於逐一解析 git tag,把 tag 字串轉成可比較、可計算的結構化物件。
|
* 用於逐一解析 git tag,把 tag 字串轉成可比較、可計算的結構化物件。
|
||||||
* 不符合格式(含 null、非字串轉出的值)一律回傳 null,不拋出例外。
|
* 不符合格式(含 null、非字串轉出的值)一律回傳 null,不拋出例外。
|
||||||
*
|
*
|
||||||
* @param {string} text - 待解析的版號字串,格式為 X.Y.Z 或 X.Y.Z-beta.N
|
* @param {string} text - 待解析的版號字串,格式為 X.Y.Z 或 X.Y.Z-beta.N(可帶 v 前綴)
|
||||||
* @returns {?{major: number, minor: number, patch: number, beta: ?number}}
|
* @returns {?{major: number, minor: number, patch: number, beta: ?number}}
|
||||||
* 版號物件;beta 為 null 表示正式版,為數字表示 beta 序號。
|
* 版號物件;beta 為 null 表示正式版,為數字表示 beta 序號。
|
||||||
* 輸入不符合版號格式時回傳 null。
|
* 輸入不符合版號格式時回傳 null。前綴 v 不會出現在回傳物件中。
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* parse('1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }
|
* parse('1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }
|
||||||
|
* parse('v1.2.3'); // => { major: 1, minor: 2, patch: 3, beta: null }(忽略 v 前綴)
|
||||||
* parse('1.2.3-beta.5'); // => { major: 1, minor: 2, patch: 3, beta: 5 }
|
* parse('1.2.3-beta.5'); // => { major: 1, minor: 2, patch: 3, beta: 5 }
|
||||||
* parse('v1.2'); // => null(不符合格式)
|
* parse('v1.2'); // => null(不符合格式)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user