@@ -0,0 +1,272 @@
# Gitea Node Action 範本
Node( JavaScript) action 讓你用 JavaScript 撰寫 action 邏輯,直接跑在 runner 內建的 Node runtime 上。相較於 composite(純 YAML 組合 step)與 Docker(包 image) action, node action 適合需要**程式邏輯、呼叫 API、跨平台**的情境,且啟動速度比 Docker action 快。
本文件整理 node action 的 `action.yml` 中**所有可用參數、說明與限制**,並特別標出 **Gitea 與 GitHub Actions 的差異 ** 。範例皆對應本 repo 的 [`action.yml` ](./action.yml ) 與 [`src/index.js` ](./src/index.js )。
> 語法基準:Gitea Actions 以相容 GitHub Actions metadata 語法為目標,但兩者有明確差異(見「Gitea vs GitHub」章節)。Gitea 端的行為亦受底層 [`act`](https://gitea.com/gitea/act) runner 版本影響——尤其**支援的 Node 版本**——實作前建議以測試機驗證。
---
## 目錄
- [完整結構總覽 ](#完整結構總覽 )
- [頂層參數 ](#頂層參數 )
- [`inputs`(輸入參數) ](#inputs輸入參數 )
- [`outputs`(輸出) ](#outputs輸出 )
- [`runs`(執行設定) ](#runs執行設定 )
- [在 JavaScript 內取值 / 設值 ](#在-javascript-內取值--設值 )
- [建置與打包(相依套件) ](#建置與打包相依套件 )
- [Node action 的限制與注意事項 ](#node-action-的限制與注意事項 )
- [Gitea vs GitHub Actions 差異 ](#gitea-vs-github-actions-差異 )
- [本 repo 範例對照 ](#本-repo-範例對照 )
- [參考來源 ](#參考來源 )
---
## 完整結構總覽
``` yaml
name : 'Gitea Node Template' # 必填
description : 'Gitea Node 範本' # 必填
author : 'Jeffery' # 選填
inputs : # 選填,定義輸入參數
message :
description : '輸入訊息'
required : false
default : 'Hello, World!'
outputs : # 選填,定義輸出
message :
description : '輸出訊息' # node action 只需 description,不需 value
runs : # 必填
using : 'node24' # 必填,node runtime(最新版;見版本說明)
main : 'src/index.js' # 必填,進入點 JS 檔
pre : 'setup.js' # 選填,main 之前執行
pre-if : "always()" # 選填,pre 的條件,預設 always()
post : 'cleanup.js' # 選填,main 之後執行
post-if : "always()" # 選填,post 的條件,預設 always()
branding : # 選填(Marketplace 用,Gitea 內部可省略)
icon : 'activity'
color : 'blue'
```
> 📌 檔名**只能**是 `action.yml` 或 `action.yaml`,放在 action repo 根目錄。
---
## 頂層參數
| 參數 | 必填 | 說明 |
|------|------|------|
| `name` | ✅ | Action 名稱。 |
| `description` | ✅ | Action 簡短說明。 |
| `author` | ❌ | 作者名稱。 |
| `inputs` | ❌ | 輸入參數定義(見下)。 |
| `outputs` | ❌ | 輸出定義(見下)。 |
| `runs` | ✅ | 執行設定;node action 用 `using: 'node24'` + `main` 。 |
| `branding` | ❌ | Marketplace 顯示用的 `icon` 與 `color` 。 |
---
## `inputs`(輸入參數)
每個 input 是 `inputs.<input_id>` 底下的一組設定。`<input_id>` 必須以字母或底線開頭,只能含英數、`-` 、`_` :
| 欄位 | 必填 | 說明 |
|------|------|------|
| `description` | ✅ | 參數說明。 |
| `required` | ❌ | 是否必填,布林值,預設 `false` 。 |
| `default` | ❌ | 預設值;呼叫端沒傳時採用。**只能是字串**。 |
| `deprecationMessage` | ❌ | 標記此 input 已棄用,使用時記錄警告訊息。 |
**呼叫端傳值 ** (用 `with` ):
``` yaml
- uses : ./
with :
message : 'Hi there'
```
> ✅ **與 composite 的關鍵差異**: node action **會**自動把每個 input 轉成 `INPUT_<NAME>` 環境變數——名稱**轉大寫**、**空白換成底線**(例:input `my message` → `INPUT_MY_MESSAGE`)。在 JS 內即可用 `process.env.INPUT_MESSAGE` 或 `core.getInput('message')` 取值。
>
> ⚠️ `required: true` **不會**在缺值時自動報錯——runner 只是標記語意,實際檢查要自己在程式裡做(或用 `core.getInput('x', { required: true })`)。
>
> input 值一律是**字串**;數字、布林傳進來也會變字串(例如 `"true"`),比較時要留意。
---
## `outputs`(輸出)
node action 的 output **只需要 `description` ** ,**不需要**(也不該有)composite 那種 `value` 欄位——實際的值是在**執行時**由程式寫入:
| 欄位 | 必填 | 說明 |
|------|------|------|
| `description` | ✅ | 輸出說明。 |
**在 JS 內設定 output ** → 寫入 `$GITHUB_OUTPUT` 檔案(或用 `core.setOutput` ):
``` js
const fs = require ( 'fs' ) ;
const os = require ( 'os' ) ;
fs . appendFileSync ( process . env . GITHUB _OUTPUT , ` message=Hello ${ os . EOL } ` ) ;
// 或(需要 @actions/core): core.setOutput('message', 'Hello');
```
**呼叫端取用 output ** :
``` yaml
- id : node-template
uses : ./
- run : echo "${{ steps.node-template.outputs.message }}"
```
> 📏 **大小限制**:單一 job 的 outputs 上限 1 MB;一次 workflow run 全部 outputs 合計上限 50 MB。大量資料請改用 artifact。
---
## `runs`(執行設定)
node action 的 `runs` 欄位:
| 欄位 | 必填 | 說明 |
|------|------|------|
| `using` | ✅ | Node runtime。最新為 `node24` (本 repo 採用);亦可用 `node20` / `node16` 。實際可用版本取決於 runner(見下方 Gitea 差異)。 |
| `main` | ✅ | 進入點 JS 檔(例:`src/index.js` 或打包後的 `dist/index.js` )。 |
| `pre` | ❌ | 在 `main` **之前 ** 、job 開始時執行的 JS 檔(可做前置設定)。 |
| `pre-if` | ❌ | 決定 `pre` 是否執行的條件,預設 `always()` 。 |
| `post` | ❌ | 在 `main` **之後**執行的 JS 檔(可做清理、即使 main 失敗仍會跑)。 |
| `post-if` | ❌ | 決定 `post` 是否執行的條件,預設 `always()` 。 |
> ⚠️ **`pre` 不支援 local action**:直接放在同一 repo、用 `uses: ./` 呼叫的 local action **無法**使用 `runs.pre`。`pre` / `post` 也是 **node action 專屬**( composite / Docker 沒有)。
---
## 在 JavaScript 內取值 / 設值
node action 進入點是一支普通的 Node 程式。兩種常見寫法:
**A) 零相依(本 repo 採用) ** ——直接讀環境變數、寫檔案,無需 `npm install` :
``` js
const message = process . env . INPUT _MESSAGE ? ? 'Hello, World!' ; // 讀 input
fs . appendFileSync ( process . env . GITHUB _OUTPUT , ` message= ${ message } \n ` ) ; // 寫 output
console . log ( ` message= ${ message } ` ) ; // 日誌
process . exit ( 1 ) ; // 讓 step 失敗
```
**B) 使用官方 toolkit `@actions/core` ** ——語意更清楚,處理跳脫與多行值較穩:
``` js
const core = require ( '@actions/core' ) ;
const message = core . getInput ( 'message' ) ; // 讀 input(等同 INPUT_MESSAGE)
core . setOutput ( 'message' , message ) ; // 設 output
core . info ( '...' ) ; // 日誌
core . setFailed ( '錯誤訊息' ) ; // 記錄失敗並以非零碼結束
```
需要呼叫 Gitea / GitHub API 時再加 `@actions/github` (提供已驗證的 REST client 與 `github.context` )。
---
## 建置與打包(相依套件)
- **沒有相依套件**(如本 repo):`main` 直接指向原始 `src/index.js` 即可,Gitea **不需要 ** build 步驟。
- **有相依套件**(用了 `@actions/core` 等):runner **不會**幫你 `npm install` ,你必須把相依一起帶進 repo。二選一:
1. **打包(建議) ** :用 [`@vercel/ncc` ](https://github.com/vercel/ncc ) 把原始碼與相依編成單一檔,再把 `main` 指到它:
```bash
npm i -D @vercel/ncc
npx ncc build src/index.js -o dist # 產生 dist/index.js
` ``
並改 ` action.yml`: ` main: 'dist/index.js'`。**打包後的 ` dist/` 要 commit 進 repo。**
2. **直接 commit ` node_modules`**:可行但體積大、易出問題,一般不建議。
> 用打包方式時,記得每次改 code 都重新 ` ncc build` 並把 ` dist/` 一起提交,否則 action 跑的是舊版。
---
## Node action 的限制與注意事項
1. **相依不會自動安裝**
runner 不會在 action repo 內跑 ` npm install`。要嘛零相依,要嘛把相依打包 / commit 進 repo(見上一節)。
2. **` required: true` 不會自動擋**
缺少必填 input 時 runner 不會報錯,要自己在程式裡驗證。
3. **input 一律是字串**
` INPUT_*` / ` core.getInput` 拿到的都是字串,數字與布林需自行轉型。
4. **output 有大小上限**
單 job 1 MB、單次 run 合計 50 MB;超量請用 artifact。
5. **` main` 路徑相對於 action 根目錄**
` main: src/index.js` 指的是相對於 action repo 根目錄的路徑,不受呼叫端工作目錄影響。要讀 action 自帶的其他檔案時,用 ` __dirname` 或 ` process.env.GITHUB_ACTION_PATH` 定位,不要用相對於呼叫端的路徑。
6. **` pre` 不支援 local action、且 ` pre`/` post` 為 node 專屬**
見 ` runs` 章節。
7. **Node 版本要對得上 runner**
` using` 指定的版本必須是該 runner 支援的版本,否則 action 直接被拒(見下方 Gitea 差異)。
8. **跨 step 共享環境變數 / PATH**
在程式內寫入 ` $GITHUB_ENV`、` $GITHUB_PATH` 指向的檔案,可讓**後續 step**取得對應的環境變數 / PATH。
---
## Gitea vs GitHub Actions 差異
Gitea Actions **不是** GitHub Actions 的 100% 複製品。撰寫 node action 時特別注意:
| 項目 | Gitea 行為 |
|------|-----------|
| **支援的 Node 版本** | ` runs.using` 可用的 node 版本**取決於 act runner 版本**: ` node20` 需 runner ≥ v0.2.6; **` node24`(最新,本 repo 採用)需較新的 runner**。若 runner 太舊,會報錯 ` The runs.using key in action.yml must be one of: [composite docker node12 node16 node20 go], got node24`——此時請**升級 act runner**,或暫時退回 ` node20`。GitHub 端自 2026/03 起 ` node24` 已為 JS action 預設。 |
| **` using: 'go'`** | Gitea 額外支援 ` using: 'go'` 寫 Go action( GitHub 沒有)。 |
| **表達式函式** | 依官方比較文件,**僅保證支援 ` always()`**; ` success()` / ` failure()` / ` cancelled()` / ` hashFiles()` 等其他函式視 ` act` runner 版本而定,不保證可用——寫 ` if:`(含 ` pre-if` / ` post-if`)前先在測試機驗證。 |
| **` uses` 支援絕對 URL** | 可寫 ` uses: https://github.com/actions/checkout@v4 ` 或 ` uses: http://your_gitea/owner/repo@branch `,不限同站 action。 |
| **context 檢查較寬鬆** | Gitea 不檢查 context 可用性,` env` context 可用在比 GitHub 更多的位置(但不代表可攜,跨到 GitHub 會失敗)。 |
| **被忽略的 job 欄位** | ` jobs.<job_id>.timeout-minutes`、` jobs.<job_id>.continue-on-error`、` jobs.<job_id>.environment` 會被忽略。 |
| **` runs-on`** | 只接受簡單格式 ` runs-on: xyz` 或 ` runs-on: [xyz]`,不支援複雜表達式。 |
| **annotations / problem matchers** | 不支援,會被忽略。 |
| **` permissions` scope** | 支援 ` permissions`,但沒有 GitHub 專屬的 ` statuses` / ` checks` / ` deployments` / ` id-token` / ` security-events` / ` pages`; Gitea 有自己的 ` code` / ` releases` / ` wiki` / ` projects`。 |
> 上表以 Gitea 官方文件為準;` act` runner 持續更新,部分限制(尤其表達式函式與 Node 版本)可能隨版本放寬,仍以你環境的實測為準。
---
## 本 repo 範例對照
- Action 定義:[` action.yml`](./action.yml)( ` using: node24` + ` main: src/index.js`)
- 進入點程式:[` src/index.js`](./src/index.js)(零相依:讀 ` INPUT_MESSAGE`、寫 ` $GITHUB_OUTPUT`)
- 專案設定:[` package.json`](./package.json)
- CI 呼叫範例:[` .gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml)
CI 的 ` BUILD` job 呼叫本 action、後續 job 取用其 output:
` ``yaml
build:
outputs:
message: ${{ steps.build.outputs.message }}
steps:
- uses: actions/checkout@${{ vars.ACTION_CHECKOUT_VERSION }}
- id: build
uses: ./
result:
needs: [build, test]
steps:
- run: echo "${{ needs.build.outputs.message }}"
` ``
---
## 參考來源
- [GitHub Actions — Metadata syntax for actions ](https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax )
- [GitHub Actions — Creating a JavaScript action ](https://docs.github.com/en/actions/tutorials/create-actions/create-a-javascript-action )
- [Gitea — Compared to GitHub Actions ](https://docs.gitea.com/usage/actions/comparison )
- [Gitea Blog — Gitea Actions now Supports Node20 based actions ](https://blog.gitea.com/node-20-actions-support/ )
- [Gitea — Act Runner ](https://docs.gitea.com/usage/actions/act-runner )
- [@actions/core toolkit ](https://github.com/actions/toolkit/tree/main/packages/core )
- [@vercel/ncc — 打包工具 ](https://github.com/vercel/ncc )