1
+ #! /bin/bash
2
+ set -e
3
+ set -u
4
+ set -o pipefail
5
+
6
+ # # DESCRIPTION:
7
+ # #
8
+ # Generic notifier for CI job failures that can create or update GitHub issues.
9
+ # # This script creates or updates GitHub issues when a jobs fail.
10
+ # # It checks for existing open failure issues and either creates
11
+ # # a new one or adds a comment to an existing one.
12
+ # #
13
+ # # PRE-REQS:
14
+ # #
15
+ # # This script assumes that the gh cli is installed and in the PATH
16
+ # # and that there is a GitHub PAT in the GITHUB_TOKEN env var
17
+ # # with the following permissions:
18
+ # # - issues (read/write)
19
+ # # or that the user is logged into the gh cli with an account with those permissions
20
+ # #
21
+ # # Usage examples:
22
+ # # ./dev/notify-fuzzing-failure.sh
23
+ # # ./dev/notify-fuzzing-failure.sh --title="Nightly Failure" --labels="area/testing,kind/bug"
24
+ # # ./dev/notify-fuzzing-failure.sh --test
25
+ # # Run this script locally like:
26
+ # # GITHUB_REPOSITORY="fork/hyperlight" GITHUB_RUN_ID=1 ./dev/notify-fuzzing-failure.sh --title="Nightly Failure" --labels="area/testing,kind/bug"
27
+
28
+ REPO=" ${GITHUB_REPOSITORY:- hyperlight-dev/ hyperlight} "
29
+ WORKFLOW_RUN_URL=" ${GITHUB_SERVER_URL:- https:// github.com} /${REPO} /actions/runs/${GITHUB_RUN_ID:- unknown} "
30
+ TEST_MODE=false
31
+ ISSUE_TITLE=" "
32
+ LABELS=" area/testing,kind/bug,area/fuzzing,lifecycle/needs-review"
33
+
34
+ for arg in " $@ " ; do
35
+ case $arg in
36
+ --test)
37
+ TEST_MODE=true
38
+ shift
39
+ ;;
40
+ --title=* )
41
+ ISSUE_TITLE=" ${arg#* =} "
42
+ shift
43
+ ;;
44
+ --labels=* )
45
+ LABELS=" ${arg#* =} "
46
+ shift
47
+ ;;
48
+ * )
49
+ esac
50
+ done
51
+
52
+ # Normalize labels into an array
53
+ IFS=' ,' read -r -a LABEL_ARRAY <<< " $LABELS"
54
+
55
+ # Choose a label to search existing issues for; prefer the first label if present
56
+ SEARCH_LABEL=" ${LABEL_ARRAY[0]:- area/ fuzzing} "
57
+
58
+ # Build issue title if not provided
59
+ if [ -z " $ISSUE_TITLE " ]; then
60
+ ISSUE_TITLE=" Job Failure - $( date ' +%Y-%m-%d' ) "
61
+ fi
62
+
63
+
64
+ if [ " $TEST_MODE " = true ]; then
65
+ echo " ✅ Running in test mode - script structure is valid"
66
+ echo " Would check for issues in $REPO "
67
+ echo " Workflow URL would be: $WORKFLOW_RUN_URL "
68
+ echo " Issue Title would be: $ISSUE_TITLE "
69
+ echo " Labels would be: $LABELS "
70
+ echo " Search Label would be: $SEARCH_LABEL "
71
+ exit 0
72
+ fi
73
+
74
+ # Extract owner and repo name from the repository
75
+ OWNER=$( echo " $REPO " | cut -d' /' -f1)
76
+ REPO_NAME=$( echo " $REPO " | cut -d' /' -f2)
77
+
78
+ echo " Checking for existing issues in $REPO with label '$SEARCH_LABEL '..."
79
+ EXISTING_ISSUES=$( gh api graphql -f query='
80
+ query($owner: String!, $repo: String!, $label: String!) {
81
+ repository(owner: $owner, name: $repo) {
82
+ issues(first: 10, states: OPEN, labels: [$label]) {
83
+ totalCount
84
+ nodes {
85
+ number
86
+ title
87
+ url
88
+ labels(first: 20) {
89
+ nodes {
90
+ name
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }' -f owner=" $OWNER " -f repo=" $REPO_NAME " -f label=" $SEARCH_LABEL " --jq ' .data.repository.issues' ) || EXISTING_ISSUES=" "
97
+
98
+ FUZZING_ISSUES=$( echo " $EXISTING_ISSUES " | jq ' .nodes[]' 2> /dev/null || echo " " )
99
+ FUZZING_ISSUE_COUNT=0
100
+ if [ -n " $FUZZING_ISSUES " ]; then
101
+ FUZZING_ISSUE_COUNT=$( echo " $FUZZING_ISSUES " | jq -s ' length' 2> /dev/null || echo " 0" )
102
+ fi
103
+
104
+ echo " Found $FUZZING_ISSUE_COUNT existing issue(s) matching label '$SEARCH_LABEL '"
105
+
106
+ if [ " $FUZZING_ISSUE_COUNT " -gt 0 ]; then
107
+ ISSUE_NUMBER=$( echo " $FUZZING_ISSUES " | jq -r ' .number' | head -1)
108
+ ISSUE_URL=$( echo " $FUZZING_ISSUES " | jq -r ' .url' | head -1)
109
+ if [ " $ISSUE_NUMBER " = " null" ] || [ -z " $ISSUE_NUMBER " ]; then
110
+ echo " ⚠️ Could not parse issue number from search results; will create a new issue"
111
+ FUZZING_ISSUE_COUNT=0
112
+ else
113
+ echo " Adding comment to existing issue #$ISSUE_NUMBER "
114
+ COMMENT_BODY=" ## Job Failed Again
115
+
116
+ **Date:** $( date ' +%Y-%m-%d %H:%M:%S UTC' )
117
+ **Workflow Run:** [$WORKFLOW_RUN_URL ]($WORKFLOW_RUN_URL )
118
+
119
+ The scheduled job has failed again. Please check the workflow logs and artifacts for details."
120
+
121
+ if gh issue comment " $ISSUE_NUMBER " --body " $COMMENT_BODY " --repo " $REPO " ; then
122
+ echo " ✅ Added comment to existing issue #$ISSUE_NUMBER : $ISSUE_URL "
123
+ exit 0
124
+ else
125
+ echo " ❌ Failed to add comment to existing issue. Will attempt to create a new issue instead."
126
+ FUZZING_ISSUE_COUNT=0
127
+ fi
128
+ fi
129
+ fi
130
+
131
+ if [ " $FUZZING_ISSUE_COUNT " -eq 0 ]; then
132
+ echo " No existing matching issues found. Creating a new issue..."
133
+
134
+ ISSUE_BODY=" ## Job Failure Report
135
+
136
+ **Date:** $( date ' +%Y-%m-%d %H:%M:%S UTC' )
137
+ **Workflow Run:** [$WORKFLOW_RUN_URL ]($WORKFLOW_RUN_URL )
138
+
139
+ ### Details
140
+ The scheduled job failed during execution. This issue was automatically created to track the failure. Please check the workflow logs and any uploaded artifacts for more details.
141
+
142
+ ### Next Steps
143
+ - [ ] Review the workflow logs for error details
144
+ - [ ] Download and analyze any crash artifacts if available
145
+ - [ ] Determine the root cause of the failure
146
+ - [ ] Fix the underlying issue
147
+
148
+ ---
149
+ *This issue was automatically created by the CI failure notification system.*"
150
+
151
+ # Build label args for gh issue create
152
+ LABEL_ARGS=()
153
+ for lbl in " ${LABEL_ARRAY[@]} " ; do
154
+ LABEL_ARGS+=(" --label" " $lbl " )
155
+ done
156
+
157
+ if ISSUE_URL=$( gh issue create \
158
+ --title " $ISSUE_TITLE " \
159
+ --body " $ISSUE_BODY " \
160
+ " ${LABEL_ARGS[@]} " \
161
+ --repo " $REPO " ) ; then
162
+ echo " ✅ Created new issue: $ISSUE_URL "
163
+ else
164
+ echo " ❌ Failed to create new issue"
165
+ exit 1
166
+ fi
167
+ fi
168
+
169
+ echo " Notification script completed successfully"
0 commit comments