From a0339d805ec8b32ce335fe404c2f1786485ec31c Mon Sep 17 00:00:00 2001 From: Maud <110405507+mushroomempires@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:45:53 +0200 Subject: [PATCH] feat(ci-nightly): add nightly to centreon-plugins (#5640) --- .github/actions/create-jira-ticket/action.yml | 308 ++++++++++++++++++ .../nightly-ticket-template.json | 55 ++++ .github/workflows/get-environment.yml | 28 ++ .github/workflows/plugins.yml | 134 +++++++- 4 files changed, 507 insertions(+), 18 deletions(-) create mode 100644 .github/actions/create-jira-ticket/action.yml create mode 100644 .github/actions/create-jira-ticket/nightly-ticket-template.json diff --git a/.github/actions/create-jira-ticket/action.yml b/.github/actions/create-jira-ticket/action.yml new file mode 100644 index 000000000..f8400fbc1 --- /dev/null +++ b/.github/actions/create-jira-ticket/action.yml @@ -0,0 +1,308 @@ +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' + ticket_squad: + required: true + description: id of the squad to assign the ticket to + default: 'DevSecOps' + + +runs: + using: "composite" + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Get ticket elements from context + id: get_context + run: | + # Safely set/unset IFS in order to properly parse the table of labels + [ -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 the context elements (summary, parent epic, etc.) that are checked depending on these ticket labels + if [[ "${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 + + case "${{ inputs.ticket_squad }}" in + "DevSecOps") + ticket_squad_id=10524 + ticket_board_id=184 + squad_name="DEVSECOPS" + project_name="MON" + ;; + "Connectors") + ticket_squad_id=10504 + ticket_board_id=222 + squad_name="CONNECTORS" + project_name="CTOR" + *) + echo "::error::Cannot find a valid squad for value ${{ inputs.ticket_squad }}." + exit 1 + ;; + esac + + echo "Ticket will be assigned to the $squad_name team." + + current_sprint=$(curl --request GET \ + --url ${{ inputs.jira_base_url }}/rest/agile/1.0/board/$ticket_board_id/sprint?state=active \ + --user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \ + --header "Accept: application/json" | jq --arg squad_name "$squad_name" '.values[] | select(.name | test($squad_name; "i")) | .id') + + echo "[DEBUG] current_sprint: $current_sprint" + + # General updates on all template files + 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 "ticket_board_id=$ticket_board_id" >> $GITHUB_OUTPUT + echo "ticket_squad_id=$ticket_squad_id" >> $GITHUB_OUTPUT + echo "project_name=$project_name" >> $GITHUB_OUTPUT + echo "current_sprint=$current_sprint" >> $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 = ${{ steps.get_context.outputs.project_name }} 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 + + ticket_key=$(echo "$response" | head -c -4 | jq .issues[0].key | xargs) + if [[ "$ticket_key" != "null" ]]; then + echo "abort_ticket_creation=true" >> $GITHUB_ENV + echo "ticket_key=$ticket_key" >> $GITHUB_ENV + echo "::notice::ticket found as $ticket_key aborting ticket creation" + fi + shell: bash + + - name: Update existing nightly Jira ticket + if: | + env.abort_ticket_creation == 'true' && + contains(steps.get_context.outputs.parent_epic_key, 'MON-151547') + run: | + # Adding failed job labels for already existing ticket + [ -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; } + + for label in ${ticket_labels[@]}; do + response=$(curl \ + --request PUT \ + --url "${{ inputs.jira_base_url }}/rest/api/3/issue/${{ env.ticket_key }}" \ + --user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --data "{ \"update\": { \"labels\": [ { \"add\": \"$label\" } ] } }" + ) + done + + ticket_description=$(curl --request GET \ + --url "${{ inputs.jira_base_url }}/rest/api/3/issue/${{ env.ticket_key }}" \ + --user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \ + --header "Accept: application/json" | jq '.fields.description') + + mapfile -t jobs_failed < <(gh run view ${{ github.run_id }} --json jobs -q '.jobs[] | select(.conclusion == "failure") | .name') + echo "[DEBUG] - jobs failed for component ${FAILED_COMPONENTS[index]}: $jobs_failed" + + new_list_of_failed_jobs=$(for job in "${jobs_failed[@]}"; do + cat < { + const nightly_manual_trigger = process.env.NIGHTLY_MANUAL_TRIGGER; + console.log(nightly_manual_trigger); + if (typeof nightly_manual_trigger === 'undefined' || nightly_manual_trigger === '' || '${{ github.repository }}'.match(/^workflow-.*$/)) { + 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 version id: get_version uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -342,6 +369,7 @@ jobs: ['release_type', '${{ steps.get_release_type.outputs.release_type || 'not defined because this is not a release' }}'], ['is_targeting_feature_branch', '${{ steps.get_stability.outputs.is_targeting_feature_branch }}'], ['target_stability', '${{ steps.get_stability.outputs.target_stability || 'not defined because current run is not triggered by pull request event' }}'], + ['is_nightly', '${{ steps.get_nightly_status.outputs.is_nightly }}'], ['skip_workflow', '${{ steps.skip_workflow.outputs.result }}'], ['labels', '${{ steps.has_skip_label.outputs.labels }}'], ]; diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a4d4aa87c..66e62ec26 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -1,4 +1,5 @@ name: plugins +run-name: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.nightly_manual_trigger == 'true')) && format('plugins nightly {0}', github.ref_name) || '' }} concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -6,6 +7,14 @@ concurrency: on: workflow_dispatch: + inputs: + nightly_manual_trigger: + description: 'Set to true to trigger a nightly run' + required: true + default: false + type: boolean + schedule: + - cron: "30 1 * * 3" pull_request: paths: - '.github/workflows/plugins.yml' @@ -27,9 +36,42 @@ on: jobs: get-environment: uses: ./.github/workflows/get-environment.yml + with: + nightly_manual_trigger: ${{ inputs.nightly_manual_trigger || false }} + + changes: + needs: [get-environment] + runs-on: ubuntu-24.04 + outputs: + changes_common: ${{ steps.filter.outputs.common || 'true' }} + changes_packages: ${{ steps.filter.outputs.packages || 'false' }} + changes_plugins: ${{ steps.filter.outputs.plugins || 'false' }} + packages_files: ${{ steps.filter.outputs.packages_files }} + plugins_files: ${{ steps.filter.outputs.plugins_files }} + steps: + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + if: | + github.event_name == 'pull_request' && + contains(fromJson('["testing", "unstable", "canary"]'), needs.get-environment.outputs.stability) + with: + base: ${{ github.head_ref || github.ref_name }} + list-files: shell + filters: | + common: + - added|deleted|modified: src/centreon/** + - modified: .github/packaging/centreon-plugin.yaml.template + packages: + - added|modified: packaging/** + plugins: + - added|modified: src/** get-plugins: runs-on: ubuntu-24.04 + needs: [get-environment, changes] outputs: plugins: ${{ steps.get_plugins.outputs.plugins }} steps: @@ -41,24 +83,10 @@ jobs: with: python-version: '3.9' - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: filter - with: - base: ${{ github.ref }} - list-files: shell - filters: | - common: - - added|deleted|modified: src/centreon/** - - modified: .github/packaging/centreon-plugin.yaml.template - packages: - - added|modified: packaging/** - plugins: - - added|modified: src/** - - name: transform to directories run: | folders=() - for f in ${{ steps.filter.outputs.packages_files }}; do + for f in ${{ needs.changes.outputs.packages_files }}; do echo "Adding $(dirname $f) to folders" folders+=($(dirname $f)) done @@ -66,7 +94,7 @@ jobs: jq --compact-output --null-input '$ARGS.positional' --args -- ${unique_folders[@]} > package_directories.txt files=() - for f in ${{ steps.filter.outputs.plugins_files }}; do + for f in ${{ needs.changes.outputs.plugins_files }}; do echo "Adding $f to files" files+=($f) done @@ -76,9 +104,9 @@ jobs: - name: Get plugins for build id: get_plugins - if: ${{ steps.filter.outputs.common == 'true' || steps.filter.outputs.packages == 'true' || steps.filter.outputs.plugins == 'true' }} + if: ${{ needs.changes.outputs.changes_common == 'true' || needs.changes.outputs.changes_packages == 'true' || needs.changes.outputs.changes_plugins == 'true' }} run: | - PLUGINS="$(python3 .github/scripts/process-plugins.py '${{ steps.filter.outputs.common == 'true' }}')" + PLUGINS="$(python3 .github/scripts/process-plugins.py '${{ needs.changes.outputs.changes_common == 'true' }}')" echo "plugins=$(echo $PLUGINS)" >> $GITHUB_OUTPUT if [ "$PLUGINS" == '' ]; then @@ -87,6 +115,20 @@ jobs: shell: bash + - name: Create Jira ticket on 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: "monitoring-plugins" + ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]' + ticket_squad: "DevSecOps" + unit-tests: needs: [get-environment, get-plugins] if: | @@ -146,6 +188,20 @@ jobs: path: ./lastlog.jsonl retention-days: 1 + - name: Create Jira ticket on 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: "monitoring-plugins" + ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]' + ticket_squad: "Connectors" + fatpacker: needs: [get-environment, get-plugins, unit-tests] if: | @@ -305,6 +361,20 @@ jobs: rpm_gpg_signing_passphrase: ${{ secrets.RPM_GPG_SIGNING_PASSPHRASE }} stability: ${{ needs.get-environment.outputs.stability }} + - name: Create Jira ticket on 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: "monitoring-plugins" + ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]' + ticket_squad: "DevSecOps" + test-plugins: needs: [get-environment, get-plugins, package] if: | @@ -368,6 +438,20 @@ jobs: path: /var/log/robot-plugins-installation-tests.log retention-days: 1 + - name: Create Jira ticket on 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: "monitoring-plugins" + ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]' + ticket_squad: "Connectors" + deliver-packages: needs: [get-environment, get-plugins, test-plugins] if: | @@ -412,6 +496,20 @@ jobs: release_type: ${{ needs.get-environment.outputs.release_type }} artifactory_token: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }} + - name: Create Jira ticket on 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: "monitoring-plugins" + ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]' + ticket_squad: "DevSecOps" + deliver-sources: needs: [get-environment, fatpacker] if: |