Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Part of
<!--
Only for standalone PRs without Jira issue in the PR title:
* Replace this comment with Epic ID to create a new Task in Jira
* Replace this comment with Epic ID to create a new Maintenance ticket in Jira
* Replace this comment with Issue ID to create a new Sub-Task in Jira
* Ignore or delete this note to create a new Task in Jira without a parent
* Ignore or delete this note to create a new Maintenance ticket in Jira without a parent
-->
2 changes: 1 addition & 1 deletion .github/workflows/automated-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ on:
description: "Jira issue categories to include in the release notes"
required: false
type: string
default: "New Feature,False Positive,False Negative,Bug,Improvement,Task"
default: "Feature,False Positive,False Negative,Bug,Security"

outputs:
new-version:
Expand Down
143 changes: 140 additions & 3 deletions .github/workflows/test-get-jira-release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,144 @@ jobs:
test-default-issue-categories:
name: Test Action Integration with Default Issue Categories
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Get release notes from a sandbox project
id: test-sandbox
uses: ./get-jira-release-notes
with:
jira-project-key: 'SONARIAC'
jira-version-name: '1.52'
use-jira-sandbox: 'true'

- name: Output results
run: |
echo "Jira Release URL: ${{ steps.test-sandbox.outputs.jira-release-url }}"
echo "Jira Release Issue Filter URL: ${{ steps.test-sandbox.outputs.jira-release-issue-filter-url }}"
echo "Release Notes:"
echo "${{ steps.test-sandbox.outputs.release-notes }}"
echo "Jira Release Notes:"
echo "${{ steps.test-sandbox.outputs.jira-release-notes }}"

- name: Verify Jira Release URL
run: |
if [[ "${{ steps.test-sandbox.outputs.jira-release-url }}" != https://sonarsource-sandbox-608.atlassian.net/projects/SONARIAC/versions/*/tab/release-report-all-issues ]]; then
echo "❌ Jira Release URL format is incorrect"
exit 1
else
echo "✅ Jira Release URL format is correct"
fi

- name: Verify Jira Release Issue Filter URL
run: |
if [[ "${{ steps.test-sandbox.outputs.jira-release-issue-filter-url }}" != https://sonarsource-sandbox-608.atlassian.net/issues/?jql=fixVersion%3D23819 ]]; then
echo "❌ Jira Release Issue Filter URL is incorrect"
echo "Expected: https://sonarsource-sandbox-608.atlassian.net/issues/?jql=fixVersion%3D23819"
echo "Got: ${{ steps.test-sandbox.outputs.jira-release-issue-filter-url }}"
exit 1
else
echo "✅ Jira Release Issue Filter URL is correct"
fi

- name: Verify Release Notes Content
run: |
if grep -q "### Feature" <<< "${{ steps.test-sandbox.outputs.release-notes }}"; then
echo "✅ Release Notes contain default issue categories"
else
echo "❌ Release Notes are missing some default issue categories"
exit 1
fi

if grep -q "### Maintenance" <<< "${{ steps.test-sandbox.outputs.release-notes }}"; then
echo "❌ Release Notes should not contain Maintenance category with default settings"
exit 1
else
echo "✅ Release Notes correctly exclude Maintenance category"
fi

- name: Verify Jira Release Notes Content
run: |
if grep -q "h3. Feature" <<< "${{ steps.test-sandbox.outputs.jira-release-notes }}"; then
echo "✅ Jira Release Notes contain default issue category"
else
echo "❌ Jira Release Notes are missing some default issue categories"
exit 1
fi

if grep -q "h3. Maintenance" <<< "${{ steps.test-sandbox.outputs.jira-release-notes }}"; then
echo "❌ Jira Release Notes should not contain Maintenance category with default settings"
exit 1
else
echo "✅ Jira Release Notes correctly exclude Maintenance category"
fi

test-custom-issue-categories:
name: Test Action Integration with Custom Issue Categories
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Get release notes from a sandbox project with custom issue types
id: test-sandbox-custom
uses: ./get-jira-release-notes
with:
jira-project-key: 'SONARIAC'
jira-version-name: '1.52'
use-jira-sandbox: 'true'
issue-types: 'Maintenance,Bug,Security'

- name: Output results
run: |
echo "Jira Release URL: ${{ steps.test-sandbox-custom.outputs.jira-release-url }}"
echo "Release Notes:"
echo "${{ steps.test-sandbox-custom.outputs.release-notes }}"
echo "Jira Release Notes:"
echo "${{ steps.test-sandbox-custom.outputs.jira-release-notes }}"

- name: Verify Release Notes Content
run: |
if grep -q "### Maintenance" <<< "${{ steps.test-sandbox-custom.outputs.release-notes }}"; then
echo "✅ Release Notes contain Maintenance category as specified"
else
echo "❌ Release Notes are missing Maintenance category"
exit 1
fi

if grep -q "### Feature" <<< "${{ steps.test-sandbox-custom.outputs.release-notes }}"; then
echo "❌ Release Notes should not contain Feature category with custom settings"
exit 1
else
echo "✅ Release Notes correctly exclude Feature category"
fi

- name: Verify Jira Release Notes Content
run: |
if grep -q "h3. Maintenance" <<< "${{ steps.test-sandbox-custom.outputs.jira-release-notes }}"; then
echo "✅ Jira Release Notes contain Maintenance category as specified"
else
echo "❌ Jira Release Notes are missing Maintenance category"
exit 1
fi

if grep -q "h3. Feature" <<< "${{ steps.test-sandbox-custom.outputs.jira-release-notes }}"; then
echo "❌ Jira Release Notes should not contain Feature category with custom settings"
exit 1
else
echo "✅ Jira Release Notes correctly exclude Feature category"
fi

test-default-issue-categories-deprecated-taxonomy:
name: Test Action Integration with Default Issue Categories on project with deprecated Jira taxonomy
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
Expand Down Expand Up @@ -207,8 +345,8 @@ jobs:
echo "✅ Jira Release Notes correctly exclude Task category"
fi

test-custom-issue-categories:
name: Test Action Integration with Custom Issue Categories
test-custom-issue-categories-deprecated-taxonomy:
name: Test Action Integration with Custom Issue Categories on project with deprecated Jira taxonomy
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down Expand Up @@ -265,4 +403,3 @@ jobs:
else
echo "✅ Jira Release Notes correctly exclude Improvement category"
fi

4 changes: 1 addition & 3 deletions .github/workflows/test-publish-github-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,9 @@ jobs:
release-notes: |
# Release notes - Test - 1.0.3

### New Feature
### Feature
[SONARIAC-2361](https://example.com) S4830: Server certificates should be verified during SSL/TLS connections
[SONARIAC-2366](https://example.com) S6573: Expanded filenames should not become options

### Improvement
[SONARIAC-2230](https://example.com) S6596 should have different message when tag is present but not compliant
[SONARIAC-2301](https://example.com) Improve "ShellCmdDetector" performance

Expand Down
2 changes: 1 addition & 1 deletion create-integration-ticket/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ This action requires:
## Features

- Creates a Jira ticket in the specified project with optional description
- Automatically detects appropriate issue type (Task, Improvement, or first available)
- Automatically detects appropriate issue type (Maintenance, Feature, Task, Improvement, or first available)
- Links the created ticket to an existing ticket using the specified link type
- Validates that the release ticket exists before creating the new ticket
- Gracefully handles description field limitations (warns if description cannot be set but continues)
Expand Down
5 changes: 3 additions & 2 deletions create-integration-ticket/create_integration_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ def create_integration_ticket(jira_client, args):
createmeta_data = jira_client.createmeta(projectKeys=args.target_jira_project, expand='projects.issuetypes')
issue_types = createmeta_data['projects'][0]['issuetypes']

# Try to find a suitable issue type (prefer improvement, then first available)
# Try to find a suitable issue type (prefer feature, then first available)
issue_type = None
for it in issue_types:
if it['name'].lower() in ['improvement', 'task']:
# Improvement and Task are included for backwards compatibility of deprecated Jira taxonomy
if it['name'].lower() in ['feature', 'maintenance', 'improvement', 'task']:
issue_type = it['name']
break

Expand Down
95 changes: 82 additions & 13 deletions create-integration-ticket/test_create_integration_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,18 @@ def test_validate_release_ticket_unexpected_error(self):
validate_release_ticket(mock_jira, 'REL-123')
self.assertEqual(cm.exception.code, 1)

def test_create_integration_ticket_with_task_type(self):
"""Test creating integration ticket with Task issue type."""
def test_create_integration_ticket_with_maintenance_type(self):
"""Test creating integration ticket with Maintenance issue type."""
mock_jira = Mock()
mock_project = Mock()
mock_jira.project.return_value = mock_project

# Mock issue types with Task available
# Mock issue types with Maintenance available
mock_jira.createmeta.return_value = {
'projects': [{
'issuetypes': [
{'name': 'Bug'},
{'name': 'Task'},
{'name': 'Maintenance'},
{'name': 'Story'}
]
}]
Expand All @@ -164,7 +164,7 @@ def test_create_integration_ticket_with_task_type(self):
# Verify the issue creation call
call_args = mock_jira.create_issue.call_args[1]['fields']
self.assertEqual(call_args['project'], 'INT')
self.assertEqual(call_args['issuetype'], {'name': 'Task'})
self.assertEqual(call_args['issuetype'], {'name': 'Maintenance'})
self.assertEqual(call_args['summary'], 'Integration ticket for release')
self.assertNotIn('description', call_args) # No description provided

Expand All @@ -174,11 +174,11 @@ def test_create_integration_ticket_with_description(self):
mock_project = Mock()
mock_jira.project.return_value = mock_project

# Mock issue types with Task available
# Mock issue types with Maintenance available
mock_jira.createmeta.return_value = {
'projects': [{
'issuetypes': [
{'name': 'Task'}
{'name': 'Maintenance'}
]
}]
}
Expand All @@ -201,7 +201,7 @@ def test_create_integration_ticket_with_description(self):
# Verify the issue creation call does NOT include description
call_args = mock_jira.create_issue.call_args[1]['fields']
self.assertEqual(call_args['project'], 'INT')
self.assertEqual(call_args['issuetype'], {'name': 'Task'})
self.assertEqual(call_args['issuetype'], {'name': 'Maintenance'})
self.assertEqual(call_args['summary'], 'Integration ticket for release')
self.assertNotIn('description', call_args)

Expand All @@ -216,7 +216,7 @@ def test_create_integration_ticket_description_update_fails(self, mock_eprint):
mock_jira.project.return_value = mock_project

mock_jira.createmeta.return_value = {
'projects': [{'issuetypes': [{'name': 'Task'}]}]
'projects': [{'issuetypes': [{'name': 'Maintenance'}]}]
}

mock_ticket = Mock()
Expand All @@ -241,8 +241,77 @@ def test_create_integration_ticket_description_update_fails(self, mock_eprint):
mock_ticket.update.assert_called_once_with(fields={'description': 'This description will fail to set'})

# noinspection DuplicatedCode
def test_create_integration_ticket_with_feature_type(self):
"""Test creating integration ticket with Feature issue type when Maintenance is not available."""
mock_jira = Mock()
mock_project = Mock()
mock_jira.project.return_value = mock_project

# Mock issue types with only Feature available (no Bug)
mock_jira.createmeta.return_value = {
'projects': [{
'issuetypes': [
{'name': 'Bug'},
{'name': 'Feature'},
]
}]
}

mock_ticket = Mock()
mock_ticket.key = 'INT-124'
mock_jira.create_issue.return_value = mock_ticket

args = Mock()
args.target_jira_project = 'INT'
args.ticket_summary = 'Integration ticket for release'
args.ticket_description = None

result = create_integration_ticket(mock_jira, args)

self.assertEqual(result, mock_ticket)

# Verify the issue creation call - should use Feature
call_args = mock_jira.create_issue.call_args[1]['fields']
self.assertEqual(call_args['issuetype'], {'name': 'Feature'})

# noinspection DuplicatedCode
def test_create_integration_ticket_with_task_type(self):
"""Test creating integration ticket with Task issue type when Maintenance and Feature is not available."""
mock_jira = Mock()
mock_project = Mock()
mock_jira.project.return_value = mock_project

# Mock issue types with Task and Improvement available (no Bug)
mock_jira.createmeta.return_value = {
'projects': [{
'issuetypes': [
{'name': 'Bug'},
{'name': 'Task'},
{'name': 'Improvement'},
]
}]
}

mock_ticket = Mock()
mock_ticket.key = 'INT-124'
mock_jira.create_issue.return_value = mock_ticket

args = Mock()
args.target_jira_project = 'INT'
args.ticket_summary = 'Integration ticket for release'
args.ticket_description = None

result = create_integration_ticket(mock_jira, args)

self.assertEqual(result, mock_ticket)

# Verify the issue creation call - should use Feature
call_args = mock_jira.create_issue.call_args[1]['fields']
self.assertEqual(call_args['issuetype'], {'name': 'Task'})

# noinspection DuplicatedCode
def test_create_integration_ticket_with_improvement_type(self):
"""Test creating integration ticket with Improvement issue type when Task is not available."""
"""Test creating integration ticket with Improvement issue type when Maintenance, Feature and Task is not available."""
mock_jira = Mock()
mock_project = Mock()
mock_jira.project.return_value = mock_project
Expand Down Expand Up @@ -270,7 +339,7 @@ def test_create_integration_ticket_with_improvement_type(self):

self.assertEqual(result, mock_ticket)

# Verify the issue creation call - should use Improvement
# Verify the issue creation call - should use Feature
call_args = mock_jira.create_issue.call_args[1]['fields']
self.assertEqual(call_args['issuetype'], {'name': 'Improvement'})

Expand All @@ -281,7 +350,7 @@ def test_create_integration_ticket_with_first_available_type(self):
mock_project = Mock()
mock_jira.project.return_value = mock_project

# Mock issue types with neither Task nor Story available
# Mock issue types with neither Maintenance, Feature, Task, Improvement available
mock_jira.createmeta.return_value = {
'projects': [{
'issuetypes': [
Expand Down Expand Up @@ -349,7 +418,7 @@ def test_create_integration_ticket_creation_error(self, mock_eprint):
mock_jira.project.return_value = mock_project

mock_jira.createmeta.return_value = {
'projects': [{'issuetypes': [{'name': 'Task'}]}]
'projects': [{'issuetypes': [{'name': 'Maintenance'}]}]
}

mock_response = Mock()
Expand Down
Loading
Loading