Skip to content

Automation Recipes zh TW

KoukeNeko edited this page Sep 9, 2026 · 3 revisions

自動化實例

English · 繁體中文

三個「不是用鍵盤操作 Taiga」的可執行範例。三者都依賴 JSON contract:--json 提供 穩定形狀、--fields 縮小回應、--no-input 確保不會卡在提示等待輸入,並以 exit code 判斷發生了什麼事。

三個範例都需要在環境中設定 TAIGA_API_URL 與 TAIGA_TOKEN,或先用 taiga auth login 登入。

1. CI 失敗時開立或更新 Taiga issue

這裡的陷阱是每次失敗都開一張新 issue。先搜尋,已經有開著的就改成留言。

# .github/workflows/report-failure.yml
name: Report failures to Taiga
on:
  workflow_run:
    workflows: ["CI"]
    types: [completed]

jobs:
  report:
    if: ${{ github.event.workflow_run.conclusion == 'failure' }}
    runs-on: ubuntu-latest
    env:
      TAIGA_API_URL: ${{ secrets.TAIGA_API_URL }}
      TAIGA_TOKEN: ${{ secrets.TAIGA_TOKEN }}
      TAIGA_PROJECT: example-project
    steps:
      - run: curl -fsSL https://raw.githubusercontent.com/KoukeNeko/taiga-cli/main/scripts/install.sh | sh
      - name: Open or update the issue
        run: |
          export PATH="$HOME/.local/bin:$PATH"
          subject="CI failed on ${{ github.ref_name }}"
          run_url="${{ github.event.workflow_run.html_url }}"

          # --fields 讓回應只含腳本會讀的欄位。
          existing=$(taiga search "$subject" --type issue --json --fields ref,subject --no-input |
                     jq -r --arg s "$subject" 'first(.items[] | select(.subject == $s) | .ref) // empty')

          if [ -n "$existing" ]; then
            taiga issue comment "$existing" --body "Failed again: $run_url" --json --no-input
          else
            taiga issue create --subject "$subject" \
              --description "First failure: $run_url" \
              --type Bug --json --no-input
          fi

值得抄的是兩個細節,而不是整個檔案。--fields ref,subject 讓腳本只解析它真正用到的欄位,所以上游新增 欄位不會改變它的行為。另外它完全不重試:寫入途中連線中斷時 taiga 會以 11 結束,而不是開出兩張 issue,該步驟隨即明確失敗 —— 對一個回報用的 job 來說,這正是我們要的結果。

2. 用 shell 腳本讀取 backlog

--json 的輸出是穩定的封包結構,所以今天寫的 jq 運算式之後仍然可用。

#!/usr/bin/env sh
set -eu

# 未指派且未關閉的 issue。
taiga issue list --limit 200 --json --fields ref,subject,status,assignee,is_closed --no-input |
    jq -r '.items[]
           | select(.is_closed == false and (.assignee // "") == "")
           | "#\(.ref)\t\(.status)\t\(.subject)"'

要依結果分支時,判斷 exit code 而不是比對訊息文字:

if taiga issue view 42 --json --fields ref,version --no-input > /tmp/issue.json; then
    version=$(jq -r '.data.version' /tmp/issue.json)
else
    case $? in
        3) echo "尚未認證" >&2 ;;
        5) echo "issue 42 不存在" >&2 ;;
        9) echo "Taiga 無法連線,可安全重試" >&2 ;;
        *) echo "未預期的失敗" >&2 ;;
    esac
    exit 1
fi

完整對照表見 JSON Contract。對腳本最重要的三個是 6(別人先改了這筆資料)、 9(傳輸問題,可安全重試)與 11(寫入可能已經送達,不要重試)。

3. 讓 agent 先檢視、再排練、最後才寫入

Agent 不應該用猜的來判斷一個指令能不能無人監督執行 —— 直接問工具。

taiga schema issue create --json --no-input
{
  "data": {
    "command": "issue create",
    "safety": "write",
    "idempotency": "non_idempotent",
    "input_schema": { "...": "JSON Schema draft 2020-12" },
    "output_schema": { "...": "..." }
  },
  "meta": { "contract": 1 }
}

safety 與 idempotency 是 agent 需要的兩個欄位:read 可以直接執行,write 需要做決定,而 non_idempotent 代表失敗的嘗試不可以直接重送。

寫入前先排練一次。--dry-run 仍會連線 Taiga —— 它會把狀態名稱解析成 ID、確認 assignee 存在 —— 但保證 不會有任何寫入請求離開這個行程:

taiga issue edit 42 --status "In progress" --assignee alice --dry-run --json --no-input
{
  "plan": {
    "action": "edit",
    "target": "example-project#42",
    "would_write": true,
    "performed": false,
    "changes": {
      "assignee": "alice",
      "base_version": 7,
      "description": null,
      "priority": "",
      "severity": "",
      "status": "In progress",
      "subject": null,
      "type": ""
    }
  },
  "meta": { "contract": 1 }
}

所有可編輯欄位都會出現,不論你有沒有指定,所以請讀取你在意的鍵,不要假設這個物件只包含你傳入的項目。

接著才真正寫入,並處理兩種「不是成功」的回應:

if taiga issue edit 42 --status "In progress" --base-version 7 --json --no-input; then
    echo "已套用"
else
    case $? in
        6)  echo "別人先改了,重新讀取後再決定" >&2 ;;
        11) echo "寫入可能已生效,重試前先確認" >&2 ;;
        *)  exit 1 ;;
    esac
fi

明確傳入 --base-version 是在表達意圖:這個變更要套用在版本 7 上,否則就拒絕。不指定時指令會自己讀取 目前版本 —— 那通常是人想要的行為,但很少是無人監督的 agent 該依賴的。

4. 並行編輯實際會發生什麼

以下逐字紀錄是真的,對著 Taiga 6.10.2 伺服器、由兩個行為者編輯同一張 issue 抓下來的。它重要是因為 Taiga 是逐欄位比對版本 —— 比鎖定寬鬆,而且很容易被想成別的樣子。

不同欄位:兩邊都生效

$ taiga issue view 4 --json --fields ref,subject,status,version
{"data":{"ref":4,"status":"New","subject":"Refresh tokens on 401","version":1},"meta":{"contract":1}}

# 此時另一人在網頁 UI 改了 subject

$ taiga issue edit 4 --status "In progress" --base-version 1
Updated issue atlas-commerce#4: Refresh tokens on 401 and 403 (version 3)
$ echo $?
0

這次寫入以版本 1 為基礎卻仍然成功,因為對方動的是 subject,而這個指令動的是 status。對方的文字保留、 這次的狀態變更生效,版本前進了兩次。

相同欄位:寫入被拒絕

# 另一人先把 status 改成 In progress

$ taiga issue edit 5 --status Closed --base-version 1
Error: version: The version doesn't match with the current one
$ echo $?
6
$ taiga --json issue edit 5 --status Closed --base-version 1
{"error":{"code":"occ_conflict","message":"version: The version doesn't match with the current one","retryable":false,"details":{"version":"The version doesn't match with the current one"},"upstream_status":400},"meta":{"contract":1}}

沒有任何東西被覆寫,也沒有任何重試。對腳本而言,面對 exit 6 的正確反應是重新讀取該筆資料再做決定 —— 對方的值很可能才是對的。

Clone this wiki locally