Skip to content

Commit 850bd55

Browse files
authored
ci: cap build errors in the parser instead of piping into head (#3490)
1 parent 7ca208f commit 850bd55

3 files changed

Lines changed: 41 additions & 7 deletions

File tree

‎.claude/skills/fix-issue/scripts/lib/platform-apple.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ apply_tally() {
223223
# Build errors with their file and line, which the result bundle keeps even when the log interleaves them.
224224
report_build_errors() {
225225
local errors
226-
errors="$(python3 "$XCRESULT_CASES" errors "$1" | head -12)"
226+
errors="$(python3 "$XCRESULT_CASES" errors --limit 12 "$1")"
227227
[ -n "$errors" ] || return 1
228228
note "errors:"
229229
note "$(sed 's/^/ /' <<< "$errors")"

‎scripts/lib/test_xcresult_cases.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,34 @@ def test_no_build_results(self):
175175
self.assertEqual(list(parser.build_errors(None)), [])
176176

177177

178+
class ErrorsCommandTests(unittest.TestCase):
179+
def setUp(self):
180+
self.original = parser.xcresulttool
181+
parser.xcresulttool = lambda bundle, *arguments: BROKEN_BUILD if arguments == ("build-results",) else None
182+
183+
def tearDown(self):
184+
parser.xcresulttool = self.original
185+
186+
def errors(self, *arguments):
187+
with redirect_stdout(io.StringIO()) as out:
188+
code = parser.main(["errors", *arguments])
189+
return code, out.getvalue().splitlines()
190+
191+
def test_prints_file_name_line_and_message(self):
192+
self.assertEqual(self.errors("Build.xcresult"), (0, [
193+
"Broken.swift:4: Cannot convert value of type 'String' to specified type 'Int'",
194+
"Linker command failed with exit code 1",
195+
]))
196+
197+
def test_limit_stops_after_n_errors(self):
198+
self.assertEqual(self.errors("--limit", "1", "Build.xcresult"),
199+
(0, ["Broken.swift:4: Cannot convert value of type 'String' to specified type 'Int'"]))
200+
201+
def test_a_bad_limit_is_a_usage_error(self):
202+
with redirect_stderr(io.StringIO()):
203+
self.assertEqual(self.errors("--limit", "x", "Build.xcresult")[0], 2)
204+
205+
178206
class XunitTests(unittest.TestCase):
179207
def setUp(self):
180208
self.directory = tempfile.TemporaryDirectory()

‎scripts/lib/xcresult-cases.py‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
Usage:
55
xcresult-cases.py tally [--filters <file>] <input>...
66
verify.sh's notes, then "<executed> <failed> <filters that ran nothing> <runner failures>"
7-
xcresult-cases.py errors <bundle.xcresult>
8-
build errors as "<file>:<line>: <message>"
7+
xcresult-cases.py errors [--limit <n>] <bundle.xcresult>
8+
the first n build errors as "<file>:<line>: <message>"
99
xcresult-cases.py summary <input>...
1010
the CI step summary; exits 1 when a bundle or a swift test run executed no test,
1111
which xcodebuild and swift test both call success
@@ -21,6 +21,7 @@
2121
import urllib.parse
2222
import xml.etree.ElementTree as ET
2323
from collections import Counter, namedtuple
24+
from itertools import islice
2425
from pathlib import Path
2526

2627
Case = namedtuple("Case", "target suite name label result retried seconds message")
@@ -252,10 +253,15 @@ def main(arguments):
252253
filters, rest = read_filters(rest[1]), rest[2:]
253254
print("\n".join(tally([case for argument in rest for case in cases_of(argument)], filters)))
254255
return 0
255-
if command == "errors" and len(rest) == 1:
256-
for where, message in build_errors(xcresulttool(Path(rest[0]), "build-results")):
257-
print(f"{Path(where).name}: {message}" if where else message)
258-
return 0
256+
if command == "errors":
257+
limit = None
258+
if rest[:1] == ["--limit"] and len(rest) > 1 and rest[1].isdigit():
259+
limit, rest = int(rest[1]), rest[2:]
260+
if len(rest) == 1:
261+
errors = islice(build_errors(xcresulttool(Path(rest[0]), "build-results")), limit)
262+
for where, message in errors:
263+
print(f"{Path(where).name}: {message}" if where else message)
264+
return 0
259265
if command == "summary":
260266
destination = os.environ.get("GITHUB_STEP_SUMMARY")
261267
if not destination:

0 commit comments

Comments
 (0)