Skip to content

Commit

Permalink
feat(ci): automate ticket creation on scheduled build failure (#1876)
Browse files Browse the repository at this point in the history
* incorporate action from web + 'backport' get-environment is_nightly management

* add nightly creation ticket blocks in collect

* remove useless github.base_ref reference

* rewrite secret inheritance on package-collect

* fix missing secrets

* fix lint
  • Loading branch information
mushroomempires authored Dec 3, 2024
1 parent 8307521 commit 18fd906
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 22 deletions.
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 @@ -116,6 +116,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 @@ -136,12 +137,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 @@ -290,12 +289,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 @@ -350,18 +359,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 @@ -422,6 +434,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 @@ -456,6 +481,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

0 comments on commit 18fd906

Please sign in to comment.