Files

252 lines
23 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.
---
name: action-node
description: 將 GiteaGitHub「NodeJavaScriptaction」專案標準化(目錄沒有 action manifest 時可問答式從零建立),並串接文件化流程:(1) 檢查 action 主程式是否為 Node,否則保守改寫為 Node,且主程式及其依賴鏈的 `.js` 檔集中到 `src/` 資料夾(工具設定檔與測試目錄不搬);(2) 對齊 `action.yml` 為 node action`runs.using` 設為 `node24`、指定 `runs.main``outputs` 只留 `description` 不設 `value``inputs` 由 runner 自動注入 `INPUT_*`),並在主程式入口最前面輸出 action 名稱/用途/更新時間橫幅;(3) 處理相依打包——零相依則 `main` 直接指向 `src/index.js`,有相依則以 `@vercel/ncc` 打包成 `dist/index.js`、`main` 改指 `dist/index.js` 並將 `dist/` commit 進 reporunner 不會自動 `npm install`);(4) 開發中需要新參數時優先取用 runner 注入的 `GITHUB_*`Gitea 亦有 `GITEA_*`)執行期環境變數,無法取得才詢問使用者是否新增 `inputs``secrets``vars` 一律視為不可用,需要時宣告為 `inputs` 由呼叫端 workflow 傳入);(5) 最後完整執行 `/jsc-doc:funcs` 處理流程(function 文件、指令檔逐行註解、重建 README)。當使用者說把 action 改成 nodeJavaScript action、從零建立/產生一個 node action、action 主程式 node 化、把 js 收進 src、用 ncc 打包 action、處理 node action 相依或參數來源,或提到 action-node 時觸發。不適用於:Docker 容器 action(用 action-docker)、composite action(用 action-composite)、只整理既有 Dockerfile(用 image)、或不需 action 化/文件化的一般 repo。
argument-hint: "[--action-dir <action 根目錄>] [--node-version <node runtime,如 node24>] [--main <主程式檔>]"
---
# action-node — NodeJavaScriptaction 標準化+文件化
五階段 skill:先做**前置設定與偵測**(找出 action 專案、讀取名稱/用途、判斷主程式語言),再把**主程式 Node 化並將主程式依賴鏈的 `.js` 收進 `src/`**,接著**對齊 `action.yml` 為 node action 並在主程式入口最前面注入輸出名稱/用途/更新時間的啟動橫幅**,然後**處理相依打包**(零相依直接跑 `src/`,有相依以 `@vercel/ncc` 打包成 `dist/` 並 commit),最後**完整執行 `/jsc-doc:funcs` 處理流程**替整個專案補文件並重建 README。產出的 node action 由 **runner 內建的 Node runtime 直接執行 `runs.main`**(不需 Docker、啟動比 docker action 快)。各階段開發中需要新參數時,一律套用下方「**參數來源優先序**」規則。目錄沒有 action manifest 時,先走 A1a 問答式「**從零建立**」分支產生 `action.yml` 與 Node 主程式骨架,再進入後續階段。
| 階段 | 動作 |
| --- | --- |
| A. 前置設定與偵測 | 決定 action 根目錄 → 讀 `action.yml``action.yaml``name``description` → 判斷目前主程式與語言(無 manifest 可走 A1a 從零建立) |
| B. 主程式 Node 化 | 主程式非 Node 則**保守改寫**為 Node(高風險,先確認)→ 主程式依賴鏈的 `.js` 集中到 `src/`,更新所有引用、`package.json``action.yml` |
| C. 對齊 action.yml + 啟動橫幅 | `runs` 對齊 node action`using: node24` + `main`)、`outputs` 去除 `value``inputs` 契約保留;在主程式入口最前面輸出 action **名稱/用途/更新時間** |
| D. 相依處理與打包 | 零相依 → `main``src/index.js`;有相依 → `ncc build``dist/index.js``main` 改指 `dist/index.js``dist/` commit 進 reporunner 不會 `npm install` |
| E. 串接 funcs | 對整個 action 專案完整執行 `/jsc-doc:funcs` 流程(function 文件、指令檔逐行註解、重建 README) |
---
## 共用規範(shared plugin,必要前置)
執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行:
- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼。
- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認、不擴及無關檔案。
- `/jsc-shared:spec-git-safety`:不破壞既有工作(絕不 `reset --hard``clean`)、`git mv` 保留歷史。
- `/jsc-shared:spec-action-params`:action 參數來源優先序(環境變數 → 經同意新增 `inputs`)、`secrets``vars` 一律視為不可用。
- `/jsc-shared:spec-time-log`:更新時間 Asia/Taipei `yyyy/MM/dd HH:mm:ss`、完成後統一同步時間戳。
- `/jsc-shared:spec-doc-funcs-handoff`:最終階段完整執行 `/jsc-doc:funcs` 的標準流程。
本 skill 特有補充:
- **一定會中斷詢問的點**:階段 B 主程式「非 Node 需改寫」時(破壞性,須先確認),階段 D「新增 `@vercel/ncc` 等 build 相依」時(依 `/jsc-shared:spec-action-params` 經同意),以及階段 E 由 `/jsc-doc:funcs` 自身的「如何實作」詢問。
- **保留行為**:Node 化只「翻譯」既有邏輯,不得擅自改變對外行為、輸入輸出契約或副作用;任何無法可靠等價推論的改動一律不做,並以註解或回報標註「需人工確認」。新增 `input` 僅限依 `/jsc-shared:spec-action-params` 經使用者同意後為之。
- **本 skill 只動**:action 專案根目錄內的主程式、主程式依賴鏈的 `.js`(搬移到 `src/`)、`package.json`(含 `scripts.build`)、`action.yml``action.yaml`、打包產物 `dist/``.gitignore`(確保 `dist/` 不被忽略),以及階段 E 由 funcs 流程處理的目標。**不產生** `Dockerfile``entrypoint.sh`(那是 action-docker 的範疇)。
---
## 三種 action 的分工(先確認選對 skill)
| 型態 | `runs.using` | 執行方式 | 對應 skill |
| --- | --- | --- | --- |
| **node(本 skill** | `node24``node20`… | runner 內建 Node 直接跑 `main` 指定的 `.js`**不會 `npm install`** | `action-node` |
| composite | `composite` | 依序執行 `steps`(純 YAML 組合) | `action-composite` |
| docker | `docker` | 建 image、於容器內執行(可 `npm ci` | `action-docker` |
node action 相對 dockercomposite 的**關鍵差異**(全程據此處理):
1. **相依不會自動安裝**runner 不在 action repo 內跑 `npm install`。要嘛**零相依**,要嘛把相依**打包/commit 進 repo**(見階段 D)。
2. **無 `entrypoint.sh``Dockerfile`**:啟動橫幅改由**主程式(JS)最前面 `console.log` 輸出**,不另外產生 shell 入口。
3. **`inputs` 自動注入**runner 會把每個 input 轉成 `INPUT_<NAME>` 環境變數(名稱轉大寫、空白換底線),JS 內以 `process.env.INPUT_<NAME>``core.getInput('<name>')` 取值;composite 需手動映射,node **不用**
4. **`outputs` 只需 `description`**:實際值在執行時由程式寫入 `$GITHUB_OUTPUT`(或 `core.setOutput`),`action.yml``outputs` **不設也不該有 `value`**(那是 composite 的寫法)。
5. **`required: true` 不會自動擋、input 一律字串**:必填檢查與型別轉換都要在程式裡自己做。
6. **`main` 路徑相對 action 根目錄**:讀 action 自帶檔案用 `__dirname``process.env.GITHUB_ACTION_PATH` 定位,不要用相對呼叫端工作目錄的路徑。
7. **node 專屬的 `pre``post`**:可用 `runs.pre``runs.post``pre` 不支援 `uses: ./` 的 local action);compositedocker 沒有。
---
## 參數來源優先序(開發中需要新參數時)
從零建立(A1a)、主程式 Node 化(階段 B)、對齊 `action.yml`(階段 C)或打包(階段 D)的過程中,若需要新的參數值,一律依 `/jsc-shared:spec-action-params` 的優先序處理:node action 主程式優先取 runner 注入的執行期環境變數(`process.env.GITHUB_*`Gitea 亦提供 `GITEA_*` 同義變數,建議讀 `GITHUB_*` 以相容兩邊),既有 `inputs``process.env.INPUT_<大寫名稱>`(或 `core.getInput`)取用,取不到才以 `AskUserQuestion` 經使用者同意新增 `inputs`(由呼叫端 workflow 以 `with:` 傳入);`secrets``vars` 一律視為不可用,需要時宣告為 `input` 由呼叫端傳入(範例見該 spec)。
---
## 參數
格式:`[--action-dir <action 根目錄>] [--node-version <node runtime>] [--main <主程式檔>]`
- `--action-dir <action 根目錄>`:action 專案根目錄。**省略時預設目前工作目錄**(須含 `action.yml``action.yaml`,否則依 A1 詢問)。
- `--node-version <node runtime>``action.yml``runs.using` node runtime(如 `node24``node20`)。**省略時預設 `node24`**(現行最新,本生態範本採用);`node24` 需較新的 act runnerrunner 太舊會報 `The runs.using key ... got node24`,此時升級 act runner 或退回 `node20`(見階段 C)。
- `--main <主程式檔>`:指定主程式入口檔(相對 action 根目錄)。省略時依 A3 自動判斷。
---
## 階段 A:前置設定與偵測
### A1. 決定 action 根目錄
-`--action-dir` → 採用(展開 `~`)。
- 省略 → 用目前工作目錄。
- 根目錄須存在 `action.yml``action.yaml`action manifest)。**找不到** action manifest → **不臆測**、不逕自動工;以 `AskUserQuestion` 詢問使用者要「**從零建立**新的 Node action」還是「提供正確的 action 路徑」:選「從零建立」→ 進入 A1a;選「提供路徑」→ 依新路徑重跑 A1。
### A1a. 從零建立 Node action(問答式)
依序以問答收集需求,再產生 manifest 與主程式骨架:
1. **action 名稱**:用於 `action.yml``name`
2. **輸入與輸出參數**:逐一收集 `inputs`(名稱、`description``required``default`)與 `outputs`(名稱、`description`**不收集 `value`**);description 盡量繁體中文、無亂碼;沒有可留空。
3. **執行目標**:詢問此 action 要達成什麼,整理濃縮成一句話作為 `action.yml``description`(盡量繁體中文、無亂碼)。
收集完成後,於 action 根目錄產生:`action.yml``runs.using: node24``runs.main: src/index.js`,含收集到的 `name``description``inputs``outputs`)與 `src/index.js` 主程式(依執行目標以 Node 實作,輸入以 `process.env.INPUT_<NAME>` 讀取、輸出以附加寫入 `process.env.GITHUB_OUTPUT`,開發中需要新參數時套用「參數來源優先序」);預設走**零相依**路線。完成後接續 A2 往後流程(A3 判定為 Node,階段 B 走「已是 Node」分支,階段 C/D 照常對齊 manifest 與處理相依)。
### A2. 讀取 action 名稱與用途
從 action manifest 讀取:
- `name`:action 名稱(供啟動橫幅與 README 使用)。
- `description`action 用途。
- `runs`:目前的執行設定(`using``main``image``entrypoint``steps` 等)。
- `inputs``outputs`:對外契約(後續不得破壞)。
`name``description` 缺漏,回報並請使用者補;無法取得時於後續輸出以「(未提供)」保守標示,**不編造**。
### A3. 判斷目前主程式與語言
依序判斷主程式入口(取第一個成立者):
1.`--main <主程式檔>` → 直接採用。
2. `action.yml` 為 JS action`runs.using: node*`)→ 取 `runs.main`
3. `action.yml` 為 docker action`runs.using: docker`)→ 看 `runs.entrypoint``Dockerfile``ENTRYPOINT``CMD` 推主程式(並提醒:轉為 node action 會**移除容器化**,若原本刻意用 docker,宜改用 `action-docker`)。
4. `action.yml` 為 composite`runs.using: composite`)→ 從 `steps` 內實際執行的指令推主程式(提醒:composite 轉 node 屬破壞性改寫)。
5. 以上皆無 → 依專案檔與副檔名分布推斷(`package.json``main``bin``index.js``*.sh``*.py` 等)。
判定**主程式是否為 Node**:入口為 `.js``.mjs``.cjs` 且由 `node` 執行即視為 Node;入口為 shellpython/其他則視為非 Node。輸出偵測結果(action 名稱、用途、主程式檔、判定語言、是否為 Node、目前 `runs.using`),再進入階段 B。
---
## 階段 B:主程式 Node 化,並將主程式依賴鏈的 `.js` 收進 `src/`
### B1. 主程式 Node 化
- **已是 Node** → 確認入口檔,不改寫邏輯,直接進 B2。
- **非 Nodeshellpython/其他)** → 這是**破壞性高風險決策**:先以 `AskUserQuestion` 向使用者確認是否改寫為 Node,選項至少含「改寫為 Node」「改用 action-docker(維持原語言容器化)」「其他」。經確認後才改寫:
- 逐段把原主程式邏輯**保守翻譯**為 Node(建議 `src/index.js`);保留對外行為、輸入(環境變數/`INPUT_*`args)與輸出(`$GITHUB_OUTPUT`stdoutexit code/檔案副作用)契約。
- 外部指令呼叫以 `child_process``execFileSync``spawnSync`)對應;檔案操作以 `fs`;環境變數以 `process.env`
- 任何無法可靠等價翻譯處,**不臆測**:以 `// 需人工確認:...` 標註並回報。
- 改寫完成後,原非 Node 主程式於 B2 一併處理(移除或保留由使用者裁示;預設保留並在 README/回報標註已由 Node 取代)。
### B2. 主程式依賴鏈的 `.js` 集中到 `src/`
- 在 action 根目錄建立 `src/`(若不存在)。
- 將**主程式入口及其 `require``import` 依賴鏈**的 `.js``.mjs``.cjs`(排除 `node_modules``.git``.docs``dist`/第三方依賴)**移入 `src/`**,優先 `git mv` 保留歷史。主程式入口統一為 `src/index.js`(或 `src/<main>.js`)。
- **明文排除、不搬**:外部工具依慣例路徑尋找的檔案——`*.config.js``eslint.config.js``jest.config.js``ncc` 等工具設定)、`.*rc.js`、huskycommitlint 等工具設定,以及 `test/``tests/``scripts/` 目錄;搬走會弄壞 linttestbuild 流程。
- **移動後必須更新所有引用**,確保不破壞:
- 模組間的 `require``import` 相對路徑。
- `package.json``main``bin``scripts``exports`(指向 `src/...``scripts.build` 的 ncc 入口指向 `src/index.js`)。
- `action.yml``runs.main`(先指向 `src/index.js`;若階段 D 判定需打包,再改指 `dist/index.js`)。
-`src/` 需要的相依尚未宣告,於 `package.json` 補上;不擅自新增與功能無關的相依(`@vercel/ncc` 屬階段 D 的 build 相依,經同意後才加)。
---
## 階段 C:對齊 action.yml 為 node action,並注入啟動橫幅
### C1. 對齊 `runs` 為 node action
將 manifest 的 `runs` 對齊為 nodeJavaScriptaction**保留既有 `inputs``outputs``name``description`**
```yaml
runs:
using: 'node24' # 見版本說明;--node-version 可覆寫、runner 太舊退 node20
main: 'src/index.js' # 有相依打包後於階段 D 改為 'dist/index.js'
```
- **`using` 版本**依 `/jsc-shared:spec-time-log` 之外另遵循版本規則:**預設 `node24`**(現行最新、本生態範本採用),帶 `--node-version` 時改用指定值。`node24` 需較新的 act runnerrunner 太舊會報 `The runs.using key in action.yml must be one of: [composite docker node12 node16 node20 go], got node24`——此時**升級 act runner**,或(回報後)暫時退回 `node20`。**不用**沒有意義的 `latest`
- **`outputs``value`**node action 的 `outputs.<name>` 只保留 `description`**移除**任何 `value: ${{ steps.* }}`(那是 composite 寫法);實際值於執行時由程式寫入 `$GITHUB_OUTPUT`
- **`inputs` 契約保留**:不更動既有 `inputs` 名稱/`required``default`;主程式以 `process.env.INPUT_<大寫名稱>`(或 `core.getInput`)取用,runner 會自動注入,**不需**像 composite 手動映射 `env:`
- 原本是 **dockercomposite** action 改為 node 時,一併移除只服務原型態的欄位(`image``entrypoint``steps`),並在回報標示型態已變更;無法可靠等價轉換處以 `# 需人工確認` 標註、不臆測。
- 選填的 `runs.pre``runs.post`(node 專屬)僅在原本已有、或使用者明確要求時才設定;`pre` 不支援 `uses: ./` 的 local action。
### C2. 注入啟動橫幅(主程式入口最前面輸出名稱/用途/更新時間)
node action 沒有 `entrypoint.sh`,橫幅改由**主程式 JS 最前面輸出**。在 `src/index.js`(或階段 B 決定的入口)最上方,於任何其他邏輯**之前**插入(或更新)橫幅輸出:
- **更新時間**依 `/jsc-shared:spec-time-log`Asia/Taipei、固定 `yyyy/MM/dd HH:mm:ss`、寫成檔內固定字串)。本階段先寫入暫定時間戳,**階段 E 完成後會統一同步各處時間戳**(見階段 E)。
- 名稱/用途取自階段 A2 的 `action.yml`(缺漏時以「(未提供)」標示)。
- 若已存在本 skill 先前插入的橫幅(依可辨識註解標記),則**更新**其內容與時間戳,不重複插入。
範例(純 `console.log`、零相依;用 `@actions/core` 時可改 `core.info`):
```js
// action 啟動橫幅:輸出名稱/用途/更新時間(此區塊由 action-node 維護)
console.log('================================================');
console.log('Action : <action name>');
console.log('用途 : <action description>');
console.log('更新時間: 2026/07/17 12:00:00');
console.log('================================================');
```
- 橫幅只做輸出,**不得**改變既有主邏輯的執行順序或副作用。
- 此區塊的「用途/更新時間」與檔內註解,最終會在階段 E 由 `/jsc-doc:funcs` 的指令檔/function 流程統一補齊/覆寫為標準格式;本階段先確保**執行期輸出**正確即可。
---
## 階段 D:相依處理與打包(零相依直接跑 src/,有相依用 ncc 打包成 dist/
node action 的 runner **不會自動 `npm install`**,因此相依必須隨 repo 提供。先盤點主程式相依,再二擇一:
### D0. 盤點相依
檢視 `src/` 主程式與 `package.json``dependencies`
- **完全零相依**(只用 Node 內建模組,如 `fs``os``path``child_process`)→ 走 D1。
- **有第三方相依**(如 `@actions/core``@actions/github` 或其他 npm 套件)→ 走 D2 打包。
### D1. 零相依:main 直接指 src/index.js
- 確認 `action.yml``runs.main: src/index.js`**不需** build 步驟、**不需** `dist/`
- `package.json` 可保留(或補上)`scripts.build``ncc build src/index.js -o dist`)備用,但零相依情況不必執行。
### D2. 有相依:以 @vercel/ncc 打包成 dist/ 並 commit
打包能把原始碼與相依編成單一檔,避免 commit 龐大的 `node_modules`
1. 新增 build 相依(依 `/jsc-shared:spec-action-params`/本 skill「一定會中斷詢問的點」,屬新增相依,先向使用者確認):`npm i -D @vercel/ncc`,並確保 `package.json` 有:
```json
"scripts": { "build": "ncc build src/index.js -o dist" }
```
2. 執行打包產生 `dist/index.js``npx ncc build src/index.js -o dist`。
3. 將 `action.yml` 的 `runs.main` 改為 `dist/index.js`。
4. **把 `dist/` commit 進 repo**:確認 `.gitignore` **沒有**忽略 `dist/`(若有,移除該規則或加 `!dist/`),並 `git add dist/`。
5. 於回報提醒:**每次改 `src/` 都要重新 `ncc build` 並提交 `dist/`**,否則 action 執行的是舊版打包產物。
> 另一可行但不建議的做法:直接 commit `node_modules`(體積大、跨平台原生模組易出問題)。預設採用 ncc 打包。
### D3. 版本與自我檢查
- `runs.main` 指向的檔案(`src/index.js` 或 `dist/index.js`)必須**確實存在於 repo**、且為 runner 可直接執行的 JS。
- `runs.using` 的 node 版本需與目標 runner 相容(見階段 C)。
- 橫幅輸出、`INPUT_*` 讀取、`$GITHUB_OUTPUT` 寫入在打包後仍正確(ncc 打包不改執行語意,但入口路徑改變,確認 `main` 已同步更新)。
---
## 階段 E:完整執行 /jsc-doc:funcs 處理流程
標準化與打包完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**完整執行 `/jsc-doc:funcs` 流程(前置可用性檢查、完整流程、由使用者裁示實作方式、重建 README;本次新增/變更的 `src/` 內 Node 主程式、`action.yml`、`package.json` 都會被涵蓋)。完成後依該 spec **統一時間戳**:回頭同步主程式橫幅輸出與註解區塊、`action.yml` 開頭註解區塊與 README 的更新時間,確保各處一致。
---
## 總結
各階段執行後輸出:
- **階段 A**action 根目錄、action 名稱/用途、原主程式檔與語言、是否為 Node、目前 `runs.using`;若走 A1a 從零建立,列出問答收集結果(名稱/輸入輸出/目標)。
- **階段 B**:是否改寫主程式(及改寫摘要與「需人工確認」清單)、搬入 `src/` 的 `.js` 清單、已更新的引用(`package.json``action.yml`/路徑);依「參數來源優先序」新增的 `inputs` 清單與呼叫端傳入寫法(若有)。
- **階段 C**:對齊後的 `runs``using``main`)、`outputs` 是否已去除 `value`、`inputs` 契約保留情形、橫幅輸出內容(名稱/用途/更新時間)與插入位置。
- **階段 D**:相依判定(零相依/打包)、是否新增 `@vercel/ncc`、`runs.main` 最終指向(`src/index.js` 或 `dist/index.js`)、`dist/` 是否已 commit。
- **階段 E**:funcs 流程的處理結果(文件化的 function 與指令檔、重建的 README)。
- 列出本次新增/變更的檔案與其相對路徑,並提醒使用者於提交前確認 `runs.main` 檔案存在、(打包時)`dist/` 已提交、目標 runner 支援指定的 node 版本。
---
## 呼叫方式
格式:`[--action-dir <action 根目錄>] [--node-version <node runtime>] [--main <主程式檔>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設 `node24`;主程式自動判斷)。
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc-code:action-node`,或 `/jsc-code:action-node --action-dir ~/work/my-action --node-version node24` |
| Codex | `$action-node`,或 `$action-node --action-dir ~/work/my-action`,或用 `/skills` 選單 |
| OpenCode | 描述需求(如「把這個 action 主程式改成 node、主程式相關 js 收進 src/,對齊 action.yml 為 node action、在主程式最前面輸出 action 名稱/用途/更新時間,有相依就用 ncc 打包成 dist 並 commit,最後跑 funcs 補文件並重建 README」)自動觸發 |