Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/automated-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ jobs:
jira-version-name: ${{ steps.get-jira-version.outputs.jira-version-name }}
release-notes: ${{ inputs.release-notes != '' && inputs.release-notes || steps.get-jira-release-notes.outputs.release-notes }}
jira-release-notes: ${{ inputs.release-notes != '' && inputs.release-notes || steps.get-jira-release-notes.outputs.jira-release-notes }}
jira-release-url: ${{ steps.get-jira-release-notes.outputs.jira-release-url }}
steps:
- name: Get Release Version
id: get-release-version
Expand Down Expand Up @@ -251,7 +250,6 @@ jobs:
project-name: ${{ inputs.project-name }}
short-description: ${{ inputs.short-description }}
rule-props-changed: ${{ inputs.rule-props-changed == 'true' && 'Yes' || 'No' }}
jira-release-url: ${{ needs.prepare-release.outputs.jira-release-url }}
start-progress: true
version: ${{ needs.prepare-release.outputs.release-version }}

Expand Down
354 changes: 330 additions & 24 deletions .github/workflows/test-create-jira-release-ticket.yml

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions .github/workflows/test-get-jira-release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
# Create a mock script that simulates the Python output
cat > mock_output.py << 'EOF'
print("jira-release-url=https://test.atlassian.net/projects/TEST/versions/123/tab/release-report-all-issues")
print("jira-release-id=123")
print("release-notes<<EOF")
print("# Release notes - Test Project - 1.0.0")
print("")
Expand Down Expand Up @@ -141,6 +142,7 @@ jobs:
- name: Output results
run: |
echo "Jira Release URL: ${{ steps.test-sandbox.outputs.jira-release-url }}"
echo "Jira Release ID: ${{ steps.test-sandbox.outputs.jira-release-id }}"
echo "Release Notes:"
echo "${{ steps.test-sandbox.outputs.release-notes }}"
echo "Jira Release Notes:"
Expand All @@ -155,6 +157,15 @@ jobs:
echo "✅ Jira Release URL format is correct"
fi

- name: Verify Jira Release ID
run: |
if [[ "${{ steps.test-sandbox.outputs.jira-release-id }}" != 22169 ]]; then
echo "❌ Jira Release ID is incorrect"
exit 1
else
echo "✅ Jira Release ID is correct"
fi

- name: Verify Release Notes Content
run: |
if grep -q "### Improvement" <<< "${{ steps.test-sandbox.outputs.release-notes }}"; then
Expand Down
127 changes: 103 additions & 24 deletions create-jira-release-ticket/README.md

Large diffs are not rendered by default.

56 changes: 43 additions & 13 deletions create-jira-release-ticket/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ inputs:
description: 'Whether rule properties have changed'
default: 'No'
jira-release-url:
description: 'The URL to the Jira release notes page. Required if JIRA_RELEASE_URL env var is not set.'
description: 'The URL to the Jira release notes page. Required if JIRA_RELEASE_URL env var is not set. DEPRECATED: use jira-release-id instead.'
default: ''
jira-release-id:
description: 'The ID of the Jira release version.'
default: ''
sonarlint-changelog:
description: 'The SonarLint changelog content.'
default: ''
Expand Down Expand Up @@ -73,26 +77,52 @@ runs:
env:
JIRA_USER: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
JIRA_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
JIRA_PROD_URL: "https://sonarsource.atlassian.net/"
JIRA_SANDBOX_URL: "https://sonarsource-sandbox-608.atlassian.net/"
JIRA_URL: "https://sonarsource${{ ((inputs.use-jira-sandbox || env.USE_JIRA_SANDBOX) == 'true') && '-sandbox-608' || '' }}.atlassian.net/"
PROJECT_KEY: ${{ inputs.jira-project-key || env.JIRA_PROJECT_KEY }}
PROJECT_NAME: ${{ inputs.project-name }}
VERSION: ${{ inputs.version || env.RELEASE_VERSION }}
SHORT_DESCRIPTION: ${{ inputs.short-description }}
DOCUMENTATION_STATUS: ${{ inputs.documentation-status }}
RULE_PROPS_CHANGED: ${{ inputs.rule-props-changed }}
JIRA_RELEASE_URL: ${{ inputs.jira-release-url || env.JIRA_RELEASE_URL }}
JIRA_RELEASE_ID: ${{ inputs.jira-release-id || env.JIRA_RELEASE_ID }}
SONARLINT_CHANGELOG: ${{ inputs.sonarlint-changelog }}
run: |
PROJECT_KEY="${{ inputs.jira-project-key || env.JIRA_PROJECT_KEY }}"

if [[ -z "$PROJECT_KEY" ]]; then
echo "::error::Both jira-project-key input and JIRA_PROJECT_KEY environment variable are missing. One must be provided."
exit 1
fi

if [[ -z "$JIRA_RELEASE_URL" && -z "$JIRA_RELEASE_ID" ]]; then
echo "::error::Both jira-release-url/jira-release-id input and JIRA_RELEASE_URL/JIRA_RELEASE_ID environment variable are missing. One must be provided."
exit 1
fi

if [[ -n "$JIRA_RELEASE_URL" ]]; then
echo "::warning::The jira-release-url input and JIRA_RELEASE_URL env is deprecated. Please use jira-release-id/JIRA_RELEASE_ID instead."
fi

if [[ -n "$JIRA_RELEASE_ID" ]]; then
FIXED_VERSION_ID="$JIRA_RELEASE_ID"
else
# Extract the ID from the URL
FIXED_VERSION_ID=$(echo "$JIRA_RELEASE_URL" | grep -oE '/versions/([0-9]+)' | grep -oE '[0-9]+')
fi

ISSUE_FILTER="${JIRA_URL}issues/?jql=fixVersion%3D${FIXED_VERSION_ID}"

echo "ISSUE_FILTER=$ISSUE_FILTER"

python ${{ github.action_path }}/create_release_ticket.py \
--project-key="$PROJECT_KEY" \
--project-name="${{ inputs.project-name }}" \
--version="${{ inputs.version || env.RELEASE_VERSION }}" \
--short-description="${{ inputs.short-description }}" \
--jira-url="${{ ((inputs.use-jira-sandbox || env.USE_JIRA_SANDBOX) == 'true') && env.JIRA_SANDBOX_URL || env.JIRA_PROD_URL }}" \
--documentation-status="${{ inputs.documentation-status }}" \
--rule-props-changed="${{ inputs.rule-props-changed }}" \
--jira-release-url="${{ inputs.jira-release-url || env.JIRA_RELEASE_URL }}" \
--sonarlint-changelog="${{ inputs.sonarlint-changelog }}" >> $GITHUB_OUTPUT
--project-name="$PROJECT_NAME" \
--version="$VERSION" \
--short-description="$SHORT_DESCRIPTION" \
--jira-url="$JIRA_URL" \
--documentation-status="$DOCUMENTATION_STATUS" \
--rule-props-changed="$RULE_PROPS_CHANGED" \
--issue-filter-url="$ISSUE_FILTER" \
--sonarlint-changelog="$SONARLINT_CHANGELOG" >> $GITHUB_OUTPUT

- name: Start Progress on Release Ticket
if: ${{ inputs.start-progress == 'true' }}
Expand Down
19 changes: 12 additions & 7 deletions create-jira-release-ticket/create_release_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

CUSTOM_FIELDS = {
'SHORT_DESCRIPTION': 'customfield_10146',
'LINK_TO_RELEASE_NOTES': 'customfield_10145',
'LINK_TO_ISSUE_FILTER': 'customfield_10145',
'DOCUMENTATION_STATUS': 'customfield_10147',
'RULE_PROPS_CHANGED': 'customfield_11263',
'SONARLINT_CHANGELOG': 'customfield_11264',
Expand Down Expand Up @@ -63,17 +63,22 @@ def get_jira_instance(jira_url):
sys.exit(1)


def create_release_ticket(jira_client, args, link_to_release_notes):
def create_release_ticket(jira_client, args, issue_filter_url):
"""
Creates the 'Ask for release' ticket in Jira.

Args:
jira_client: The JIRA client instance
args: Parsed command line arguments
issue_filter_url: URL to filter all issues in the release
"""
eprint("\nPreparing to create release ticket...")
ticket_details = {
'project': 'REL',
'issuetype': 'Ask for release',
'summary': f'{args.project_name} {args.version}',
CUSTOM_FIELDS['SHORT_DESCRIPTION']: args.short_description,
CUSTOM_FIELDS['LINK_TO_RELEASE_NOTES']: link_to_release_notes,
CUSTOM_FIELDS['LINK_TO_ISSUE_FILTER']: issue_filter_url,
CUSTOM_FIELDS['DOCUMENTATION_STATUS']: args.documentation_status,
CUSTOM_FIELDS['RULE_PROPS_CHANGED']: {'value': args.rule_props_changed},
CUSTOM_FIELDS['SONARLINT_CHANGELOG']: args.sonarlint_changelog
Expand Down Expand Up @@ -105,21 +110,21 @@ def main():
parser.add_argument("--documentation-status", default="N/A", help="Status of the documentation.")
parser.add_argument("--rule-props-changed", default="No", choices=['Yes', 'No'],
help="Whether rule properties have changed.")
parser.add_argument("--jira-release-url", default="", help="The URL to the Jira release notes page.")
parser.add_argument("--issue-filter-url", default="", help="The URL to filter all issues in the release.")
parser.add_argument("--sonarlint-changelog", default="", help="The SonarLint changelog content.")

args = parser.parse_args()

jira = get_jira_instance(args.jira_url)

eprint(f"Using release URL: {args.jira_release_url}")
ticket = create_release_ticket(jira, args, args.jira_release_url)
eprint(f"Using issue filter URL: {args.issue_filter_url}")
ticket = create_release_ticket(jira, args, args.issue_filter_url)

eprint("\n" + "=" * 50)
eprint("🎉 Successfully created release ticket!")
eprint(f" Ticket Key: {ticket.key}")
eprint(f" Ticket URL: {ticket.permalink()}")
eprint(f" Release URL: {args.jira_release_url}")
eprint(f" Issue Filter URL: {args.issue_filter_url}")
eprint("=" * 50)

print(f"ticket_key={ticket.key}")
Expand Down
20 changes: 10 additions & 10 deletions create-jira-release-ticket/test_create_release_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ def test_create_release_ticket_with_all_fields(self):
args.rule_props_changed = 'Yes'
args.sonarlint_changelog = 'Test changelog'

release_url = 'https://jira.com/release/notes'
issue_filter_url = 'https://jira.com/issues/?jql=fixVersion%3D12345'

result = create_release_ticket(mock_jira, args, release_url)
result = create_release_ticket(mock_jira, args, issue_filter_url)

self.assertEqual(result, mock_ticket)
mock_jira.create_issue.assert_called_once()
Expand All @@ -86,7 +86,7 @@ def test_create_release_ticket_with_all_fields(self):
self.assertEqual(call_args['issuetype'], 'Ask for release')
self.assertEqual(call_args['summary'], 'TestProject 1.2.3')
self.assertEqual(call_args['customfield_10146'], 'Test release') # SHORT_DESCRIPTION
self.assertEqual(call_args['customfield_10145'], release_url) # LINK_TO_RELEASE_NOTES
self.assertEqual(call_args['customfield_10145'], issue_filter_url) # LINK_TO_ISSUE_FILTER
self.assertEqual(call_args['customfield_10147'], 'Ready') # DOCUMENTATION_STATUS
self.assertEqual(call_args['customfield_11263'], {'value': 'Yes'}) # RULE_PROPS_CHANGED
self.assertEqual(call_args['customfield_11264'], 'Test changelog') # SONARLINT_CHANGELOG
Expand All @@ -109,7 +109,7 @@ def test_create_release_ticket_jira_error(self, mock_eprint):
args.sonarlint_changelog = ''

with self.assertRaises(SystemExit) as cm:
create_release_ticket(mock_jira, args, 'https://release.url')
create_release_ticket(mock_jira, args, 'https://jira.com/issues/?jql=fixVersion%3D12345')

self.assertEqual(cm.exception.code, 1)

Expand All @@ -120,7 +120,7 @@ def test_create_release_ticket_jira_error(self, mock_eprint):
'--version', '1.0.0',
'--short-description', 'Test release',
'--jira-url', 'https://test.jira.com',
'--jira-release-url', 'https://jira.com/release/notes'
'--issue-filter-url', 'https://jira.com/issues/?jql=fixVersion%3D12345'
])
@patch('create_release_ticket.get_jira_instance')
@patch('create_release_ticket.create_release_ticket')
Expand All @@ -145,15 +145,15 @@ def test_main_successful_ticket_creation(self, mock_stderr, mock_stdout, mock_cr
mock_create_ticket.assert_called_once()
call_args = mock_create_ticket.call_args
self.assertEqual(call_args[0][0], mock_jira) # jira client
self.assertEqual(call_args[0][2], 'https://jira.com/release/notes') # release URL
self.assertEqual(call_args[0][2], 'https://jira.com/issues/?jql=fixVersion%3D12345') # issue filter URL

# Verify output
stdout_output = mock_stdout.getvalue()
self.assertIn('ticket_key=REL-456', stdout_output)
self.assertIn('ticket_url=https://jira.com/REL-456', stdout_output)

stderr_output = mock_stderr.getvalue()
self.assertIn('Using release URL: https://jira.com/release/notes', stderr_output)
self.assertIn('Using issue filter URL: https://jira.com/issues/?jql=fixVersion%3D12345', stderr_output)
self.assertIn('🎉 Successfully created release ticket!', stderr_output)

# noinspection PyUnusedLocal
Expand All @@ -164,7 +164,7 @@ def test_main_successful_ticket_creation(self, mock_stderr, mock_stdout, mock_cr
'--version', '1.0.0',
'--short-description', 'Test release',
'--jira-url', 'https://sandbox.jira.com',
'--jira-release-url', 'https://sandbox.jira.com/release/notes',
'--issue-filter-url', 'https://sandbox.jira.com/issues/?jql=fixVersion%3D67890',
'--documentation-status', 'Ready',
'--rule-props-changed', 'Yes',
'--sonarlint-changelog', 'Some changelog'
Expand Down Expand Up @@ -197,13 +197,13 @@ def test_main_with_all_parameters(self, mock_stderr, mock_stdout, mock_create_ti
self.assertEqual(args.version, '1.0.0')
self.assertEqual(args.short_description, 'Test release')
self.assertEqual(args.jira_url, 'https://sandbox.jira.com')
self.assertEqual(args.jira_release_url, 'https://sandbox.jira.com/release/notes')
self.assertEqual(args.issue_filter_url, 'https://sandbox.jira.com/issues/?jql=fixVersion%3D67890')
self.assertEqual(args.documentation_status, 'Ready')
self.assertEqual(args.rule_props_changed, 'Yes')
self.assertEqual(args.sonarlint_changelog, 'Some changelog')

stderr_output = mock_stderr.getvalue()
self.assertIn('Using release URL: https://sandbox.jira.com/release/notes', stderr_output)
self.assertIn('Using issue filter URL: https://sandbox.jira.com/issues/?jql=fixVersion%3D67890', stderr_output)


if __name__ == '__main__':
Expand Down
13 changes: 8 additions & 5 deletions get-jira-release-notes/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ outputs:
jira-release-url:
description: 'The URL to the Jira release notes page.'
value: ${{ steps.get_notes.outputs.jira-release-url }}
jira-release-id:
description: 'The unique ID of the Jira release.'
value: ${{ steps.get_notes.outputs.jira-release-id }}

runs:
using: "composite"
Expand Down Expand Up @@ -54,8 +57,8 @@ runs:
env:
JIRA_USER: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
JIRA_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
JIRA_PROD_URL: "https://sonarsource.atlassian.net/"
JIRA_SANDBOX_URL: "https://sonarsource-sandbox-608.atlassian.net/"
ISSUE_TYPES: ${{ inputs.issue-types }}
JIRA_URL: "https://sonarsource${{ ((inputs.use-jira-sandbox || env.USE_JIRA_SANDBOX) == 'true') && '-sandbox-608' || '' }}.atlassian.net/"
run: |
PROJECT_KEY="${{ inputs.jira-project-key || env.JIRA_PROJECT_KEY }}"
VERSION_NAME="${{ inputs.jira-version-name || env.JIRA_VERSION_NAME }}"
Expand All @@ -68,6 +71,6 @@ runs:
python ${{ github.action_path }}/get_jira_release_notes.py \
--project-key="$PROJECT_KEY" \
--version-name="$VERSION_NAME" \
--issue-types="${{ inputs.issue-types }}" \
--jira-url="${{ ((inputs.use-jira-sandbox || env.USE_JIRA_SANDBOX) == 'true') && env.JIRA_SANDBOX_URL || env.JIRA_PROD_URL }}" \
| tee -a $GITHUB_OUTPUT | sed 's/^jira-release-url=/JIRA_RELEASE_URL=/' | sed 's/^release-notes<</RELEASE_NOTES<</' | sed 's/^jira-release-notes<</JIRA_RELEASE_NOTES<</' >> $GITHUB_ENV
--issue-types="$ISSUE_TYPES" \
--jira-url="$JIRA_URL" \
| tee -a $GITHUB_OUTPUT | sed 's/^jira-release-url=/JIRA_RELEASE_URL=/' | sed 's/^release-notes<</RELEASE_NOTES<</' | sed 's/^jira-release-notes<</JIRA_RELEASE_NOTES<</' | sed 's/^jira-release-id<</JIRA_RELEASE_ID<</'>> $GITHUB_ENV
1 change: 1 addition & 0 deletions get-jira-release-notes/get_jira_release_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def main():
# Output results for GitHub Actions
# Using multiline string format for the release notes
print(f"jira-release-url={release_notes_url}")
print(f"jira-release-id={version_id}")
print("release-notes<<EOF")
print(markdown_notes)
print("EOF")
Expand Down
15 changes: 8 additions & 7 deletions get-jira-release-notes/test_get_jira_release_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,16 @@ def test_main_success(self, mock_print, mock_stderr, mock_generate_url, mock_for

# Verify expected output format
print_calls = mock_print.call_args_list
self.assertEqual(len(print_calls), 8) # URL + markdown start/content/end + jira start/content/end
self.assertEqual(len(print_calls), 9) # URL + ID + markdown start/content/end + jira start/content/end
# Check the main outputs
self.assertEqual(print_calls[1][0][0], "jira-release-url=https://test.jira.com/projects/TEST/versions/10001/tab/release-report-all-issues")
self.assertEqual(print_calls[2][0][0], "release-notes<<EOF")
self.assertEqual(print_calls[3][0][0], "# Release notes - Test Project - 1.0.0\n\nNo issues found.")
self.assertEqual(print_calls[4][0][0], "EOF")
self.assertEqual(print_calls[5][0][0], "jira-release-notes<<EOF")
self.assertEqual(print_calls[6][0][0], "h1. Release notes - Test Project - 1.0.0\n\nNo issues found.")
self.assertEqual(print_calls[7][0][0], "EOF")
self.assertEqual(print_calls[2][0][0], "jira-release-id=10001")
self.assertEqual(print_calls[3][0][0], "release-notes<<EOF")
self.assertEqual(print_calls[4][0][0], "# Release notes - Test Project - 1.0.0\n\nNo issues found.")
self.assertEqual(print_calls[5][0][0], "EOF")
self.assertEqual(print_calls[6][0][0], "jira-release-notes<<EOF")
self.assertEqual(print_calls[7][0][0], "h1. Release notes - Test Project - 1.0.0\n\nNo issues found.")
self.assertEqual(print_calls[8][0][0], "EOF")

@patch('sys.argv', [
'get_jira_release_notes.py',
Expand Down
Loading