Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/fix-issue/scripts/lib/platform-apple.sh
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ apply_tally() {
# Build errors with their file and line, which the result bundle keeps even when the log interleaves them.
report_build_errors() {
local errors
errors="$(python3 "$XCRESULT_CASES" errors "$1" | head -12)"
errors="$(python3 "$XCRESULT_CASES" errors --limit 12 "$1")"
[ -n "$errors" ] || return 1
note "errors:"
note "$(sed 's/^/ /' <<< "$errors")"
Expand Down
28 changes: 28 additions & 0 deletions scripts/lib/test_xcresult_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,34 @@ def test_no_build_results(self):
self.assertEqual(list(parser.build_errors(None)), [])


class ErrorsCommandTests(unittest.TestCase):
def setUp(self):
self.original = parser.xcresulttool
parser.xcresulttool = lambda bundle, *arguments: BROKEN_BUILD if arguments == ("build-results",) else None

def tearDown(self):
parser.xcresulttool = self.original

def errors(self, *arguments):
with redirect_stdout(io.StringIO()) as out:
code = parser.main(["errors", *arguments])
return code, out.getvalue().splitlines()

def test_prints_file_name_line_and_message(self):
self.assertEqual(self.errors("Build.xcresult"), (0, [
"Broken.swift:4: Cannot convert value of type 'String' to specified type 'Int'",
"Linker command failed with exit code 1",
]))

def test_limit_stops_after_n_errors(self):
self.assertEqual(self.errors("--limit", "1", "Build.xcresult"),
(0, ["Broken.swift:4: Cannot convert value of type 'String' to specified type 'Int'"]))

def test_a_bad_limit_is_a_usage_error(self):
with redirect_stderr(io.StringIO()):
self.assertEqual(self.errors("--limit", "x", "Build.xcresult")[0], 2)


class XunitTests(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
Expand Down
18 changes: 12 additions & 6 deletions scripts/lib/xcresult-cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Usage:
xcresult-cases.py tally [--filters <file>] <input>...
verify.sh's notes, then "<executed> <failed> <filters that ran nothing> <runner failures>"
xcresult-cases.py errors <bundle.xcresult>
build errors as "<file>:<line>: <message>"
xcresult-cases.py errors [--limit <n>] <bundle.xcresult>
the first n build errors as "<file>:<line>: <message>"
xcresult-cases.py summary <input>...
the CI step summary; exits 1 when a bundle or a swift test run executed no test,
which xcodebuild and swift test both call success
Expand All @@ -21,6 +21,7 @@
import urllib.parse
import xml.etree.ElementTree as ET
from collections import Counter, namedtuple
from itertools import islice
from pathlib import Path

Case = namedtuple("Case", "target suite name label result retried seconds message")
Expand Down Expand Up @@ -252,10 +253,15 @@ def main(arguments):
filters, rest = read_filters(rest[1]), rest[2:]
print("\n".join(tally([case for argument in rest for case in cases_of(argument)], filters)))
return 0
if command == "errors" and len(rest) == 1:
for where, message in build_errors(xcresulttool(Path(rest[0]), "build-results")):
print(f"{Path(where).name}: {message}" if where else message)
return 0
if command == "errors":
limit = None
if rest[:1] == ["--limit"] and len(rest) > 1 and rest[1].isdigit():
limit, rest = int(rest[1]), rest[2:]
if len(rest) == 1:
errors = islice(build_errors(xcresulttool(Path(rest[0]), "build-results")), limit)
for where, message in errors:
print(f"{Path(where).name}: {message}" if where else message)
return 0
if command == "summary":
destination = os.environ.get("GITHUB_STEP_SUMMARY")
if not destination:
Expand Down
Loading