fix(release-cleanup): 日誌改為 [階段][等級][時間] 格式並移除區段分隔線
- 新增 stage() 設定目前階段、formatLog() 共用組裝,log 統一為 [階段][等級][時間]: 訊息 - 階段對應 參數檢查/取得舊版本/刪除舊版本;移除 separator()/section() 的 ==== / ---- 分隔區塊 - 行為、控制流、exit code 不變;fail 仍走 stderr、其餘走 stdout 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
9d8cbb6f1c
commit
c37b33f397
+39
-30
@@ -2,26 +2,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 stdout 輸出一條視覺分隔線(前後各一個換行 + 50 個等號),
|
* 目前流程階段名稱,作為每行 log 的 `[階段]` 前綴使用;
|
||||||
* 用於在 log 中切割不同區段。
|
* 為空字串時省略整個 `[階段]` 區塊。
|
||||||
*
|
* @type {string}
|
||||||
* @returns {void}
|
|
||||||
*/
|
*/
|
||||||
function separator() {
|
let currentStage = '';
|
||||||
process.stdout.write('\n==================================================\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 輸出一個區段標題區塊:上緣等號分隔線、標題文字、下緣虛線,
|
* 設定目前流程階段名稱,之後輸出的 log 會以 `[階段]` 前綴標示所屬階段。
|
||||||
* 用於在 log 中標示流程進入新階段。
|
* 傳入空字串可清除階段前綴。
|
||||||
*
|
*
|
||||||
* @param {string} title 區段標題文字。
|
* @param {string} name 階段名稱(顯示於每行 log 開頭的 `[階段]` 區塊)。
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function section(title) {
|
function stage(name) {
|
||||||
separator();
|
currentStage = name;
|
||||||
process.stdout.write(`${title}\n`);
|
|
||||||
process.stdout.write('--------------------------------------------------\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,45 +33,59 @@ function timestamp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以 `[INF][時間]:` 前綴將一般資訊訊息輸出到 stdout(時間為 Asia/Taipei)。
|
* 依統一格式組出一行 log:`[階段?][等級][時間]: 訊息`。
|
||||||
|
* 階段取自目前 currentStage(為空字串時省略整個 `[階段]` 區塊);
|
||||||
|
* 時間為 Asia/Taipei 的 `yyyy/MM/dd HH:mm:ss`。
|
||||||
|
*
|
||||||
|
* @param {string} level 等級碼(INF/WRN/ERR)。
|
||||||
|
* @param {string} msg 訊息內容。
|
||||||
|
* @returns {string} 組裝後的 log 字串。
|
||||||
|
*/
|
||||||
|
function formatLog(level, msg) {
|
||||||
|
const stagePart = currentStage ? `[${currentStage}]` : '';
|
||||||
|
return `${stagePart}[${level}][${timestamp()}]: ${msg}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以 `[階段?][INF][時間]:` 前綴將一般資訊訊息輸出到 stdout(時間為 Asia/Taipei)。
|
||||||
*
|
*
|
||||||
* @param {string} msg 要輸出的訊息內容。
|
* @param {string} msg 要輸出的訊息內容。
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function info(msg) {
|
function info(msg) {
|
||||||
console.log(`[INF][${timestamp()}]: ${msg}`);
|
console.log(formatLog('INF', msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以 `[INF][時間]:` 前綴將成功訊息輸出到 stdout(時間為 Asia/Taipei)。
|
* 以 `[階段?][INF][時間]:` 前綴將成功訊息輸出到 stdout(時間為 Asia/Taipei)。
|
||||||
* 規範等級碼無「成功」,故等級採 INF,成功語意保留於訊息文字。
|
* 規範等級碼無「成功」,故等級採 INF,成功語意保留於訊息文字。
|
||||||
*
|
*
|
||||||
* @param {string} msg 要輸出的訊息內容。
|
* @param {string} msg 要輸出的訊息內容。
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function success(msg) {
|
function success(msg) {
|
||||||
console.log(`[INF][${timestamp()}]: ${msg}`);
|
console.log(formatLog('INF', msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以 `[WRN][時間]:` 前綴將警告訊息輸出到 stdout(時間為 Asia/Taipei)。
|
* 以 `[階段?][WRN][時間]:` 前綴將警告訊息輸出到 stdout(時間為 Asia/Taipei)。
|
||||||
*
|
*
|
||||||
* @param {string} msg 要輸出的訊息內容。
|
* @param {string} msg 要輸出的訊息內容。
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function warn(msg) {
|
function warn(msg) {
|
||||||
console.log(`[WRN][${timestamp()}]: ${msg}`);
|
console.log(formatLog('WRN', msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以 `[ERR][時間]:` 前綴將錯誤訊息輸出到 stderr(時間為 Asia/Taipei)。
|
* 以 `[階段?][ERR][時間]:` 前綴將錯誤訊息輸出到 stderr(時間為 Asia/Taipei)。
|
||||||
* 僅負責輸出,不會結束程序(是否離開由呼叫端決定)。
|
* 僅負責輸出,不會結束程序(是否離開由呼叫端決定)。
|
||||||
*
|
*
|
||||||
* @param {string} msg 要輸出的錯誤訊息內容。
|
* @param {string} msg 要輸出的錯誤訊息內容。
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function fail(msg) {
|
function fail(msg) {
|
||||||
console.error(`[ERR][${timestamp()}]: ${msg}`);
|
console.error(formatLog('ERR', msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,8 +101,8 @@ function isEmptyOrNull(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 檢查必填值。先以 [INFO] 印出 name=value,
|
* 檢查必填值。先以 info([INF])印出 name=value,
|
||||||
* 若值被判定為空(見 isEmptyOrNull),印出 [ERR] 並以 exit code 1 結束程序。
|
* 若值被判定為空(見 isEmptyOrNull),以 fail([ERR])輸出並以 exit code 1 結束程序。
|
||||||
*
|
*
|
||||||
* @param {string} name 參數名稱(用於 log 與錯誤訊息)。
|
* @param {string} name 參數名稱(用於 log 與錯誤訊息)。
|
||||||
* @param {*} value 要驗證的參數值。
|
* @param {*} value 要驗證的參數值。
|
||||||
@@ -157,7 +166,9 @@ async function main() {
|
|||||||
const GITEA_TOKEN = process.env.GITEA_TOKEN;
|
const GITEA_TOKEN = process.env.GITEA_TOKEN;
|
||||||
const KEEP_COUNT_RAW = process.env.KEEP_COUNT;
|
const KEEP_COUNT_RAW = process.env.KEEP_COUNT;
|
||||||
|
|
||||||
section('參數檢查');
|
// 於容器啟動橫幅與後續 log 之間留一空行,方便在 CI log 中閱讀
|
||||||
|
process.stdout.write('\n');
|
||||||
|
stage('參數檢查');
|
||||||
requireValue('GITEA_SERVER_URL', GITEA_SERVER_URL);
|
requireValue('GITEA_SERVER_URL', GITEA_SERVER_URL);
|
||||||
requireValue('GITEA_REPOSITORY', GITEA_REPOSITORY);
|
requireValue('GITEA_REPOSITORY', GITEA_REPOSITORY);
|
||||||
requireValue('KEEP_COUNT', KEEP_COUNT_RAW);
|
requireValue('KEEP_COUNT', KEEP_COUNT_RAW);
|
||||||
@@ -225,7 +236,7 @@ async function main() {
|
|||||||
|
|
||||||
const releaseApiUrl = `${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases`;
|
const releaseApiUrl = `${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases`;
|
||||||
|
|
||||||
section('取得成品資訊');
|
stage('取得舊版本');
|
||||||
info(`GET ${releaseApiUrl}`);
|
info(`GET ${releaseApiUrl}`);
|
||||||
|
|
||||||
let releases = await fetchAllPages(releaseApiUrl);
|
let releases = await fetchAllPages(releaseApiUrl);
|
||||||
@@ -251,7 +262,7 @@ async function main() {
|
|||||||
if (releasesToDelete.length === 0) {
|
if (releasesToDelete.length === 0) {
|
||||||
success('沒有需要清理的舊版本成品');
|
success('沒有需要清理的舊版本成品');
|
||||||
} else {
|
} else {
|
||||||
section('刪除舊版本成品');
|
stage('刪除舊版本');
|
||||||
info(`DELETE_RELEASE_COUNT=${releasesToDelete.length}`);
|
info(`DELETE_RELEASE_COUNT=${releasesToDelete.length}`);
|
||||||
|
|
||||||
for (const release of releasesToDelete) {
|
for (const release of releasesToDelete) {
|
||||||
@@ -276,7 +287,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section('刪除未指定 release 的 tag');
|
stage('刪除舊版本');
|
||||||
|
|
||||||
// 重新取得 release 清單,得到刪除舊版本後仍指定 tag 的成品
|
// 重新取得 release 清單,得到刪除舊版本後仍指定 tag 的成品
|
||||||
const currentReleases = await fetchAllPages(releaseApiUrl);
|
const currentReleases = await fetchAllPages(releaseApiUrl);
|
||||||
@@ -311,8 +322,6 @@ async function main() {
|
|||||||
fail(`刪除 tag 失敗: ${tagName}, HTTP ${code}`);
|
fail(`刪除 tag 失敗: ${tagName}, HTTP ${code}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
separator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user