mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-23 22:01:14 -05:00
ci: notify with GH issue + project item on e2e failure (#2607)
Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com> Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
This commit is contained in:
parent
284c7e99d1
commit
ed22137edb
64
.github/actions/gh_create_issue/action.yml
vendored
Normal file
64
.github/actions/gh_create_issue/action.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: Create a GitHub issue
|
||||||
|
description: "Create an issue on GitHub, and optionally add it to a project board."
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
title:
|
||||||
|
description: "The title of the issue."
|
||||||
|
required: true
|
||||||
|
owner:
|
||||||
|
description: "The owner of the repository to create the issue in."
|
||||||
|
required: false
|
||||||
|
default: ${{ github.repository_owner }}
|
||||||
|
repo:
|
||||||
|
description: "The repository to create the issue in."
|
||||||
|
required: false
|
||||||
|
default: ${{ github.repository }}
|
||||||
|
token:
|
||||||
|
description: "The GitHub token to use to authenticate."
|
||||||
|
required: false
|
||||||
|
default: ${{ github.token }}
|
||||||
|
body:
|
||||||
|
description: "The body of the issue."
|
||||||
|
required: false
|
||||||
|
body-file:
|
||||||
|
description: "The absolute path to a file containing the body of the issue."
|
||||||
|
required: false
|
||||||
|
assignee:
|
||||||
|
description: "The GitHub username to assign the issue to."
|
||||||
|
required: false
|
||||||
|
label:
|
||||||
|
description: "A comma-separated list of labels to add to the issue."
|
||||||
|
required: false
|
||||||
|
milestone:
|
||||||
|
description: "The milestone to add the issue to."
|
||||||
|
required: false
|
||||||
|
project:
|
||||||
|
description: "Number of the project to add the issue to."
|
||||||
|
required: false
|
||||||
|
template:
|
||||||
|
description: "The template to use for the issue."
|
||||||
|
required: false
|
||||||
|
fields:
|
||||||
|
description: "A JSON object containing the fields to use for the issue."
|
||||||
|
required: false
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
issue-url:
|
||||||
|
description: "The URL of the created issue."
|
||||||
|
value: ${{ steps.run.outputs.issue-url }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Run create_issue.sh
|
||||||
|
id: run
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ inputs.token }}
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
cat << EOF | tee inputs.json
|
||||||
|
${{ toJSON(inputs) }}
|
||||||
|
EOF
|
||||||
|
out=$(./.github/actions/gh_create_issue/create_issue.sh inputs.json)
|
||||||
|
echo "issue-url=${out}" | tee -a "$GITHUB_OUTPUT"
|
248
.github/actions/gh_create_issue/create_issue.sh
vendored
Executable file
248
.github/actions/gh_create_issue/create_issue.sh
vendored
Executable file
@ -0,0 +1,248 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
function debug() {
|
||||||
|
echo "DEBUG: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
function warn() {
|
||||||
|
echo "WARN: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputs() {
|
||||||
|
name="${1}"
|
||||||
|
local val
|
||||||
|
val=$(jq -r ".\"${name}\"" "${inputFile}")
|
||||||
|
if [[ ${val} == "null" ]]; then
|
||||||
|
warn "Input ${name} not found in ${inputFile}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "${val}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function flagsFromInput() {
|
||||||
|
flagNames=("${@}")
|
||||||
|
for name in "${flagNames[@]}"; do
|
||||||
|
val=$(inputs "${name}")
|
||||||
|
if [[ -n ${val} ]]; then
|
||||||
|
echo "--${name}=${val}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function createIssue() {
|
||||||
|
flags=(
|
||||||
|
"assignee"
|
||||||
|
"body"
|
||||||
|
"body-file"
|
||||||
|
"label"
|
||||||
|
"milestone"
|
||||||
|
"project"
|
||||||
|
"template"
|
||||||
|
"title"
|
||||||
|
)
|
||||||
|
readarray -t flags <<< "$(flagsFromInput "${flags[@]}")"
|
||||||
|
flags+=("--repo=$(inputs owner)/$(inputs repo)")
|
||||||
|
debug gh issue create "${flags[@]}"
|
||||||
|
gh issue create "${flags[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function listProjects() {
|
||||||
|
flags=(
|
||||||
|
"owner"
|
||||||
|
)
|
||||||
|
readarray -t flags <<< "$(flagsFromInput "${flags[@]}")"
|
||||||
|
flags+=("--format=json")
|
||||||
|
debug gh project list "${flags[@]}"
|
||||||
|
gh project list "${flags[@]}" >> projects.json
|
||||||
|
}
|
||||||
|
|
||||||
|
function findProjectID() {
|
||||||
|
project=$(inputs "project")
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg project "${project}" \
|
||||||
|
'.projects[]
|
||||||
|
| select(.title == $project)
|
||||||
|
| .id' \
|
||||||
|
projects.json
|
||||||
|
)"
|
||||||
|
debug "Project ID: ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function findProjectNo() {
|
||||||
|
project=$(inputs "project")
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg project "${project}" \
|
||||||
|
'.projects[]
|
||||||
|
| select(.title == $project)
|
||||||
|
| .number' \
|
||||||
|
projects.json
|
||||||
|
)"
|
||||||
|
debug "Project Number: ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function listItems() {
|
||||||
|
local projectNo="${1}"
|
||||||
|
flags=(
|
||||||
|
"owner"
|
||||||
|
)
|
||||||
|
readarray -t flags <<< "$(flagsFromInput "${flags[@]}")"
|
||||||
|
flags+=("--limit=1000")
|
||||||
|
flags+=("--format=json")
|
||||||
|
debug gh project item-list "${flags[@]}" "${projectNo}"
|
||||||
|
gh project item-list "${flags[@]}" "${projectNo}" >> issues.json
|
||||||
|
}
|
||||||
|
|
||||||
|
function findIssueItemID() {
|
||||||
|
local issueURL="${1}"
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg issueURL "${issueURL}" \
|
||||||
|
'.items[]
|
||||||
|
| select(.content.url == $issueURL)
|
||||||
|
| .id' \
|
||||||
|
issues.json
|
||||||
|
)"
|
||||||
|
debug "Issue Item ID: ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function listFields() {
|
||||||
|
local projectNo="${1}"
|
||||||
|
flags=(
|
||||||
|
"owner"
|
||||||
|
)
|
||||||
|
readarray -t flags <<< "$(flagsFromInput "${flags[@]}")"
|
||||||
|
flags+=("--limit=1000")
|
||||||
|
flags+=("--format=json")
|
||||||
|
debug gh project field-list "${flags[@]}" "${projectNo}"
|
||||||
|
gh project field-list "${flags[@]}" "${projectNo}" >> fields.json
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFieldID() {
|
||||||
|
local fieldName="${1}"
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg fieldName "${fieldName}" \
|
||||||
|
'.fields[]
|
||||||
|
| select(.name == $fieldName)
|
||||||
|
| .id' \
|
||||||
|
fields.json
|
||||||
|
)"
|
||||||
|
debug "Field ID of '${fieldName}': ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function findSelectFieldID() {
|
||||||
|
local fieldName="${1}"
|
||||||
|
local fieldValue="${2}"
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg fieldName "${fieldName}" \
|
||||||
|
--arg fieldValue "${fieldValue}" \
|
||||||
|
'.fields[]
|
||||||
|
| select(.name == $fieldName)
|
||||||
|
| .options[]
|
||||||
|
| select(.name == $fieldValue)
|
||||||
|
| .id' \
|
||||||
|
fields.json
|
||||||
|
)"
|
||||||
|
debug "Field ID of '${fieldName}': ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFieldType() {
|
||||||
|
local fieldName="${1}"
|
||||||
|
out="$(
|
||||||
|
jq -r \
|
||||||
|
--arg fieldName "${fieldName}" \
|
||||||
|
'.fields[]
|
||||||
|
| select(.name == $fieldName)
|
||||||
|
| .type' \
|
||||||
|
fields.json
|
||||||
|
)"
|
||||||
|
debug "Field type of '${fieldName}': ${out}"
|
||||||
|
echo "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function editItem() {
|
||||||
|
local projectID="${1}"
|
||||||
|
local itemID="${2}"
|
||||||
|
local id="${3}"
|
||||||
|
local value="${4}"
|
||||||
|
flags=(
|
||||||
|
"--project-id=${projectID}"
|
||||||
|
"--id=${itemID}"
|
||||||
|
"--field-id=${id}"
|
||||||
|
"--text=${value}"
|
||||||
|
)
|
||||||
|
debug gh project item-edit "${flags[@]}"
|
||||||
|
gh project item-edit "${flags[@]}" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFields() {
|
||||||
|
local projectID="${1}"
|
||||||
|
local itemID="${2}"
|
||||||
|
|
||||||
|
fieldsLen="$(jq -r '.fields' "${inputFile}" | yq 'length')"
|
||||||
|
debug "Number of fields in input: ${fieldsLen}"
|
||||||
|
for ((i = 0; i < fieldsLen; i++)); do
|
||||||
|
name="$(jq -r '.fields' "${inputFile}" |
|
||||||
|
yq "to_entries | .[${i}].key")"
|
||||||
|
value="$(jq -r '.fields' "${inputFile}" |
|
||||||
|
yq "to_entries | .[${i}].value")"
|
||||||
|
debug "Field ${i}: ${name} = ${value}"
|
||||||
|
type=$(findFieldType "${name}")
|
||||||
|
|
||||||
|
case "${type}" in
|
||||||
|
"ProjectV2Field")
|
||||||
|
id=$(findFieldID "${name}")
|
||||||
|
;;
|
||||||
|
"ProjectV2SingleSelectField")
|
||||||
|
id=$(findSelectFieldID "${name}" "${value}")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
warn "Unknown field type: ${type}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
editItem "${projectID}" "${itemID}" "${id}" "${value}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
inputFile="$(realpath "${1}")"
|
||||||
|
|
||||||
|
workdir=$(mktemp -d)
|
||||||
|
pushd "${workdir}" > /dev/null
|
||||||
|
trap 'debug "not cleaning up, working directory at: ${workdir}"' ERR
|
||||||
|
|
||||||
|
issueURL=$(createIssue)
|
||||||
|
echo "${issueURL}"
|
||||||
|
|
||||||
|
project=$(inputs "project")
|
||||||
|
if [[ -z ${project} ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
listProjects
|
||||||
|
projectNo=$(findProjectNo)
|
||||||
|
projectID=$(findProjectID)
|
||||||
|
|
||||||
|
listItems "${projectNo}"
|
||||||
|
issueItemID=$(findIssueItemID "${issueURL}")
|
||||||
|
listFields "${projectNo}"
|
||||||
|
|
||||||
|
setFields "${projectID}" "${issueItemID}"
|
||||||
|
|
||||||
|
popd > /dev/null
|
||||||
|
rm -rf "${workdir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
56
.github/actions/notify_e2e_failure/action.yml
vendored
56
.github/actions/notify_e2e_failure/action.yml
vendored
@ -31,6 +31,52 @@ runs:
|
|||||||
id: pick-assignee
|
id: pick-assignee
|
||||||
uses: ./.github/actions/pick_assignee
|
uses: ./.github/actions/pick_assignee
|
||||||
|
|
||||||
|
- name: Get the current date
|
||||||
|
id: date
|
||||||
|
shell: bash
|
||||||
|
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create body template
|
||||||
|
id: body-template
|
||||||
|
run: |
|
||||||
|
# TODO(katexochen): add job number when possible
|
||||||
|
jobURL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
|
# TODO(msanft): Add Self-managed param once logcollection is fixed.
|
||||||
|
opensearchURL="https://search-e2e-logs-y46renozy42lcojbvrt3qq7csm.eu-central-1.es.amazonaws.com/_dashboards/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-7d,to:now))&_a=(columns:!(metadata.name,systemd.unit,kubernetes.pod_name,message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',key:metadata.github.e2e-test-provider,negate:!f,params:(query:${{ inputs.provider }}),type:phrase),query:(match_phrase:(metadata.github.e2e-test-provider:${{ inputs.provider }}))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',key:metadata.github.run-id,negate:!f,params:(query:${{ github.run_id }}),type:phrase),query:(match_phrase:(metadata.github.run-id:${{ github.run_id }}))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',key:metadata.github.ref-stream.keyword,negate:!f,params:(query:'${{ inputs.refStream }}'),type:phrase),query:(match_phrase:(metadata.github.ref-stream.keyword:'${{ inputs.refStream }}'))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',key:metadata.github.kubernetes-version.keyword,negate:!f,params:(query:'${{ inputs.kubernetesVersion }}'),type:phrase),query:(match_phrase:(metadata.github.kubernetes-version.keyword:'${{ inputs.kubernetesVersion }}'))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',key:metadata.github.e2e-test-payload,negate:!f,params:(query:'${{ inputs.test }}'),type:phrase),query:(match_phrase:(metadata.github.e2e-test-payload:'${{ inputs.test }}')))),index:'74517cf0-6442-11ed-acf1-47dda8fdfbbb',interval:auto,query:(language:kuery,query:''),sort:!())"
|
||||||
|
cat << EOF > header.md
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
* [Job URL](${jobURL})
|
||||||
|
* [OpenSearch URL](${opensearchURL// /%20})
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat header.md .github/failure_project_template.md > body.md
|
||||||
|
echo "BODY_PATH=$(pwd)/body.md" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: ./.github/actions/gh_create_issue
|
||||||
|
id: gh_create_issue
|
||||||
|
with:
|
||||||
|
title: "${{ env.CURRENT_DATE }}"
|
||||||
|
body-file: ${{ env.BODY_PATH }}
|
||||||
|
repo: issues
|
||||||
|
label: "e2e failure"
|
||||||
|
assignee: ${{ steps.pick-assignee.outputs.assignee }}
|
||||||
|
project: Constellation bugs
|
||||||
|
fields: |
|
||||||
|
Status: New failures
|
||||||
|
workflow: ${{ github.workflow }}
|
||||||
|
kubernetesVersion: ${{ inputs.kubernetesVersion }}
|
||||||
|
cloudProvider: ${{ inputs.provider }}
|
||||||
|
test: ${{ inputs.test }}
|
||||||
|
refStream: ${{ inputs.refStream }}
|
||||||
|
token: ${{ inputs.projectWriteToken }}
|
||||||
|
|
||||||
|
- name: Issue URL ${{ steps.gh_create_issue.outputs.issue-url }}
|
||||||
|
shell: bash
|
||||||
|
run: echo ${{ steps.gh_create_issue.outputs.issue-url }}
|
||||||
|
|
||||||
- name: Create project card in case of failure
|
- name: Create project card in case of failure
|
||||||
id: create-project-card
|
id: create-project-card
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@ -112,13 +158,3 @@ runs:
|
|||||||
|
|
||||||
echo "additionalFields=$(cat facts.json)" | tee -a "$GITHUB_OUTPUT"
|
echo "additionalFields=$(cat facts.json)" | tee -a "$GITHUB_OUTPUT"
|
||||||
echo "additionalButtons=$buttons" | tee -a "$GITHUB_OUTPUT"
|
echo "additionalButtons=$buttons" | tee -a "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Notify teams channel
|
|
||||||
continue-on-error: true
|
|
||||||
uses: ./.github/actions/notify_teams
|
|
||||||
with:
|
|
||||||
teamsWebhookURI: ${{ inputs.teamsWebhookUri }}
|
|
||||||
title: "Constellation E2E test failed"
|
|
||||||
assignee: ${{ steps.pick-assignee.outputs.assignee }}
|
|
||||||
additionalFields: ${{ steps.create-fields.outputs.additionalFields }}
|
|
||||||
additionalButtons: ${{ steps.create-fields.outputs.additionalButtons }}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user