Skip to content

Commit 85d6f0b

Browse files
SONARIAC-2093 Add "Update Release Ticket Status" Action (#6)
* add 'Update Release Ticket Status' action * add full SHA hash for used actions * print all available transitions * change In Progress transition to Start Progress * add action readme link to main readme
1 parent 757d1f1 commit 85d6f0b

File tree

8 files changed

+275
-3
lines changed

8 files changed

+275
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ A centralized collection of reusable GitHub Actions designed to streamline and a
66

77
* [**Create Jira Release Ticket**](create-jira-release-ticket/README.md): Automates the creation of an "Ask for release" ticket in Jira.
88
* [**Check Releasability Status**](check-releasability-status/README.md): Checks the releasability status of the master branch and extracts the version if successful.
9+
* [**Update Release ticket Status**](update-release-ticket-status/README.md): Updates the status of a Jira release ticket and can change its assignee.

check-releasability-status/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ runs:
2121
steps:
2222
- name: Check releasability status
2323
id: check_releasability_script
24-
uses: actions/github-script@v7
24+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
2525
with:
2626
github-token: ${{ inputs.github-token }}
2727
script: |

create-jira-release-ticket/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The action is self-contained and uses a Python script to interact with the Jira
66
## Prerequisites
77

88
The action requires that the repository needs to have the `development/kv/data/jira` token configured in vault.
9-
This can done using the SPEED self-service portal ([more info](https://xtranet-sonarsource.atlassian.net/wiki/spaces/Platform/pages/3553787989/Manage+Vault+Policy+-+SPEED)).
9+
This can be done using the SPEED self-service portal ([more info](https://xtranet-sonarsource.atlassian.net/wiki/spaces/Platform/pages/3553787989/Manage+Vault+Policy+-+SPEED)).
1010

1111
## Inputs
1212

create-jira-release-ticket/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ runs:
5454
using: "composite"
5555
steps:
5656
- name: Set up Python
57-
uses: actions/setup-python@v5
57+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
5858
with:
5959
python-version: '3.8'
6060

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Update Jira Release Ticket Status Action
2+
3+
This GitHub Action automates updating the status of an "Ask for release" ticket in Jira. It can also be used to change the assignee of the ticket.
4+
5+
## Prerequisites
6+
7+
The action requires that the repository needs to have the `development/kv/data/jira` token configured in vault.
8+
This can be done using the SPEED self-service portal ([more info](https://xtranet-sonarsource.atlassian.net/wiki/spaces/Platform/pages/3553787989/Manage+Vault+Policy+-+SPEED)).
9+
10+
## Inputs
11+
12+
| Input | Description | Required | Default |
13+
|---------------|---------------------------------------------------------------------------------|----------|---------|
14+
| `jira_user` | The Jira user (email) for authentication. | `true` | |
15+
| `jira_token` | The Jira API token for authentication. | `true` | |
16+
| `ticket_key` | The key of the Jira ticket to update (e.g., `REL-1234`). | `true` | |
17+
| `status` | The target status. Possible values: `Start Progress`, `Technical Release Done`. | `true` | |
18+
| `assignee` | The email of the user to assign the ticket to. | `false` | `''` |
19+
| `use_sandbox` | Set to `false` to use the Jira production server. | `false` | `true` |
20+
21+
## Example Usage
22+
23+
Here is an example of how to use this action in a workflow. This step would typically run after a release ticket has been created.
24+
25+
```yaml
26+
name: Update Release Ticket
27+
28+
on:
29+
workflow_dispatch:
30+
inputs:
31+
ticket_key:
32+
description: 'Jira Ticket Key to update'
33+
required: true
34+
new_status:
35+
description: 'New status for the ticket'
36+
required: true
37+
type: choice
38+
options:
39+
- Start Progress
40+
- Technical Release Done
41+
assignee_email:
42+
description: 'Email of the new assignee (optional)'
43+
required: false
44+
45+
jobs:
46+
update_release_ticket:
47+
name: Update release ticket
48+
runs-on: ubuntu-latest
49+
permissions:
50+
contents: read
51+
id-token: write
52+
53+
steps:
54+
- name: Get Jira Credentials from Vault
55+
id: secrets
56+
uses: SonarSource/vault-action-wrapper@v3
57+
with:
58+
secrets: |
59+
development/kv/data/jira user | JIRA_USER;
60+
development/kv/data/jira token | JIRA_TOKEN;
61+
62+
- name: Update Jira Release Ticket
63+
uses: SonarSource/release-github-actions/update-release-ticket-status@v1
64+
with:
65+
jira_user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
66+
jira_token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
67+
ticket_key: ${{ github.event.inputs.ticket_key }}
68+
status: ${{ github.event.inputs.new_status }}
69+
assignee: ${{ github.event.inputs.assignee_email }}
70+
71+
- name: Echo Status
72+
run: echo "Successfully updated ticket ${{ github.event.inputs.ticket_key }} to status ${{ github.event.inputs.new_status }}."
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: 'Update Release ticket Status'
2+
description: 'Updates the status of a Jira release ticket and can change its assignee.'
3+
author: 'SonarSource'
4+
5+
inputs:
6+
jira_user:
7+
description: 'Jira user (email) for authentication.'
8+
required: true
9+
jira_token:
10+
description: 'Jira API token for authentication.'
11+
required: true
12+
ticket_key:
13+
description: 'The key of the Jira ticket to update (e.g., REL-1234).'
14+
required: true
15+
status:
16+
description: 'The target status for the ticket. Must be a valid transition.'
17+
required: true
18+
assignee:
19+
description: 'The email of the user to assign the ticket to. Optional.'
20+
required: false
21+
default: ''
22+
use_sandbox:
23+
description: "Set to 'false' to use the production Jira server."
24+
required: false
25+
default: 'true'
26+
27+
runs:
28+
using: "composite"
29+
steps:
30+
- name: Set up Python
31+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
32+
with:
33+
python-version: '3.8'
34+
35+
- name: Install dependencies
36+
shell: bash
37+
run: pip install -r ${{ github.action_path }}/requirements.txt
38+
39+
- name: Run Python Script to Update Ticket
40+
id: run_python_script
41+
shell: bash
42+
run: |
43+
SANDBOX_FLAG=""
44+
if [[ "${{ inputs.use_sandbox }}" == "true" ]]; then
45+
SANDBOX_FLAG="--use-sandbox"
46+
fi
47+
48+
ASSIGNEE_FLAG=""
49+
if [[ -n "${{ inputs.assignee }}" ]]; then
50+
ASSIGNEE_FLAG="--assignee=${{ inputs.assignee }}"
51+
fi
52+
53+
python ${{ github.action_path }}/update_release_ticket.py \
54+
--ticket-key="${{ inputs.ticket_key }}" \
55+
--status="${{ inputs.status }}" \
56+
${ASSIGNEE_FLAG} \
57+
${SANDBOX_FLAG}
58+
env:
59+
JIRA_USER: ${{ inputs.jira_user }}
60+
JIRA_TOKEN: ${{ inputs.jira_token }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
jira==3.8.0
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
This script automates the process of updating a Jira release ticket's status
6+
and optionally reassigning it.
7+
"""
8+
9+
import argparse
10+
import os
11+
import sys
12+
from jira import JIRA
13+
from jira.exceptions import JIRAError
14+
15+
# Jira server URLs
16+
JIRA_SANDBOX_URL = "https://sonarsource-sandbox-608.atlassian.net/"
17+
JIRA_PROD_URL = "https://sonarsource.atlassian.net/"
18+
19+
20+
def eprint(*args, **kwargs):
21+
"""
22+
Prints messages to the standard error stream (stderr) for logging purposes.
23+
This separates diagnostic output from the script's primary result.
24+
"""
25+
print(*args, file=sys.stderr, **kwargs)
26+
27+
28+
# noinspection DuplicatedCode
29+
def get_jira_instance(use_sandbox=False):
30+
"""
31+
Initializes and returns a JIRA client instance based on environment variables.
32+
33+
Args:
34+
use_sandbox (bool): If True, connects to the sandbox Jira server.
35+
36+
Returns:
37+
JIRA: An authenticated JIRA client instance.
38+
"""
39+
jira_user = os.environ.get('JIRA_USER')
40+
jira_token = os.environ.get('JIRA_TOKEN')
41+
42+
if not jira_user or not jira_token:
43+
eprint("Error: JIRA_USER and JIRA_TOKEN environment variables must be set.")
44+
sys.exit(1)
45+
46+
jira_url = JIRA_SANDBOX_URL if use_sandbox else JIRA_PROD_URL
47+
48+
eprint(f"Connecting to JIRA server at: {jira_url}")
49+
eprint(f"Authenticating with user: {jira_user}")
50+
51+
try:
52+
jira_client = JIRA(jira_url, basic_auth=(jira_user, jira_token), get_server_info=True)
53+
eprint("JIRA authentication successful.")
54+
return jira_client
55+
except JIRAError as e:
56+
eprint(f"Error: JIRA authentication failed. Status: {e.status_code}")
57+
eprint(f"Response text: {e.text}")
58+
sys.exit(1)
59+
except Exception as e:
60+
eprint(f"An unexpected error occurred during JIRA connection: {e}")
61+
sys.exit(1)
62+
63+
64+
def update_ticket_status(jira_client, ticket_key, new_status, assignee_email):
65+
"""
66+
Updates the status of a Jira ticket and optionally assigns it to a new user.
67+
68+
Args:
69+
jira_client (JIRA): The authenticated JIRA client.
70+
ticket_key (str): The key of the ticket to update (e.g., 'REL-1234').
71+
new_status (str): The target status for the ticket.
72+
assignee_email (str): The email of the user to assign the ticket to. Can be None.
73+
"""
74+
try:
75+
eprint(f"Fetching ticket: {ticket_key}")
76+
issue = jira_client.issue(ticket_key)
77+
except JIRAError as e:
78+
if e.status_code == 404:
79+
eprint(f"Error: Ticket '{ticket_key}' not found.")
80+
else:
81+
eprint(f"Error: Failed to fetch ticket '{ticket_key}'. Status: {e.status_code}")
82+
eprint(f"Response text: {e.text}")
83+
sys.exit(1)
84+
85+
if assignee_email:
86+
eprint(f"Attempting to assign ticket to: {assignee_email}")
87+
try:
88+
jira_client.assign_issue(ticket_key, assignee_email)
89+
eprint(f"Successfully assigned ticket to {assignee_email}")
90+
except JIRAError as e:
91+
eprint(f"Error: Failed to assign ticket to '{assignee_email}'. Status: {e.status_code}")
92+
eprint(f"Response text: {e.text}")
93+
eprint("Please ensure the user exists and has assignable permissions for this project.")
94+
sys.exit(1)
95+
96+
try:
97+
jira_client.transition_issue(issue, new_status)
98+
eprint(f"Successfully transitioned ticket to '{new_status}'.")
99+
except JIRAError as e:
100+
eprint(f"Error: Failed to transition ticket to '{new_status}'. Status: {e.status_code}")
101+
eprint(f"Response text: {e.text}")
102+
eprint("Please ensure this is a valid transition from the ticket's current status.")
103+
sys.exit(1)
104+
105+
106+
def main():
107+
"""
108+
Main function to parse arguments and orchestrate the ticket update process.
109+
"""
110+
parser = argparse.ArgumentParser(
111+
description="Update a Jira release ticket's status and assignee.",
112+
formatter_class=argparse.ArgumentDefaultsHelpFormatter
113+
)
114+
115+
parser.add_argument("--ticket-key", required=True, help="The key of the Jira ticket to update (e.g., REL-1234).")
116+
parser.add_argument("--status", required=True, choices=['Start Progress', 'Technical Release Done'],
117+
help="The target status for the ticket.")
118+
parser.add_argument("--assignee", required=False, default=None,
119+
help="The email of the user to assign the ticket to.")
120+
parser.add_argument('--use-sandbox', action='store_true',
121+
help="Use the sandbox server instead of the production Jira.")
122+
123+
args = parser.parse_args()
124+
125+
jira = get_jira_instance(args.use_sandbox)
126+
update_ticket_status(jira, args.ticket_key, args.status, args.assignee)
127+
128+
eprint("\n" + "=" * 50)
129+
eprint("🎉 Successfully updated Jira ticket!")
130+
eprint(f" Ticket Key: {args.ticket_key}")
131+
eprint(f" New Status: {args.status}")
132+
if args.assignee:
133+
eprint(f" Assignee: {args.assignee}")
134+
eprint("=" * 50)
135+
136+
137+
if __name__ == "__main__":
138+
main()

0 commit comments

Comments
 (0)