Skip to content

Commit 044079a

Browse files
canton-network-daDA Automation
andauthored
Update Splice from CCI (#403)
Signed-off-by: DA Automation <splice-maintainers@digitalasset.com> Co-authored-by: DA Automation <splice-maintainers@digitalasset.com>
1 parent d07f3b6 commit 044079a

File tree

372 files changed

+13447
-5624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

372 files changed

+13447
-5624
lines changed

.github/actions/scripts/notification-scripts/failure_github_issue.py

Lines changed: 25 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -3,220 +3,39 @@
33
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
44
# SPDX-License-Identifier: Apache-2.0
55

6-
import re
7-
from gql import gql, Client
8-
from gql.transport.requests import RequestsHTTPTransport
9-
from abc import ABC, abstractmethod
106
from failure_notification_args import FailureArgs
7+
import os
8+
from git import Repo
119

1210
GH_ORGANIZATION="DACH-NY"
1311
GH_REPO="cn-test-failures"
1412
GH_FAILURES_PROJECT=48
1513

16-
class ProjectFields:
17-
def __init__(self, data: list[dict[str, str]]):
18-
self.data = data
14+
class FailureGithubIssue():
1915

20-
def get_field_id(self, name: str) -> str:
21-
return [f for f in self.data if f["name"] == name][0]["id"]
22-
23-
class FailureGithubIssue(ABC):
24-
25-
26-
def get_repo_id(self, client: Client, organization: str, repo_name: str) -> str:
27-
query = gql(
28-
"""
29-
query getRepoId($owner: String!, $name: String!) {
30-
repository(owner: $owner, name: $name) {
31-
id
32-
}
33-
}
34-
"""
35-
)
36-
params = {
37-
"owner": organization,
38-
"name": repo_name
39-
}
40-
response = client.execute(query, params)
41-
return response["repository"]["id"]
42-
43-
def get_project_id(self, client: Client, organization: str, project_number: str) -> str:
44-
query = gql(
45-
"""
46-
query getProjectId($organization: String!, $projectNumber: Int!) {
47-
organization(login: $organization) {
48-
projectV2(number: $projectNumber) {
49-
id
50-
}
51-
}
52-
}
53-
"""
54-
)
55-
params = {
56-
"organization": organization,
57-
"projectNumber": project_number
58-
}
59-
response = client.execute(query, params)
60-
return response["organization"]["projectV2"]["id"]
61-
62-
def create_issue(self, client: Client, repo_id: str, title: str, body: str) -> str:
63-
query = gql(
64-
"""
65-
mutation createIssue($repositoryId: ID!, $title: String!, $body: String!) {
66-
createIssue(input: {
67-
repositoryId: $repositoryId,
68-
title: $title,
69-
body: $body
70-
}) {
71-
issue {
72-
id
73-
}
74-
}
75-
}
76-
"""
77-
)
78-
params = {
79-
"repositoryId": repo_id,
80-
"title": title,
81-
"body": body
82-
}
83-
response = client.execute(query, params)
84-
return response["createIssue"]["issue"]["id"]
85-
86-
def add_issue_to_project(self, client: Client, project_id: str, issue_id: str) -> str:
87-
query = gql(
88-
"""
89-
mutation addToProject($projectId: ID!, $contentId: ID!) {
90-
addProjectV2ItemById(input: {
91-
projectId: $projectId,
92-
contentId: $contentId
93-
}) { item { id } }
94-
}
95-
"""
96-
)
97-
params = {
98-
"projectId": project_id,
99-
"contentId": issue_id
100-
}
101-
response = client.execute(query, params)
102-
return response["addProjectV2ItemById"]["item"]["id"]
103-
104-
def get_issue_number(self, client: Client, issue_id: str) -> str:
105-
query = gql(
106-
"""
107-
query getIssueNumber($issueId: ID!) {
108-
node(id: $issueId) {
109-
... on Issue {
110-
number
111-
}
112-
}
113-
}
114-
"""
115-
)
116-
params = {
117-
"issueId": issue_id
118-
}
119-
response = client.execute(query, params)
120-
return response["node"]["number"]
121-
122-
def get_project_fields(self, client: Client, project_id: str) -> ProjectFields:
123-
query = gql(
124-
"""
125-
query getFields($projectId: ID!) {
126-
node(id: $projectId) {
127-
... on ProjectV2 {
128-
fields(first: 20) {
129-
nodes {
130-
... on ProjectV2FieldCommon {
131-
id
132-
name
133-
}
134-
}
135-
}
136-
}
137-
}
138-
}
139-
"""
140-
)
141-
params = {
142-
"projectId": project_id
143-
}
144-
response = client.execute(query, params)
145-
return ProjectFields(response["node"]["fields"]["nodes"])
146-
147-
def set_field_value(self, client: Client, project_id: str, item_id: str, field_id: str, value: str) -> str:
148-
query = gql(
149-
"""
150-
mutation setProjectItemValue($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: String!) {
151-
updateProjectV2ItemFieldValue(
152-
input: {
153-
projectId: $projectId
154-
itemId: $itemId
155-
fieldId: $fieldId
156-
value: {
157-
text: $value
158-
}
159-
}
160-
)
161-
{
162-
projectV2Item {
163-
id
164-
}
165-
}
166-
}
167-
168-
"""
169-
)
170-
171-
params = {
172-
"projectId": project_id,
173-
"itemId": item_id,
174-
"fieldId": field_id,
175-
"value": value
176-
}
177-
response = client.execute(query, params)
178-
return response["updateProjectV2ItemFieldValue"]["projectV2Item"]["id"]
179-
180-
@abstractmethod
18116
def get_msg(self) -> tuple[str, str]:
182-
pass
183-
184-
@abstractmethod
185-
def get_workflow_name(self) -> str:
186-
pass
187-
188-
def report_failure(self) -> str:
189-
if not re.match(self.args.branch_pattern, self.args.branch):
190-
print(f"Branch {self.args.branch} does not match pattern {self.args.branch_pattern}, skipping notification")
191-
exit(0)
192-
193-
(title, body) = self.get_msg()
194-
195-
if self.args.dry_run:
196-
print(f"Creating an issue with title: {title} and body: {body}")
197-
return
198-
199-
transport = RequestsHTTPTransport(url="https://api.github.com/graphql",
200-
headers={'Authorization': 'token ' + self.args.github_token})
201-
client = Client(transport=transport, fetch_schema_from_transport=True)
202-
203-
repo_id = self.get_repo_id(client, GH_ORGANIZATION, GH_REPO)
204-
project_id = self.get_project_id(client, GH_ORGANIZATION, GH_FAILURES_PROJECT)
205-
fields = self.get_project_fields(client, project_id)
206-
cluster_field_id = fields.get_field_id("Cluster")
207-
job_field_id = fields.get_field_id("Job")
208-
209-
issue_id = self.create_issue(client, repo_id, title, body)
210-
issue_project_item_id = self.add_issue_to_project(client, project_id, issue_id)
211-
if self.args.cluster:
212-
self.set_field_value(client, project_id, issue_project_item_id, cluster_field_id, self.args.cluster)
213-
workflow_name = self.get_workflow_name()
214-
self.set_field_value(client, project_id, issue_project_item_id, job_field_id, f"{workflow_name}:{self.args.job_name}")
215-
216-
issue_number = self.get_issue_number(client, issue_id)
217-
issue_url = f"https://github.com/{GH_ORGANIZATION}/{GH_REPO}/issues/{issue_number}"
218-
219-
return issue_url
17+
GITHUB_SERVER_URL=os.environ.get('GITHUB_SERVER_URL')
18+
GITHUB_REPOSITORY=os.environ.get('GITHUB_REPOSITORY')
19+
GIT_SHA=os.environ.get('GITHUB_SHA')
20+
branch_url=f"{GITHUB_SERVER_URL}/{GITHUB_REPOSITORY}/tree/{self.args.branch}"
21+
github_url=f"{GITHUB_SERVER_URL}/{GITHUB_REPOSITORY}/commit/{GIT_SHA}"
22+
# Unfortunately, github does not provide a way to get the job number, s.a. https://github.com/orgs/community/discussions/129314
23+
# so we can only link to the run, not directly to the failed job
24+
job_url=f"{GITHUB_SERVER_URL}/{GITHUB_REPOSITORY}/actions/runs/{self.args.gha_run_id}"
25+
repo = Repo(search_parent_directories=True)
26+
commit_msg = repo.head.commit.summary
27+
commit_sha_short = GIT_SHA[:7]
28+
29+
title = f"GHA Run {self.args.gha_run_id} : job {self.args.job_name} Failed :fire:"
30+
body = f"""
31+
[GitHub Actions Run]({job_url}).
32+
Branch: [{self.args.branch}]({branch_url})
33+
Workflow: {self.args.gha_workflow_name}
34+
Commit: [{commit_sha_short}]({github_url}) {commit_msg}
35+
Actor: {os.environ.get('GITHUB_ACTOR')}
36+
"""
37+
38+
return (title, body)
22039

22140
def __init__(self, args: FailureArgs):
22241
self.args = args

.github/actions/scripts/notification-scripts/failure_github_issue_gha.py

Lines changed: 0 additions & 35 deletions
This file was deleted.

.github/actions/scripts/notification-scripts/failure_notification.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,51 @@
33
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
44
# SPDX-License-Identifier: Apache-2.0
55

6-
from failure_github_issue import *
6+
from failure_github_issue import FailureGithubIssue
7+
from slack_notification import SlackNotification
78
from failure_notification_args import *
8-
from failure_github_issue_gha import *
9-
from slack_notification_gha import *
9+
import requests
10+
import sys
11+
import os
1012

1113
if __name__ == "__main__":
12-
print("Parsing arguments")
1314
args = parse_args()
14-
print(f"Creating issue with args {args}")
15-
gh_url = GHAFailureGithubIssue(args).report_failure()
16-
GHASlackNotification(args).send_notification(gh_url)
17-
print("Sent notification")
15+
16+
gh = FailureGithubIssue(args)
17+
(gh_title, gh_msg) = gh.get_msg()
18+
workflow_name = args.gha_workflow_name
19+
job_name = args.job_name
20+
21+
slack_msg = SlackNotification(args).build_msg("##GH_ISSUE##")
22+
channel = os.environ.get('NOTIFICATION_SLACK_CHANNEL')
23+
24+
token = os.environ.get('FAILURE_NOTIFICATIONS_TOKEN')
25+
if not token:
26+
print("FAILURE_NOTIFICATIONS_TOKEN is not set")
27+
sys.exit(1)
28+
29+
r = requests.post(
30+
url = os.environ.get('FAILURE_NOTIFICATIONS_URL'),
31+
headers = {
32+
"Authorization": f"Bearer {token}",
33+
},
34+
json={
35+
"issue": {
36+
"title": gh_title,
37+
"body": gh_msg,
38+
"workflow_name": workflow_name,
39+
"job_name": job_name,
40+
},
41+
"slack_notification": {
42+
"msg": slack_msg,
43+
"channel": channel,
44+
}
45+
},
46+
)
47+
if r.status_code != 200:
48+
print(f"Error sending notifications: {r.text}")
49+
sys.exit(1)
50+
else:
51+
print(f"Sent notification: {r.text}")
52+
sys.exit(0)
53+
Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,15 @@
11
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4-
import os
5-
import json
6-
import requests
7-
import sys
8-
import re
9-
from abc import ABC, abstractmethod
104
from failure_notification_args import FailureArgs
115

12-
class SlackNotification(ABC):
13-
@abstractmethod
6+
class SlackNotification():
147
def build_msg(self, gh_url: str):
15-
pass
16-
17-
def send_notification(self, gh_url: str):
18-
19-
if not re.match(self.args.branch_pattern, self.args.branch):
20-
print(f"Branch {self.args.branch} does not match pattern {self.args.branch_pattern}, skipping notification")
21-
sys.exit(0)
22-
23-
text = self.build_msg(gh_url)
24-
msg = {"text": text, "channel": self.args.slack_channel}
25-
26-
print(f"Notification: {json.dumps(msg)}")
27-
28-
if not self.args.dry_run:
29-
requests.post(
30-
"https://slack.com/api/chat.postMessage",
31-
json=msg,
32-
headers={
33-
"Authorization": f"Bearer {os.environ.get('SLACK_ACCESS_TOKEN')}",
34-
"Content-Type": "application/json"})
8+
cluster_text = f" on cluster {self.args.cluster}" if self.args.cluster else ""
9+
text=f"""*GHA job {self.args.gha_workflow_name}:{self.args.job_name} {self.args.gha_run_id} failed {cluster_text}:dumpster-fire:.
10+
(<{gh_url}|Issue in GitHub>)"""
3511

12+
return text
3613

3714
def __init__(self, args: FailureArgs):
3815
self.args = args
39-
40-

0 commit comments

Comments
 (0)