Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ci): automate ticket creation on scheduled build failure #1876

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
142 changes: 142 additions & 0 deletions .github/actions/create-jira-ticket/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Workflow incident tracking
description: Create Jira ticket on incident

inputs:
jira_base_url:
required: true
description: jira base url
jira_user_email:
required: true
description: jira user email
jira_api_token:
required: true
description: jira api token
module_name:
required: true
description: module name
ticket_labels:
required: true
description: ticket labels, usually Pipeline + Nightly/Veracode + x
default: 'Pipeline'

runs:
using: "composite"
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0

- name: Get ticket elements from context
id: get_context
run: |
# Safely set/unset IFS in order to parse the table of labels properly
[ -n "${IFS+set}" ] && saved_IFS=$IFS
IFS=', ' read -a ticket_labels <<< $(echo "${{ inputs.ticket_labels }}" | tr -d "[],'")
unset IFS
[ -n "${saved_IFS+set}" ] && { IFS=$saved_IFS; unset saved_IFS; }
# Change context elements (summary, parent epic, etc.) that is checked depending on the ticket labels in the input
if [[ "${ticket_labels[@]}" =~ "Veracode" ]]; then
parent_epic_id=83818
parent_epic_key="AT-268"
ticket_summary="PR-${{ github.event.pull_request.number }} incident on ${{ inputs.module_name }}"
JSON_TEMPLATE_FILE="./.github/actions/create-jira-ticket/veracode-ticket-template.json"
sed -i \
-e 's|@PULL_REQUEST_NUMBER@|${{ github.event.pull_request.number }}|g' \
-e 's|@PULL_REQUEST_URL@|${{ github.event.pull_request.html_url }}|g' $JSON_TEMPLATE_FILE
elif [[ "${ticket_labels[@]}" =~ "Nightly" ]]; then
parent_epic_id=206242
parent_epic_key="MON-151547"
ticket_summary="$(date '+%Y-%m-%d') ${{ inputs.module_name }}-${{ github.ref_name }} nightly build failure"
JSON_TEMPLATE_FILE="./.github/actions/create-jira-ticket/nightly-ticket-template.json"
sed -i \
-e 's|@MODULE_NAME@|${{ inputs.module_name }}|g' \
-e "s|@DATE@|$(date '+%Y-%m-%d')|g" $JSON_TEMPLATE_FILE
else
echo "::error::Cannot find a valid labelling option for the ticket."
exit 1
fi
sed -i \
-e 's|@GITHUB_BRANCH@|${{ github.base_ref || github.ref_name }}|g' \
-e 's|@GITHUB_SERVER_URL@|${{ github.server_url }}|g' \
-e 's|@GITHUB_REPOSITORY@|${{ github.repository }}|g' \
-e 's|@GITHUB_RUN_ID@|${{ github.run_id }}|g' \
-e 's|@GITHUB_RUN_ATTEMPT@|${{ github.run_attempt }}|g' $JSON_TEMPLATE_FILE
echo "parent_epic_id=$parent_epic_id" >> $GITHUB_OUTPUT
echo "parent_epic_key=$parent_epic_key" >> $GITHUB_OUTPUT
echo "ticket_summary=$ticket_summary" >> $GITHUB_OUTPUT
echo "json_template_file=$JSON_TEMPLATE_FILE" >> $GITHUB_OUTPUT
cat $JSON_TEMPLATE_FILE
cat $GITHUB_OUTPUT
shell: bash
env:
GH_TOKEN: ${{ github.token }}

- name: Check if the ticket already exists
id: check_ticket
run: |
# Checking if an incident ticket already exists
response=$(curl \
--write-out "%{http_code}" \
--request POST \
--url "${{ inputs.jira_base_url }}/rest/api/3/search" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header "Accept:application/json" \
--header "Content-Type:application/json" \
--data '{
"fields": ["summary"],
"jql": "project = MON AND parentEpic = ${{ steps.get_context.outputs.parent_epic_key }} AND issueType = Technical AND summary ~ \"${{ steps.get_context.outputs.ticket_summary }}\" AND component = \"${{ inputs.module_name }}\" AND resolution = unresolved ORDER BY key ASC",
"maxResults": 1
}'
)
echo "[DEBUG] $response"
if [[ $(echo "$response" | tr -d '\n' | tail -c 3) -ne 200 ]]; then
echo "::error:: Jira API request was not completed properly."
fi
check_if_ticket_exists=$(echo "$response" | head -c -4 | jq .issues[0].key)
if [[ "$check_if_ticket_exists" != "null" ]]; then
echo "abort_ticket_creation=true" >> $GITHUB_ENV
echo "::error::ticket found as $check_if_ticket_exists aborting ticket creation"
fi
shell: bash

- name: Create Jira Issue
if: ${{ env.abort_ticket_creation != 'true' }}
run: |
# Creating a new incident ticket on Jira
DATA=$( cat <<-EOF
{
"fields": {
"summary": "${{ steps.get_context.outputs.ticket_summary }}",
"project": {"key": "MON"},
"issuetype": {"id": "10209"},
"parent": {"id": "${{ steps.get_context.outputs.parent_epic_id }}", "key": "${{ steps.get_context.outputs.parent_epic_key }}"},
"labels": ${{ inputs.ticket_labels }},
"components":[{"name": "${{ inputs.module_name }}"}],
"customfield_10902": {"id": "10524", "value": "DevSecOps"},
"customfield_10005": 1.0,
"description": $(cat ${{ steps.get_context.outputs.json_template_file }})
}
}
EOF
)
response=$(curl \
--request POST \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data "$DATA")
echo $response
if [ $? -ne 0 ]; then
echo "::error::Failed to create ticket: $response"
exit 1
fi
ticket_key=$(echo "$response" | jq -r .key)
echo "::notice::Created ticket: $ticket_key"
shell: bash
32 changes: 32 additions & 0 deletions .github/actions/create-jira-ticket/nightly-ticket-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": 1,
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This incident ticket relates to the @MODULE_NAME@ nightly on the @GITHUB_BRANCH@ branch which failed on @DATE@."
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Link to the failed nightly",
"marks": [
{
"type": "link",
"attrs": {
"href": "@GITHUB_SERVER_URL@/@GITHUB_REPOSITORY@/actions/runs/@GITHUB_RUN_ID@/attempts/@GITHUB_RUN_ATTEMPT@"
}
}
]
}
]
}
]
}
60 changes: 49 additions & 11 deletions .github/workflows/centreon-collect.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Centreon collect
run-name: |
${{
(github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_nightly == 'true'))
(github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.nightly_manual_trigger == 'true'))
&& format('collect nightly {0}', github.ref_name)
|| ''
}}
Expand All @@ -13,7 +13,7 @@ concurrency:
on:
workflow_dispatch:
inputs:
is_nightly:
nightly_manual_trigger:
description: 'Set to true for nightly run'
required: true
default: false
Expand Down Expand Up @@ -114,6 +114,7 @@ jobs:
uses: ./.github/workflows/get-environment.yml
with:
version_file: CMakeLists.txt
nightly_manual_trigger: ${{ inputs.nightly_manual_trigger }}

veracode-analysis:
needs: [get-environment]
Expand All @@ -134,12 +135,10 @@ jobs:
unit-test:
needs: [get-environment]
if: ${{ github.event.inputs.unit_tests == 'true' && ! contains(fromJson('["stable"]'), needs.get-environment.outputs.stability) }}

strategy:
fail-fast: false
matrix:
distrib: [alma8, alma9, debian-bookworm]

runs-on: [self-hosted, collect]
env:
SCCACHE_PATH: "/usr/bin/sccache"
Expand Down Expand Up @@ -293,12 +292,22 @@ jobs:
package_extension: ${{ matrix.package_extension }}
runner: ${{ matrix.runner }}
arch: ${{ matrix.arch }}
secrets: inherit

is_nightly: ${{ needs.get-environment.outputs.is_nightly }}
secrets:
collect_s3_access_key: ${{ secrets.COLLECT_S3_ACCESS_KEY }}
collect_s3_secret_key: ${{ secrets.COLLECT_S3_SECRET_KEY }}
registry_username: ${{ secrets.HARBOR_CENTREON_PULL_USERNAME }}
registry_password: ${{ secrets.HARBOR_CENTREON_PULL_TOKEN }}
rpm_gpg_key: ${{ secrets.RPM_GPG_SIGNING_KEY }}
rpm_gpg_signing_key_id: ${{ secrets.RPM_GPG_SIGNING_KEY_ID }}
rpm_gpg_signing_passphrase: ${{ secrets.RPM_GPG_SIGNING_PASSPHRASE }}
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
robot-test:
needs: [get-environment, package]
if: |
(github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_nightly == 'true')) &&
needs.get-environment.outputs.is_nightly == 'true' &&
! cancelled() &&
! contains(needs.*.result, 'failure') &&
! contains(needs.*.result, 'cancelled')
Expand Down Expand Up @@ -353,18 +362,21 @@ jobs:
image_test: ${{ matrix.image }}:${{ needs.get-environment.outputs.test_img_version }}
image_version: ${{ needs.get-environment.outputs.img_version }}
package_cache_key: ${{ github.run_id }}-${{ github.sha }}-${{ matrix.package_extension }}-centreon-collect-${{ matrix.distrib }}-${{ matrix.arch }}-${{ github.head_ref || github.ref_name }}
package_cache_path: ./*.${{ matrix.package_extension}}
package_cache_path: ./*.${{ matrix.package_extension }}
database_type: ${{ matrix.database_type }}
tests_params: ${{matrix.tests_params}}
test_group_name: ${{matrix.test_group_name}}
tests_params: ${{ matrix.tests_params }}
test_group_name: ${{ matrix.test_group_name }}
is_nightly: ${{ needs.get-environment.outputs.is_nightly }}
secrets:
registry_username: ${{ secrets.HARBOR_CENTREON_PULL_USERNAME }}
registry_password: ${{ secrets.HARBOR_CENTREON_PULL_TOKEN }}
collect_s3_access_key: ${{ secrets.COLLECT_S3_ACCESS_KEY }}
collect_s3_secret_key: ${{ secrets.COLLECT_S3_SECRET_KEY }}
xray_client_id: ${{ secrets.XRAY_CLIENT_ID }}
xray_client_secret: ${{ secrets.XRAY_CLIENT_SECRET }}

jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
deliver-sources:
runs-on: [self-hosted, common]
needs: [get-environment, package]
Expand Down Expand Up @@ -425,6 +437,19 @@ jobs:
release_type: ${{ needs.get-environment.outputs.release_type }}
is_cloud: ${{ needs.get-environment.outputs.is_cloud }}

- name: Create Jira ticket if nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "centreon-collect"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'

deliver-deb:
if: |
contains(fromJson('["unstable", "testing"]'), needs.get-environment.outputs.stability) &&
Expand Down Expand Up @@ -459,6 +484,19 @@ jobs:
release_type: ${{ needs.get-environment.outputs.release_type }}
is_cloud: ${{ needs.get-environment.outputs.is_cloud }}

- name: Create Jira ticket if nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "centreon-collect"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'

promote:
needs: [get-environment, deliver-rpm, deliver-deb]
if: |
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/get-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
required: false
type: string
default: CMakeLists.txt
nightly_manual_trigger:
required: false
type: boolean
outputs:
latest_major_version:
description: "latest major version"
Expand Down Expand Up @@ -33,6 +36,9 @@ on:
is_targeting_feature_branch:
description: "if it is a PR, check if targeting a feature branch"
value: ${{ jobs.get-environment.outputs.is_targeting_feature_branch }}
is_nightly:
description: "if the current workflow run is considered a nightly"
value: ${{ jobs.get-environment.outputs.is_nightly }}
img_version:
description: "docker image version (vcpkg checksum)"
value: ${{ jobs.get-environment.outputs.img_version }}
Expand All @@ -53,6 +59,7 @@ jobs:
target_stability: ${{ steps.get_stability.outputs.target_stability }}
release_type: ${{ steps.get_release_type.outputs.release_type }}
is_targeting_feature_branch: ${{ steps.get_stability.outputs.is_targeting_feature_branch }}
is_nightly: ${{ steps.get_nightly_status.outputs.is_nightly }}
img_version: ${{ steps.get_docker_images_version.outputs.img_version }}
test_img_version: ${{ steps.get_docker_images_version.outputs.test_img_version }}

Expand Down Expand Up @@ -226,6 +233,25 @@ jobs:

return false;

- name: Detect nightly status
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: get_nightly_status
env:
NIGHTLY_MANUAL_TRIGGER: ${{ inputs.nightly_manual_trigger }}
with:
script: |
const getNightlyInput = () => {
const nightly_manual_trigger = process.env.NIGHTLY_MANUAL_TRIGGER;
console.log(nightly_manual_trigger);
if (typeof nightly_manual_trigger === 'undefined' || nightly_manual_trigger === '') {
return 'false';
} else if (context.eventName === 'schedule' || context.eventName === 'workflow_dispatch' && nightly_manual_trigger === 'true' ) {
return 'true';
}
return 'false';
};
core.setOutput('is_nightly', getNightlyInput());

- name: Get docker images version
id: get_docker_images_version
run: |
Expand All @@ -251,6 +277,7 @@ jobs:
['stability', '${{ steps.get_stability.outputs.stability }}'],
['release_type', '${{ steps.get_release_type.outputs.release_type || '<em>not defined because this is not a release</em>' }}'],
['is_targeting_feature_branch', '${{ steps.get_stability.outputs.is_targeting_feature_branch }}'],
['is_nightly', '${{ steps.get_nightly_status.outputs.is_nightly }}'],
['img_version', '${{ steps.get_docker_images_version.outputs.img_version }}'],
['test_img_version', '${{ steps.get_docker_images_version.outputs.test_img_version }}'],
];
Expand Down
Loading