# 策展 · X (Twitter) 🔥

> 作者：Andrea (@acolombiadev) · 平台：X (Twitter) · 日期：2026-04-02

> 原始來源：https://x.com/i/article/2038750798575030272

## 中文摘要

# GitHub Actions：沒人告訴你的那些事

我在 GitHub 工作，每天都使用 Actions。我也曾在凌晨兩點除錯 YAML，看著日誌檢視器（log viewer）吃掉我的瀏覽器記憶，並為了找出為什麼條件判斷式（conditional）無法正確執行，而連續推送（push）了一堆 commit。

我不是來告訴你 Actions 是完美的。它並不完美。日誌檢視器曾讓成熟的工程師懷疑自己的職涯選擇。YAML 表達式語法有一種學習曲線，感覺更像是「學習懸崖」。這種「推送-等待-失敗-重複」的除錯循環，可以把一個五分鐘就能修好的問題，變成一整個下午的「人質危機」。

我之所以知道這些，是因為我親身經歷過。而且我看過成千上萬的開發者也經歷過同樣的痛苦。

但我也看到了這一點：大部分的痛苦源於可以避免的模式。並非全部如此，有些是因為平台還在追趕技術發展。但很多問題其實現在就有解決方案，只是人們還沒發現，因為最簡單的路徑就是不斷複製貼上 YAML，然後繼續受苦。

這篇文章是我希望在第一天就有人告訴我的事。

## 別再使用日誌檢視器了

我是認真的。用於閱讀建置日誌的網頁 UI 是 Actions 最令人沮喪的單一來源，而最快的解決方法就是停止使用它。

```
gh run view --log-failed
```

就這樣。失敗的步驟會直接出現在你的終端機中，即時呈現。不需要點擊三個頁面，不需要等待瀏覽器決定今天是否要渲染，也不需要玩「上一頁」的輪盤賭。

如果你需要完整的日誌：

```
gh run view --log
```

如果你想即時觀看執行過程：

```
gh run watch
```

CLI 的速度更快，可以使用 grep 搜尋，而且當你的測試套件輸出 50,000 行內容時，它也不會崩潰。如果你在 2026 年還在透過網頁 UI 點擊閱讀建置日誌，這就是你該停手的訊號。

## YAML 的問題是真實存在的。以下是如何縮減它。

每個 CI 系統最終都會變成「一堆 YAML」。Actions 也不例外。但一個清楚執行單一任務的 40 行工作流程（workflow），與一個包含巢狀條件判斷、矩陣策略（matrix strategies）以及會讓 Shell 程式設計師哭泣的內嵌 Bash 腳本的 400 行怪物，兩者之間是有區別的。

之所以會出現 400 行的怪物，是因為人們不知道——或者沒有使用——那兩個旨在防止這種情況的功能。

### Reusable Workflows (可重複使用工作流程)

如果你在多個專案庫（repo）中有相同的 CI 步驟，你可能正在複製貼上工作流程。請停止這樣做。

```yaml
# .github/workflows/ci.yml
jobs:
  build:
    uses: your-org/.github/.github/workflows/build.yml@main  # @main 對於你控制的內部專案庫來說沒問題 — 但如果你對其他所有東西都進行了 SHA 鎖定，請考慮也鎖定這個
    with:
      node-version: '20'
    secrets: inherit
```

一個工作流程，維護在一個地方，從各處呼叫。當你更新它時，每個使用它的專案庫都會獲得更新。不會有版本差異。不會再出現「等等，哪個專案庫才有我們部署腳本的最新版本？」這種困惑。

### Composite Actions (組合式 Action)

Reusable Workflows 是為了整個管線（pipeline）設計的。Composite Actions 則是為了步驟（steps）——也就是建構模組——而設計的。

```yaml
# .github/actions/setup-project/action.yml
name: 'Setup Project'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
      with:
        node-version: ${{ inputs.node-version }}
    - run: npm ci
      shell: bash
    - run: npm run build
      shell: bash
```

現在你的工作流程檔案讀起來就像句子，而不是肥皂劇：

```yaml
steps:
  - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  - uses: ./.github/actions/setup-project
    with:
      node-version: '20'
  - run: npm test
```

YAML 依然存在。但它是 12 行，而不是 120 行。而且當設定變更時，你只需要在一個地方修改即可。

## 打破「推送-等待-失敗」的循環

Actions 除錯中最令人崩潰的部分是：你對工作流程檔案做了一個字元的修改，推送它，等待四分鐘讓 Runner 啟動，然後發現你少打了一個引號。經過一堆 commit 之後，你的 git 歷史紀錄看起來就像是在求救。

`act` 是一個由社群維護的工具，它可以在 Docker 容器中於本地執行你的工作流程。環境相同，無需推送。

```
act -j build
```

它並非完美的複製品——有些 GitHub 特有的 context 在本地並不存在。但對於「我是否弄壞了 YAML」以及「我的 Bash 腳本是否真的能運作」這類問題，它能將回饋循環從幾分鐘縮短到幾秒鐘。

在執行任何操作之前，若要進行簡單的語法驗證：

```
gh workflow view ci.yml
```

如果 YAML 有明顯的問題，它會很快顯示出來。它不是一個完整的 linter，但它能在不經過推送循環的情況下捕捉到基本錯誤。

## Marketplace 的信任問題（以及該怎麼做）

每一個 `uses: some-stranger/cool-action@v2` 都是你沒寫過的程式碼，卻擁有存取你專案庫和 secret 的權限。這是一個真正的安全隱憂，而「鎖定到 SHA」是正確的答案，但沒人遵守。

以下是真正有效的方法：

1. **鎖定到 SHA 並讓 Dependabot 管理更新：**

```
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
```

在註解中加上版本號，這樣人類就能閱讀。當有新版本發布時，Dependabot 會開啟 PR，你可以在更新前審查差異（diff）。（本文中的 SHA 已於 2026 年 3 月 30 日驗證。）

2. **盡可能堅持使用 GitHub 維護的 Action。** `actions/checkout`、`actions/setup-node`、`actions/cache` —— 這些是由建構該平台的同一個團隊所維護的。它們經過審計、測試並持續更新。

3. **對於其他所有內容，閱讀原始碼。** 它是開源的。如果一個 Marketplace 的 Action 只是包在 Dockerfile 裡的 20 行 Shell 腳本，也許直接把這 20 行複製到 `run:` 步驟中並自己維護會更好。

4. **在採用之前，使用 OpenSSF Scorecard 來評估 Action 維護者的實踐方式。**

## 在不崩潰的情況下處理條件邏輯

`${{ }}` 表達式語法是那種「簡單直到它變得不簡單」的東西，一旦變得複雜，就會令人困惑。關於字串插值（string interpolation）、真值（truthiness）和型別轉換的邊緣情況，每個人都至少被坑過一次。

幾條生存法則：

```
# 在 `if:` 中始終為表達式加上引號
if: ${{ github.event_name == 'push' }}

# 對於來自輸入（inputs）的布林值，使用 fromJSON
if: ${{ fromJSON(inputs.deploy) == true }}

# 多重條件？使用 >- 將換行符號摺疊為空格。
if: >-
  ${{ github.ref == 'refs/heads/main' &&
      github.event_name == 'push' }}
```

如果你的條件邏輯複雜到需要流程圖，那這就是一個訊號：你需要的是帶有輸入參數的 Reusable Workflow，而不是更多的 `if:` 語句。

`case()` 函數是最近新增的功能，值得了解。把它想像成表達式的 switch 語句。它取代了沒人看得懂的巢狀三元運算子。

## 讓 Copilot 撰寫 YAML

我不會假裝寫 YAML 很有趣。但在 2026 年，你不需要親自寫大部分的內容。

在安裝了 GitHub Copilot 的 VS Code 中，在註解中描述你想要的內容：

```
# Deploy to production on push to main, run tests first,
# cache node_modules, notify Slack on failure

```

Copilot 會產生工作流程。你進行審查和調整。它會處理語法、`on:` 觸發器、`runs-on:`、步驟順序，讓你專注於管線應該做什麼，而不是去記住 `environment` 應該放在 `jobs:` 裡面還是外面。

對於現有的工作流程，Copilot Agent 模式可以將 400 行的 YAML 檔案重構為 Reusable Workflows 和 Composite Actions。告訴它你想要什麼，然後審查 PR。

這並不能修復 Actions 本身。它修復的是你必須直接與它搏鬥的問題。

## 快取（Caching）：你可能沒在用的免費速度

如果你的工作流程在每次執行時都安裝依賴項，且你還沒設定快取，那你就是在浪費免費的時間。

```yaml
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
  with:
    node-version: '20'
    cache: 'npm'
```

那一行 —— `cache: 'npm'` —— 告訴 `setup-node` 在執行之間快取你的 `node_modules`。第一次執行是正常的。之後的每次執行都會跳過完整的 `npm ci` 下載。對於大型專案，這可以讓每次建置節省兩到四分鐘。

相同的模式也適用於其他生態系統：

```python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  with:
    python-version: '3.12'
    cache: 'pip'

# Go (手動快取)
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
  with:
    path: ~/go/pkg/mod
    key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
```

如果你需要更多控制權，`actions/cache` 讓你快取任何東西 —— 建置產物（artifacts）、Docker 層、編譯後的二進位檔案。關鍵在於對你的鎖定檔案（lockfile）進行雜湊（hashing），這樣當依賴項真正變更時，快取就會失效。

## 停止重複執行相同的工作流程

如果你的團隊推送速度很快，你可能見過這種情況：十分鐘內三個 commit，排隊了三個工作流程執行，當前兩個完成時，它們已經過時了。

並行群組（Concurrency groups）可以解決這個問題：

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
```

將此內容加在工作流程的最頂層。相同的分支，相同的工作流程 —— 當新的執行開始時，舊的執行就會被取消。不再浪費 Runner 時間在已經被取代的 commit 上。

這對於開發者快速迭代的 PR 工作流程特別有用。沒有它，你就是在為沒人會看的建置付費。

## 什麼正在變得更好

我說過我不會美化這一切，所以讓我具體說明什麼已經改進，以及什麼仍需要努力。

**現在變得更好：**

- Job summaries，結構化的輸出，而不僅僅是日誌行。

- 更大的 Runner，付費方案（Team/Enterprise）提供 64 核心機器，ARM Runner 已在公開專案庫中正式發布（GA）。

- 透過 rulesets 強制執行必要的工作流程，無需複製貼上即可實現組織級別的政策。

- 不可變的 Action，發布到 GHCR 並帶有來源證明（provenance）的 Action。

- `case()` 和最近的表達式改進。

**仍需要努力：**

- 日誌檢視器。它已經改進了，但對於大型建置來說還不夠好。請使用 CLI。

- 除錯體驗。`act` 有所幫助，但第一方的本地執行將會改變一切。

- 表達式的學習曲線。文件團隊一直在穩步改進這一點，效果顯著 —— 但仍有成長空間。

我寫這篇文章不是為了捍衛 GitHub 的名聲。我寫它是因為我看過太多團隊在那些已有解決方案的問題中掙扎，而這些解決方案傳遞給人們的速度不夠快。

基礎已經具備。平台是可以運作的。但「可以運作」和「令人愉悅」是兩回事，而它們之間的差距就是你下午時間消失的地方。這些模式縮小了那個差距。雖然不是完全消除，但已經足夠了。

---

本文來自《Main Branch》，這是一份關於 GitHub 功能和基礎知識的每週電子報。沒有廢話，沒有炒作 —— 只有讓你更擅長交付產品的乾貨。 📖 [Leer en español](https://github.blog/es) · [閱讀中文版](https://github.blog/zh)

## 標籤

教學資源, 其他, GitHub
