-
Notifications
You must be signed in to change notification settings - Fork 58
feat: GitHub-based JIRA YAML workflow #1553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
jonsnyder
wants to merge
11
commits into
main
Choose a base branch
from
github-jira-yaml-workflow
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 7 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
0d3734c
feat: add OpenSpec change for GitHub-based JIRA YAML workflow
claude 64ab057
feat: address PR review feedback on github-jira-yaml-workflow
claude 31c4ac9
chore: remove PR JIRA ticket requirement (separate change)
claude c3596d8
feat: implement GitHub-based JIRA YAML workflow
claude 8ccb960
chore: apply prettier formatting to jira scripts
claude a8fc943
refactor(jira): extract api.mjs with DI, add process.mjs, fix globalI…
claude e6b962a
chore(jira): rename scripts from .mjs to .js, switch to named js-yaml…
claude cd78f71
chore: merge main into github-jira-yaml-workflow, regenerate lockfile
claude 892367f
fix(jira): dryRun only blocks writes, fetch always reads JIRA, token …
claude e056822
fix(lint): convert jira script function declarations to arrow constants
claude ef9e592
feat(jira): address review — local config, simpler apply logic, globa…
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| # /jira-propose — Propose JIRA ticket changes locally | ||
|
|
||
| Create or update a `.jira/*.yml` file to represent a JIRA ticket change. This skill operates **entirely locally** — no JIRA API calls are made. The resulting file is committed as part of a PR and applied to JIRA automatically when the PR merges. | ||
|
|
||
| ## When to invoke | ||
|
|
||
| Invoke `/jira-propose` when: | ||
| - A feature, bug fix, or improvement is being built and should be tracked in JIRA | ||
| - An existing JIRA ticket needs updates (status, components, description, etc.) | ||
| - The user explicitly asks to propose a JIRA ticket for the current work | ||
|
|
||
| ## Steps | ||
|
|
||
| ### 1. Gather context | ||
|
|
||
| Run these commands in parallel to understand the current work: | ||
|
|
||
| ```bash | ||
| git diff --stat HEAD | ||
| git log --oneline -10 | ||
| git branch --show-current | ||
| ls .jira/*.yml 2>/dev/null || echo "(no existing jira files)" | ||
| ``` | ||
|
|
||
| Also read any `.jira/*.yml` files that exist, to check for matches. | ||
|
|
||
| ### 2. Determine issue type | ||
|
|
||
| Default to **story** (`id: "7"`). Use **bug** (`id: "1"`) when: | ||
| - Branch name contains `fix`, `bug`, `hotfix`, or `patch` | ||
| - Commit messages contain `fix:`, `bug:`, `fixes`, `closes`, or `regression` | ||
| - The diff clearly addresses a defect rather than adding capability | ||
|
|
||
| ### 3. Search for a matching existing ticket | ||
|
|
||
| Check each `.jira/*.yml` file: | ||
| - Compare the filename slug against the branch name and commit messages | ||
| - Compare `details.summary` (if present) against the likely ticket title | ||
| - If a file matches the current work, **update it** (append to `updates` array) | ||
| - If no file matches, **create a new one** | ||
|
|
||
| ### 4a. New ticket — write `PDCL-XXXX-<short-description>.yml` | ||
|
|
||
| Generate a kebab-case short description (3–6 words) from the branch name and commits. | ||
|
|
||
| Write `.jira/PDCL-XXXX-<short-description>.yml` with this structure: | ||
|
|
||
| ```yaml | ||
| updates: | ||
| - path: /rest/api/2/issue | ||
| method: POST | ||
| body: | ||
| fields: | ||
| project: | ||
| key: PDCL | ||
| issuetype: | ||
| id: "7" # Story (use "1" for Bug, "14801" for Documentation) | ||
| summary: <concise one-line ticket title> | ||
| description: | | ||
| <paragraph describing the change, motivation, and business value. | ||
| Include: customer names or personas who benefit, what problem this solves, | ||
| and the elevator-pitch value statement — why this matters to the product.> | ||
| components: | ||
| - id: "155901" # AEP Web SDK | ||
| customfield_23300: # AEP Web SDK product field | ||
| id: "116005" | ||
| - path: /rest/api/2/issue/{key}/remotelink | ||
| method: POST | ||
| body: | ||
| globalId: <generate a short unique random hex string, e.g. "a3f7b2c9e1d6"> | ||
| relationship: mentioned in | ||
| object: | ||
| url: "{GITHUB_PR_URL}" | ||
| title: "{GITHUB_PR_TITLE}" | ||
| ``` | ||
|
|
||
| The `globalId` is the idempotency key for this ticket — it must be unique per ticket file so that multiple new tickets can be created in a single PR merge. Generate a random 12-character hex string (e.g. using `Math.random().toString(16).slice(2,14)` mentally). The `{GITHUB_PR_URL}` and `{GITHUB_PR_TITLE}` placeholders are substituted by `apply.mjs` at run time. | ||
|
|
||
| **Description guidelines:** | ||
| - Lead with who benefits (e.g., "Analytics customers using the Web SDK...") | ||
| - Explain the problem being solved | ||
| - State the business value / outcome (e.g., "This eliminates the need for manual re-configuration after...") | ||
| - Keep to 2–4 sentences | ||
|
|
||
| ### 4b. Existing ticket — update `updates` array | ||
|
|
||
| When a matching file is found, append (or replace if same operation exists) to the `updates` array: | ||
|
|
||
| ```yaml | ||
| updates: | ||
| - path: /rest/api/2/issue/{key} | ||
| method: PUT | ||
| body: | ||
| update: | ||
| summary: | ||
| - set: <new summary> | ||
| # Add other fields as needed; always use "set", never "add"/"remove" | ||
| ``` | ||
|
|
||
| Use `PUT /rest/api/2/issue/{key}` with `update.field[].set` for field changes. | ||
| Use `POST /rest/api/2/issue/{key}/transitions` for status transitions. | ||
|
|
||
| ### 5. Write the file | ||
|
|
||
| - Place it in `.jira/` at the repo root | ||
| - Use YAML comments (`#`) to document custom field IDs inline | ||
| - Verify the YAML is valid before finishing | ||
|
|
||
| ### 6. Report back | ||
|
|
||
| After writing the file, tell the user: | ||
| - The filename created or updated | ||
| - The ticket key (PDCL-XXXX for new, PDCL-NNNN for existing) | ||
| - The proposed summary | ||
| - What will happen when the PR merges (apply.mjs will execute the updates) | ||
|
|
||
| ## Reference: PDCL custom fields | ||
|
|
||
| | Field | ID / value | | ||
| |-------|-----------| | ||
| | Project key | `PDCL` | | ||
| | AEP Web SDK component | `components[].id: "155901"` | | ||
| | Documentation component | `components[].id: "157512"` | | ||
| | Product field (`customfield_23300`) | `id: "116005"` | | ||
| | Story issue type | `issuetype.id: "7"` | | ||
| | Bug issue type | `issuetype.id: "1"` | | ||
| | Documentation issue type | `issuetype.id: "14801"` | | ||
| | JIRA base URL | `https://jira.corp.adobe.com` | | ||
|
|
||
| ## Constraints | ||
|
|
||
| - **No JIRA API calls** — all information comes from git state, context, and existing `.jira/` files | ||
| - All `update` operations must use `set` (never `add`/`remove`) to ensure idempotency | ||
| - New tickets must have a `POST /rest/api/2/issue` as the first entry in `updates` | ||
| - File must be valid YAML | ||
| - Do not populate the `details` section — that is written by `fetch.mjs` after apply |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # .jira/ — Version-controlled JIRA ticket management | ||
|
|
||
| YAML files in this directory represent pending JIRA changes. When a PR merges, the build workflow applies each file to JIRA and replaces it with a refreshed snapshot. | ||
|
|
||
| ## File naming convention | ||
|
|
||
| | File type | Name format | Example | | ||
| |-----------|-------------|---------| | ||
| | Existing ticket | `{PROJECT}-{TICKET#}-{short-title}.yml` | `PDCL-1234-support-for-adcloud.yml` | | ||
| | New ticket (not yet in JIRA) | `{PROJECT}-XXXX-{short-description}.yml` | `PDCL-XXXX-add-identity-map-support.yml` | | ||
|
|
||
| Short titles and descriptions use kebab-case, 3–6 words. | ||
|
|
||
| ## YAML schema | ||
|
|
||
| ```yaml | ||
| # Optional read-only snapshot of the ticket's current JIRA state. | ||
| # Populated by fetch.mjs; ignored by apply.mjs. | ||
| details: | ||
| key: PDCL-1234 | ||
| summary: Support for AdCloud | ||
| status: { name: "In Progress" } | ||
| # ... other non-null fields from JIRA | ||
|
|
||
| # Optional ordered array of idempotent REST calls to apply on merge. | ||
| # Omit if there are no pending changes. | ||
| updates: | ||
| - path: /rest/api/2/issue/PDCL-1234 | ||
| method: PUT | ||
| body: | ||
| update: | ||
| summary: | ||
| - set: "Updated title" | ||
| customfield_23300: # AEP Web SDK product field | ||
| - set: { id: "116005" } | ||
| ``` | ||
|
|
||
| ### `details` section | ||
|
|
||
| - Written by `fetch.mjs`; never modified manually | ||
| - Read-only snapshot; apply.mjs ignores it entirely | ||
| - Null and empty-array fields are omitted | ||
| - String fields longer than 500 chars are truncated with `...` | ||
|
|
||
| ### `updates` section | ||
|
|
||
| - Ordered array of `{ path, method, body }` REST call objects | ||
| - `body` is YAML and is serialized to JSON before sending to JIRA | ||
| - All field updates must use `set` operations (never `add`/`remove`) to ensure idempotency | ||
| - For new tickets: first entry is a `POST /rest/api/2/issue` with `fields` (not `update`) | ||
| - Absent or empty `updates` → apply.mjs prints the key and exits 0 without any API calls | ||
|
|
||
| ## Custom field reference (PDCL project) | ||
|
|
||
| | Field | ID | Value | | ||
| |-------|----|-------| | ||
| | AEP Web SDK component | `components[].id` | `"155901"` | | ||
| | Documentation component | `components[].id` | `"157512"` | | ||
| | AEP Web SDK product (`customfield_23300`) | `id` | `"116005"` | | ||
| | Issue type: Story | `issuetype.id` | `"7"` | | ||
| | Issue type: Bug | `issuetype.id` | `"1"` | | ||
| | Issue type: Documentation | `issuetype.id` | `"14801"` | | ||
|
|
||
| ## End-to-end flow | ||
|
|
||
| ``` | ||
| Developer CI (on merge to main) | ||
| ───────── ───────────────────── | ||
| /jira-propose → apply.mjs <file> | ||
| ↓ writes .jira/*.yml ↓ calls JIRA REST API | ||
| PR review ↓ creates remote link (repo-{PR#}) | ||
| ↓ approves JIRA changes delete file | ||
| Merge PR fetch.mjs <key> <new-file> | ||
| ↓ writes refreshed details | ||
| skip-ci commit | ||
| ``` | ||
|
|
||
| 1. **Propose** — Run `/jira-propose` in Claude Code. Reads git diff and context, creates or updates a `.jira/*.yml` file locally. No JIRA API calls. | ||
| 2. **Review** — The `.jira/` file diff appears in the PR. Reviewers can inspect and amend JIRA changes before they land. | ||
| 3. **Apply** — On merge, `apply.mjs` executes each `updates` entry in order. For new (`XXXX`) tickets it creates the ticket first (idempotent: checks whether a remote link with `globalId: repo-{PR#}` already exists). | ||
| 4. **Fetch** — After apply succeeds, the XXXX file is deleted and `fetch.mjs` writes a fresh file named with the real ticket key. A `[skip ci]` commit lands both JIRA and changeset changes. | ||
|
|
||
| ## Scripts | ||
|
|
||
| ```bash | ||
| # Apply a ticket file's updates to JIRA (CI usage) | ||
| JIRA_API_TOKEN=... GITHUB_PR_URL=... GITHUB_PR_TITLE=... \ | ||
| node scripts/jira/apply.mjs .jira/PDCL-1234-my-feature.yml | ||
|
|
||
| # Dry-run: see planned API calls without making them | ||
| node scripts/jira/apply.mjs --dry-run .jira/PDCL-1234-my-feature.yml | ||
|
|
||
| # Fetch current JIRA state into a file | ||
| JIRA_API_TOKEN=... node scripts/jira/fetch.mjs PDCL-1234 .jira/PDCL-1234-my-feature.yml | ||
|
|
||
| # Dry-run: see what would be written | ||
| node scripts/jira/fetch.mjs --dry-run PDCL-1234 .jira/PDCL-1234-my-feature.yml | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Alloy — Claude Code guide | ||
|
|
||
| ## JIRA ticket management | ||
|
|
||
| JIRA changes are version-controlled as YAML files in `.jira/`. No JIRA API calls are made locally. | ||
|
|
||
| ### Workflow | ||
|
|
||
| 1. **Propose** — Run `/jira-propose` to create or update a `.jira/*.yml` file based on current git changes and context. | ||
| 2. **Review** — The `.jira/` diff appears in the PR. Reviewers approve JIRA changes alongside code changes. | ||
| 3. **Apply** — On merge to `main`, the `apply-jira` CI job executes each file's `updates` array against the JIRA REST API, creates a remote link back to the PR, and posts a PR comment listing updated tickets. | ||
| 4. **Fetch** — After apply, the XXXX file is deleted and `fetch.js` writes a fresh file with the real ticket key and current JIRA state. A `[skip ci]` commit lands these changes. | ||
|
|
||
| ### File naming | ||
|
|
||
| - Existing ticket: `PDCL-1234-short-title.yml` | ||
| - New ticket (not yet in JIRA): `PDCL-XXXX-short-description.yml` | ||
|
|
||
| ### Scripts | ||
|
|
||
| ```bash | ||
| # See planned JIRA calls without making them (safe for local use) | ||
| node scripts/jira/apply.js --dry-run .jira/PDCL-1234-my-feature.yml | ||
|
|
||
| # Fetch current JIRA state into a file | ||
| JIRA_API_TOKEN=<token> node scripts/jira/fetch.js PDCL-1234 .jira/PDCL-1234-my-feature.yml | ||
| ``` | ||
|
|
||
| ### CI environment variables (Production GitHub Actions environment) | ||
|
|
||
| | Variable | Required for | | ||
| |----------|-------------| | ||
| | `JIRA_API_TOKEN` | apply.js and fetch.js API calls | | ||
| | `ALLOY_BOT_GITHUB_SSH_PRIVATE_KEY` | Pushing skip-ci commits to main | | ||
|
|
||
| ### Custom fields (PDCL project) | ||
|
|
||
| | Field | Value | | ||
| |-------|-------| | ||
| | AEP Web SDK component | `components[].id: "155901"` | | ||
| | Product (`customfield_23300`) | `id: "116005"` | | ||
| | Story `issuetype.id` | `"7"` | | ||
| | Bug `issuetype.id` | `"1"` | | ||
|
|
||
| See [`.jira/README.md`](./.jira/README.md) for the full schema reference. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.