Files
release-cleanup/README.md
T
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

238 lines
10 KiB
Markdown
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.
# 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 action`action.yaml``Dockerfile``entrypoint.sh``node /app/index.js`
## 行為總覽
1. **參數檢查**:驗證 `GITEA_SERVER_URL``GITEA_REPOSITORY``KEEP_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。例如:
```text
[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` | env`gitea.server_url` | 是 | — | Gitea 站台 URL |
| `GITEA_REPOSITORY` | env`gitea.repository` | 是 | — | `owner/repo` 形式的儲存庫 |
| `GITEA_TOKEN` | env`gitea.token` | 否 | — | API token;為空時以匿名呼叫 |
### Workflow 使用範例
```yaml
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Release Cleanup
uses: docker-actions/release-cleanup@develop
with:
keep_count: 3
```
## 專案列表
### 專案描述
| 專案名稱 | 專案描述 |
| --- | --- |
| [release-cleanup](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app) | 清理 Gitea repo 舊 release 與孤兒 tag 的 docker container action;正式版與 beta 版各自保留指定數量,並以統一等級式日誌輸出處理過程 |
### 參考專案
| 專案名稱 | 參考專案列表 |
| --- | --- |
| [release-cleanup](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app) | 無 |
### NuGet 套件
| 專案名稱 | NuGet 套件列表 |
| --- | --- |
| [release-cleanup](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app) | 無 |
> 註:本專案為 Node.js 專案,無 .NET ProjectReference 與 NuGet 套件;`app/package.json` 亦無第三方相依(僅使用 Node 內建 API)。
## 功能列表
### release-cleanup
> 說明:本專案為單一 Node 模組(`app/index.js`),下列為模組內可文件化的頂層函式(不含 `main` 內部的巢狀私有函式 `fetchAllPages` / `deleteUrl`)。
| 功能名稱 | 功能描述 |
| --- | --- |
| [separator](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L10) | [在 stdout 輸出視覺分隔線](#separator) |
| [section](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L21) | [輸出區段標題區塊](#section) |
| [timestamp](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L34) | [取得 Asia/Taipei 時間字串](#timestamp) |
| [info](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L46) | [以 \[INF\] 等級輸出資訊訊息](#info) |
| [success](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L57) | [以 \[INF\] 等級輸出成功訊息](#success) |
| [warn](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L67) | [以 \[WRN\] 等級輸出警告訊息](#warn) |
| [fail](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L78) | [以 \[ERR\] 等級輸出錯誤訊息到 stderr](#fail) |
| [isEmptyOrNull](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L90) | [判斷值是否視為空 / 未設定](#isemptyornull) |
| [requireValue](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L103) | [檢查必填值,空值時結束程序](#requirevalue) |
| [requireInteger](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L120) | [驗證非負整數,不符時結束程序](#requireinteger) |
| [isBeta](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L134) | [判斷名稱是否為 beta 版本](#isbeta) |
| [main](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L154) | [Action 主流程:清理舊 release 與孤兒 tag](#main) |
## 使用範例
<a id="separator"></a>
### separator
在 stdout 輸出一條前後帶換行、由 50 個等號組成的視覺分隔線,用於在 log 中切割區段。無參數、無回傳值。
```js
separator();
// 輸出:
//
// ==================================================
```
<a id="section"></a>
### section
輸出一個區段標題區塊:上緣等號分隔線、標題文字、下緣虛線,用於標示流程進入新階段。
```js
section('參數檢查');
// 輸出:
// ==================================================
// 參數檢查
// --------------------------------------------------
```
<a id="timestamp"></a>
### timestamp
取得目前 **Asia/Taipei** 時區的時間字串,格式為 `yyyy/MM/dd HH:mm:ss`。使用 Node 內建 `Intl``toLocaleString('sv-SE')` 輸出 24 小時制再把 `-` 換成 `/`),不需額外相依。供各等級式 log helper 組裝統一格式前綴使用。
```js
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+ 內建)。結果:回傳當下台灣時間字串,無副作用。
<a id="info"></a>
### info
`[INF][時間]:` 前綴將一般資訊訊息輸出到 stdout(`console.log`,時間為 Asia/Taipei)。
```js
info('RELEASE_COUNT=10');
// 輸出:[INF][2026/06/30 17:58:12]: RELEASE_COUNT=10
```
<a id="success"></a>
### success
`[INF][時間]:` 前綴將成功訊息輸出到 stdout。規範等級碼無「成功」一項,故等級採 `INF`,成功語意保留於訊息文字。
```js
success('成功刪除: v1.0.0 (Release 1.0.0)');
// 輸出:[INF][2026/06/30 17:58:12]: 成功刪除: v1.0.0 (Release 1.0.0)
```
<a id="warn"></a>
### warn
`[WRN][時間]:` 前綴將警告訊息輸出到 stdout(非致命情況,程式會繼續執行)。
```js
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
```
<a id="fail"></a>
### fail
`[ERR][時間]:` 前綴將錯誤訊息輸出到 **stderr**`console.error`)。僅負責輸出,不會結束程序;是否離開由呼叫端決定。
```js
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); // 終止由呼叫端負責
```
<a id="isemptyornull"></a>
### isEmptyOrNull
判斷值是否視為「空 / 未設定」,涵蓋 `undefined``null`、空字串,以及字面字串 `'null'`(用於處理 CI/環境變數把未設定值帶成字串 `"null"` 的情況)。
```js
isEmptyOrNull(''); // true
isEmptyOrNull('null'); // true(字面字串)
isEmptyOrNull('abc'); // false
```
<a id="requirevalue"></a>
### requireValue
檢查必填值。先以 `[INF]` 印出 `name=value`,若值被判定為空則以 `[ERR]` 輸出並以 exit code 1 結束程序。
```js
requireValue('GITEA_SERVER_URL', process.env.GITEA_SERVER_URL);
// 值為空 → 輸出 [ERR][時間]: GITEA_SERVER_URL is required,並 process.exit(1)
```
> 前置條件:應在程式啟動早期呼叫。注意此函式會將值原樣印到 stdout,故不用於機密值(程式對 `GITEA_TOKEN` 改以 redacted 方式輸出)。
<a id="requireinteger"></a>
### requireInteger
驗證值是否為非負整數字串(正則 `/^[0-9]+$/`,允許前導零、不允許負號或小數),不符合時以 `[ERR]` 輸出並以 exit code 1 結束程序。
```js
requireInteger('KEEP_COUNT', '2'); // 通過
requireInteger('KEEP_COUNT', '-1'); // 輸出 [ERR][時間]: ... 並 process.exit(1)
```
<a id="isbeta"></a>
### isBeta
判斷 release / tag 名稱是否為 beta 版本,規則為名稱中含有子字串 `-beta.`
```js
isBeta('v1.2.3-beta.1'); // true
isBeta('v1.2.3'); // false
isBeta('v1.2.3-beta'); // false(無結尾的點,不符合 -beta.)
```
<a id="main"></a>
### main
Action 主流程(`async`)。讀取環境變數後依序執行參數檢查、取得 release、刪除舊版本(正式版與 beta 版各自保留 `KEEP_COUNT` 筆)、刪除未指定 release 的 tag。由檔尾 `main().catch(...)` 立即執行。
```js
// 容器啟動時由 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,並以統一等級式日誌輸出處理過程。