Issue & Project Metadata Automation
Use this guide to build repeatable triage workflows for issues.
Overview
Section titled “Overview”A typical triage flow needs two kinds of operations:
- Label management — read and write issue labels
- Project card management — place or move the issue on a project board
Treat Forgejo Proxy as the canonical HTTP surface for both.
- Native Forgejo resources are available through the proxy under
/api/v1/... - Proxy-only project resources are also under
/api/v1/... - Root-path exceptions stay narrow and are not part of this workflow
The practical result is that your scripts can usually use one base URL: FORGEJO_PROXY_URL.
1. Discover label metadata
Section titled “1. Discover label metadata”In the forgejo-proxy org, triage labels are maintained as org-level labels such as Kind/*, Priority/*, and Reviewed/*.
thfg label list-org --org forgejo-proxycurl against the canonical proxy surface
Section titled “curl against the canonical proxy surface”curl -s \ -H "Authorization: token $FORGEJO_TOKEN" \ "$FORGEJO_PROXY_URL/api/v1/orgs/forgejo-proxy/labels" \ | jq '.[] | {id, name, color}'Example output:
{ "id": 12, "name": "Kind/Bug", "color": "#d73a4a" }{ "id": 13, "name": "Kind/Feature", "color": "#a2eeef" }{ "id": 14, "name": "Priority/High", "color": "#e4e669" }2. Apply labels to the issue
Section titled “2. Apply labels to the issue”Replace all labels
Section titled “Replace all labels”# CLIthfg issue label-set forgejo-proxy/forgejo-proxy 7 \ --label "Kind/Bug" \ --label "Priority/High"
# curl via proxy /api/v1curl -s -X PUT \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"labels":[12,14]}' \ "$FORGEJO_PROXY_URL/api/v1/repos/forgejo-proxy/forgejo-proxy/issues/7/labels"Add one label
Section titled “Add one label”# CLIthfg issue label-add forgejo-proxy/forgejo-proxy 7 --label "Reviewed/Yes"
# curl via proxy /api/v1curl -s -X POST \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"labels":[16]}' \ "$FORGEJO_PROXY_URL/api/v1/repos/forgejo-proxy/forgejo-proxy/issues/7/labels"Resolve labels by name
Section titled “Resolve labels by name”BUG_ID=$(thfg resolve label "Kind/Bug" --org forgejo-proxy)HIGH_ID=$(thfg resolve label "Priority/High" --org forgejo-proxy)3. Discover project and column IDs
Section titled “3. Discover project and column IDs”Project automation is now documented with the normalized /api/v1/projects/... contract.
thfg project list --org forgejo-proxy --output-format json | jq '.[] | {id, title}'thfg project columns --id 2 --output-format json | jq '.[] | {id, title}'# List projects in the orgcurl -s \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \ -H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \ "$FORGEJO_PROXY_URL/api/v1/orgs/forgejo-proxy/projects" \ | jq '.[] | {id, title}'
# List columns in project 2curl -s \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \ -H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \ "$FORGEJO_PROXY_URL/api/v1/projects/2/columns" \ | jq '.[] | {id, title}'4. Add or move the issue card
Section titled “4. Add or move the issue card”The normalized raw API is column-scoped:
POST /api/v1/projects/{project_id}/columns/{column_id}/cardsUse it both to place a new issue into a project and to move an existing card to another column.
CLI shortcuts
Section titled “CLI shortcuts”# Add the issue using the CLI convenience commandthfg project add-issue 2 forgejo-proxy/forgejo-proxy#7
# Move the card to In Progress (column 7)thfg project move-card 2 7 forgejo-proxy/forgejo-proxy#7Raw API: add to Backlog (column 5)
Section titled “Raw API: add to Backlog (column 5)”curl -s -X POST \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \ -H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \ -H "Content-Type: application/json" \ -d '{"issue_id":7,"owner":"forgejo-proxy","repo":"forgejo-proxy"}' \ "$FORGEJO_PROXY_URL/api/v1/projects/2/columns/5/cards"Raw API: move to In Progress (column 7)
Section titled “Raw API: move to In Progress (column 7)”curl -s -X POST \ -H "Authorization: token $FORGEJO_TOKEN" \ -H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \ -H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \ -H "Content-Type: application/json" \ -d '{"issue_id":7,"owner":"forgejo-proxy","repo":"forgejo-proxy"}' \ "$FORGEJO_PROXY_URL/api/v1/projects/2/columns/7/cards"5. Put it together in one script
Section titled “5. Put it together in one script”#!/usr/bin/env bashset -euo pipefail
ORG="forgejo-proxy"REPO="forgejo-proxy/forgejo-proxy"ISSUE=42PROJECT_ID=2TARGET_COLUMN_ID=7
BUG_ID=$(thfg resolve label "Kind/Bug" --org "$ORG")HIGH_ID=$(thfg resolve label "Priority/High" --org "$ORG")
thfg issue label-set "$REPO" "$ISSUE" \ --label "$BUG_ID" \ --label "$HIGH_ID"
thfg project add-issue "$PROJECT_ID" "$REPO#$ISSUE"thfg project move-card "$PROJECT_ID" "$TARGET_COLUMN_ID" "$REPO#$ISSUE"6. Configuration requirements
Section titled “6. Configuration requirements”| Variable | Required for |
|---|---|
FORGEJO_PROXY_URL | All curl examples in this guide |
FORGEJO_TOKEN or FORGEJO_API_TOKEN | Authenticated API access |
FORGEJO_WEBUI_USERNAME / FORGEJO_WEBUI_PASSWORD | Raw project API calls and other WebUI-backed operations |
For CLI usage, the same values usually come from your active thfg host entry:
proxyUrltokenusernamepassword
7. Known pitfalls
Section titled “7. Known pitfalls”Legacy /api/v1/proxy/... paths
Section titled “Legacy /api/v1/proxy/... paths”If a script still calls /api/v1/proxy/..., update it to the normalized /api/v1/... contract before debugging anything else.
Project writes are explicit now
Section titled “Project writes are explicit now”Older scripts sometimes relied on the proxy choosing a default project column implicitly. The normalized raw API is more explicit: choose the target column yourself and call the column-scoped cards endpoint.
Org labels are the authoritative triage labels
Section titled “Org labels are the authoritative triage labels”If triage depends on org-level labels, resolve those label IDs from the org and reuse them. Do not create repo-local duplicates just because a repo endpoint accepts writes.