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
35 changes: 35 additions & 0 deletions .github/workflows/test-create-jira-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Test Create Jira Version Action

on:
pull_request:
merge_group:
push:
branches:
- master
- branch-*
workflow_dispatch:

jobs:
unit-tests:
name: Run Unit Tests
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.8'

- name: Install dependencies
run: |
cd create-jira-version
pip install -r requirements.txt
pip install pytest pytest-cov

- name: Run unit tests
run: |
cd create-jira-version
python -m pytest test_create_jira_version.py -v --cov=create_jira_version --cov-report=term-missing
102 changes: 102 additions & 0 deletions create-jira-version/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Create Jira Version Action

This GitHub Action creates a new version in a Jira project, with the ability to automatically determine the next version number based on existing versions.

## Description

The action creates a new version in the specified Jira project by:
1. Connecting to Jira using authentication credentials from Vault
2. Either using a provided version name or automatically determining the next version number
3. Creating the new version in the specified Jira project
4. Returning the created version's ID and name

## Dependencies

This action depends on:
- [SonarSource/vault-action-wrapper](https://github.com/SonarSource/vault-action-wrapper) for retrieving Jira credentials
- [SonarSource/release-github-actions/get-jira-version](https://github.com/SonarSource/release-github-actions) when auto-determining version names

## Inputs

| Input | Description | Required | Default |
|-------|-------------|----------|---------|
| `jira_project_key` | The key of the Jira project (e.g., SONARIAC) | Yes | - |
| `jira_version_name` | The name of the Jira version to create (e.g., 1.2.3) | No | Auto-determined |
| `use_jira_sandbox` | Use the sandbox Jira server instead of production | No | `false` |

## Outputs

| Output | Description |
|--------|-------------|
| `jira_version_id` | The ID of the created Jira version |
| `jira_version_name` | The name of the created Jira version |

## Usage

### Basic usage with explicit version name

```yaml
- name: Create Jira Version
id: create-version
uses: SonarSource/release-github-actions/create-jira-version@master
with:
jira_project_key: 'SONARIAC'
jira_version_name: '1.2.3'

- name: Use created version
run: |
echo "Created version ID: ${{ steps.create-version.outputs.jira_version_id }}"
echo "Created version name: ${{ steps.create-version.outputs.jira_version_name }}"
```

### Auto-determine next version number

```yaml
- name: Create Next Jira Version
id: create-version
uses: SonarSource/release-github-actions/create-jira-version@master
with:
jira_project_key: 'SONARIAC'
# jira_version_name is omitted - will auto-increment from latest version

- name: Use created version
run: |
echo "Created version: ${{ steps.create-version.outputs.jira_version_name }}"
```

### Using sandbox environment

```yaml
- name: Create Jira Version in Sandbox
uses: SonarSource/release-github-actions/create-jira-version@master
with:
jira_project_key: 'TESTPROJECT'
jira_version_name: '1.0.0-beta'
use_jira_sandbox: 'true'
```

## Example

If you have existing versions `1.2.1` and `1.2.2` in your Jira project and don't specify a version name:
1. The action will get the latest version (`1.2.2`)
2. Auto-increment to determine the next version (`1.2.3`)
3. Create version `1.2.3` in Jira
4. Return the new version's ID and name

## Implementation Details

The action uses a Python script that:
- Authenticates with Jira using credentials from HashiCorp Vault
- Supports both production and sandbox Jira environments
- Can auto-determine the next version number by incrementing the latest existing version
- Creates the new version using the Jira REST API
- Outputs the created version's metadata for use in subsequent steps


## Notes

- This action requires access to SonarSource's HashiCorp Vault for Jira credentials
- When `jira_version_name` is not provided, the action automatically determines the next version by incrementing the latest existing version
- The action supports both production and sandbox Jira environments
- Version names should follow semantic versioning patterns for best results
- The action will fail if a version with the same name already exists in the project
68 changes: 68 additions & 0 deletions create-jira-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Create Jira Version

description: 'Creates a Jira version.'

inputs:
jira_project_key:
description: 'The key of the Jira project (e.g., SONARIAC).'
required: true
jira_version_name:
description: 'The name of the Jira version to create (e.g., 1.2.3).'
required: false
use_jira_sandbox:
description: "Use the sandbox Jira server instead of production."
required: false
default: 'false'

outputs:
jira_version_id:
description: 'The ID of the created Jira version.'
value: ${{ steps.run_python_script.outputs.new_version_id }}
jira_version_name:
description: 'The name of the created Jira version.'
value: ${{ steps.run_python_script.outputs.new_version_name }}

runs:
using: "composite"
steps:
- name: Get Jira Credentials from Vault
uses: SonarSource/vault-action-wrapper@v3
id: secrets
with:
secrets: |
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.8'

- name: Install Dependencies
shell: bash
run: pip install -r ${{ github.action_path }}/requirements.txt

- uses: SonarSource/release-github-actions/get-jira-version@9fcdc700a2046505f6ebeed43be2176569910084
if: ${{ !inputs.jira_version_name }}

- name: Determine New Jira Version
id: determine_version_name
if: ${{ !inputs.jira_version_name }}
shell: bash
run: |
echo "VERSION_NAME=$(echo "${{ steps.get-jira-version.outputs.jira_version }}" | awk -F. '{$NF+=1; OFS="."; print $0}')" >> $GITHUB_OUTPUT

- name: Create Jira Version
id: run_python_script
shell: bash
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/"
run: |
python ${{ github.action_path }}/create_jira_version.py \
--project-key="${{ inputs.jira_project_key }}" \
--version-name="${{ inputs.jira_version_name || steps.determine_version_name.outputs.VERSION_NAME }}" \
${{ (inputs.use_jira_sandbox == 'true' || env.USE_JIRA_VERSION == 'true') && '--use-sandbox' || '' }} \
>> $GITHUB_OUTPUT
96 changes: 96 additions & 0 deletions create-jira-version/create_jira_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
This script automates releasing a version in Jira and creating the next one.
"""

import argparse
import os
import sys
from jira import JIRA
from jira.exceptions import JIRAError

def eprint(*args, **kwargs):
"""Prints messages to the standard error stream (stderr) for logging."""
print(*args, file=sys.stderr, **kwargs)

# noinspection DuplicatedCode
def get_jira_instance(use_sandbox=False):
"""
Initializes and returns a JIRA client instance.
Authentication is handled via environment variables.
"""
jira_user = os.environ.get('JIRA_USER')
jira_token = os.environ.get('JIRA_TOKEN')
jira_prod_url = os.environ.get('JIRA_PROD_URL')
jira_sandbox_url = os.environ.get('JIRA_SANDBOX_URL')

if not jira_user or not jira_token:
eprint("Error: JIRA_USER and JIRA_TOKEN environment variables must be set.")
sys.exit(1)

jira_url = jira_sandbox_url if use_sandbox else jira_prod_url

eprint(f"Connecting to JIRA server at: {jira_url}")
try:
jira_client = JIRA(jira_url, basic_auth=(jira_user, jira_token))
# Verify connection
jira_client.server_info()
eprint("JIRA authentication successful.")
return jira_client
except JIRAError as e:
eprint(f"Error: JIRA authentication failed. Status: {e.status_code}")
eprint(f"Response text: {e.text}")
sys.exit(1)
except Exception as e:
eprint(f"An unexpected error occurred during JIRA connection: {e}")
sys.exit(1)

def main():
"""Main function to orchestrate the release and creation process."""
parser = argparse.ArgumentParser(
description="Releases a Jira version and creates the next one.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("--project-key", required=True, help="The key of the Jira project (e.g., SONARIAC).")
parser.add_argument("--version-name", required=True, help="The name for the next version.")
parser.add_argument('--use-sandbox', action='store_true', help="Use the sandbox Jira server.")
args = parser.parse_args()

jira = get_jira_instance(args.use_sandbox)

eprint(f"Try to create new version '{args.version_name}'")
try:
new_version = jira.create_version(name=args.version_name, project=args.project_key)
eprint(f"✅ Successfully created new version '{new_version.name}'")

print(f"new_version_id={new_version.id}")
print(f"new_version_name={new_version.name}")

except JIRAError as e:
if "A version with this name already exists" in e.text:
eprint(f"Warning: Version '{args.version_name}' already exists. Skipping creation.")

# Fetch the existing version details
project = jira.project(args.project_key)
existing_version = None
for version in project.versions:
if version.name == args.version_name:
existing_version = version
break

if existing_version:
print(f"new_version_id={existing_version.id}")
print(f"new_version_name={existing_version.name}")
else:
eprint(f"Error: Could not find existing version '{args.version_name}' in project.")
sys.exit(1)
else:
eprint(f"Error: Failed to create new version. Status: {e.status_code}, Text: {e.text}")
sys.exit(1)



if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions create-jira-version/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jira==3.8.0
Loading