--- 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**: 1. **Fetch the bug using MCP**: ``` mcp__atlassian__jira_get_issue( issue_key=, fields="summary,description,issuetype,status,issuelinks,customfield_12320850,customfield_12317313,comment" ) ``` 2. **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 3. **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 4. **Check if release note already exists**: - If `customfield_12317313` is 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 ### 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} {code} Cause: {code:none} {code} Consequence: {code:none} {code} Version-Release number of selected component (if applicable): ... How reproducible: ... Steps to Reproduce: ... Actual results: ... Expected results: ... ``` **Parsing Strategy**: 1. **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 2. **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 3. **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 4. **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 ``` 5. **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): 1. **Remote Links** (Primary source - web links in Jira): - Check the Jira issue response for web links - Field name varies: `remotelinks`, or `issuelinks` with 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 2. **Bug Description**: - Scan the `description` field 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 3. **Bug Comments**: - Iterate through `comment.comments` array - For each comment, scan `body` field for GitHub PR URLs - Use same regex pattern - Extract all matches 4. **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: ```bash 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: ``` 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 ``` Exit without updating the ticket. **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**: ```bash gh pr view {number} --json body,title,commits,url,state --repo {repo} ``` **Error handling**: ```bash 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): ```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 description - `commits`: Array of commit objects with messages - `state`: PR state (MERGED, OPEN, CLOSED) #### 4.2: Fetch PR Diff **Command**: ```bash 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 - Don't include the entire diff in the release note - Summarize the nature of changes **Example diff analysis**: ```diff +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**: ```bash gh pr view {number} --json comments --repo {repo} ``` **Expected output** (JSON): ```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**: 1. **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}` 2. **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/+=]+` 3. **Passwords in URLs**: - Pattern: `https?://[^:]+:[^@]+@` - Example: `https://user:password@example.com` 4. **JWT Tokens**: - Pattern: `eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+` 5. **SSH Private Keys**: - Pattern: `-----BEGIN (RSA|OPENSSH|DSA|EC|PGP) PRIVATE KEY-----` 6. **Kubernetes Secrets**: - Pattern: `[a-zA-Z0-9+/]{40,}==?` (base64 encoded secrets) - Context: If appears after "token:", "secret:", "password:" **Validation logic**: ```python 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 - - ******** 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): 1. Bug Fix 2. Release Note Not Required 3. Known Issue 4. Enhancement 5. Rebase 6. Technology Preview 7. Deprecated Functionality 8. CVE **Auto-detection strategy**: 1. **For OCPBUGS**: Default suggestion is "Bug Fix" (most common) 2. **Check for CVE references**: - If bug description or PRs mention "CVE-" → Suggest "CVE" 3. **Check for deprecation**: - If PRs mention "deprecated", "deprecating", "removing" → Suggest "Deprecated Functionality" 4. **Check for new features**: - If PRs add significant new functionality → Suggest "Enhancement" - Though typically bugs should be "Bug Fix" 5. **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=, fields={ "customfield_12320850": {"value": ""}, "customfield_12317313": "" } ) ``` **Field details**: - `customfield_12320850`: Release Note Type (must be exact match from dropdown) - `customfield_12317313`: Release Note Text (plain text) **Error handling**: 1. **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} --- ``` 2. **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. ``` 3. **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**: 1. Log warning for each failed PR 2. Continue with successfully analyzed PRs 3. If all PRs fail, treat as "No PRs linked" error 4. 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**: 1. Offer interactive input option 2. Provide template for user 3. Allow user to update bug and retry 4. Don't proceed without both fields ### Ambiguous PR Content **Problem**: PRs don't clearly explain the fix **Recovery**: 1. Use PR title as fallback 2. Analyze code diff more carefully 3. 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**: 1. Present both descriptions to user 2. Ask which is correct or how to combine 3. Use AI to attempt intelligent synthesis 4. If synthesis fails, escalate to user ## Best Practices for Implementation 1. **Always validate inputs**: Check that issue exists, PRs are accessible, fields are present 2. **Fail gracefully**: Provide helpful error messages with recovery instructions 3. **Be defensive**: Handle missing data, API errors, permission issues 4. **Log progress**: Show user what's happening at each step 5. **Preserve data**: If update fails, show generated content so it's not lost 6. **Security first**: Always validate before updating Jira 7. **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)