| name | Create Release Note |
| description | Detailed implementation guide for generating bug fix release notes from Jira and GitHub PRs |
Create Release Note
This skill provides detailed step-by-step implementation guidance for the /jira:create-release-note command, which automatically generates bug fix release notes by analyzing Jira bug tickets and their linked GitHub pull requests.
When to Use This Skill
This skill is automatically invoked by the /jira:create-release-note command and should not be called directly by users.
Prerequisites
- MCP Jira server configured and accessible
- GitHub CLI (
gh) installed and authenticated - User has read access to the Jira bug
- User has write access to Release Note fields in Jira
- User has read access to linked GitHub repositories
Implementation Steps
Step 1: Fetch and Validate Jira Bug
Objective: Retrieve the bug ticket and validate it's appropriate for release note generation.
Actions:
Fetch the bug using MCP:
mcp__atlassian__jira_get_issue( issue_key=<issue-key>, fields="summary,description,issuetype,status,issuelinks,customfield_12320850,customfield_12317313,comment" )Parse the response:
- Extract
issuetype.name- verify it's "Bug" - Extract
description- full bug description text - Extract
issuelinks- array of linked issues - Extract
customfield_12320850- current Release Note Type (if already set) - Extract
customfield_12317313- current Release Note Text (if already set) - Extract
comment.comments- array of comment objects
- Extract
Validate issue type:
- If
issuetype.name != "Bug", show warning:Warning: {issue-key} is not a Bug (it's a {issuetype.name}). Release notes are typically for bugs. Continue anyway? (yes/no) - If user says no, exit gracefully
- If
Check if release note already exists:
- If
customfield_12317313is not empty, show warning:This bug already has a release note: --- {existing release note} --- Do you want to regenerate it? (yes/no) - If user says no, exit gracefully
- If
Step 2: Parse Bug Description for Cause and Consequence
Objective: Extract the required Cause and Consequence sections from the bug description.
Bug Description Format:
Jira bug descriptions often follow this structure:
Description of problem:
{code:none}
<problem description>
{code}
Cause:
{code:none}
<root cause>
{code}
Consequence:
{code:none}
<impact>
{code}
Version-Release number of selected component (if applicable):
...
How reproducible:
...
Steps to Reproduce:
...
Actual results:
...
Expected results:
...
Parsing Strategy:
Look for "Cause:" section:
- Search for the string "Cause:" (case-insensitive)
- Extract text between "Cause:" and the next major section
- Remove Jira markup:
{code:none},{code}, etc. - Trim whitespace
Look for "Consequence:" section:
- Search for the string "Consequence:" (case-insensitive)
- Extract text between "Consequence:" and the next major section
- Remove Jira markup
- Trim whitespace
Alternative patterns:
- Some bugs may use "Root Cause:" instead of "Cause:"
- Some bugs may use "Impact:" instead of "Consequence:"
- Try variations if exact match not found
Handle missing sections:
- If Cause is missing:
Bug description is missing the "Cause" section. Would you like to: 1. Provide the Cause interactively 2. Update the bug description in Jira first 3. Cancel - If Consequence is missing:
Bug description is missing the "Consequence" section. Would you like to: 1. Provide the Consequence interactively 2. Update the bug description in Jira first 3. Cancel
- If Cause is missing:
Interactive input (if user chooses option 1):
- Prompt: "What is the root cause of this bug?"
- Collect user input
- Use as Cause value
- Repeat for Consequence if needed
Example Parsing:
Input:
Description of problem:
{code:none}
The control plane operator crashes when CloudProviderConfig.Subnet is not specified
{code}
Cause:
{code:none}
hostedcontrolplane controller crashes when hcp.Spec.Platform.AWS.CloudProviderConfig.Subnet.ID is undefined
{code}
Consequence:
{code:none}
control-plane-operator enters a crash loop
{code}
Output:
Cause: "hostedcontrolplane controller crashes when hcp.Spec.Platform.AWS.CloudProviderConfig.Subnet.ID is undefined"
Consequence: "control-plane-operator enters a crash loop"
Step 3: Extract Linked GitHub PRs
Objective: Find all GitHub PR URLs associated with this bug.
Sources to check (in priority order):
Remote Links (Primary source - web links in Jira):
- Check the Jira issue response for web links
- Field name varies:
remotelinks, orissuelinkswith outward GitHub PR links - Extract GitHub PR URLs matching pattern:
https://github\.com/[\w-]+/[\w-]+/pull/\d+ - IMPORTANT: Never use
gh issue view {JIRA-KEY}- Jira keys are NOT GitHub issue numbers
Bug Description:
- Scan the
descriptionfield for GitHub PR URLs - Use regex:
https://github\.com/([\w-]+)/([\w-]+)/pull/(\d+) - Extract and parse all matches
- IMPORTANT: Only extract full PR URLs, not issue references
- Scan the
Bug Comments:
- Iterate through
comment.commentsarray - For each comment, scan
bodyfield for GitHub PR URLs - Use same regex pattern
- Extract all matches
- Iterate through
Search by bug number (Fallback if no PR URLs found):
- If no PRs found via links, search GitHub for PRs mentioning the bug
- For OCPBUGS: Try common OpenShift repos:
for repo in "openshift/hypershift" "openshift/cluster-api-provider-aws" "openshift/origin"; do gh pr list --repo "$repo" --search "{issue-key} in:title,body" --state all --limit 10 --json number,url,title done - Display found PRs and ask user to confirm which are relevant:
Found PRs mentioning {issue-key}: 1. openshift/hypershift#4567 - Fix panic when CloudProviderConfig.Subnet is undefined 2. openshift/hypershift#4568 - Add tests for Subnet validation Which PRs should be included in the release note? (enter numbers separated by commas, or 'all') - IMPORTANT: Never use
gh issue view {JIRA-KEY}- this will fail
URL Parsing:
For each found URL https://github.com/openshift/hypershift/pull/4567:
- Extract
org: "openshift" - Extract
repo: "hypershift" - Extract
pr_number: "4567" - Store as:
{"url": "...", "repo": "openshift/hypershift", "number": "4567"}
Deduplication:
- Keep only unique PR URLs
- If same PR is mentioned multiple times, include it only once
Validation:
- If no PRs found after all attempts:
Exit without updating the ticket.No GitHub PRs found linked to {issue-key}. Please link at least one PR to generate release notes. How to link PRs in Jira: 1. Edit the bug in Jira 2. Add a web link to the GitHub PR URL 3. Or mention the PR URL in a comment 4. Then run this command again
Example:
Found PRs:
[
{
"url": "https://github.com/openshift/hypershift/pull/4567",
"repo": "openshift/hypershift",
"number": "4567"
},
{
"url": "https://github.com/openshift/hypershift/pull/4568",
"repo": "openshift/hypershift",
"number": "4568"
}
]
Step 4: Analyze Each GitHub PR
Objective: Extract Fix, Result, and Workaround information from each linked PR.
For each PR in the list:
4.1: Fetch PR Details
Command:
gh pr view {number} --json body,title,commits,url,state --repo {repo}
Error handling:
if ! gh pr view {number} --json body,title,commits,url,state --repo {repo} 2>/dev/null; then
echo "Warning: Unable to access PR {url}"
echo "Verify the PR exists and you have permissions."
# Skip this PR, continue with next
fi
Expected output (JSON):
{
"body": "This PR fixes the panic when CloudProviderConfig.Subnet is not specified...",
"title": "Fix panic when CloudProviderConfig.Subnet is not specified",
"commits": [
{
"messageHeadline": "Add nil check for Subnet field",
"messageBody": "This prevents the controller from crashing..."
}
],
"url": "https://github.com/openshift/hypershift/pull/4567",
"state": "MERGED"
}
Parse and store:
title: PR title (short summary)body: Full PR descriptioncommits: Array of commit objects with messagesstate: PR state (MERGED, OPEN, CLOSED)
4.2: Fetch PR Diff
Command:
gh pr diff {number} --repo {repo}
Purpose: Understand what code was actually changed
Analysis strategy:
- Look for added lines (starting with
+) - Identify key changes:
- New error handling (
if err != nil) - New validation checks (
if x == nil) - New return statements
- New error messages
- New error handling (
- Don't include the entire diff in the release note
- Summarize the nature of changes
Example diff analysis:
+if hcp.Spec.Platform.AWS.CloudProviderConfig.Subnet == nil {
+ return fmt.Errorf("Subnet configuration is required")
+}
Summary: "Added nil check for CloudProviderConfig.Subnet field"
4.3: Fetch PR Comments
Command:
gh pr view {number} --json comments --repo {repo}
Expected output (JSON):
{
"comments": [
{
"author": {"login": "reviewer1"},
"body": "This looks good. The nil check will prevent the crash."
},
{
"author": {"login": "author"},
"body": "Yes, and I also added a more descriptive error message."
}
]
}
Analysis strategy:
- Look for mentions of:
- "workaround"
- "temporary fix"
- "until this is merged"
- "users can work around this by..."
- Extract workaround information if found
- Look for clarifications about the fix
- Ignore unrelated discussion
4.4: Synthesize PR Analysis
Combine all sources (title, body, commits, diff, comments) to extract:
Fix (what was changed):
- Prefer: PR body description of the fix
- Fallback: PR title + commit message summaries
- Focus on: What code/configuration was modified
- Keep concise: 1-3 sentences
- Avoid: Implementation details (specific function names, line numbers)
Example Fix:
Added nil check for CloudProviderConfig.Subnet before accessing Subnet.ID field to prevent nil pointer dereference
Result (outcome after the fix):
- Prefer: PR body description of expected behavior
- Fallback: Inverse of the bug's "Actual results"
- Focus on: What changed for users
- Keep concise: 1-2 sentences
Example Result:
The control-plane-operator no longer crashes when CloudProviderConfig.Subnet is not specified
Workaround (temporary solution before fix):
- Only include if explicitly mentioned in PR or comments
- Look for keywords: "workaround", "temporary", "manually"
- If not found: Omit this section entirely
Example Workaround (if found):
Users can manually specify a Subnet value in the HostedCluster spec to avoid the crash
Step 5: Combine Multiple PRs
Objective: If multiple PRs are linked, synthesize them into a single coherent release note.
Scenarios:
Scenario A: Multiple PRs with different fixes
Example:
- PR #123: Fixes nil pointer crash
- PR #456: Adds better error message
- PR #789: Adds validation tests
Strategy: Combine all fixes into a narrative
Fix: Added nil check for CloudProviderConfig.Subnet field (PR #123), improved error messaging for missing configuration (PR #456), and added validation tests to prevent regression (PR #789)
Scenario B: Multiple PRs for same fix (backports)
Example:
- PR #123: Fix for main branch
- PR #456: Backport to release-4.20
- PR #789: Backport to release-4.19
Strategy: Mention the fix once, note the backports
Fix: Added nil check for CloudProviderConfig.Subnet field (backported to 4.20 and 4.19)
Scenario C: Multiple PRs with conflicting descriptions
Example:
- PR #123 says: "Fixed by adding validation"
- PR #456 says: "Fixed by removing the field access"
Strategy: Analyze the code diffs to determine what actually happened, or ask user:
Found multiple PRs with different fix descriptions:
- PR #123: "Fixed by adding validation"
- PR #456: "Fixed by removing the field access"
Which description is more accurate, or should I combine them?
Step 6: Format Release Note
Objective: Create the final release note text following the standard template.
Template:
Cause: {cause from Jira}
Consequence: {consequence from Jira}
Fix: {synthesized from PRs}
Result: {synthesized from PRs}
Workaround: {synthesized from PRs - optional}
Formatting rules:
- Each line starts with the field name followed by a colon and space
- No blank lines between fields
- Workaround field is optional - omit if no workaround exists
- Keep each field concise but complete
- Use proper capitalization and punctuation
Example output:
Cause: hostedcontrolplane controller crashes when hcp.Spec.Platform.AWS.CloudProviderConfig.Subnet.ID is undefined
Consequence: control-plane-operator enters a crash loop
Fix: Added nil check for CloudProviderConfig.Subnet before accessing Subnet.ID field
Result: The control-plane-operator no longer crashes when CloudProviderConfig.Subnet is not specified
Step 7: Security Validation
Objective: Scan the release note text for sensitive data before submission.
Patterns to detect:
API Tokens and Keys:
- Pattern:
(sk|pk)_[a-zA-Z0-9]{20,} - Pattern:
ghp_[a-zA-Z0-9]{36} - Pattern:
gho_[a-zA-Z0-9]{36} - Pattern:
github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}
- Pattern:
AWS Credentials:
- Pattern:
AKIA[0-9A-Z]{16} - Pattern:
aws_access_key_id\s*=\s*[A-Z0-9]+ - Pattern:
aws_secret_access_key\s*=\s*[A-Za-z0-9/+=]+
- Pattern:
Passwords in URLs:
- Pattern:
https?://[^:]+:[^@]+@ - Example:
https://user:password@example.com
- Pattern:
JWT Tokens:
- Pattern:
eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+
- Pattern:
SSH Private Keys:
- Pattern:
-----BEGIN (RSA|OPENSSH|DSA|EC|PGP) PRIVATE KEY-----
- Pattern:
Kubernetes Secrets:
- Pattern:
[a-zA-Z0-9+/]{40,}==?(base64 encoded secrets) - Context: If appears after "token:", "secret:", "password:"
- Pattern:
Validation logic:
sensitive_patterns = {
"API Token": r"(sk|pk)_[a-zA-Z0-9]{20,}",
"GitHub Token": r"gh[po]_[a-zA-Z0-9]{36}",
"AWS Access Key": r"AKIA[0-9A-Z]{16}",
"URL with credentials": r"https?://[^:]+:[^@]+@",
"JWT Token": r"eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+",
"Private Key": r"-----BEGIN .* PRIVATE KEY-----"
}
for name, pattern in sensitive_patterns.items():
if re.search(pattern, release_note_text):
print(f"Security validation failed!")
print(f"Detected what appears to be {name} in the release note text.")
print(f"Please review the source PRs and remove any credentials.")
exit(1)
If validation fails:
Security validation failed!
Detected what appears to be an API token in the release note text.
This may have come from:
- PR description
- Commit messages
- Code changes (diff)
- PR comments
Please review the source PRs and remove any credentials before proceeding.
Use placeholder values instead:
- YOUR_API_KEY
- <redacted>
- ********
Aborting release note creation.
Step 8: Select Release Note Type
Objective: Determine the appropriate Release Note Type for this bug.
Available types (from Jira dropdown):
- Bug Fix
- Release Note Not Required
- Known Issue
- Enhancement
- Rebase
- Technology Preview
- Deprecated Functionality
- CVE
Auto-detection strategy:
For OCPBUGS: Default suggestion is "Bug Fix" (most common)
Check for CVE references:
- If bug description or PRs mention "CVE-" → Suggest "CVE"
Check for deprecation:
- If PRs mention "deprecated", "deprecating", "removing" → Suggest "Deprecated Functionality"
Check for new features:
- If PRs add significant new functionality → Suggest "Enhancement"
- Though typically bugs should be "Bug Fix"
Check for known issues:
- If PRs don't actually fix the issue, just document it → Suggest "Known Issue"
User interaction:
Use the AskUserQuestion tool:
questions = [{
"question": "What type of release note is this?",
"header": "Type",
"multiSelect": false,
"options": [
{
"label": "Bug Fix",
"description": "Fix for a defect (most common)"
},
{
"label": "Known Issue",
"description": "Documents a known problem without a fix"
},
{
"label": "Enhancement",
"description": "New feature or improvement"
},
{
"label": "CVE",
"description": "Security vulnerability fix"
}
]
}]
Store selection for use in the next step.
Step 9: Update Jira Ticket
Objective: Write the release note to the Jira ticket.
MCP tool call:
mcp__atlassian__jira_update_issue(
issue_key=<issue-key>,
fields={
"customfield_12320850": {"value": "<selected_type>"},
"customfield_12317313": "<formatted_release_note_text>"
}
)
Field details:
customfield_12320850: Release Note Type (must be exact match from dropdown)customfield_12317313: Release Note Text (plain text)
Error handling:
Permission denied:
Failed to update {issue-key}. Error: You do not have permission to edit Release Note fields Please contact your Jira administrator or manually add the release note. Generated release note (for manual entry): --- {release_note_text} ---Invalid field value:
Failed to update Release Note Type field. Error: Value "{selected_type}" is not valid Please select a different type or manually update in Jira.Field not found:
Failed to update {issue-key}. Error: Field customfield_12320850 not found This may indicate a different Jira instance or configuration. Please manually add the release note.
Success response:
{
"success": true,
"issue": {
"key": "OCPBUGS-38358",
"fields": {
"customfield_12320850": {"value": "Bug Fix"},
"customfield_12317313": "Cause: ... Consequence: ... Fix: ... Result: ..."
}
}
}
Step 10: Display Results
Objective: Show the user what was created and provide next steps.
Output format:
✓ Release Note Created for {issue-key}
Type: {Release Note Type}
Text:
---
{Release Note Text with proper formatting}
---
Updated: https://issues.redhat.com/browse/{issue-key}
Next steps:
- Review the release note in Jira
- Edit manually if any adjustments are needed
- The release note will be included in the next release
Example:
✓ Release Note Created for OCPBUGS-38358
Type: Bug Fix
Text:
---
Cause: hostedcontrolplane controller crashes when hcp.Spec.Platform.AWS.CloudProviderConfig.Subnet.ID is undefined
Consequence: control-plane-operator enters a crash loop
Fix: Added nil check for CloudProviderConfig.Subnet before accessing Subnet.ID field
Result: The control-plane-operator no longer crashes when CloudProviderConfig.Subnet is not specified
---
Updated: https://issues.redhat.com/browse/OCPBUGS-38358
Next steps:
- Review the release note in Jira
- Edit manually if any adjustments are needed
- The release note will be included in the next release
Error Recovery Strategies
PR Analysis Failures
Problem: Some PRs can't be accessed or analyzed
Recovery:
- Log warning for each failed PR
- Continue with successfully analyzed PRs
- If all PRs fail, treat as "No PRs linked" error
- Show summary of what was analyzed:
Analyzed 2 of 3 linked PRs: ✓ PR #123 ✓ PR #456 ✗ PR #789 (access denied)
Incomplete Bug Description
Problem: Missing Cause or Consequence sections
Recovery:
- Offer interactive input option
- Provide template for user
- Allow user to update bug and retry
- Don't proceed without both fields
Ambiguous PR Content
Problem: PRs don't clearly explain the fix
Recovery:
- Use PR title as fallback
- Analyze code diff more carefully
- Ask user for clarification:
The linked PR doesn't clearly describe the fix. Based on code analysis, it appears to: {best guess from diff} Is this correct? If not, please describe the fix:
Conflicting Information
Problem: Multiple PRs describe different fixes
Recovery:
- Present both descriptions to user
- Ask which is correct or how to combine
- Use AI to attempt intelligent synthesis
- If synthesis fails, escalate to user
Best Practices for Implementation
- Always validate inputs: Check that issue exists, PRs are accessible, fields are present
- Fail gracefully: Provide helpful error messages with recovery instructions
- Be defensive: Handle missing data, API errors, permission issues
- Log progress: Show user what's happening at each step
- Preserve data: If update fails, show generated content so it's not lost
- Security first: Always validate before updating Jira
- User in control: Confirm selections, allow overrides
Testing Checklist
- Bug with single linked PR
- Bug with multiple linked PRs
- Bug with no linked PRs (error case)
- Bug with inaccessible PR (warning case)
- Bug missing Cause section (error case)
- Bug missing Consequence section (error case)
- Bug with existing release note (warning case)
- Bug that's not a Bug type (warning case)
- Release note with detected credentials (security failure)
- Different Release Note Type selections
- Update permission denied (error case)
- MCP server not configured (error case)
- gh CLI not authenticated (error case)