refactor(calculate-version): 將版本計算邏輯由 bash 改寫為 Node.js 並依功能分檔
保留 entrypoint.sh 作為容器進入點,改為啟動 app/ 下的 Node.js 程式; 依功能拆分為 logger/config/version/releases/output/index,並更新 Dockerfile 改用 node:lts-alpine。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4983a72524
commit
bd6b6017ab
+140
@@ -0,0 +1,140 @@
|
||||
'use strict';
|
||||
|
||||
// 每個版本號區段的進位上限:patch / minor 達到 10 即向上進位
|
||||
const SEGMENT_LIMIT = 10;
|
||||
|
||||
/**
|
||||
* 逐區段(element-wise)比較兩個版本號數值陣列,較短的陣列視為較小。
|
||||
* @param {number[]} a - 第一個版本號區段陣列,例如 [1, 2, 3]。
|
||||
* @param {number[]} b - 第二個版本號區段陣列,例如 [1, 2, 0]。
|
||||
* @returns {number} 大於 0 表示 a 大於 b;小於 0 表示 a 小於 b;0 表示兩者相等。
|
||||
*/
|
||||
function compareVersionArrays(a, b) {
|
||||
const length = Math.max(a.length, b.length);
|
||||
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
if (a[i] === undefined) {
|
||||
return -1;
|
||||
}
|
||||
if (b[i] === undefined) {
|
||||
return 1;
|
||||
}
|
||||
if (a[i] !== b[i]) {
|
||||
return a[i] - b[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 從 release 清單解析出所有穩定版(排除 beta、排除格式不合法者)的版本號數值陣列。
|
||||
* 會去除 tag 開頭的 'v',並將版本字串切成數字陣列。
|
||||
* @param {Array<{tag_name?: string}>} releases - release 物件清單;每個項目以 tag_name 表示版本標籤。
|
||||
* @returns {number[][]} 穩定版版本號的數值陣列清單,例如 [[1,2,3],[1,2,0]];輸入非陣列時回傳空陣列。
|
||||
*/
|
||||
function parseStableVersions(releases) {
|
||||
if (!Array.isArray(releases)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return releases
|
||||
.filter((release) => release
|
||||
&& typeof release.tag_name === 'string'
|
||||
&& !release.tag_name.includes('-beta.'))
|
||||
.map((release) => release.tag_name.replace(/^v/, ''))
|
||||
.filter((tag) => /^[0-9]+(\.[0-9]+)*$/.test(tag))
|
||||
.map((tag) => tag.split('.').map(Number));
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得 release 清單中最新(最大)的穩定版版本號字串,固定格式化為三段 "major.minor.patch"。
|
||||
* @param {Array<{tag_name?: string}>} releases - release 物件清單。
|
||||
* @returns {string} 最新穩定版版本號字串;查無穩定版時回傳 "0.0.0"。
|
||||
*/
|
||||
function latestStableVersion(releases) {
|
||||
const versions = parseStableVersions(releases);
|
||||
|
||||
if (versions.length === 0) {
|
||||
return '0.0.0';
|
||||
}
|
||||
|
||||
const max = versions.reduce(
|
||||
(best, current) => (compareVersionArrays(current, best) > 0 ? current : best),
|
||||
);
|
||||
|
||||
return `${max[0] ?? 0}.${max[1] ?? 0}.${max[2] ?? 0}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 依最新穩定版字串計算下一個發行版本號:patch 加 1,patch 達 10 進位至 minor,minor 達 10 進位至 major。
|
||||
* @param {string} latest - 最新穩定版版本號字串,例如 "1.2.9"。
|
||||
* @returns {string} 下一個發行版本號字串,格式為 "major.minor.patch",例如 "1.3.0"。
|
||||
*/
|
||||
function nextReleaseVersion(latest) {
|
||||
const parts = String(latest).split('.').map((part) => Number(part) || 0);
|
||||
let [major = 0, minor = 0, patch = 0] = parts;
|
||||
|
||||
patch += 1;
|
||||
if (patch >= SEGMENT_LIMIT) {
|
||||
patch = 0;
|
||||
minor += 1;
|
||||
}
|
||||
if (minor >= SEGMENT_LIMIT) {
|
||||
minor = 0;
|
||||
major += 1;
|
||||
}
|
||||
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 計算指定版本號的下一個 beta 流水號:取現有相符 beta 標籤的最大序號加 1,查無時回傳 1。
|
||||
* @param {Array<{tag_name?: string}>} releases - release 物件清單。
|
||||
* @param {string} version - 不含前綴 'v' 的版本號字串,例如 "1.3.0";用於組出 "v<version>-beta." 前綴比對。
|
||||
* @returns {number} 下一個 beta 流水號;查無對應 beta 時回傳 1。
|
||||
*/
|
||||
function nextBetaNumber(releases, version) {
|
||||
if (!Array.isArray(releases)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const prefix = `v${version}-beta.`;
|
||||
const numbers = releases
|
||||
.filter((release) => release
|
||||
&& typeof release.tag_name === 'string'
|
||||
&& release.tag_name.startsWith(prefix))
|
||||
.map((release) => Number(release.tag_name.slice(prefix.length)))
|
||||
.filter((number) => Number.isFinite(number));
|
||||
|
||||
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 計算最新穩定版與下一個版本號;isBeta 為 true 時產生 beta 版本號。
|
||||
* @param {Array<{tag_name?: string}>} releases - release 物件清單。
|
||||
* @param {boolean} isBeta - 是否產生 beta 版本號;true 時 version 形如 "<next>-beta.<n>"。
|
||||
* @returns {{latest: string, version: string}} 回傳物件:
|
||||
* - latest: 現有最新穩定版版本號字串(查無時為 "0.0.0")。
|
||||
* - version: 本次要使用的版本號字串;非 beta 為下一個發行版(如 "1.3.0"),beta 為 "<next>-beta.<n>"(如 "1.3.0-beta.1")。
|
||||
*/
|
||||
function calculateVersion(releases, isBeta) {
|
||||
const latest = latestStableVersion(releases);
|
||||
const next = nextReleaseVersion(latest);
|
||||
|
||||
const version = isBeta
|
||||
? `${next}-beta.${nextBetaNumber(releases, next)}`
|
||||
: next;
|
||||
|
||||
return { latest, version };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
compareVersionArrays,
|
||||
parseStableVersions,
|
||||
latestStableVersion,
|
||||
nextReleaseVersion,
|
||||
nextBetaNumber,
|
||||
calculateVersion,
|
||||
};
|
||||
Reference in New Issue
Block a user