-
Notifications
You must be signed in to change notification settings - Fork 851
Expand file tree
/
Copy pathcode-format-helper.py
More file actions
196 lines (158 loc) · 5.49 KB
/
code-format-helper.py
File metadata and controls
196 lines (158 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env python3
#
# ====- code-format-helper, runs code formatters from the ci --*- python -*--==#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# ==-------------------------------------------------------------------------==#
import argparse
import os
import subprocess
import sys
from functools import cached_property
import github
from github import IssueComment, PullRequest
class FormatHelper:
COMMENT_TAG = "<!--LLVM CODE FORMAT COMMENT: {fmt}-->"
name = "unknown"
@property
def comment_tag(self) -> str:
return self.COMMENT_TAG.replace("fmt", self.name)
def format_run(self, changed_files: [str], args: argparse.Namespace) -> str | None:
pass
def pr_comment_text(self, diff: str) -> str:
return f"""
{self.comment_tag}
:warning: {self.friendly_name}, {self.name} found issues in your code. :warning:
<details>
<summary>
You can test this locally with the following command:
</summary>
``````````bash
{self.instructions}
``````````
</details>
<details>
<summary>
View the diff from {self.name} here.
</summary>
``````````diff
{diff}
``````````
</details>
- [ ] Check this box to apply formatting changes to this branch."""
def find_comment(
self, pr: PullRequest.PullRequest
) -> IssueComment.IssueComment | None:
for comment in pr.as_issue().get_comments():
if self.comment_tag in comment.body:
return comment
return None
def update_pr(self, diff: str, args: argparse.Namespace):
repo = github.Github(args.token).get_repo(args.repo)
pr = repo.get_issue(args.issue_number).as_pull_request()
existing_comment = self.find_comment(pr)
pr_text = self.pr_comment_text(diff)
if existing_comment:
existing_comment.edit(pr_text)
else:
pr.as_issue().create_comment(pr_text)
def update_pr_success(self, args: argparse.Namespace):
repo = github.Github(args.token).get_repo(args.repo)
pr = repo.get_issue(args.issue_number).as_pull_request()
existing_comment = self.find_comment(pr)
if existing_comment:
existing_comment.edit(
f"""
{self.comment_tag}
:white_check_mark: With the latest revision this PR passed the {self.friendly_name}.
"""
)
def run(self, changed_files: [str], args: argparse.Namespace):
diff = self.format_run(changed_files, args)
if diff:
self.update_pr(diff, args)
return False
else:
self.update_pr_success(args)
return True
class ClangFormatHelper(FormatHelper):
name = "clang-format"
friendly_name = "C/C++ code formatter"
@property
def instructions(self):
return " ".join(self.cf_cmd)
@cached_property
def libcxx_excluded_files(self):
return [] # HLSL Change - libcxx is not in DXC's repo
#with open("libcxx/utils/data/ignore_format.txt", "r") as ifd:
# return [excl.strip() for excl in ifd.readlines()]
def should_be_excluded(self, path: str) -> bool:
if path in self.libcxx_excluded_files:
print(f"Excluding file {path}")
return True
return False
def filter_changed_files(self, changed_files: [str]) -> [str]:
filtered_files = []
for path in changed_files:
_, ext = os.path.splitext(path)
if ext in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx"):
if not self.should_be_excluded(path):
filtered_files.append(path)
return filtered_files
def format_run(self, changed_files: [str], args: argparse.Namespace) -> str | None:
cpp_files = self.filter_changed_files(changed_files)
if not cpp_files:
return
cf_cmd = [
"git-clang-format",
"--diff",
args.start_rev,
args.end_rev,
"--",
] + cpp_files
print(f"Running: {' '.join(cf_cmd)}")
self.cf_cmd = cf_cmd
proc = subprocess.run(cf_cmd, capture_output=True)
# formatting needed
if proc.returncode == 1:
return proc.stdout.decode("utf-8")
return None
ALL_FORMATTERS = (ClangFormatHelper(),)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--token", type=str, required=True, help="GitHub authentiation token"
)
parser.add_argument(
"--repo",
type=str,
default=os.getenv("GITHUB_REPOSITORY", "llvm/llvm-project"),
help="The GitHub repository that we are working with in the form of <owner>/<repo> (e.g. llvm/llvm-project)",
)
parser.add_argument("--issue-number", type=int, required=True)
parser.add_argument(
"--start-rev",
type=str,
required=True,
help="Compute changes from this revision.",
)
parser.add_argument(
"--end-rev", type=str, required=True, help="Compute changes to this revision"
)
parser.add_argument(
"--changed-files",
type=str,
help="Comma separated list of files that has been changed",
)
args = parser.parse_args()
changed_files = []
if args.changed_files:
changed_files = args.changed_files.split(",")
exit_code = 0
for fmt in ALL_FORMATTERS:
if not fmt.run(changed_files, args):
exit_code = 1
sys.exit(exit_code)