Files
code/skills/code-action-docker/SKILL.md
T

225 lines
16 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.
---
name: code-action-docker
description: 以互動問答從零打造 GiteaGitHub「Docker 容器 action」:(1) 詢問 action 名稱(用於 action.yml 的 name);(2) 詢問輸入與輸出參數及其 description(盡量繁體中文、無亂碼);(3) 詢問執行此流程的目標,整理濃縮成一句話(用於 action.yml 的 description,盡量繁體中文、無亂碼);(4) 依標準實作目標——開發中需要新參數時依 `${{ gitea.* }}` → `${{ secrets.* }}` → `${{ vars.* }}` 順序尋找、都沒有才詢問使用者是否加入 inputs;盡量以 Node.js 開發、程式檔一律放 `src/`、盡量詳細輸出訊息且格式統一為 `[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息`(無階段則移除該 `[]`,等級為 INF/WRN/ERR/TRC/DBG),並產生 action.ymlentrypoint.sh/最新 node 版本的 Dockerfile(5) 檢查是否有 doc-funcs 技能,有則完整執行 `/jsc:doc-funcs`,否則結束。當使用者說建立/產生一個 docker action、從零做一個容器 action、幫我問完需求後生出 action、把需求做成 node docker action,或提到 code-action-docker 時觸發。不適用於:composite action(用 code-action-composite)、只整理既有 Dockerfile(用 code-image)、非 action 專案。
argument-hint: "[--action-dir <action 根目錄>] [--node-version <node tag>]"
---
# code-action-docker — 問答式打造 Docker 容器 actionNode 主程式)+文件化
五階段 skill:先以問答收齊需求——**action 名稱**(階段 1)、**輸入與輸出參數**(階段 2)、**流程目標並濃縮成一句話**(階段 3)——再依標準**實作目標**(階段 4:Node.js、程式檔一律放 `src/`、統一訊息格式,並產生 `action.yml``entrypoint.sh`/最新 node 版本的 `Dockerfile`),最後**檢查是否有 doc-funcs 技能**(階段 5):有則完整執行 `/jsc:doc-funcs`,否則結束。
| 階段 | 動作 |
| --- | --- |
| 1. 詢問 action 名稱 | 向使用者詢問 action 名稱 → 寫入 `action.yml``name` |
| 2. 詢問輸入與輸出參數 | 逐一收齊 `inputs``outputs`(名稱、description、required、default)→ description 盡量繁體中文、無亂碼 |
| 3. 詢問流程目標 | 詢問此 action 要達成什麼 → 整理並**濃縮成一句話**,經使用者確認後寫入 `action.yml``description`(盡量繁體中文、無亂碼) |
| 4. 實作目標 | 盡量以 **Node.js** 開發、程式檔**一律放 `src/`**;訊息詳細且格式統一為 `[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息`;產生 `action.yml``entrypoint.sh`/最新 node 版本的 `Dockerfile` |
| 5. 檢查 doc-funcs | 有 doc-funcs 技能 → 完整執行 `/jsc:doc-funcs`;沒有 → 回報後結束 |
---
## 輸出規範(務必遵守)
- **語言**:所有面向使用者的輸出(問題、計畫、進度、總結)一律使用**繁體中文(台灣用語)**;僅識別字、檔名、git/docker 指令、API 路徑、程式碼等技術標識保留原文,**不可**使用簡體字。
- **編碼無亂碼**:凡輸出含繁體中文、全形標點、emoji,一律 **UTF-8(不含 BOM**,不得出現問號方框或錯碼。產生的 `action.yml``src/*.js``entrypoint.sh``Dockerfile``README.md` 同樣需 UTF-8(不含 BOM);`action.yml` 內的中文 `name``description`/參數說明必須能被 YAML 正確解析與顯示。
- **問答原則**:階段 1~3 是**必要輸入**,缺一不可;使用者已在呼叫時提供的答案不再重複詢問,未提供的必須以 `AskUserQuestion`(或明確反問)取得,**不得臆測代答**。階段 4 起除必要決策外直接執行到完成。
- **不破壞既有工作**:若 action 根目錄已存在 `action.yml``src/``entrypoint.sh``Dockerfile`,覆寫前先回報並確認;工作區有未提交變更時提醒使用者先 commit/備份;**絕不** `reset --hard``checkout -f``clean`,也不刪除使用者既有原始碼。
- **不擴及無關檔案**:本 skill 只產生/修改 action 根目錄內的 `action.yml``src/``entrypoint.sh``Dockerfile``package.json`,以及階段 5 由 doc-funcs 流程處理的目標;排除 `node_modules``.git``.docs``bin``obj`/第三方依賴。
---
## 參數
格式:`[--action-dir <action 根目錄>] [--node-version <node tag>]`
- `--action-dir <action 根目錄>`:action 專案根目錄。**省略時預設目前工作目錄**。
- `--node-version <node tag>``Dockerfile` 的 node base image tag(如 `22``22-alpine``lts`)。**省略時預設使用最新 node 版本**(`node:latest`,見階段 4)。
階段 1~3 的答案(名稱、輸入輸出參數、目標)不設 CLI 參數;使用者若已在呼叫訊息中口述,直接採用、不再重問。
---
## 階段 1:詢問 action 名稱
向使用者詢問 **action 名稱**,用於 `action.yml``name`
- 使用者已在呼叫時提供 → 直接採用並回報。
- 未提供 → 以 `AskUserQuestion`(自由輸入為主,可依目錄名/repo 名給 2~3 個建議選項)詢問,**不得自行編造**。
- 名稱不得為空;可為繁體中文或英文,原樣寫入 `name`,不擅自翻譯或改寫。
## 階段 2:詢問輸入與輸出參數
向使用者收齊 action 的**輸入(`inputs`)與輸出(`outputs`)參數**,逐項確認:
- **每個輸入**:參數名稱(建議 kebab-case)、`description`(**盡量繁體中文、無亂碼**)、是否必填(`required`)、預設值(`default`,選填)。
- **每個輸出**:參數名稱、`description`**盡量繁體中文、無亂碼**)。
- 使用者明確表示「無輸入」或「無輸出」→ 對應區塊留空(省略該區塊),不硬造參數。
- description 缺漏時**反問補齊**,不編造;使用者堅持留空則標示「(未提供)」。
- 收齊後輸出整理表(名稱/必填/預設值/說明)讓使用者確認,再進入階段 3。
實作對應(供階段 4 使用):輸入在容器內以 `INPUT_<大寫參數名>` 環境變數取得(`-``_`);輸出以附加 `name=value``$GITHUB_OUTPUT` 檔案寫出。
## 階段 3:詢問執行此流程的目標
向使用者詢問**此 action 要達成什麼目標**(要處理什麼、產出什麼、成功/失敗準則):
- 追問到「可實作」為止:不清楚的環節(資料來源、外部指令、失敗時的行為)必須問清,**不得臆測**。
- 將目標**整理並濃縮成一句話**(盡量繁體中文、無亂碼),向使用者確認後寫入 `action.yml``description`
- 完整目標描述(含細節與邊界)保留於回報與階段 4 的實作依據,不因濃縮而遺失。
## 階段 4:實作目標(Node.js、src/、統一訊息格式)
依階段 1~3 收齊的需求實作 action,遵守以下標準:
### 4.0 開發中需要參數時的來源優先序
實作過程中發現需要某個參數(例如 repo 資訊、token、環境設定)而階段 2 的 `inputs` 沒有時,**依下列順序尋找來源,前者可用就不往後找**:
1. **`${{ gitea.* }}` context**runner 內建 context(如 `gitea.repository``gitea.ref``gitea.actor``gitea.token`);docker action 執行期對應 runner 注入的 `GITHUB_*``GITEA_*` 環境變數(如 `GITHUB_REPOSITORY``GITHUB_REF`),主程式直接讀 `process.env` 即可。
2. **`${{ secrets.* }}`**repoorg 的 secrets(機敏值:token、密碼、金鑰)。
3. **`${{ vars.* }}`**repoorg 的 variables(非機敏設定值)。
4. **以上都沒有** → 以 `AskUserQuestion` 詢問使用者**是否要把該參數加入 `inputs`**(加入則回到階段 2 規格補齊名稱/descriptionrequireddefault,並同步 `action.yml`);**不得臆測預設值或硬編碼**。
注意:`secrets.*``vars.*` 無法在 `action.yml``inputs.default` 直接引用,須由呼叫端 workflow 以 `with:``env:` 傳入(如 `with: token: ${{ secrets.MY_TOKEN }}`);採用這類來源時,在 README 使用範例中示範呼叫端如何帶入,機敏值一律不落地、不輸出明文(log 需遮蔽)。
### 4.1 盡量以 Node.js 開發,程式檔一律放 `src/`
- 主程式與所有模組**盡量以 Node.js 撰寫**;只有目標必須依賴外部工具時,才以 `child_process``execFileSync``spawnSync`)在 Node 內呼叫,不另寫 shellpython 主程式。
- 所有 `.js``.mjs``.cjs`**一律放在 `src/` 資料夾內**,主程式入口統一為 `src/index.js`;需要相依套件時於 `src/` 建立 `package.json``main` 指向入口檔),不擅自新增與目標無關的相依。
- 輸入用 `process.env.INPUT_*` 讀取(必填缺漏 → 輸出 `ERR` 訊息並以非零 exit code 結束);輸出寫入 `process.env.GITHUB_OUTPUT`
### 4.2 盡量詳細輸出訊息,格式統一
- 執行過程**盡量詳細輸出**:開始/結束、讀到的輸入、每個關鍵步驟、外部指令與結果、寫出的輸出、警告與錯誤。
- 每一則訊息格式固定為:
```
[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息
```
- **時間**:台灣時區(Asia/Taipei),固定 `yyyy/MM/dd HH:mm:ss`。
- **階段**:該訊息所屬的流程階段名稱(如 `讀取輸入`、`執行`、`寫出結果`);**沒有階段時整個 `[階段]` 區塊移除**,即 `[yyyy/MM/dd HH:mm:ss][等級]: 訊息`。
- **等級**:必為 `INF``WRN``ERR``TRC``DBG` 其中之一(一般資訊 INF、警告 WRN、錯誤 ERR、細部追蹤 TRC、除錯 DBG)。
- **一行一則訊息**:每則訊息獨立一行輸出,不得多則併成一坨。
- 在 `src/logger.js` 實作共用 logger(產生時間戳、組合前綴、`INF`/`WRN`/`ERR`/`TRC`/`DBG` 各一個方法、可帶階段名稱),主程式一律經由它輸出;`ERR` 走 `console.error`,其餘走 `console.log`。
- 錯誤處理:可預期錯誤輸出 `ERR` 訊息並 `process.exit(1)`;未捕捉例外亦須被攔截(`process.on('uncaughtException'/'unhandledRejection')`)以 `ERR` 格式輸出後非零結束。
### 4.3 產生 action.yml
以階段 12 的答案組出 `action.yml`(中文內容 UTF-8、無亂碼):
```yaml
name: <階段 1 的 action 名稱>
description: <階段 3 濃縮的一句話>
inputs:
<參數名>:
description: <階段 2 的說明(盡量繁體中文)>
required: true|false
default: <預設值(若有)>
outputs:
<參數名>:
description: <階段 2 的說明(盡量繁體中文)>
runs:
using: docker
image: Dockerfile
entrypoint: entrypoint.sh
```
無輸入/輸出時省略對應區塊。
### 4.4 產生 entrypoint.sh
在 action 根目錄產生 `entrypoint.sh`:啟動 node 主程式前,先輸出 action **名稱/用途/更新時間**,且每則輸出同樣遵守 4.2 訊息格式(階段用 `啟動`):
```sh
#!/bin/sh
set -e
# action 啟動訊息:名稱/用途/更新時間(此檔由 code-action-docker 產生)
echo "[<產生當下 Asia/Taipei 時間>][啟動][INF]: Action<action 名稱>"
echo "[<產生當下 Asia/Taipei 時間>][啟動][INF]: 用途:<action.yml 的 description>"
echo "[<產生當下 Asia/Taipei 時間>][啟動][INF]: 更新時間:<產生當下 Asia/Taipei 時間>"
# 啟動 node 主程式(以 exec 取代 shell,正確處理訊號與 exit code
exec node /action/src/index.js "$@"
```
- 「更新時間」為產生此檔當下的時間戳(`TZ='Asia/Taipei' date +'%Y/%m/%d %H:%M:%S'`),**寫成檔內固定字串**;名稱/用途取自階段 1/3。
- 賦予可執行權限:`chmod +x entrypoint.sh`(並於 git 標記為可執行)。
### 4.5 產生 Dockerfile(使用最新 node 版本)
在 action 根目錄產生 `Dockerfile`**base image 使用最新 node 版本**,採**多階段建置**縮小最終映像,內容依 `src/` 實作量身決定(有相依才裝、需要 OS 工具才 `apt-get install`,用不到的不硬塞):
```Dockerfile
# syntax=docker/dockerfile:1
# 1. 參數處理:node 版本以 ARG 注入(預設最新版;--node-version 可覆寫)
ARG NODE_VERSION=latest
ARG NODE_RUNTIME=slim
# ---- build 階段:安裝相依 ----
FROM node:${NODE_VERSION} AS build
WORKDIR /action
# 2. 安裝套件:先帶相依描述以利 layer 快取(有 lockfile 用 npm ci
COPY src/package*.json /action/src/
RUN if [ -f /action/src/package.json ]; then \
cd /action/src && (npm ci --omit=dev || npm install --omit=dev); \
fi
# 3. 複製檔案:帶入主程式與入口
COPY src/ /action/src/
COPY entrypoint.sh /action/entrypoint.sh
# 4. 執行程序:賦予 entrypoint 執行權限
RUN chmod +x /action/entrypoint.sh
# 5. 縮小映像檔:runtime 改用 slim 基底,只帶必要產物
FROM node:${NODE_RUNTIME} AS runtime
WORKDIR /action
COPY --from=build /action /action
# 6. 設定入口:以 entrypoint.sh 啟動 node 主程式
ENTRYPOINT ["/action/entrypoint.sh"]
```
- **版號對齊**:預設 build 基底 `node:latest`、runtime 基底 `node:slim`**沒有 `node:latest-slim` 這個 tag**,故以獨立 `NODE_RUNTIME` 參數帶入);帶 `--node-version 22` 時設 `NODE_VERSION=22`、`NODE_RUNTIME=22-slim`。
- 主程式以 `child_process` 呼叫外部執行檔(如 `git`、`openssl`)時,於 runtime 階段補 `apt-get install` 對應套件並清快取。
- **自我檢查**`entrypoint.sh` 的 `node /action/src/index.js` 路徑須與 `Dockerfile` 的 `WORKDIR``COPY` 落點一致。
### 4.6 驗證
- `node --check src/*.js` 驗證語法;有 `package.json` 時執行 `npm install`(或 `npm ci`)確認相依可解析。
- 環境有 docker 時執行 `docker build .` 驗證映像可建置;無法執行時明確說明原因並標註風險。
## 階段 5:檢查是否有 doc-funcs 技能
實作完成後,檢查目前環境是否有 **doc-funcs** 技能(可用 skill 清單中是否存在 `doc-funcs`,含 `/jsc:doc-funcs` 等帶前綴形式):
- **有** → 以階段 4 的 action 根目錄為目標,**完整執行 `/jsc:doc-funcs` 流程**(判斷語言 → 掃描 function 與指令檔 → 建立 `.docs/` 草稿 → 詢問使用者如何實作 → 寫回 → 保守優化 → 重建 README → 清理草稿 → 驗證)。doc-funcs 會涵蓋本次產出:`src/` 內各 function 的 JSDoc、`entrypoint.sh``Dockerfile` 逐行註解、重建 README;其「如何實作」詢問由使用者於該流程內裁示。既有訊息一律維持本 skill 4.2 的 `[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息` 格式,不得被改寫成其他欄位順序。
- **沒有** → 回報「未偵測到 doc-funcs 技能,略過文件化」後**結束**,不自行模擬 doc-funcs 流程。
---
## 總結
各階段執行後輸出:
- **階段 13**:action 名稱、輸入/輸出參數整理表、濃縮後的一句話 description。
- **階段 4**:產出檔案清單(`action.yml`、`src/index.js`、`src/logger.js`、`src/package.json`(若有)、`entrypoint.sh`、`Dockerfile`)與各自相對路徑、採用的 node 版本、驗證結果(語法檢查/npmdocker build)。
- **階段 5**:是否偵測到 doc-funcs;有則附 doc-funcs 流程的處理結果(文件化的 function 與指令檔、重建的 README)。
- 提醒使用者於提交前確認 `entrypoint.sh` 有可執行權限、容器能正確啟動。
---
## 呼叫方式
格式:`[--action-dir <action 根目錄>] [--node-version <node tag>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設最新)。
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc:code-action-docker`,或 `/jsc:code-action-docker --action-dir ~/work/my-action --node-version 22` |
| Codex | `$code-action-docker`,或 `$code-action-docker --action-dir ~/work/my-action`,或用 `/skills` 選單 |
| OpenCode | 描述需求(如「幫我從零建一個 docker action:先問我名稱、輸入輸出參數與目標,用 node 實作放在 src/,訊息格式統一,最後跑 doc-funcs 補文件」)自動觸發 |