Table of Contents generated with DocToc
- JIRA bridge
Capability: contract:tracker + contract:people
Kind: implementation
Vendor: Atlassian
JIRA REST helpers for the issue-* skill family.
Adopters with JIRA-based issue trackers wire this in as their
tracker bridge; adopters using GitHub Issues or other trackers
contribute a parallel tools/<tracker>/ directory.
The bridge provides both read and write subcommands.
The read-only contributor-activity queries of contract:tracker run in Python, in the jira backend of tools/contributor-metrics, through this package's read-only REST client, jira_bridge.rest; the profile lookup of contract:people is a single REST read. See People and contributor-activity reads.
Write operations require JIRA_API_TOKEN and follow the same
write-path discipline as the GitHub bridge: every mutation is
gated on explicit user confirmation in the calling skill — the
bridge only executes confirmed actions.
- Runtime: Groovy 4.x+ on
PATH(groovy tools/jira/bridge.groovy …);@Grabpulls the HTTP-client dependencies on first run, no separate install step. Python 3.11+ viauvis needed only for the pytest test harness. - CLIs:
groovy(4.x — the@Grabcoordinate uses theorg.apache.groovygroup ID);uvonly to run the tests. - Credentials / auth:
ISSUE_TRACKER_URL(required) andISSUE_TRACKER_PROJECTexported by the caller; write subcommands requireJIRA_API_TOKEN(JIRA_AUTH_SCHEME=Basicdefault, orBearerfor ASF PATs). Anonymous-read trackers need no auth for read subcommands. - Network: the configured
<issue-tracker>JIRA host (e.g.issues.apache.org/jira);@Grabreaches Maven Central on first run to resolve dependencies. - Optional:
groovyonPATHfor the pytest suite — tests auto-skip when Groovy is absent.
tools/jira/
├── README.md (this file)
├── bridge.groovy (Groovy reference implementation)
├── pyproject.toml (Python test harness config)
├── src/jira_bridge/ (read-only Python REST client, rest.py; test harness package)
└── tests/ (pytest test suite)
Other languages (Python, Bash + curl) are welcome via PR.
groovy tools/jira/bridge.groovy <subcommand> [args]The Groovy implementation uses @Grab for HTTP client dependencies
— no separate install step. Requires Groovy 4.x or newer on
PATH (the @Grab coordinate uses org.apache.groovy, which
is the Groovy 4 group ID).
Run a JQL query against <issue-tracker> and emit matching issues
as JSON to stdout:
groovy tools/jira/bridge.groovy search \
'project = <KEY> AND status = Open AND resolution = Unresolved'Output (truncated):
{
"total": 42,
"issues": [
{"key": "<KEY>-9999", "title": "...", "status": "Open", "components": [...], "fixVersion": "..."},
...
]
}The --limit <N> flag caps the result count (default: 50).
Fetch a single issue's full state (body, comments, attachments list, labels, fixVersion, etc.) as JSON:
groovy tools/jira/bridge.groovy issue <KEY>-9999Output is the JIRA REST /rest/api/2/issue/<KEY> response,
shaped for skill consumption.
List the JIRA projects available at the configured
<issue-tracker> URL. Useful during initial adoption to confirm
the project key is correct.
groovy tools/jira/bridge.groovy projectsAll write subcommands require JIRA_API_TOKEN to be set and
follow the write-path discipline described below.
Post a comment on an issue. The comment body is read from a file to avoid shell-quoting issues:
groovy tools/jira/bridge.groovy comment FOO-9999 --body-file /tmp/comment.txtOutput:
{"ok": true, "key": "FOO-9999", "commentId": "12345"}Move an issue to a new workflow state. The transition name is resolved case-insensitively against the issue's available transitions:
groovy tools/jira/bridge.groovy transition FOO-9999 "Resolve Issue"Output:
{"ok": true, "key": "FOO-9999", "transition": "Resolve Issue", "transitionId": "21"}If the transition name does not match any available transition, the command exits with an error listing the valid names.
Toggle labels on an issue. Both --add and --remove can be
specified multiple times in a single call:
groovy tools/jira/bridge.groovy label FOO-9999 --add security --remove needs-triageOutput:
{"ok": true, "key": "FOO-9999", "added": ["security"], "removed": ["needs-triage"]}Uses JIRA's atomic update API — no read-modify-write race.
Set the assignee on an issue. Data Center only — Cloud uses
accountId, which is not currently supported:
groovy tools/jira/bridge.groovy assign FOO-9999 jdoeOutput:
{"ok": true, "key": "FOO-9999", "assignee": "jdoe"}Edit a single field (including custom fields) on an issue.
Use --value for plain string/number values. Use --value-json
for structured values (priority, version, single-select, user
picker, etc.):
# String value
groovy tools/jira/bridge.groovy field FOO-9999 customfield_10100 --value "high"
# Structured value (e.g. priority)
groovy tools/jira/bridge.groovy field FOO-9999 priority --value-json '{"name":"High"}'
# Array value (e.g. fixVersions)
groovy tools/jira/bridge.groovy field FOO-9999 fixVersions --value-json '[{"name":"1.2.3"}]'Output:
{"ok": true, "key": "FOO-9999", "field": "priority", "value": {"name": "High"}}Attach a file to an issue:
groovy tools/jira/bridge.groovy attach FOO-9999 /tmp/report.txtOutput:
{"ok": true, "key": "FOO-9999", "attachments": [{"id": "99", "filename": "report.txt"}]}The read-only activity queries of contract:tracker and the profile lookup of contract:people, against the same Jira Data Center REST API v2 the bridge uses and with the same configuration (ISSUE_TRACKER_URL, ISSUE_TRACKER_PROJECT, JIRA_API_TOKEN, JIRA_AUTH_SCHEME).
The per-person streams are implemented by the jira backend of tools/contributor-metrics over jira_bridge.rest; the others are plain REST reads a skill makes.
jira_bridge.rest treats the token as the user's personal credential and the URL as project configuration, which anyone with write access can commit:
- the URL must be
https://(http://only forlocalhost,127.0.0.1or::1); - the token is sent only to the host the user confirmed for it —
JIRA_API_HOST, or ahost=line beside atoken=line in~/.config/apache-magpie/jira-token— and only on requests to that host; for any other host it is withheld, reads go out anonymously, and the caller is told why; - redirects are refused, so the token never follows one;
- the token never appears in an error message.
<user>is the person's Jira username, validated against[A-Za-z0-9._@+-]{1,255}before it reaches a query.
| Verb | Jira resolution |
|---|---|
tracker list_filed(<user>, since, end) |
GET /rest/api/2/search with JQL project = "<KEY>" AND reporter = "<user>" AND created >= "<since>" AND created <= "<end> 23:59" |
tracker list_triaged(<user>, since, end) |
GET /rest/api/2/search?expand=changelog&fields=comment,… over project = "<KEY>" AND updated >= "<since>" AND created <= "<end> 23:59"; an issue the user did not report counts when its changelog shows the user changing status, labels, component, priority, assignee, resolution or Fix Version, or the user commented, inside the window — dated by the first such action |
tracker list_commented(<user>, since, end) |
the same search; an issue counts when the user commented inside the window, dated by the first such comment |
tracker list_created(since, until) |
project = "<KEY>" AND created >= "<since>" AND created <= "<until> 23:59"; author_first_time from one reporter = "<author>" AND created < "<created>" count per author |
tracker first_reply(<KEY-N>) |
GET /rest/api/2/issue/<KEY-N>/comment, the first comment by a rostered maintainer (Jira has no author-association signal) |
people get_profile(<user>) |
GET /rest/api/2/user?username=<user> → displayName; a 404 means the account does not exist. Organisation, website and linked accounts are not exposed: null. |
people list_collaborators, add_team_member |
not provided — Jira has no repository collaborators or forge teams (NotApplicable) |
Jira has no pull requests: authored changes and reviews come from the project's code host through contract:change-request.
Comment bodies are read inside the backend to find pushback candidates and never leave it.
The bridge reads its configuration from the environment:
| Variable | Notes |
|---|---|
ISSUE_TRACKER_URL |
required; e.g. https://issues.apache.org/jira |
ISSUE_TRACKER_PROJECT |
project key (e.g. FOO) |
JIRA_API_TOKEN |
required for write subcommands — see auth notes below |
JIRA_AUTH_SCHEME |
Basic (default) or Bearer — see auth notes below |
The caller is responsible for exporting these (a skill resolves them
from <project-config>/issue-tracker-config.md
and passes them in the environment). Direct file-fallback inside the
bridge is a possible future enhancement — it is not implemented
today; the bridge exits if ISSUE_TRACKER_URL is unset.
For anonymous-read trackers, no auth is required for read
subcommands. Write subcommands always require JIRA_API_TOKEN and
exit with an error if it is unset.
Authentication: This bridge targets JIRA Data Center (DC),
specifically ASF JIRA at issues.apache.org/jira. Cloud is not
currently supported (assign uses DC name, not Cloud
accountId).
- Basic auth (default): set
JIRA_API_TOKENto the base64-encodedusername:passwordorusername:patstring. - Bearer auth (ASF PATs): set
JIRA_AUTH_SCHEME=BearerandJIRA_API_TOKENto the raw PAT string. ASF JIRA DC PATs useAuthorization: Bearer <pat>.
Every subcommand emits JSON to stdout on success, or a non-zero exit code with a human-readable error to stderr on failure.
Write subcommands return {"ok": true, "key": "<KEY>", ...} with
operation-specific fields as documented per subcommand above.
The output schema is documented per subcommand above. Skills parse the JSON via standard JSON tooling — no special envelope, no wrapper.
The bridge executes mutations but does not decide whether to mutate. Every write operation is gated on explicit user confirmation in the calling skill — the bridge only executes confirmed actions.
This mirrors the GitHub bridge's write-path discipline (see
tools/github/operations.md): skills
surface the proposed action to the maintainer, wait for
confirmation, then call the bridge to execute.
The test suite uses a mock HTTP server and requires groovy on
PATH. Tests are skipped automatically when Groovy is not
available.
cd tools/jira
uv run pytestissue-triage— primary consumer (selector resolution + per-issue fetch).issue-reassess— campaign-level consumer (pool fetch).security-issue-sync— write-path consumer (label, transition, comment, field updates).security-issue-invalidate— write-path consumer (close with label + comment).tools/github/operations.md— write-path discipline reference.<project-config>/issue-tracker-config.md— the adopter's tracker URL + project key.