Skip to content

chore(skills): Update duplicate-epic skill to work with bugs and stories#53

Open
dlabrecq wants to merge 3 commits intopatternfly:mainfrom
dlabrecq:duplicate-epic-update
Open

chore(skills): Update duplicate-epic skill to work with bugs and stories#53
dlabrecq wants to merge 3 commits intopatternfly:mainfrom
dlabrecq:duplicate-epic-update

Conversation

@dlabrecq
Copy link
Copy Markdown
Member

@dlabrecq dlabrecq commented Apr 9, 2026

Updated duplicate-epic skill to work with different JIra bugs in addition to stories.

SKILL.md

  • Updated the description frontmatter to clarify the skill accepts any issue type (Epic, Story, Bug) and uses the /duplicate-epic slash-command format
  • Rewrote the Input Parsing section with a two-column positional arg table (position, name, description)
  • Updated examples in the Workflow section to reflect that non-epic inputs are supported
  • Expanded "What the Script Does" from 5 to 7 steps, adding "Resolve current user" and "Resolve to an epic"
  • Added the Input Issue line to the Output section (only shown when the input was not itself an epic)

scripts/duplicate_epic.sh

  • Added a while loop after fetching the input issue that walks up the hierarchy (fields.parent → fields.customfield_10014) until an Epic is found — allows passing a Story or Bug directly
  • Tracks INPUT_KEY vs ORIGINAL_KEY so the resolved epic is shown separately in output
  • Strips mediaSingle and media nodes from the description when cloning (avoids Jira API errors for embedded attachments)
  • Wrapped the issueLink POST in a subshell so a permission error produces a WARNING instead of aborting the script

Summary by CodeRabbit

  • New Features

    • Accepts any Jira issue type and auto-resolves to its parent Epic when needed.
    • Cloned Epics now strip embedded attachments/images from descriptions.
  • Bug Fixes

    • Link-creation handling improved: failures emit warnings when no clone was made, but abort on unsafe partial states.
    • Output shows the original input URL only when it differed from the resolved Epic.
  • Documentation

    • Updated docs and examples to reflect new inputs, workflow steps, and output behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Accepted any Jira issue as input, resolved it up parent links to an Epic (max depth), cloned the resolved Epic while removing mediaSingle/media nodes from the description, made the "is duplicated by" link creation conditional with failure handling, and print the original input when it differed from the resolved Epic.

Changes

Cohort / File(s) Summary
Documentation Update
plugins/issue-management/skills/duplicate-epic/SKILL.md
Command contract changed: first positional arg is any Jira issue/URL (not epic-only). Workflow reordered to add "resolve current user" and "resolve to an epic" phases. Examples and invocation updated to duplicate_epic.sh <issue> <feature>. Output contract now conditionally prints "Input Issue" when input differed.
Script Implementation
plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh
Resolve input up parent chain (using .fields.parent.key or .fields.customfield_10014) until an issue with .fields.issuetype.name == "Epic" is found (enforce max depth 10; abort if none). Record original input and log when resolved epic differs. When cloning, sanitize Atlassian document description by removing nodes with .type == "mediaSingle" or .type == "media". Make POST issueLink conditional: continue with warning if linking fails and no new clone was created; abort if a clone exists but linking fails. Renumber steps and conditionally print "Input Issue" in results.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • jpuzz0
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: extending the duplicate-epic skill to handle non-epic issue types (bugs and stories) alongside epics.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/issue-management/skills/duplicate-epic/SKILL.md`:
- Around line 81-82: Update the "Clone" step in the SKILL.md "What the Script
Does" section to state that the epic description is not copied verbatim: the
script sanitizes the description by stripping mediaSingle and media nodes
(attachments/embedded media) before creating the PF clone, and note that
embedded attachments will be removed in the PF copy; keep the existing "Ensure
'is duplicated by' link" text unchanged but ensure the doc reflects this
behavior and its failure mode (attachments lost) so users aren't surprised.
- Line 3: The frontmatter "description" in SKILL.md is too long and will be
truncated in listings; shorten it to under 250 characters, front-load key use
cases and include the trigger context "/duplicate-epic <issue> <feature>"
(mentioning that it duplicates a Jira epic into the PatternFly project and links
back to the original), and remove excess detail so the core purpose and trigger
are visible in the skill listing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 07d7fca9-c7eb-49f1-ad64-1889ef5ecea5

📥 Commits

Reviewing files that changed from the base of the PR and between ae99e2e and 6085b81.

📒 Files selected for processing (2)
  • plugins/issue-management/skills/duplicate-epic/SKILL.md
  • plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh

Copy link
Copy Markdown
Collaborator

@jpuzz0 jpuzz0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good — all CodeRabbit comments addressed. Two non-blocking observations below.

echo "Fetching $ORIGINAL_KEY..."
ORIGINAL_ISSUE=$(api_request GET "issue/$ORIGINAL_KEY")

ISSUE_TYPE=$(echo "$ORIGINAL_ISSUE" | jq -r '.fields.issuetype.name')
Copy link
Copy Markdown
Collaborator

@jpuzz0 jpuzz0 Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The while loop walks up the hierarchy with no depth guard — if Jira data ever has a cycle (e.g., two issues listing each other as parent due to misconfiguration), would this loop run forever? A simple counter capping at ~10 iterations would be cheap insurance against a hang.

@@ -129,16 +161,19 @@ else
issuetype: {name: "Epic"},
assignee: {accountId: $account_id}
}
Copy link
Copy Markdown
Collaborator

@jpuzz0 jpuzz0 Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This filter only removes top-level content nodes. If a mediaSingle node is nested inside a panel, expand, or layoutColumn, wouldn't it survive the filter? Would a recursive walk be more appropriate here, or are the epic descriptions you've seen always flat enough that top-level stripping is sufficient?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh`:
- Around line 199-205: The call to api_request in the conditional can never
reach the elif/else because api_request itself calls exit on non-2xx responses;
run it in a subshell so its exit doesn't terminate the main script: replace the
direct call used in the if with a subshell invocation (e.g. run api_request POST
"issueLink" "$LINK_PAYLOAD" inside ( ) and redirect stdout/stderr as before) so
the conditional can test its exit status and allow the CREATED_NEW_CLONE /
NEW_KEY warning or abort branches to execute.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 193e08a6-c157-4ed8-8420-b1d1807a8e2f

📥 Commits

Reviewing files that changed from the base of the PR and between 3605816 and a4c7d63.

📒 Files selected for processing (1)
  • plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh

Comment on lines +199 to +205
if api_request POST "issueLink" "$LINK_PAYLOAD" >/dev/null 2>&1; then
echo "Link added"
elif [[ "$CREATED_NEW_CLONE" -eq 0 ]]; then
echo "WARNING: Could not add 'is duplicated by' link (likely missing link-issue permission in the source project). Continuing..." >&2
else
echo "ERROR: Created $NEW_KEY but could not add the duplicate link; aborting to avoid future duplicate clones." >&2
exit 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expected output: only "before" is printed.
# If that happens, the `elif`/`else` path is unreachable when the function uses `exit 1`.
set -euo pipefail

output=$(
  bash -c '
    api_request() { exit 1; }

    echo before
    if api_request; then
      echo then
    elif [[ 0 -eq 0 ]]; then
      echo elif
    else
      echo else
    fi
    echo after
  ' 2>&1 || true
)

printf '%s\n' "$output"

Repository: patternfly/ai-helpers

Length of output: 71


🏁 Script executed:

#!/bin/bash
# Find and examine the duplicate_epic.sh file
find . -name "duplicate_epic.sh" -type f

Repository: patternfly/ai-helpers

Length of output: 139


🏁 Script executed:

#!/bin/bash
# Read the file to find the api_request function definition and the code at lines 199-205
cat -n ./plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh | sed -n '80,90p; 195,210p'

Repository: patternfly/ai-helpers

Length of output: 1280


🏁 Script executed (no clone):

#!/bin/bash
# Test if the proposed fix (subshell with parentheses) resolves the issue
# Expected output: both "before" and "elif" should be printed
set -euo pipefail

output=$(
  bash -c '
    api_request() { exit 1; }

    echo before
    if (api_request POST "issueLink" "$LINK_PAYLOAD" >/dev/null 2>&1); then
      echo then
    elif [[ 0 -eq 0 ]]; then
      echo elif
    else
      echo else
    fi
    echo after
  ' 2>&1 || true
)

printf '%s\n' "$output"

Length of output: 48


The recoverable link-error path is unreachable.

api_request() calls exit 1 on line 84 for non-2xx responses. At line 199, invoking it directly without a subshell terminates the script before the elif / else branches can execute, so the warning path is never reached.

Wrap the call in a subshell to allow the conditional to handle the non-zero return:

Fix
-  if api_request POST "issueLink" "$LINK_PAYLOAD" >/dev/null 2>&1; then
+  if (api_request POST "issueLink" "$LINK_PAYLOAD" >/dev/null 2>&1); then
     echo "Link added"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/issue-management/skills/duplicate-epic/scripts/duplicate_epic.sh`
around lines 199 - 205, The call to api_request in the conditional can never
reach the elif/else because api_request itself calls exit on non-2xx responses;
run it in a subshell so its exit doesn't terminate the main script: replace the
direct call used in the if with a subshell invocation (e.g. run api_request POST
"issueLink" "$LINK_PAYLOAD" inside ( ) and redirect stdout/stderr as before) so
the conditional can test its exit status and allow the CREATED_NEW_CLONE /
NEW_KEY warning or abort branches to execute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants