Files
calculate-version/app/version.js
T
JefferyandClaude Opus 4.8 bd6b6017ab 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>
2026-06-26 10:41:34 +08:00

141 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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 進位至 minorminor 達 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,
};