Skip to content
Open
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
161 changes: 161 additions & 0 deletions .github/workflows/reusable-jira-pr_actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# This workflow will comment the PR with the JIRA issue summary
# if a JIRA issue number is detected in the branch name or title

name: Jira issue jobs

on:
workflow_call:
secrets:
JIRA_BASE_URL:
required: true
description: The base URL of your JIRA instance
JIRA_USER_EMAIL:
required: true
description: The email address for your JIRA account
JIRA_API_TOKEN:
required: true
description: The API token for your JIRA account

env:
JIRA_PATTERN: "([A-Z]+)[-# ]*([0-9]+)"
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}

jobs:
get_issue_key:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
issue_key_from_branch: steps.jira_key_from_branch.output.issue_key
issue_key_from_title: steps.jira_key_from_title.output.issue_key

steps:
- name: Find JIRA issue key from branch
id: jira_key_from_branch
env:
HEAD_REF: ${{ github.head_ref }}
run: >
echo "issue_key=$(
echo $HEAD_REF | grep -osiE "$JIRA_PATTERN"
| head -n1 | sed -E "s/\W*$JIRA_PATTERN/\1-\2/i"
| tr [:lower:] [:upper:]
)" >> $GITHUB_OUTPUT

- name: Find JIRA issue key from title
id: jira_key_from_title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >
echo "issue_key=$(
echo $PR_TITLE | grep -osiE "^\s*\[?\s*$JIRA_PATTERN"
| head -n1 | sed -E "s/\W*$JIRA_PATTERN/\1-\2/i"
| tr [:lower:] [:upper:]
)" >> $GITHUB_OUTPUT

add_jira_summary:
if: ${{ github.event.action == 'opened' }}
runs-on: ubuntu-latest
timeout-minutes: 20
needs: get_issue_key

steps:

- name: Get JIRA summary from branch
if: ${{ needs.get_issue_key.outputs.issue_key_from_branch }}
id: jira_summary_from_branch
run: >
curl -sS -X GET
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}"
-H "Content-Type: application/json"
"$JIRA_BASE_URL/rest/api/2/issue/${{ needs.get_issue_key.outputs.issue_key_from_branch }}"
| echo "summary=$(jq -r '.fields.summary // empty' | xargs)" >> $GITHUB_OUTPUT

- name: Get JIRA summary from title
if: ${{ !steps.jira_summary_from_branch.outputs.summary && needs.get_issue_key.outputs.issue_key_from_title }}
id: jira_summary_from_title
run: >
curl -sS -X GET
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}"
-H "Content-Type: application/json"
"$JIRA_BASE_URL/rest/api/2/issue/${{ needs.get_issue_key.outputs.issue_key_from_title }}"
| echo "summary=$(jq -r '.fields.summary // empty' | xargs)" >> $GITHUB_OUTPUT

- name: Extract PR title
id: get_pr_title
if: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "text=$(echo $PR_TITLE | sed -E "s/^\s*[?[A-Z]+[-# ]*[0-9]+]?[-: ]*(.*)/\1/i")" >> $GITHUB_OUTPUT

- name: Add comment
if: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
env:
ISSUE_SUMMARY: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
ISSUE_KEY: ${{ steps.jira_summary_from_branch.outputs.summary && steps.jira_key_from_branch.outputs.issue_key || steps.jira_key_from_title.outputs.issue_key }}
TITLE_TEXT: ${{ steps.get_pr_title.outputs.text }}
PR_BODY: ${{ github.event.pull_request.body }}
run: >
jq
--arg ISSUE_ID "$(cat <<< $ISSUE_KEY)"
--arg ISSUE_SUMMARY "$(cat <<< $ISSUE_SUMMARY)"
--arg TITLE_TEXT "$(cat <<< ${TITLE_TEXT:-$ISSUE_SUMMARY})"
--arg PR_BODY "$(cat <<< $PR_BODY)"
-c '{"title": ($ISSUE_ID + ": " + $TITLE_TEXT), "body": ("**" + $ISSUE_ID + " - " + $ISSUE_SUMMARY + "**\n" + $PR_BODY)}' <<< {}
| curl -sS -X POST -d @-
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}"
-H "Content-Type: application/json"
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}"
> /dev/null

check_jira_issue:
runs-on: ubuntu-latest
timeout-minutes: 20
needs: get_issue_key

- name: Check issue key
if: ${{ !needs.get_issue_key.outputs.issue_key_from_title && !needs.get_issue_key.outputs.issue_key_from_branch }}
run: |
echo "Could not determine Jira issue"

- name: Check issue status
if: ${{ needs.get_issue_key.outputs.issue_key_from_title || needs.get_issue_key.outputs.issue_key_from_branch }}
run: |
issue_key=${{ needs.get_issue_key.output.issue_key_from_title }}
if [ -z "$issue_key" ]; then
issue_key=${{ needs.get_issue_key.output.issue_key_from_branch }}
fi
jql="Sprint in (openSprints(), futureSprints()) and issue=$issue_key"
jqlencoded=$(jq -R -r @uri <<<"$jql")
response=$(curl -s --request GET \
--url "$JIRA_BASE_URL/rest/api/3/search/jql?jql=$jqlencoded&fields=statusCategory" \
--user "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json')

if [ $? -ne 0 ]; then
echo "Jira API: error"
exit 1
fi

echo "response was: $(echo $response|jq)"
error=""

echo "Checking if issue is in open or future sprint"
sprint=$(echo $response | jq '.issues | length')
if [ $sprint -eq 0 ]; then
error="Error: Jira issue is not in open or future sprint"
fi

echo "Checking if issue is In Progress"
status=$(echo $response| jq -r '.issues[0].fields.statusCategory.name')
if [ "$status" != "In Progress" ]; then
error="$error Error: Jira issue is not In Progress"
fi

if [[ ! -z "$error" ]]; then
echo $error
exit 1
fi

1 change: 1 addition & 0 deletions .github/workflows/reusable-jira-pr_add_jira_summary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,83 +19,84 @@

jobs:
add_jira_summary:
if: ${{ github.event.action == 'opened' }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
JIRA_PATTERN: "([A-Z]+)[-# ]*([0-9]+)"
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}

steps:

- name: Find JIRA issue key from branch
id: jira_key_from_branch
env:
HEAD_REF: ${{ github.head_ref }}
run: >
echo "issue_key=$(
echo $HEAD_REF | grep -osiE "$JIRA_PATTERN"
| head -n1 | sed -E "s/\W*$JIRA_PATTERN/\1-\2/i"
| tr [:lower:] [:upper:]
)" >> $GITHUB_OUTPUT

- name: Get JIRA summary from branch
if: ${{ steps.jira_key_from_branch.outputs.issue_key }}
id: jira_summary_from_branch
run: >
curl -sS -X GET
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}"
-H "Content-Type: application/json"
"$JIRA_BASE_URL/rest/api/2/issue/${{ steps.jira_key_from_branch.outputs.issue_key }}"
| echo "summary=$(jq -r '.fields.summary // empty' | xargs)" >> $GITHUB_OUTPUT

- name: Find JIRA issue key from title
id: jira_key_from_title
if: ${{ !steps.jira_summary_from_branch.outputs.summary }}
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >
echo "issue_key=$(
echo $PR_TITLE | grep -osiE "^\s*\[?\s*$JIRA_PATTERN"
| head -n1 | sed -E "s/\W*$JIRA_PATTERN/\1-\2/i"
| tr [:lower:] [:upper:]
)" >> $GITHUB_OUTPUT

- name: Get JIRA summary from title
if: ${{ steps.jira_key_from_title.outputs.issue_key }}
id: jira_summary_from_title
run: >
curl -sS -X GET
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}"
-H "Content-Type: application/json"
"$JIRA_BASE_URL/rest/api/2/issue/${{ steps.jira_key_from_title.outputs.issue_key }}"
| echo "summary=$(jq -r '.fields.summary // empty' | xargs)" >> $GITHUB_OUTPUT

- name: Extract PR title
id: get_pr_title
if: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "text=$(echo $PR_TITLE | sed -E "s/^\s*[?[A-Z]+[-# ]*[0-9]+]?[-: ]*(.*)/\1/i")" >> $GITHUB_OUTPUT

- name: Add comment
if: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
env:
ISSUE_SUMMARY: ${{ steps.jira_summary_from_branch.outputs.summary || steps.jira_summary_from_title.outputs.summary }}
ISSUE_KEY: ${{ steps.jira_summary_from_branch.outputs.summary && steps.jira_key_from_branch.outputs.issue_key || steps.jira_key_from_title.outputs.issue_key }}
TITLE_TEXT: ${{ steps.get_pr_title.outputs.text }}
PR_BODY: ${{ github.event.pull_request.body }}
run: >
jq
--arg ISSUE_ID "$(cat <<< $ISSUE_KEY)"
--arg ISSUE_SUMMARY "$(cat <<< $ISSUE_SUMMARY)"
--arg TITLE_TEXT "$(cat <<< ${TITLE_TEXT:-$ISSUE_SUMMARY})"
--arg PR_BODY "$(cat <<< $PR_BODY)"
-c '{"title": ($ISSUE_ID + ": " + $TITLE_TEXT), "body": ("**" + $ISSUE_ID + " - " + $ISSUE_SUMMARY + "**\n" + $PR_BODY)}' <<< {}
| curl -sS -X POST -d @-
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}"
-H "Content-Type: application/json"
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}"
> /dev/null

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
85 changes: 85 additions & 0 deletions .github/workflows/reusable-jira-pr_check_issue.yml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might run too soon. Somehow, we want it executed after pr_add_jira_summary
Possibly just have "Check issue status" as an extra step in pr_add_jira_summary.
However, "Check issue status" must reran at every push (e.g. to error out when pushing to a closed issue).

My suggesting: merge the two workflow into one "Check Jira Status", with "Check JIRA issue status" step last:

  • if trigger by pull_request[opened], run all steps
  • if trigger is pull_request[synchronize, reopened, ready_for_review], only run steps:
    • Find JIRA issue key from title
    • Check JIRA issue status

Not finding JIRA from title is not an error.
Possible, use a secret to configure the pattern of supported key, e.g. "\b(NI|SHRUB|BEAST|AARG|SWALLOW|TIM|BRIDGE)[-# ]*([0-9]+)"

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# This workflow will comment the PR with the JIRA issue summary
# if a JIRA issue number is detected in the branch name or title

name: Check Jira Issue

on:
workflow_call:
secrets:
JIRA_BASE_URL:
required: true
description: The base URL of your JIRA instance
JIRA_USER_EMAIL:
required: true
description: The email address for your JIRA account
JIRA_API_TOKEN:
required: true
description: The API token for your JIRA account


jobs:
check_jira_issue:
runs-on: ubuntu-latest
timeout-minutes: 20
env:
JIRA_PATTERN: "([A-Z]+)[-# ]*([0-9]+)"
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}

steps:

- name: Find JIRA issue key from title
id: jira_key_from_title
if: ${{ !steps.jira_summary_from_branch.outputs.summary }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: jira_summary_from_branch step does not exist in this workflow

env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >
echo "issue_key=$(
echo $PR_TITLE | grep -osiE "^\s*\[?\s*$JIRA_PATTERN"
| head -n1 | sed -E "s/\W*$JIRA_PATTERN/\1-\2/i"
| tr [:lower:] [:upper:]
)" >> $GITHUB_OUTPUT

- name: Check issue key
if: ${{ !steps.jira_key_from_title.outputs.issue_key }}
run: |
echo "Could not determine Jira issue from PR title"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably TODO: add exceptions for Dependabot etc PRs.

exit 1

- name: Check issue status
if: ${{ steps.jira_key_from_title.outputs.issue_key }}
run: |
jql="Sprint in (openSprints(), futureSprints()) and issue=${{ steps.jira_key_from_title.outputs.issue_key }}"
jqlencoded=$(jq -R -r @uri <<<"$jql")
response=$(curl -s --request GET \
--url "$JIRA_BASE_URL/rest/api/3/search/jql?jql=$jqlencoded&fields=statusCategory" \
--user "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json')

if [ $? -ne 0 ]; then
echo "Jira API: error"
exit 1
fi

echo "response was: $(echo $response|jq)"
error=""

echo "Checking if issue is in open or future sprint"
sprint=$(echo $response | jq '.issues | length')
if [ $sprint -eq 0 ]; then
error="Error: Jira issue is not in open or future sprint"
fi

echo "Checking if issue is In Progress"
status=$(echo $response| jq -r '.issues[0].fields.statusCategory.name')
if [ "$status" != "In Progress" ]; then
error="$error Error: Jira issue is not In Progress"
fi

if [[ ! -z "$error" ]]; then
echo $error
exit 1
fi

Loading