Skip to content

Commit e6a4102

Browse files
authored
Merge pull request #16 from redhat-developer/feat/jira-rest-graphql-fallback
feat: add REST API/GraphQL fallback to rhdh-jira skill
2 parents a756ba5 + 2a7fb69 commit e6a4102

9 files changed

Lines changed: 682 additions & 13 deletions

File tree

.github/workflows/ci.yml

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,34 @@ permissions:
1010
contents: read
1111

1212
jobs:
13+
resolve-python:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
min-version: ${{ steps.resolve.outputs.min }}
17+
max-version: ${{ steps.resolve.outputs.max }}
18+
matrix: ${{ steps.resolve.outputs.matrix }}
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- id: resolve
23+
run: |
24+
python3 << 'PYEOF' >> "$GITHUB_OUTPUT"
25+
import re, json
26+
with open('pyproject.toml') as f:
27+
spec = re.search(r'requires-python\s*=\s*"([^"]+)"', f.read())
28+
spec = spec.group(1) if spec else '>=3.9'
29+
min_m = re.search(r'>=\s*([\d.]+)', spec)
30+
max_m = re.search(r'<=?\s*([\d.]+)', spec)
31+
min_v = min_m.group(1) if min_m else '3.9'
32+
max_v = max_m.group(1) if max_m else '3.x'
33+
matrix = [min_v, max_v] if min_v != max_v else [min_v]
34+
print(f'min={min_v}')
35+
print(f'max={max_v}')
36+
print(f'matrix={json.dumps(matrix)}')
37+
PYEOF
38+
1339
lint:
40+
needs: resolve-python
1441
runs-on: ubuntu-latest
1542
steps:
1643
- uses: actions/checkout@v4
@@ -21,7 +48,7 @@ jobs:
2148

2249
- uses: actions/setup-python@v5
2350
with:
24-
python-version: "3.11"
51+
python-version: ${{ needs.resolve-python.outputs.max-version }}
2552

2653
- run: uv sync --frozen --extra dev
2754

@@ -30,11 +57,11 @@ jobs:
3057
- run: uv run ruff format --check .
3158

3259
test:
33-
needs: lint
60+
needs: resolve-python
3461
runs-on: ubuntu-latest
3562
strategy:
3663
matrix:
37-
python-version: ["3.9", "3.11"]
64+
python-version: ${{ fromJson(needs.resolve-python.outputs.matrix) }}
3865
steps:
3966
- uses: actions/checkout@v4
4067

skills/rhdh-jira/SKILL.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: rhdh-jira
33
description: |
4-
Interact with RHDH Jira projects (RHIDP, RHDHPLAN, RHDHBUGS, RHDHSUPP) using the Atlassian CLI (acli). Use when the user needs to search, create, view, edit, transition, comment on, or link Jira issues for Red Hat Developer Hub. Also use when the user asks about Jira workflows, exit criteria, issue templates, custom fields, boards, sprints, or JQL queries in the RHDH context. Trigger when the user mentions Jira issue keys like RHIDP-1234, RHDHPLAN-567, RHDHBUGS-890, or any RHDH project key. Also trigger for support case bug filing, feature request creation, or release planning in Jira. Covers acli setup, authentication, and troubleshooting.
4+
Interacts with RHDH Jira projects (RHIDP, RHDHPLAN, RHDHBUGS, RHDHSUPP) using the Atlassian CLI (acli), with REST API and GraphQL fallback for custom field updates and complex queries. Use when the user needs to search, create, view, edit, transition, comment on, or link Jira issues for Red Hat Developer Hub. Also use when the user asks about Jira workflows, exit criteria, issue templates, custom fields, boards, sprints, JQL queries, or Jira API schema discovery. Trigger when the user mentions Jira issue keys like RHIDP-1234, RHDHPLAN-567, RHDHBUGS-890, or any RHDH project key. Also trigger for support case bug filing, feature request creation, release planning, or acli setup and installation.
55
compatibility: "acli (Atlassian CLI) on PATH. Python 3 for scripts. Windows, macOS, Linux."
66
---
77

@@ -20,9 +20,17 @@ python scripts/setup.py
2020
The script checks:
2121
1. `acli` binary on PATH
2222
2. Jira API token auth configured (`~/.config/acli/jira_config.yaml`)
23-
3. Smoke test against `redhat.atlassian.net`
23+
3. `.jira-token` file next to `acli` executable (for REST API fallback)
24+
4. Smoke test against `redhat.atlassian.net`
2425

25-
If `acli` is not installed, download from [Atlassian CLI](https://developer.atlassian.com/cloud/acli/). Authenticate with `acli auth login` or configure an API token.
26+
If `acli` is not installed, download from [Atlassian CLI](https://developer.atlassian.com/cloud/acli/) and follow the [Getting Started guide](https://developer.atlassian.com/cloud/acli/guides/how-to-get-started/) for installation and authentication setup. Use API token authentication, not OAuth — OAuth sessions expire and `acli auth status` gives false negatives with token auth (see Gotchas).
27+
28+
### REST/GraphQL capability gate
29+
30+
Before attempting any REST API or GraphQL call:
31+
1. Run `python scripts/setup.py --json` and check `token_file_found`
32+
2. If missing, state: "REST API/GraphQL fallback unavailable — `.jira-token` not configured. Run `setup.py` for instructions." Continue with acli-only workflow.
33+
3. If the user needs REST/GraphQL, load `references/auth.md` for setup instructions
2634

2735
## Scripts
2836

@@ -64,6 +72,9 @@ Load only what the current task requires.
6472
| `references/templates.md` | Creating new issues. Also load `references/workflows.md` for required fields at entry status. |
6573
| `references/support.md` | Handling support cases, filing bugs from customer cases, or creating feature requests from support. |
6674
| `references/jql-patterns.md` | Building a JQL query, finding a board ID, or looking up sprint information. JQL cookbook with 23+ tested queries. |
75+
| `references/auth.md` | Setting up authentication for REST API or GraphQL calls. Token file format, path discovery, security, instance config, common auth errors. |
76+
| `references/rest-api-fallback.md` | `acli` failed to update a custom field (Team, Size, Story Points, Release Note Type). Curl examples, response handling, OpenAPI spec discovery. |
77+
| `references/graphql-queries.md` | Complex read queries needing multiple fields, relationships, or custom field data in one call. Schema introspection, JQL search via GraphQL, field type fragments. |
6778

6879
## Common Gotchas
6980

@@ -75,19 +86,23 @@ Load only what the current task requires.
7586
6. **ADF vs plain text.** Reading descriptions via `--json` returns Atlassian Document Format (nested JSON). Creating/editing with `--description` accepts plain text. Don't try to round-trip ADF through `--description`.
7687
7. **Acceptance Criteria field is almost always null.** Scan the description for "Requirements", "Acceptance Criteria", or bullet-style criteria instead of checking `customfield_10718`.
7788
8. **`--enrich` is MANDATORY for custom fields.** Both `acli search --json` and `acli view KEY --json` (without `--fields "*all"`) return only basic fields (assignee, issuetype, priority, status, summary). Story points, team, sprint, and size will appear as empty/null — looking like the data isn't set when it actually is. Always use `scripts/parse_issues.py --enrich` to get custom field data. Skipping `--enrich` is the #1 cause of false "missing data" reports.
78-
9. **`acli sprint list-workitems --json` wraps results in `{"issues": [...]}`.** The output is NOT a flat array — it's an object with an `issues` key. Extract the array before piping to `parse_issues.py`. See `references/acli-commands.md` for the workaround command.
89+
9. **Custom fields may fail to update via `acli`.** `acli jira workitem edit` can silently fail or error when setting custom fields (Team, Size, Story Points). When an `acli edit` for a custom field fails, fall back to the Jira REST API. Find the token file at `.jira-token` next to the `acli` executable (discover the path with `readlink -f "$(which acli)"` or `where acli`). Read `references/rest-api-fallback.md` for curl examples and payload formats. Never read the token file into context.
90+
10. **`acli sprint list-workitems --json` wraps results in `{"issues": [...]}`.** The output is NOT a flat array — it's an object with an `issues` key. Extract the array before piping to `parse_issues.py`. See `references/acli-commands.md` for the workaround command.
91+
11. **GraphQL search is beta.** `issueSearchStable` requires `X-ExperimentalApi: JiraIssueSearch` header. Load `references/graphql-queries.md` before attempting GraphQL queries.
92+
12. **`.jira-token` format is `email:token`, not bare token.** A file containing only the API token without the email prefix will cause 401 errors on REST/GraphQL calls. The `setup.py` script validates the format.
7993

8094
## Error Handling
8195

8296
| Error | Action |
8397
|-------|--------|
84-
| `acli` not on PATH | Run `scripts/setup.py`. Install from Atlassian if missing. |
98+
| `acli` not on PATH | Run `scripts/setup.py`. Install from Atlassian if missing. See [Getting Started](https://developer.atlassian.com/cloud/acli/guides/how-to-get-started/). |
8599
| "unauthorized" from `auth status` | Ignore. Check `jira_config.yaml` exists. Run smoke test. |
86100
| "required flag(s) not set" | Command syntax wrong. Run `acli jira <subcommand> --help`. |
87101
| "field X is not allowed" | Use `--json` instead of `--fields` for that field. |
88102
| "the value X does not exist for the field 'project'" | Project key is wrong or project is archived (e.g., RHDHPAI). |
89103
| Rate limiting (429) | Wait 5 seconds, retry once. |
90104
| Interactive prompt hangs | Missing `--yes` flag on a mutating command. |
105+
| Custom field update fails via `acli` | Fall back to Jira REST API using `.jira-token` file. See Gotcha #9. |
91106

92107
## Common Workflows
93108

@@ -106,7 +121,18 @@ Load only what the current task requires.
106121
2. Verify required fields are set before transitioning
107122
3. Run `acli jira workitem transition --key KEY --status "X" --yes`
108123

124+
### Complex queries (many fields, relationships)
125+
1. Load `references/graphql-queries.md`
126+
2. Use `issueByKey` for single issues or `issueSearchStable` (beta) for JQL search
127+
3. Fall back to `acli` + `parse_issues.py --enrich` if GraphQL returns errors
128+
129+
### Discovering unknown fields or endpoints
130+
1. For REST: load `references/rest-api-fallback.md` — use the OpenAPI spec or `/rest/api/3/field` endpoint
131+
2. For GraphQL: load `references/graphql-queries.md` — use `__type` introspection queries
132+
3. Do not guess field IDs or types — always verify against the live schema
133+
109134
## When NOT to Use
110135

111136
- **Non-RHDH Jira projects** — this skill's field mappings, workflows, and JQL patterns are specific to RHIDP/RHDHPLAN/RHDHBUGS/RHDHSUPP
112-
- **Jira REST API directly** — this skill covers `acli` CLI only
137+
- **Jira REST API directly** — use `acli` first. REST API is a fallback for custom field updates and schema discovery when `acli` cannot handle the operation (see Gotcha #9)
138+
- **GraphQL for simple lookups** — use `acli` for single-issue views and simple searches. GraphQL is for complex queries needing relationships or many custom fields in one call, and for schema introspection
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Authentication and Token Setup
2+
3+
Shared authentication for REST API and GraphQL calls. Both use the same `.jira-token` file with basic auth.
4+
5+
**Never read the `.jira-token` file into context.** Pass it to `curl -u` via shell substitution.
6+
7+
## Token File
8+
9+
The `.jira-token` file lives next to the `acli` executable (same directory). Format is a single line:
10+
11+
```
12+
email@example.com:your-api-token
13+
```
14+
15+
Use the same API token you used to authenticate `acli`.
16+
17+
## Token Discovery (Shell)
18+
19+
```bash
20+
ACLI_DIR="$(dirname "$(readlink -f "$(which acli)" 2>/dev/null || which acli)")"
21+
TOKEN_FILE="$ACLI_DIR/.jira-token"
22+
if [ ! -f "$TOKEN_FILE" ]; then
23+
echo "ERROR: .jira-token not found next to acli at $ACLI_DIR"
24+
echo "Create it with: echo 'your-email@example.com:your-api-token' > $TOKEN_FILE"
25+
echo "Then: chmod 600 $TOKEN_FILE"
26+
exit 1
27+
fi
28+
AUTH=$(cat "$TOKEN_FILE")
29+
```
30+
31+
The `AUTH` value is `email:api-token`, passed directly as basic auth credentials via `curl -u "$AUTH"`.
32+
33+
## Security
34+
35+
The `.jira-token` file contains plaintext credentials. Restrict permissions:
36+
- Unix/macOS: `chmod 600 .jira-token`
37+
- Windows: restrict access via file properties → Security tab
38+
39+
The `setup.py` script warns when the file is group/world-readable.
40+
41+
## Instance Config
42+
43+
```bash
44+
CLOUD_ID="2b9e35e3-6bd3-4cec-b838-f4249ee02432"
45+
JIRA_BASE="https://redhat.atlassian.net"
46+
```
47+
48+
### Discovering your cloudId
49+
50+
The `cloudId` above is for `redhat.atlassian.net`. To discover it for any Jira Cloud instance:
51+
52+
```bash
53+
curl -s -u "$AUTH" "$JIRA_BASE/_edge/tenant_info" | python -c "import json,sys; print(json.load(sys.stdin)['cloudId'])"
54+
```
55+
56+
## Common Auth Errors
57+
58+
| Symptom | Cause | Fix |
59+
|---------|-------|-----|
60+
| 401 on REST/GraphQL calls | `.jira-token` is bare token without email prefix | Format must be `email:token` |
61+
| 401 after token was working | API token expired or revoked | User must regenerate at Atlassian account settings |
62+
| `acli auth status` says unauthorized | False negative — `auth status` checks OAuth, not API tokens | Ignore. Use `acli jira project list --recent 1` as smoke test |
63+
| `.jira-token` not found | File is next to symlink, not the real binary | `setup.py` uses `resolve()` to follow symlinks — place file next to real binary |

0 commit comments

Comments
 (0)