Skip to content

Commit 4271e36

Browse files
Prajwal  Kiran KumarPrajwal  Kiran Kumar
authored andcommitted
Add slack notifications to backend tests
1 parent 3b09112 commit 4271e36

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

.github/slack-notify.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env python
2+
"""
3+
A script which runs as part of our GitHub Actions build to send notifications
4+
to Slack. Notifies when build status changes.
5+
Usage:
6+
./github-slack-notify.py $STATUS
7+
where $STATUS is the current build status.
8+
Requires GITHUB_TOKEN and SLACK_WEBHOOK_URL to be in the environment.
9+
"""
10+
import os
11+
import sys
12+
13+
import requests
14+
15+
16+
def statusemoji(status):
17+
return {"success": ":heavy_check_mark:", "failure": ":rotating_light:"}.get(
18+
status, ":warning:"
19+
)
20+
21+
22+
def get_message_title():
23+
return os.getenv("SLACK_MESSAGE_TITLE", "GitHub Actions Tests")
24+
25+
26+
def get_build_url(job_name, run_id):
27+
repo = os.environ["GITHUB_REPOSITORY"]
28+
token = os.environ["GITHUB_TOKEN"]
29+
response = requests.get(
30+
f"https://api.github.com/repos/"+str(repo)+"/actions/runs/" + str(run_id) +"/jobs",
31+
headers={
32+
"Authorization": f"Bearer {token}",
33+
"Accept": "application/vnd.github.v3+json",
34+
},
35+
)
36+
print("https://api.github.com/repos/"+str(repo)+"/actions/runs/" + str(run_id) +"/jobs", response.json())
37+
jobs = response.json()["jobs"]
38+
print(jobs)
39+
for job in jobs:
40+
if job_name in job["name"]:
41+
return job["html_url"]
42+
43+
44+
return "https://github.com/{GITHUB_REPOSITORY}/commit/{GITHUB_SHA}/checks".format(
45+
**os.environ
46+
)
47+
48+
49+
def is_pr_build():
50+
return os.environ["GITHUB_REF"].startswith("refs/pull/")
51+
52+
53+
def get_branchname():
54+
# split on slashes, strip 'refs/heads/*' , and rejoin
55+
# this is also the tag name if a tag is used
56+
return "/".join(os.environ["GITHUB_REF"].split("/")[2:])
57+
58+
59+
def _get_workflow_id_map():
60+
repo = os.environ["GITHUB_REPOSITORY"]
61+
token = os.environ["GITHUB_TOKEN"]
62+
r = requests.get(
63+
f"https://api.github.com/repos/{repo}/actions/workflows",
64+
headers={
65+
"Authorization": f"Bearer {token}",
66+
"Accept": "application/vnd.github.v3+json",
67+
},
68+
)
69+
70+
ret = {}
71+
for w in r.json().get("workflows", []):
72+
ret[w["name"]] = w["id"]
73+
print(f">> workflow IDs: {ret}")
74+
return ret
75+
76+
77+
def get_last_build_status():
78+
branch = get_branchname()
79+
repo = os.environ["GITHUB_REPOSITORY"]
80+
token = os.environ["GITHUB_TOKEN"]
81+
workflow_id = _get_workflow_id_map()[os.environ["GITHUB_WORKFLOW"]]
82+
83+
r = requests.get(
84+
f"https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/runs",
85+
params={"branch": branch, "status": "completed", "per_page": 1},
86+
headers={
87+
"Authorization": f"Bearer {token}",
88+
"Accept": "application/vnd.github.v3+json",
89+
},
90+
)
91+
print(f">> get past workflow runs params: branch={branch},repo={repo}")
92+
print(f">> get past workflow runs result: status={r.status_code}")
93+
runs_docs = r.json().get("workflow_runs", [])
94+
# no suitable status was found for a previous build, so the status is "None"
95+
if not runs_docs:
96+
print(">>> no previous run found for workflow")
97+
return None
98+
conclusion = runs_docs[0]["conclusion"]
99+
print(f">>> previous run found with conclusion={conclusion}")
100+
return conclusion
101+
102+
103+
def check_status_changed(status):
104+
# NOTE: last_status==None is always considered a change. This is intentional
105+
last_status = get_last_build_status()
106+
res = last_status != status
107+
if res:
108+
print(f"status change detected (old={last_status}, new={status})")
109+
else:
110+
print(f"no status change detected (old={last_status}, new={status})")
111+
return res
112+
113+
114+
def get_failure_message():
115+
return os.getenv("SLACK_FAILURE_MESSAGE", "tests failed")
116+
117+
118+
def build_payload(status, job_name, run_id):
119+
context = f"{statusemoji(status)} build for {get_branchname()}: {status}"
120+
message = f"<{get_build_url(job_name, run_id)}|{get_message_title()}>"
121+
if "fail" in status.lower():
122+
message = f"{message}: {get_failure_message()}"
123+
return {
124+
"channel": os.getenv("SLACK_CHANNEL", "#servicex-github"),
125+
"username": "Prajwal Kiran Kumar",
126+
"blocks": [
127+
{"type": "section", "text": {"type": "mrkdwn", "text": message}},
128+
{"type": "context", "elements": [{"type": "mrkdwn", "text": context}]},
129+
],
130+
}
131+
132+
133+
def on_main_repo():
134+
"""check if running from a fork"""
135+
res = os.environ["GITHUB_REPOSITORY"].lower() == "ssl-hep/servicex-backend-tests"
136+
print(f"Checking main repo: {res}")
137+
return res
138+
139+
140+
def should_notify(status):
141+
res = check_status_changed(status) and on_main_repo() and not is_pr_build()
142+
print(f"Should notify: {res}")
143+
return res
144+
145+
146+
147+
def main():
148+
status = sys.argv[1]
149+
job_name = sys.argv[2]
150+
run_id = sys.argv[3]
151+
152+
if should_notify(status):
153+
r = requests.post(os.environ["SLACK_WEBHOOK_URL"], json=build_payload(status, job_name, run_id))
154+
print(f">> webhook response: status={r.status_code}")
155+
156+
if __name__ == "__main__":
157+
main()

.github/workflows/daily_servicex_uproot_test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,17 @@ jobs:
3434
TOKEN: ${{ secrets.SECRET_TESTING1_UPROOT_TOKEN }}
3535
run: |
3636
source test_uproot.sh $TOKEN https://servicex-release-int-uproot.servicex.ssl-hep.org
37+
outputs:
38+
job_name: ${{ github.job }}
39+
run_id: ${{ github.run_id }}
40+
41+
call-slack-notify:
42+
uses: ./.github/workflows/slack_notify.yml
43+
if: always()
44+
needs: [ run-tests ]
45+
secrets: inherit
46+
with:
47+
result: ${{ needs.run-tests.result }}
48+
job_name: ${{ needs.run-tests.outputs.job_name }}
49+
run_id: ${{ needs.run-tests.outputs.run_id }}
50+
slack_message_title: 'Daily Automated Tests - Uproot River Testing'

.github/workflows/daily_servicex_uproot_test_af.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,17 @@ jobs:
3232
TOKEN: ${{ secrets.SECRET_AF_UPROOT_TOKEN }}
3333
run: |
3434
source test_uproot.sh $TOKEN https://uproot-atlas.servicex.af.uchicago.edu/
35+
outputs:
36+
job_name: ${{ github.job }}
37+
run_id: ${{ github.run_id }}
38+
39+
call-slack-notify:
40+
uses: ./.github/workflows/slack_notify.yml
41+
if: always()
42+
needs: [ build ]
43+
secrets: inherit
44+
with:
45+
result: ${{ needs.build.result }}
46+
job_name: ${{ needs.build.outputs.job_name }}
47+
run_id: ${{ needs.build.outputs.run_id }}
48+
slack_message_title: 'Daily Automated Tests - Uproot AF Testing'

.github/workflows/daily_servicex_xaod_test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,17 @@ jobs:
3434
TOKEN: ${{ secrets.SECRET_TESTING2_XAOD_TOKEN }}
3535
run: |
3636
source test_xaod.sh $TOKEN https://servicex-release-int-xaod.servicex.ssl-hep.org
37+
outputs:
38+
job_name: ${{ github.job }}
39+
run_id: ${{ github.run_id }}
40+
41+
call-slack-notify:
42+
uses: ./.github/workflows/slack_notify.yml
43+
if: always()
44+
needs: [ run-tests ]
45+
secrets: inherit
46+
with:
47+
result: ${{ needs.run-tests.result }}
48+
job_name: ${{ needs.run-tests.outputs.job_name }}
49+
run_id: ${{ needs.run-tests.outputs.run_id }}
50+
slack_message_title: 'Daily Automated Tests - xAOD River Testing'

.github/workflows/daily_servicex_xaod_test_af.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,18 @@ jobs:
3131
TOKEN: ${{ secrets.SECRET_AF_XAOD_TOKEN }}
3232
run: |
3333
source test_xaod.sh $TOKEN https://xaod.servicex.af.uchicago.edu/
34+
outputs:
35+
job_name: ${{ github.job }}
36+
run_id: ${{ github.run_id }}
37+
38+
call-slack-notify:
39+
uses: ./.github/workflows/slack_notify.yml
40+
if: always()
41+
needs: [ build ]
42+
secrets: inherit
43+
with:
44+
result: ${{ needs.build.result }}
45+
job_name: ${{ needs.build.outputs.job_name }}
46+
run_id: ${{ needs.build.outputs.run_id }}
47+
slack_message_title: 'Daily Automated Tests - xAOD AF Testing'
48+

.github/workflows/slack_notify.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
name: Slack Notify
3+
4+
on:
5+
workflow_call:
6+
inputs:
7+
result:
8+
required: true
9+
type: string
10+
job_name:
11+
required: true
12+
type: string
13+
run_id:
14+
required: true
15+
type: string
16+
slack_message_title:
17+
required: true
18+
type: string
19+
20+
jobs:
21+
slack-notify:
22+
if: always()
23+
timeout-minutes: 10
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v2
27+
- uses: actions/setup-python@v2
28+
- run: python -m pip install -U requests
29+
- name: notify slack
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
33+
SLACK_CHANNEL: '#servicex-github'
34+
SLACK_MESSAGE_TITLE: ${{ inputs.slack_message_title }}
35+
SLACK_FAILURE_MESSAGE: 'Daily run failed'
36+
run: python ./.github/slack-notify.py ${{ inputs.result }} ${{ inputs.job_name }} ${{ inputs.run_id }}
37+

0 commit comments

Comments
 (0)