--- name: code-action-docker description: 以互動問答從零打造 Gitea/GitHub「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.yml/entrypoint.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 ] [--node-version ]" --- # code-action-docker — 問答式打造 Docker 容器 action(Node 主程式)+文件化 五階段 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 ] [--node-version ]` - `--action-dir `:action 專案根目錄。**省略時預設目前工作目錄**。 - `--node-version `:`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.* }}`**:repo/org 的 secrets(機敏值:token、密碼、金鑰)。 3. **`${{ vars.* }}`**:repo/org 的 variables(非機敏設定值)。 4. **以上都沒有** → 以 `AskUserQuestion` 詢問使用者**是否要把該參數加入 `inputs`**(加入則回到階段 2 規格補齊名稱/description/required/default,並同步 `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 內呼叫,不另寫 shell/python 主程式。 - 所有 `.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 以階段 1~2 的答案組出 `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:" echo "[<產生當下 Asia/Taipei 時間>][啟動][INF]: 用途:" 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 流程。 --- ## 總結 各階段執行後輸出: - **階段 1~3**:action 名稱、輸入/輸出參數整理表、濃縮後的一句話 description。 - **階段 4**:產出檔案清單(`action.yml`、`src/index.js`、`src/logger.js`、`src/package.json`(若有)、`entrypoint.sh`、`Dockerfile`)與各自相對路徑、採用的 node 版本、驗證結果(語法檢查/npm/docker build)。 - **階段 5**:是否偵測到 doc-funcs;有則附 doc-funcs 流程的處理結果(文件化的 function 與指令檔、重建的 README)。 - 提醒使用者於提交前確認 `entrypoint.sh` 有可執行權限、容器能正確啟動。 --- ## 呼叫方式 格式:`[--action-dir ] [--node-version ]` — 全部可省略(根目錄預設目前工作目錄;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 補文件」)自動觸發 |