Files
release-cleanup/README.md
T
JefferyandClaude Opus 4.8 fcf6b0838b
CI / Release Tag Version (pull_request) Successful in 2m4s
CI / Release Cleanup (pull_request) Successful in 4s
docs(README): 配合日誌格式調整重建功能列表與使用範例
- 移除 separator/section、新增 stage/formatLog 條目與錨點
- 更新「日誌輸出格式」章節與各範例為 [階段][等級][時間],同步函式起始行號

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 09:07:05 +08:00

247 lines
11 KiB
Markdown
Raw Permalink 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/07/01 09:00:53(台灣時區 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(單筆刪除失敗只記錄錯誤,不中斷);接著重新取得 release 後,刪除所有未被任何 release 指定的 tag。
`GITEA_TOKEN` 為空時以匿名身分呼叫 API。取得分頁失敗或參數不合法則直接以 exit code 1 結束程序。
## 日誌輸出格式
所有等級式輸出統一為 `[{階段}][{等級}][{時間}]: {訊息}`
- `階段`:目前流程階段(`參數檢查` / `取得舊版本` / `刪除舊版本`),由 `stage()` 設定;未設定時省略整個 `[階段]` 區塊。
- `等級``INF`(含成功訊息)/ `WRN` / `ERR`
- `時間`Asia/Taipei 的 `yyyy/MM/dd HH:mm:ss`
- `fail` 走 stderr,其餘走 stdout。
範例:
```text
[參數檢查][INF][2026/07/01 09:00:53]: GITEA_SERVER_URL=https://gitea.jsc.idv.tw
[取得舊版本][INF][2026/07/01 09:00:53]: RELEASE_COUNT=3
[取得舊版本][INF][2026/07/01 09:00:53]: 沒有需要清理的舊版本成品
[刪除舊版本][ERR][2026/07/01 09:00:53]: 刪除 tag 失敗: v0.0.1, HTTP 500
```
> 容器啟動橫幅(名稱/用途/更新時間)由 `entrypoint.sh` 輸出,不帶等級與階段前綴。
## 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`)。
| 功能名稱 | 功能描述 |
| --- | --- |
| [stage](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L18) | [設定目前流程階段名稱(log 的 \[階段\] 前綴)](#stage) |
| [timestamp](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L29) | [取得 Asia/Taipei 時間字串](#timestamp) |
| [formatLog](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L44) | [組出統一格式的 log 字串](#formatlog) |
| [info](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L55) | [以 \[INF\] 等級輸出資訊訊息](#info) |
| [success](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L66) | [以 \[INF\] 等級輸出成功訊息](#success) |
| [warn](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L76) | [以 \[WRN\] 等級輸出警告訊息](#warn) |
| [fail](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L87) | [以 \[ERR\] 等級輸出錯誤訊息到 stderr](#fail) |
| [isEmptyOrNull](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L99) | [判斷值是否視為空 / 未設定](#isemptyornull) |
| [requireValue](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L112) | [檢查必填值,空值時結束程序](#requirevalue) |
| [requireInteger](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L129) | [驗證非負整數,不符時結束程序](#requireinteger) |
| [isBeta](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L143) | [判斷名稱是否為 beta 版本](#isbeta) |
| [main](https://gitea.jsc.idv.tw/docker-actions/release-cleanup/src/branch/develop/app/index.js#L163) | [Action 主流程:清理舊 release 與孤兒 tag](#main) |
## 使用範例
<a id="stage"></a>
### stage
設定目前流程階段名稱,之後 `info`/`success`/`warn`/`fail` 輸出的每行 log 會以 `[階段]` 前綴標示所屬階段;傳入空字串可清除前綴。
```js
stage('取得舊版本');
info('RELEASE_COUNT=3');
// 輸出:[取得舊版本][INF][2026/07/01 09:00:53]: RELEASE_COUNT=3
```
> 前置條件:無。結果:更新模組層級的目前階段字串;本身不輸出任何內容。
<a id="timestamp"></a>
### timestamp
取得目前 **Asia/Taipei** 時區的時間字串,格式為 `yyyy/MM/dd HH:mm:ss`。使用 Node 內建 `Intl``toLocaleString('sv-SE')` 輸出 24 小時制再把 `-` 換成 `/`),不需額外相依。供 `formatLog` 組裝統一格式前綴使用。
```js
timestamp();
// 例如回傳: '2026/07/01 09:00:53'
```
> 前置條件:執行環境須支援 `Intl`(Node 18+ 內建)。結果:回傳當下台灣時間字串,無副作用。
<a id="formatlog"></a>
### formatLog
依統一格式組出一行 log 字串:`[階段?][等級][時間]: 訊息`。階段取自目前 `currentStage`(為空字串時省略整個 `[階段]` 區塊),時間為 Asia/Taipei。為 `info`/`success`/`warn`/`fail` 共用的內部格式化函式,本身不輸出。
```js
stage('刪除舊版本');
formatLog('ERR', '刪除 tag 失敗: v0.0.1, HTTP 500');
// 回傳: '[刪除舊版本][ERR][2026/07/01 09:00:53]: 刪除 tag 失敗: v0.0.1, HTTP 500'
```
<a id="info"></a>
### info
`[階段?][INF][時間]:` 前綴將一般資訊訊息輸出到 stdout(`console.log`)。
```js
stage('取得舊版本');
info('RELEASE_COUNT=10');
// 輸出:[取得舊版本][INF][2026/07/01 09:00:53]: RELEASE_COUNT=10
```
<a id="success"></a>
### success
`[階段?][INF][時間]:` 前綴將成功訊息輸出到 stdout。規範等級碼無「成功」一項,故等級採 `INF`,成功語意保留於訊息文字。
```js
stage('取得舊版本');
success('沒有需要清理的舊版本成品');
// 輸出:[取得舊版本][INF][2026/07/01 09:00:53]: 沒有需要清理的舊版本成品
```
<a id="warn"></a>
### warn
`[階段?][WRN][時間]:` 前綴將警告訊息輸出到 stdout(非致命情況,程式會繼續執行)。
```js
stage('參數檢查');
warn('GITEA_TOKEN is empty; release API calls will be anonymous');
// 輸出:[參數檢查][WRN][2026/07/01 09:00:53]: GITEA_TOKEN is empty; release API calls will be anonymous
```
<a id="fail"></a>
### fail
`[階段?][ERR][時間]:` 前綴將錯誤訊息輸出到 **stderr**`console.error`)。僅負責輸出,不會結束程序;是否離開由呼叫端決定。
```js
stage('刪除舊版本');
fail('刪除 tag 失敗: v0.0.1, HTTP 500');
// 輸出(stderr):[刪除舊版本][ERR][2026/07/01 09:00:53]: 刪除 tag 失敗: v0.0.1, 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
檢查必填值。先以 `info``[INF]`)印出 `name=value`,若值被判定為空則以 `fail``[ERR]`)輸出並以 exit code 1 結束程序。
```js
stage('參數檢查');
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;各階段以 `stage()` 標示,log 皆帶 `[階段]` 前綴。由檔尾 `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,並以統一的 `[階段][等級][時間]` 日誌輸出處理過程。