Repository navigation
Automation Recipes
English · 繁體中文
Three working examples of driving Taiga from something other than a keyboard. Each one leans on the
JSON contract: --json for a stable shape, --fields to keep the payload small,
--no-input so nothing waits for a prompt, and exit codes to decide what happened.
Set TAIGA_API_URL and TAIGA_TOKEN in the environment for all three, or log in first with
taiga auth login.
The trap here is creating a duplicate issue on every failed run. Search first, and comment on the existing issue instead when one is already open.
# .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 keeps the response to what the script reads.
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
fiTwo details worth copying rather than the whole file. --fields ref,subject means the script parses
only what it uses, so an added field upstream cannot change its behaviour. And nothing retries: if
the connection drops mid-write, taiga exits 11 rather than creating the issue twice, and the step
fails loudly. That is the desired outcome for a reporting job.
--json output is a stable envelope, so jq expressions written today keep working.
#!/usr/bin/env sh
set -eu
# Unassigned open issues, oldest first.
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)"'To act on the result, branch on the exit code rather than on message text:
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 "not authenticated" >&2 ;;
5) echo "issue 42 does not exist" >&2 ;;
9) echo "Taiga is unreachable; safe to retry" >&2 ;;
*) echo "unexpected failure" >&2 ;;
esac
exit 1
fiThe full table is in JSON Contract. The codes that matter most to a script are 6 (someone else changed the record), 9 (transport, safe to retry) and 11 (the write may already have happened, do not retry).
An agent should not have to guess whether a command is safe to run unattended. Ask the tool.
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 and idempotency are the two fields an agent needs: read commands are free to run,
write commands need a decision, and non_idempotent means a failed attempt must not simply be
repeated.
Rehearse the mutation before making it. --dry-run still contacts Taiga, so it resolves the status
name to an ID and checks that the assignee exists, but it guarantees that no write leaves the
process:
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 }
}Every editable field appears, whether or not it was given, so read the keys you care about rather than assuming the object only holds what you passed.
Then commit, and handle the two answers that are not success:
if taiga issue edit 42 --status "In progress" --base-version 7 --json --no-input; then
echo "applied"
else
case $? in
6) echo "someone edited it first; re-read and decide" >&2 ;;
11) echo "the write may have landed; check before retrying" >&2 ;;
*) exit 1 ;;
esac
fiPassing --base-version makes the intent explicit: apply this change to version 7 or refuse. Without
it the command reads the current version itself, which is usually what a human wants and rarely what
an unattended agent should rely on.
The transcripts below are real, captured against a Taiga 6.10.2 server with two actors editing the same issue. They matter because Taiga compares the version per field, which is less restrictive than a lock and easy to assume wrongly.
$ 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}}
# meanwhile, someone edits the subject in the web UI
$ taiga issue edit 4 --status "In progress" --base-version 1
Updated issue atlas-commerce#4: Refresh tokens on 401 and 403 (version 3)
$ echo $?
0The write was based on version 1 and still succeeded, because the other person touched subject
while this command touched status. Their wording survives, this status change lands, and the
version has advanced twice.
# someone sets the status to "In progress" first
$ 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}}Nothing was overwritten and nothing was retried. For a script the correct response to exit 6 is to re-read the record and decide, because the other value may well be the right one.
Getting started
Command reference
- Projects
- Members and Roles
- Work Items
- Sprints
- Wiki Pages
- Attachments
- Metadata and Custom Fields
- Search, Timeline and Stats
- Batch Operations
- Automation Recipes
- Alternatives
- Webhooks
- Account and Integrations
Operations