Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e92a509
feat: Automate JIRA ticket transitions
Aug 27, 2025
847d047
feat: Introduce `jira-transition-tickets` action with consolidated co…
matejsemancik Nov 12, 2025
cb2a507
ci: Remove JIRA component-based ticket transition
matejsemancik Nov 12, 2025
fac1246
fix(jira): Standardize API token key name
matejsemancik Nov 12, 2025
c764d75
fix(jira): Add missing action inputs
matejsemancik Nov 12, 2025
9d486c5
refactor(jira): Simplify action to pass issue keys directly
matejsemancik Nov 12, 2025
e661132
feat(jira): Implement transition logic for issue keys
matejsemancik Nov 12, 2025
b76120e
Fix jira-transition-tickets call
matejsemancik Nov 12, 2025
268507d
feat(workflow): Add conditional JIRA transition with helpful message
matejsemancik Nov 12, 2025
b29d3a7
Add double quotes
matejsemancik Nov 12, 2025
8909f2d
feat(jira): Add tests for issue key extraction script
matejsemancik Nov 12, 2025
a66b1aa
refactor(jira): Replace sed with bash parameter expansion
matejsemancik Nov 12, 2025
56db4a6
chore: reformat android-cloud-nightly-build.yml
matejsemancik Nov 12, 2025
b012afb
feat(jira): Add comprehensive tests for transition-issues script
matejsemancik Nov 12, 2025
4027bc0
refactor(jira): Use HTTP status codes for error handling
matejsemancik Nov 12, 2025
e714cf4
refactor(jira): Replace base_url with cloud_id for Atlassian Cloud API
matejsemancik Nov 13, 2025
ffca9cf
docs(jira): Update README to match actual action implementation
matejsemancik Nov 13, 2025
7059906
docs(jira): Add note about integration with detect-changes action
matejsemancik Nov 13, 2025
dc15707
feat(jira): Add support for numbers in project id
matejsemancik Nov 13, 2025
f030e31
test(jira): Add integration test and enhance branch name extraction
matejsemancik Nov 13, 2025
83d4682
feat(jira): Add case-insensitive transition name matching
matejsemancik Nov 13, 2025
53fe27e
feat(ci): Add automated test runner for all .bats files
matejsemancik Nov 13, 2025
ab97d6c
fix(ci): Update bats env
matejsemancik Nov 13, 2025
8443536
refactor(test): Use BATS_TEST_DIRNAME for proper path resolution in t…
matejsemancik Nov 13, 2025
fbc0257
housekeep(ci): Remove obsolete files
matejsemancik Nov 13, 2025
1ef4c17
fix(test): Fix all failing tests in universal-detect-changes-and-gene…
Nov 13, 2025
4b1f040
feat(jira): Add JIRA transition automation to iOS and KMP workflows
matejsemancik Nov 14, 2025
4e35486
Merge branch 'main' into feature/JIRA-automatization
matejsemancik Nov 14, 2025
be10a00
Merge pull request #75 from futuredapp/fix/Changelog-tests
matejsemancik Nov 14, 2025
85b5c9c
feat(jira): Update JIRA transition action references in CI workflows …
matejsemancik Nov 14, 2025
79f1658
fix(jira): Update misleading comment
matejsemancik Nov 14, 2025
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
105 changes: 105 additions & 0 deletions .github/actions/jira-transition-tickets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# JIRA Transition Tickets

A GitHub Action that automatically transitions JIRA tickets to a specified status based on merged branch names.

## Overview

This action helps automate JIRA ticket workflows by:
- Extracting JIRA ticket keys from branch names (e.g., `feature/ABC-123-add-feature`)
- Transitioning matched tickets to a target status (e.g., "Done", "In QA", "Ready for Testing")

This action was designed to work with `merged_branches` output of [universal-detect-changes-and-generate-changelog](../universal-detect-changes-and-generate-changelog) action.

## Inputs

### `jira_context` (required)

A base64-encoded JSON string containing JIRA authentication credentials and configuration.

**Structure:**
```json
{
"cloud_id": "your-cloud-id",
"user_email": "[email protected]",
"api_token": "YourJiraApiToken"
}
```

**How to obtain Cloud ID:**

Navigate to [https://<cloudname>.atlassian.net/_edge/tenant_info](https://<cloudname>.atlassian.net/_edge/tenant_info)

**How to encode:**
```bash
echo -n '{"cloud_id":"your-cloud-id","user_email":"[email protected]","api_token":"token"}' | base64
```

**GitHub Secrets:**
Store the base64-encoded string in a GitHub secret (e.g., `JIRA_CONTEXT`) for secure usage.

### `transition` (required)

The name of the JIRA transition to execute. This must match the exact transition name in your JIRA workflow.

**Examples:** `"Done"`, `"In QA"`, `"Ready for Testing"`, `"Closed"`

### `merged_branches` (required)

A comma-separated string of branch names from which to extract JIRA ticket keys.

The action extracts keys matching the pattern `[A-Z]+-[0-9]+` from each branch name.

**Example:** `"feature/ABC-123-login,bugfix/XYZ-456-fix-crash"`

## How It Works

1. **Extract JIRA Keys:** Parses branch names to extract ticket keys (e.g., `ABC-123`)
2. **Get Available Transitions:** For each issue key, fetches available transitions from JIRA API
3. **Find Target Transition:** Matches the target status name to find the corresponding transition ID
4. **Perform Transition:** Executes the transition for each issue to move it to the target status

## Usage Examples

### Example 1: Transition tickets from merged branches

```yaml
- name: Transition JIRA tickets
uses: futuredapp/.github/.github/actions/jira-transition-tickets@main
with:
jira_context: ${{ secrets.JIRA_CONTEXT }}
transition: "Ready for Testing"
merged_branches: "feature/PROJ-123-new-feature,bugfix/PROJ-456-bug-fix"
```

### Example 2: Transition tickets from dynamic branch list

```yaml
- name: Transition tickets
uses: futuredapp/.github/.github/actions/jira-transition-tickets@main
with:
jira_context: ${{ secrets.JIRA_CONTEXT }}
transition: "Ready for Testing"
merged_branches: ${{ steps.get_branches.outputs.branches }}
```

### Example 3: In a reusable workflow

```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Build app
run: ./build.sh

- name: Transition JIRA tickets on success
if: success()
uses: futuredapp/.github/.github/actions/jira-transition-tickets@main
with:
jira_context: ${{ secrets.JIRA_CONTEXT }}
transition: "Ready for Testing"
merged_branches: ${{ github.head_ref }}
```
40 changes: 40 additions & 0 deletions .github/actions/jira-transition-tickets/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: 'JIRA Transition Tickets'
description: 'Finds and transitions JIRA tickets based on branch names.'

inputs:
jira_context:
description: 'A base64-encoded string of Jira context object. See README for required structure.'
required: true
transition:
description: 'The name of the transition to transition the JIRA tickets.'
required: true
merged_branches:
description: 'A comma-separated string of merged branch names.'
required: true

runs:
using: "composite"
steps:
- name: Make scripts executable
shell: bash
working-directory: ${{ github.action_path }}
run: |
chmod +x ./scripts/extract-issue-keys.sh
chmod +x ./scripts/transition-issues.sh

- name: Extract Issue Keys
id: extract
shell: bash
working-directory: ${{ github.action_path }}
run: |
./scripts/extract-issue-keys.sh \
"${{ inputs.merged_branches }}"

- name: Transition JIRA Tickets
shell: bash
working-directory: ${{ github.action_path }}
run: |
./scripts/transition-issues.sh \
"${{ inputs.jira_context }}" \
"${{ inputs.transition }}" \
"${{ steps.extract.outputs.issue_keys }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -e

MERGED_BRANCHES="$1"

# Extract JIRA keys from branches
JIRA_KEYS=()
if [[ -n "$MERGED_BRANCHES" ]]; then
# Prevents globbing and word splitting issues
IFS=',' read -ra BRANCHES <<< "$MERGED_BRANCHES"
for branch in "${BRANCHES[@]}"; do
# Trim leading/trailing whitespace from branch name
branch=$(echo "$branch" | xargs)
while IFS= read -r key; do
JIRA_KEYS+=("$key")
done < <(echo "$branch" | grep -oE '[A-Z0-9]+-[0-9]+')
done
fi

# Remove duplicate keys and create a space-separated string
UNIQUE_JIRA_KEYS_STR=$(echo "${JIRA_KEYS[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs)

# Build issue keys list
ISSUE_KEYS=""
if [[ -n "$UNIQUE_JIRA_KEYS_STR" ]]; then
ISSUE_KEYS="${UNIQUE_JIRA_KEYS_STR// /,}"
fi

# Set the output for the GitHub Action step
echo "issue_keys=$ISSUE_KEYS" >> "$GITHUB_OUTPUT"
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash
set -e

JIRA_CONTEXT="$1"
TRANSITION_NAME="$2"
ISSUE_KEYS="$3"

# Decode and parse JIRA_CONTEXT
JIRA_CONTEXT_JSON=$(echo "$JIRA_CONTEXT" | base64 --decode)
JIRA_CLOUD_ID=$(echo "$JIRA_CONTEXT_JSON" | jq -r '.cloud_id')
JIRA_USER_EMAIL=$(echo "$JIRA_CONTEXT_JSON" | jq -r '.user_email')
JIRA_API_TOKEN=$(echo "$JIRA_CONTEXT_JSON" | jq -r '.api_token')
JIRA_BASE_URL="https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}"

if [[ -z "$ISSUE_KEYS" ]]; then
echo "No issue keys provided. Skipping transition."
exit 0
fi

echo "Processing issue keys: $ISSUE_KEYS"

# Split comma-separated issue keys
IFS=',' read -ra KEYS <<< "$ISSUE_KEYS"

# Loop through each issue key and transition it
for issue_key in "${KEYS[@]}"; do
# Trim whitespace
issue_key=$(echo "$issue_key" | xargs)

if [[ -z "$issue_key" ]]; then
continue
fi

echo "Processing issue: $issue_key"

# Get available transitions for this issue
TRANSITIONS_URL="${JIRA_BASE_URL}/rest/api/3/issue/${issue_key}/transitions"

TRANSITIONS_FULL_RESPONSE=$(curl -s -u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
-X GET -H "Content-Type: application/json" \
-w '\n%{http_code}' \
"$TRANSITIONS_URL")

# Extract HTTP status code (last line) and response body (everything else)
TRANSITIONS_HTTP_CODE=$(echo "$TRANSITIONS_FULL_RESPONSE" | tail -n1)
TRANSITIONS_RESPONSE=$(echo "$TRANSITIONS_FULL_RESPONSE" | sed '$d')

# Check if the HTTP status code indicates success (2xx)
if [[ ! "$TRANSITIONS_HTTP_CODE" =~ ^2[0-9]{2}$ ]]; then
echo "Error getting transitions for issue $issue_key: HTTP $TRANSITIONS_HTTP_CODE"
if [[ -n "$TRANSITIONS_RESPONSE" ]]; then
echo "$TRANSITIONS_RESPONSE" | jq . 2>/dev/null || echo "$TRANSITIONS_RESPONSE"
fi
continue
fi

# Find the transition ID by matching the transition name (case-insensitive)
TRANSITION_ID=$(echo "$TRANSITIONS_RESPONSE" | \
jq -r --arg name "$TRANSITION_NAME" \
'.transitions[] | select(.name | ascii_downcase == ($name | ascii_downcase)) | .id')

if [[ -z "$TRANSITION_ID" || "$TRANSITION_ID" == "null" ]]; then
echo "Warning: Could not find transition '$TRANSITION_NAME' for issue $issue_key. It might already be in the target status or the transition is not available. Skipping."
continue
fi

echo "Found transition ID '$TRANSITION_ID' for issue $issue_key. Attempting to transition to '$TRANSITION_NAME'."

# Perform the transition
TRANSITION_URL="${JIRA_BASE_URL}/rest/api/3/issue/${issue_key}/transitions"

TRANSITION_FULL_RESULT=$(curl -s -u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
-X POST \
-H "Content-Type: application/json" \
-w '\n%{http_code}' \
--data "{\"transition\": {\"id\": \"$TRANSITION_ID\"}}" \
"$TRANSITION_URL")

# Extract HTTP status code (last line) and response body (everything else)
TRANSITION_HTTP_CODE=$(echo "$TRANSITION_FULL_RESULT" | tail -n1)
TRANSITION_RESULT=$(echo "$TRANSITION_FULL_RESULT" | sed '$d')

# Check if the HTTP status code indicates success (2xx)
if [[ ! "$TRANSITION_HTTP_CODE" =~ ^2[0-9]{2}$ ]]; then
echo "Error transitioning issue $issue_key: HTTP $TRANSITION_HTTP_CODE"
if [[ -n "$TRANSITION_RESULT" ]]; then
echo "$TRANSITION_RESULT" | jq . 2>/dev/null || echo "$TRANSITION_RESULT"
fi
else
echo "Successfully transitioned issue $issue_key to '$TRANSITION_NAME'."
fi
done

echo "JIRA transition process completed."
Loading
Loading