JefferyandClaude Opus 4.8 15381d07a7 docs(release-cleanup): 補齊 CI workflow 註解、統一日誌格式並重建 README
- .gitea/workflows/ci.yaml、cd.yaml:新增用途/更新日期區塊與逐行註解(指令邏輯不變)
- app/index.js:日誌統一為 [等級][台灣時間]: 訊息(新增 timestamp helper,INF/WRN/ERR;行為、控制流、exit code、stdout/stderr 皆等價)
- README.md:重建專案列表三表/功能列表/使用範例,新增日誌格式說明與 timestamp 功能

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:03:10 +08:00

Release Cleanup

更新時間:2026/06/30 17:58:12(台灣時區 Asia/Taipei

清理舊成品的 Gitea / GitHub docker container action。依保留數量清理 repo 的舊 release,並刪除沒有對應 release 的 tag;正式版與 beta 版各自獨立保留指定數量。

  • 主程式語言:JavaScriptNode.js,使用原生 fetch,需 Node 18+
  • 執行方式:docker container actionaction.yamlDockerfileentrypoint.shnode /app/index.js

行為總覽

  1. 參數檢查:驗證 GITEA_SERVER_URLGITEA_REPOSITORYKEEP_COUNT(缺值或 KEEP_COUNT 非整數時以 exit code 1 結束)。
  2. 取得成品資訊:取得所有 release,依建立時間由新到舊排序,並以 tag 名稱含 -beta. 與否分為正式版與 beta 版。
  3. 刪除舊版本成品:正式版與 beta 版各自保留最新 KEEP_COUNT 筆,刪除其餘 release(單筆刪除失敗只記錄錯誤,不中斷)。
  4. 刪除未指定 release 的 tag:重新取得 release 後,刪除所有未被任何 release 指定的 tag。

GITEA_TOKEN 為空時以匿名身分呼叫 API。取得分頁失敗或參數不合法則直接以 exit code 1 結束程序。

日誌輸出格式

所有等級式輸出統一為 [{等級}][{時間}]: {訊息}(等級為 INF/WRN/ERR,時間為 Asia/Taipei 的 yyyy/MM/dd HH:mm:ss);fail 走 stderr,其餘走 stdout。例如:

[INF][2026/06/30 17:58:12]: RELEASE_COUNT=42
[WRN][2026/06/30 17:58:12]: GITEA_TOKEN is empty; release API calls will be anonymous
[ERR][2026/06/30 17:58:12]: 刪除失敗: v1.0.0 (Release 1.0.0), HTTP 500

註:separator / section 為純分隔線/標題輸出,不帶等級與時間戳。

Action 輸入與環境變數

名稱 來源 必填 預設 說明
keep_count inputs 2 正式版與 beta 版各自要保留的成品數量
GITEA_SERVER_URL envgitea.server_url Gitea 站台 URL
GITEA_REPOSITORY envgitea.repository owner/repo 形式的儲存庫
GITEA_TOKEN envgitea.token API token;為空時以匿名呼叫

Workflow 使用範例

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Release Cleanup
        uses: docker-actions/release-cleanup@develop
        with:
          keep_count: 3

專案列表

專案描述

專案名稱 專案描述
release-cleanup 清理 Gitea repo 舊 release 與孤兒 tag 的 docker container action;正式版與 beta 版各自保留指定數量,並以統一等級式日誌輸出處理過程

參考專案

專案名稱 參考專案列表
release-cleanup

NuGet 套件

專案名稱 NuGet 套件列表
release-cleanup

註:本專案為 Node.js 專案,無 .NET ProjectReference 與 NuGet 套件;app/package.json 亦無第三方相依(僅使用 Node 內建 API)。

功能列表

release-cleanup

說明:本專案為單一 Node 模組(app/index.js),下列為模組內可文件化的頂層函式(不含 main 內部的巢狀私有函式 fetchAllPages / deleteUrl)。

功能名稱 功能描述
separator 在 stdout 輸出視覺分隔線
section 輸出區段標題區塊
timestamp 取得 Asia/Taipei 時間字串
info 以 [INF] 等級輸出資訊訊息
success 以 [INF] 等級輸出成功訊息
warn 以 [WRN] 等級輸出警告訊息
fail 以 [ERR] 等級輸出錯誤訊息到 stderr
isEmptyOrNull 判斷值是否視為空 / 未設定
requireValue 檢查必填值,空值時結束程序
requireInteger 驗證非負整數,不符時結束程序
isBeta 判斷名稱是否為 beta 版本
main Action 主流程:清理舊 release 與孤兒 tag

使用範例

separator

在 stdout 輸出一條前後帶換行、由 50 個等號組成的視覺分隔線,用於在 log 中切割區段。無參數、無回傳值。

separator();
// 輸出:
//
// ==================================================

section

輸出一個區段標題區塊:上緣等號分隔線、標題文字、下緣虛線,用於標示流程進入新階段。

section('參數檢查');
// 輸出:
// ==================================================
// 參數檢查
// --------------------------------------------------

timestamp

取得目前 Asia/Taipei 時區的時間字串,格式為 yyyy/MM/dd HH:mm:ss。使用 Node 內建 IntltoLocaleString('sv-SE') 輸出 24 小時制再把 - 換成 /),不需額外相依。供各等級式 log helper 組裝統一格式前綴使用。

timestamp();
// 例如回傳: '2026/06/30 17:58:12'
info(`RELEASE_COUNT=42`);
// 內部即以 timestamp() 組出: [INF][2026/06/30 17:58:12]: RELEASE_COUNT=42

前置條件:執行環境須支援 Intl(Node 18+ 內建)。結果:回傳當下台灣時間字串,無副作用。

info

[INF][時間]: 前綴將一般資訊訊息輸出到 stdout(console.log,時間為 Asia/Taipei)。

info('RELEASE_COUNT=10');
// 輸出:[INF][2026/06/30 17:58:12]: RELEASE_COUNT=10

success

[INF][時間]: 前綴將成功訊息輸出到 stdout。規範等級碼無「成功」一項,故等級採 INF,成功語意保留於訊息文字。

success('成功刪除: v1.0.0 (Release 1.0.0)');
// 輸出:[INF][2026/06/30 17:58:12]: 成功刪除: v1.0.0 (Release 1.0.0)

warn

[WRN][時間]: 前綴將警告訊息輸出到 stdout(非致命情況,程式會繼續執行)。

warn('GITEA_TOKEN is empty; release API calls will be anonymous');
// 輸出:[WRN][2026/06/30 17:58:12]: GITEA_TOKEN is empty; release API calls will be anonymous

fail

[ERR][時間]: 前綴將錯誤訊息輸出到 stderrconsole.error)。僅負責輸出,不會結束程序;是否離開由呼叫端決定。

fail('刪除失敗: v0.1.0 (Release 0.1.0), HTTP 500');
// 輸出(stderr):[ERR][2026/06/30 17:58:12]: 刪除失敗: v0.1.0 (Release 0.1.0), HTTP 500
process.exit(1); // 終止由呼叫端負責

isEmptyOrNull

判斷值是否視為「空 / 未設定」,涵蓋 undefinednull、空字串,以及字面字串 'null'(用於處理 CI/環境變數把未設定值帶成字串 "null" 的情況)。

isEmptyOrNull('');      // true
isEmptyOrNull('null');  // true(字面字串)
isEmptyOrNull('abc');   // false

requireValue

檢查必填值。先以 [INF] 印出 name=value,若值被判定為空則以 [ERR] 輸出並以 exit code 1 結束程序。

requireValue('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL);
// 值為空 → 輸出 [ERR][時間]: GITEA_SERVER_URL is required,並 process.exit(1)

前置條件:應在程式啟動早期呼叫。注意此函式會將值原樣印到 stdout,故不用於機密值(程式對 GITEA_TOKEN 改以 redacted 方式輸出)。

requireInteger

驗證值是否為非負整數字串(正則 /^[0-9]+$/,允許前導零、不允許負號或小數),不符合時以 [ERR] 輸出並以 exit code 1 結束程序。

requireInteger('KEEP_COUNT', '2');   // 通過
requireInteger('KEEP_COUNT', '-1');  // 輸出 [ERR][時間]: ... 並 process.exit(1)

isBeta

判斷 release / tag 名稱是否為 beta 版本,規則為名稱中含有子字串 -beta.

isBeta('v1.2.3-beta.1'); // true
isBeta('v1.2.3');        // false
isBeta('v1.2.3-beta');   // false(無結尾的點,不符合 -beta.)

main

Action 主流程(async)。讀取環境變數後依序執行參數檢查、取得 release、刪除舊版本(正式版與 beta 版各自保留 KEEP_COUNT 筆)、刪除未指定 release 的 tag。由檔尾 main().catch(...) 立即執行。

// 容器啟動時由 entrypoint.sh 執行:exec node /app/index.js
// 需提供環境變數:
//   GITEA_SERVER_URL、GITEA_REPOSITORY、KEEP_COUNT(必要)、GITEA_TOKEN(選填)
main().catch((err) => {
  fail(err && err.stack ? err.stack : String(err));
  process.exit(1);
});

前置條件:執行環境須提供必要環境變數,且可連線到 Gitea API。結果:清理符合條件的舊 release 與孤兒 tag,並以統一等級式日誌輸出處理過程。

2026-07-01 01:36:02 +00:00
Languages
JavaScript 66.8%
Dockerfile 18.1%
Shell 15.1%