codetool-shell 0.1.1__py3-none-win_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codetool_shell/__init__.py +11 -0
- codetool_shell/api.py +59 -0
- codetool_shell/bin/windows-arm64/codetool-shell-rust.exe +0 -0
- codetool_shell/filters/__init__.py +14 -0
- codetool_shell/filters/build_compiler/__init__.py +7 -0
- codetool_shell/filters/build_compiler/detector.py +412 -0
- codetool_shell/filters/build_compiler/reducer.py +166 -0
- codetool_shell/filters/build_compiler/summary.py +617 -0
- codetool_shell/filters/ci_job_log/__init__.py +7 -0
- codetool_shell/filters/ci_job_log/detector.py +64 -0
- codetool_shell/filters/ci_job_log/reducer.py +99 -0
- codetool_shell/filters/ci_job_log/summary.py +243 -0
- codetool_shell/filters/diff/__init__.py +7 -0
- codetool_shell/filters/diff/detector.py +136 -0
- codetool_shell/filters/diff/reducer.py +308 -0
- codetool_shell/filters/generic_log/__init__.py +7 -0
- codetool_shell/filters/generic_log/detector.py +175 -0
- codetool_shell/filters/generic_log/reducer.py +99 -0
- codetool_shell/filters/generic_log/summary.py +161 -0
- codetool_shell/filters/git.py +514 -0
- codetool_shell/filters/html_cleanup/__init__.py +7 -0
- codetool_shell/filters/html_cleanup/detector.py +136 -0
- codetool_shell/filters/html_cleanup/reducer.py +27 -0
- codetool_shell/filters/html_cleanup/summary.py +422 -0
- codetool_shell/filters/json_payload/__init__.py +7 -0
- codetool_shell/filters/json_payload/detector.py +62 -0
- codetool_shell/filters/json_payload/reducer.py +81 -0
- codetool_shell/filters/json_payload/summary.py +233 -0
- codetool_shell/filters/listing/__init__.py +7 -0
- codetool_shell/filters/listing/detector.py +294 -0
- codetool_shell/filters/listing/reducer.py +30 -0
- codetool_shell/filters/log_template/__init__.py +7 -0
- codetool_shell/filters/log_template/constants.py +76 -0
- codetool_shell/filters/log_template/detector.py +331 -0
- codetool_shell/filters/log_template/reducer.py +78 -0
- codetool_shell/filters/log_template/template.py +280 -0
- codetool_shell/filters/log_template/types.py +21 -0
- codetool_shell/filters/opaque_payload/__init__.py +7 -0
- codetool_shell/filters/opaque_payload/detector.py +563 -0
- codetool_shell/filters/opaque_payload/reducer.py +142 -0
- codetool_shell/filters/opaque_payload/summary.py +61 -0
- codetool_shell/filters/package_manager/__init__.py +7 -0
- codetool_shell/filters/package_manager/detector.py +220 -0
- codetool_shell/filters/package_manager/reducer.py +110 -0
- codetool_shell/filters/package_manager/summary.py +172 -0
- codetool_shell/filters/pipeline.py +65 -0
- codetool_shell/filters/rg.py +250 -0
- codetool_shell/filters/system_output/__init__.py +7 -0
- codetool_shell/filters/system_output/detector.py +600 -0
- codetool_shell/filters/system_output/reducer.py +331 -0
- codetool_shell/filters/system_output/summary.py +164 -0
- codetool_shell/filters/table/__init__.py +7 -0
- codetool_shell/filters/table/detector.py +244 -0
- codetool_shell/filters/table/reducer.py +57 -0
- codetool_shell/filters/table/summary.py +37 -0
- codetool_shell/filters/test_runner/__init__.py +7 -0
- codetool_shell/filters/test_runner/ansi.py +80 -0
- codetool_shell/filters/test_runner/detector.py +409 -0
- codetool_shell/filters/test_runner/reducer.py +288 -0
- codetool_shell/filters/test_runner/summary.py +449 -0
- codetool_shell/filters/text.py +38 -0
- codetool_shell/filters/traceback/__init__.py +7 -0
- codetool_shell/filters/traceback/detector.py +209 -0
- codetool_shell/filters/traceback/reducer.py +141 -0
- codetool_shell/filters/traceback/summary.py +122 -0
- codetool_shell/filters/tree.py +59 -0
- codetool_shell/py.typed +0 -0
- codetool_shell/python_backend.py +38 -0
- codetool_shell/rust_backend.py +254 -0
- codetool_shell-0.1.1.dist-info/METADATA +152 -0
- codetool_shell-0.1.1.dist-info/RECORD +72 -0
- codetool_shell-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
"""Line classification helpers for build/compiler diagnostics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]")
|
|
8
|
+
_RUST_DIAGNOSTIC_RE = re.compile(r"^\s*(?:error|warning)(?:\[[A-Za-z0-9_]+\])?:")
|
|
9
|
+
_RUST_SPAN_RE = re.compile(r"^\s*-->\s+\S+:\d+:\d+")
|
|
10
|
+
_RUST_SOURCE_BAR_RE = re.compile(r"^\s*(?:\d+\s+)?\|")
|
|
11
|
+
_TSC_PARENS_RE = re.compile(
|
|
12
|
+
r"^\S.*\.(?:ts|tsx|js|jsx)\(\d+,\d+\):\s+(?:error|warning)\s+TS\d+:",
|
|
13
|
+
re.IGNORECASE,
|
|
14
|
+
)
|
|
15
|
+
_TSC_PRETTY_RE = re.compile(
|
|
16
|
+
r"^\S.*\.(?:ts|tsx|js|jsx):\d+:\d+\s+-\s+(?:error|warning)\s+TS\d+:",
|
|
17
|
+
re.IGNORECASE,
|
|
18
|
+
)
|
|
19
|
+
_ESLINT_ISSUE_RE = re.compile(
|
|
20
|
+
r"^\s*\d+:\d+\s+(?:error|warning)\s+.+\s+[@A-Za-z0-9_./-]+$",
|
|
21
|
+
re.IGNORECASE,
|
|
22
|
+
)
|
|
23
|
+
_ESLINT_COMPACT_RE = re.compile(
|
|
24
|
+
r"^\S.*\.(?:ts|tsx|js|jsx):\d+:\d+:\s+(?:error|warning)\s+.+\s+[@A-Za-z0-9_./-]+$",
|
|
25
|
+
re.IGNORECASE,
|
|
26
|
+
)
|
|
27
|
+
_ESLINT_FILE_HEADER_RE = re.compile(r"^(?:\.{0,2}/|/|[A-Za-z]:[\\/]|~?/)?\S+\.(?:js|jsx|ts|tsx)$")
|
|
28
|
+
_ESLINT_SUMMARY_RE = re.compile(r"\b\d+\s+problems?\b|\b\d+\s+errors?\b.*\b\d+\s+warnings?\b", re.I)
|
|
29
|
+
_MYPY_RE = re.compile(
|
|
30
|
+
r"^(?P<path>.+?\.pyi?):(?P<location>\d+(?::\d+)?):\s+"
|
|
31
|
+
r"(?P<detail>(?:error|warning|note):\s+.+)$",
|
|
32
|
+
re.IGNORECASE,
|
|
33
|
+
)
|
|
34
|
+
_RUFF_RE = re.compile(
|
|
35
|
+
r"^\S.*\.pyi?:\d+:\d+:\s+(?:[A-Z]{1,4}\d{3,4})\b\s+.+$"
|
|
36
|
+
)
|
|
37
|
+
_PYRIGHT_RE = re.compile(
|
|
38
|
+
r"^\s*\S.*\.pyi?:\d+:\d+\s+-\s+"
|
|
39
|
+
r"(?:error|warning|information):\s+.+$",
|
|
40
|
+
re.IGNORECASE,
|
|
41
|
+
)
|
|
42
|
+
_PYRIGHT_FILE_HEADER_RE = re.compile(r"^(?:\.{0,2}/|/|[A-Za-z]:[\\/]|~?/)?\S+\.pyi?$")
|
|
43
|
+
_BIOME_LOCATION_RULE_RE = re.compile(
|
|
44
|
+
r"^\S.*\.(?:js|jsx|ts|tsx|mjs|cjs|json|jsonc|css|md|mdx|vue|svelte|astro)"
|
|
45
|
+
r":\d+:\d+\s+.+\b(?:lint|style|suspicious|correctness|complexity|nursery|a11y)/"
|
|
46
|
+
r"[A-Za-z0-9_./-]+",
|
|
47
|
+
re.IGNORECASE,
|
|
48
|
+
)
|
|
49
|
+
_OXLINT_DIAGNOSTIC_RE = re.compile(
|
|
50
|
+
r"^\s*(?:×|!|error|warning)\s+(?:eslint|oxlint)\([^)]+\):\s+.+$",
|
|
51
|
+
re.IGNORECASE,
|
|
52
|
+
)
|
|
53
|
+
_OXLINT_FRAME_RE = re.compile(
|
|
54
|
+
r"^\s*╭─\[.+\.(?:js|jsx|ts|tsx|mjs|cjs):\d+:\d+\]",
|
|
55
|
+
re.IGNORECASE,
|
|
56
|
+
)
|
|
57
|
+
_SHELLCHECK_HEADER_RE = re.compile(r"^In .+ line \d+:$", re.IGNORECASE)
|
|
58
|
+
_SHELLCHECK_CODE_RE = re.compile(r"\bSC\d{4}\b")
|
|
59
|
+
_SHELLCHECK_GCC_RE = re.compile(
|
|
60
|
+
r"^\S.*:\d+:\d+:\s+(?:error|warning|note|style|info):\s+.+\s+\[SC\d{4}\]",
|
|
61
|
+
re.IGNORECASE,
|
|
62
|
+
)
|
|
63
|
+
_MARKDOWNLINT_RE = re.compile(
|
|
64
|
+
r"^\S.*\.mdx?:\d+(?::\d+)?\s+MD\d{3}(?:/[A-Za-z0-9-]+)?\s+.+$",
|
|
65
|
+
re.IGNORECASE,
|
|
66
|
+
)
|
|
67
|
+
_YAMLLINT_PARSABLE_RE = re.compile(
|
|
68
|
+
r"^\S.*\.ya?ml:\d+:\d+:\s+\[(?:error|warning)\]\s+.+\([A-Za-z0-9_-]+\)",
|
|
69
|
+
re.IGNORECASE,
|
|
70
|
+
)
|
|
71
|
+
_YAMLLINT_GROUPED_RE = re.compile(
|
|
72
|
+
r"^\s*\d+:\d+\s+(?:error|warning)\s+.+\([A-Za-z0-9_-]+\)",
|
|
73
|
+
re.IGNORECASE,
|
|
74
|
+
)
|
|
75
|
+
_YAMLLINT_FILE_HEADER_RE = re.compile(r"^(?:\.{0,2}/|/|[A-Za-z]:[\\/]|~?/)?\S+\.ya?ml$")
|
|
76
|
+
_GOLANGCI_LINTERS = (
|
|
77
|
+
"asasalint",
|
|
78
|
+
"asciicheck",
|
|
79
|
+
"bidichk",
|
|
80
|
+
"bodyclose",
|
|
81
|
+
"canonicalheader",
|
|
82
|
+
"copyloopvar",
|
|
83
|
+
"cyclop",
|
|
84
|
+
"decorder",
|
|
85
|
+
"depguard",
|
|
86
|
+
"dogsled",
|
|
87
|
+
"dupl",
|
|
88
|
+
"durationcheck",
|
|
89
|
+
"errcheck",
|
|
90
|
+
"errchkjson",
|
|
91
|
+
"errorlint",
|
|
92
|
+
"exhaustive",
|
|
93
|
+
"exhaustruct",
|
|
94
|
+
"forbidigo",
|
|
95
|
+
"forcetypeassert",
|
|
96
|
+
"gci",
|
|
97
|
+
"gocheckcompilerdirectives",
|
|
98
|
+
"gochecknoglobals",
|
|
99
|
+
"gochecknoinits",
|
|
100
|
+
"gochecksumtype",
|
|
101
|
+
"gocognit",
|
|
102
|
+
"goconst",
|
|
103
|
+
"gocritic",
|
|
104
|
+
"gocyclo",
|
|
105
|
+
"godot",
|
|
106
|
+
"godox",
|
|
107
|
+
"gofmt",
|
|
108
|
+
"gofumpt",
|
|
109
|
+
"goheader",
|
|
110
|
+
"goimports",
|
|
111
|
+
"gomoddirectives",
|
|
112
|
+
"gomodguard",
|
|
113
|
+
"goprintffuncname",
|
|
114
|
+
"gosec",
|
|
115
|
+
"gosimple",
|
|
116
|
+
"gosmopolitan",
|
|
117
|
+
"govet",
|
|
118
|
+
"grouper",
|
|
119
|
+
"iface",
|
|
120
|
+
"importas",
|
|
121
|
+
"inamedparam",
|
|
122
|
+
"ineffassign",
|
|
123
|
+
"interfacebloat",
|
|
124
|
+
"intrange",
|
|
125
|
+
"lll",
|
|
126
|
+
"loggercheck",
|
|
127
|
+
"maintidx",
|
|
128
|
+
"makezero",
|
|
129
|
+
"mirror",
|
|
130
|
+
"misspell",
|
|
131
|
+
"mnd",
|
|
132
|
+
"musttag",
|
|
133
|
+
"nakedret",
|
|
134
|
+
"nestif",
|
|
135
|
+
"nilerr",
|
|
136
|
+
"nilnil",
|
|
137
|
+
"nlreturn",
|
|
138
|
+
"noctx",
|
|
139
|
+
"nolintlint",
|
|
140
|
+
"nonamedreturns",
|
|
141
|
+
"paralleltest",
|
|
142
|
+
"perfsprint",
|
|
143
|
+
"prealloc",
|
|
144
|
+
"predeclared",
|
|
145
|
+
"promlinter",
|
|
146
|
+
"protogetter",
|
|
147
|
+
"reassign",
|
|
148
|
+
"recvcheck",
|
|
149
|
+
"revive",
|
|
150
|
+
"rowserrcheck",
|
|
151
|
+
"sloglint",
|
|
152
|
+
"spancheck",
|
|
153
|
+
"sqlclosecheck",
|
|
154
|
+
"staticcheck",
|
|
155
|
+
"stylecheck",
|
|
156
|
+
"tagalign",
|
|
157
|
+
"tagliatelle",
|
|
158
|
+
"testableexamples",
|
|
159
|
+
"testifylint",
|
|
160
|
+
"thelper",
|
|
161
|
+
"tparallel",
|
|
162
|
+
"typecheck",
|
|
163
|
+
"unconvert",
|
|
164
|
+
"unparam",
|
|
165
|
+
"unused",
|
|
166
|
+
"usestdlibvars",
|
|
167
|
+
"usetesting",
|
|
168
|
+
"varnamelen",
|
|
169
|
+
"wastedassign",
|
|
170
|
+
"whitespace",
|
|
171
|
+
"wrapcheck",
|
|
172
|
+
"wsl",
|
|
173
|
+
"zerologlint",
|
|
174
|
+
)
|
|
175
|
+
_GOLANGCI_RE = re.compile(
|
|
176
|
+
r"^(?P<path>\S.*\.go):(?P<location>\d+:\d+):\s+"
|
|
177
|
+
r"(?P<detail>.+\((?P<linter>[A-Za-z0-9_-]+)\))$"
|
|
178
|
+
)
|
|
179
|
+
_C_FAMILY_DIAGNOSTIC_RE = re.compile(
|
|
180
|
+
r"^\S.*\.(?:c|cc|cpp|cxx|h|hh|hpp|m|mm|swift):\d+:\d+:\s+"
|
|
181
|
+
r"(?:fatal error|error|warning|note):\s+.+$",
|
|
182
|
+
re.IGNORECASE,
|
|
183
|
+
)
|
|
184
|
+
_GO_BUILD_DIAGNOSTIC_RE = re.compile(
|
|
185
|
+
r"^\S.*\.go:\d+(?::\d+)?:\s+"
|
|
186
|
+
r"(?:undefined:|declared and not used:|cannot use |too many arguments|"
|
|
187
|
+
r"not enough arguments|assignment mismatch|syntax error:|"
|
|
188
|
+
r"imported and not used:|.+\b(?:is not a type|has no field or method)\b).+$"
|
|
189
|
+
)
|
|
190
|
+
_GO_BUILD_PACKAGE_RE = re.compile(r"^#\s+(?:command-line-arguments|\S+[./]\S+)$")
|
|
191
|
+
_MSBUILD_DIAGNOSTIC_RE = re.compile(
|
|
192
|
+
r"^(?:(?:\S.*\.(?:cs|vb|fs)\(\d+,\d+\):|[A-Z][A-Z0-9]*\s+:)\s+)?(?:fatal\s+)?"
|
|
193
|
+
r"(?:error|warning)\s+(?:CS|MSB|NETSDK|NU)\d+:.+$",
|
|
194
|
+
re.IGNORECASE,
|
|
195
|
+
)
|
|
196
|
+
_MSBUILD_TOTAL_RE = re.compile(r"^\s*\d+\s+(?:Warning|Error)\(s\)$", re.IGNORECASE)
|
|
197
|
+
_MAVEN_GRADLE_ERROR_RE = re.compile(r"^\[(?:ERROR|WARNING)\]\s+.+$")
|
|
198
|
+
_GRADLE_TASK_FAILED_RE = re.compile(r"^>\s+Task\s+.+\s+FAILED$")
|
|
199
|
+
_MAKE_NINJA_CMAKE_RE = re.compile(
|
|
200
|
+
r"^(?:make(?:\[\d+\])?: \*\*\* .+|ninja: build stopped: .+|"
|
|
201
|
+
r"CMake Error at .+|CMake Warning at .+)$",
|
|
202
|
+
re.IGNORECASE,
|
|
203
|
+
)
|
|
204
|
+
_NX_TURBO_FAILURE_RE = re.compile(
|
|
205
|
+
r"^(?:Failed tasks?:|Tasks:\s+.*\bfailed\b.*|ERROR\s+run failed:|"
|
|
206
|
+
r".+(?:#|:)(?:build|test|lint|typecheck):\s+command (?:failed|exited).*)$",
|
|
207
|
+
re.IGNORECASE,
|
|
208
|
+
)
|
|
209
|
+
_GOLANGCI_RUNNER_RE = re.compile(
|
|
210
|
+
r"^(?:ERRO|level=error\b|level=fatal\b).*(?:running error|context loading failed|"
|
|
211
|
+
r"can't run linter|failed to load|golangci-lint)",
|
|
212
|
+
re.IGNORECASE,
|
|
213
|
+
)
|
|
214
|
+
_LINT_CONTEXT_RE = re.compile(
|
|
215
|
+
r"^\s*(?:[>|·╭╰├│]|[0-9]+\s*│|[│╰╭].*)$"
|
|
216
|
+
)
|
|
217
|
+
_NOTE_HELP_RE = re.compile(r"^\s*(?:=\s*)?(?:note|help):", re.IGNORECASE)
|
|
218
|
+
_FINAL_SUMMARY_RE = re.compile(
|
|
219
|
+
r"(could not compile|aborting due to|error: build failed|"
|
|
220
|
+
r"warning: .+ generated \d+ warnings?|"
|
|
221
|
+
r"finished `|found \d+ errors? in \d+ files?|"
|
|
222
|
+
r"found \d+ errors?|found \d+ warnings?|"
|
|
223
|
+
r"\d+\s+errors?,\s+\d+\s+warnings?,\s+\d+\s+informations?|"
|
|
224
|
+
r"found \d+\s+warnings?\s+and\s+\d+\s+errors?|"
|
|
225
|
+
r"\d+\s+issues?:|checked \d+ files?|"
|
|
226
|
+
r"success: no issues found|"
|
|
227
|
+
r"\d+\s+problems?\s+\(\d+\s+errors?,\s+\d+\s+warnings?\))",
|
|
228
|
+
re.IGNORECASE,
|
|
229
|
+
)
|
|
230
|
+
_PROGRESS_RE = re.compile(
|
|
231
|
+
r"^\s*(?:Compiling|Checking|Fresh|Building|Running|Downloading|Downloaded|"
|
|
232
|
+
r"Blocking waiting|Waiting|Starting compilation|File change detected|Linting)\b",
|
|
233
|
+
re.IGNORECASE,
|
|
234
|
+
)
|
|
235
|
+
_ERROR_WORD_RE = re.compile(r"(?<![A-Za-z0-9_])(?:error|fatal)(?![A-Za-z0-9_])", re.IGNORECASE)
|
|
236
|
+
_WARNING_WORD_RE = re.compile(r"(?<![A-Za-z0-9_])warning(?![A-Za-z0-9_])", re.IGNORECASE)
|
|
237
|
+
_NONZERO_ERROR_COUNT_RE = re.compile(r"\b[1-9]\d*\s+errors?\b", re.IGNORECASE)
|
|
238
|
+
_NONZERO_WARNING_COUNT_RE = re.compile(r"\b[1-9]\d*\s+warnings?\b", re.IGNORECASE)
|
|
239
|
+
_ZERO_ERROR_WARNING_SUMMARY_RE = re.compile(
|
|
240
|
+
r"\b(?:0\s+errors?.*\b[1-9]\d*\s+warnings?|"
|
|
241
|
+
r"[1-9]\d*\s+warnings?\s+and\s+0\s+errors?)\b",
|
|
242
|
+
re.IGNORECASE,
|
|
243
|
+
)
|
|
244
|
+
_WARNING_DIAGNOSTIC_RE = re.compile(
|
|
245
|
+
r"^\s*(?:\S.*:\d+:\d+\s+-\s+)?warning\b|^\s*[×!]\s+.+\(.*\):",
|
|
246
|
+
re.IGNORECASE,
|
|
247
|
+
)
|
|
248
|
+
_FAILURE_RE = re.compile(
|
|
249
|
+
r"\b(failed|failure)\b|could not compile|aborting due to|running error",
|
|
250
|
+
re.IGNORECASE,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def strip_ansi(text: str) -> str:
|
|
255
|
+
return _ANSI_RE.sub("", text)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def is_rust_diagnostic_line(line: str) -> bool:
|
|
259
|
+
return _RUST_DIAGNOSTIC_RE.match(line) is not None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def is_rust_span_line(line: str) -> bool:
|
|
263
|
+
return _RUST_SPAN_RE.match(line) is not None
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def is_rust_source_bar_line(line: str) -> bool:
|
|
267
|
+
return _RUST_SOURCE_BAR_RE.match(line) is not None
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def is_tsc_diagnostic_line(line: str) -> bool:
|
|
271
|
+
return _TSC_PARENS_RE.match(line) is not None or _TSC_PRETTY_RE.match(line) is not None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def is_eslint_issue_line(line: str) -> bool:
|
|
275
|
+
return _ESLINT_ISSUE_RE.match(line) is not None or _ESLINT_COMPACT_RE.match(line) is not None
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def is_eslint_file_header(line: str) -> bool:
|
|
279
|
+
stripped = line.strip()
|
|
280
|
+
return " " not in stripped and _ESLINT_FILE_HEADER_RE.match(stripped) is not None
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def is_eslint_summary_line(line: str) -> bool:
|
|
284
|
+
stripped = line.strip()
|
|
285
|
+
return stripped.startswith("✖") or _ESLINT_SUMMARY_RE.search(stripped) is not None
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def is_mypy_diagnostic_line(line: str) -> bool:
|
|
289
|
+
return _MYPY_RE.match(line) is not None
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def parse_mypy_diagnostic_line(line: str) -> tuple[str, str, str] | None:
|
|
293
|
+
match = _MYPY_RE.match(line)
|
|
294
|
+
if match is None:
|
|
295
|
+
return None
|
|
296
|
+
return match.group("path"), match.group("location"), match.group("detail")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def is_ruff_diagnostic_line(line: str) -> bool:
|
|
300
|
+
return _RUFF_RE.match(line.strip()) is not None
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def is_ruff_summary_line(line: str) -> bool:
|
|
304
|
+
lower = line.strip().lower()
|
|
305
|
+
return (
|
|
306
|
+
lower.startswith("found ") and "error" in lower
|
|
307
|
+
or "fixable with the" in lower
|
|
308
|
+
or lower.startswith("[*] ")
|
|
309
|
+
or lower.startswith("all checks passed")
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def is_pyright_diagnostic_line(line: str) -> bool:
|
|
314
|
+
return _PYRIGHT_RE.match(line.strip()) is not None
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def is_pyright_summary_line(line: str) -> bool:
|
|
318
|
+
lower = line.strip().lower()
|
|
319
|
+
return (
|
|
320
|
+
" errors," in lower
|
|
321
|
+
and (" warning," in lower or " warnings," in lower)
|
|
322
|
+
and (" information" in lower or " informations" in lower)
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def is_pyright_file_header(line: str) -> bool:
|
|
327
|
+
stripped = line.strip()
|
|
328
|
+
return " " not in stripped and _PYRIGHT_FILE_HEADER_RE.match(stripped) is not None
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def is_biome_diagnostic_line(line: str) -> bool:
|
|
332
|
+
stripped = line.strip()
|
|
333
|
+
return _BIOME_LOCATION_RULE_RE.match(stripped) is not None
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def is_biome_summary_line(line: str) -> bool:
|
|
337
|
+
lower = line.strip().lower()
|
|
338
|
+
return (
|
|
339
|
+
lower.startswith("found ") and ("error" in lower or "warning" in lower)
|
|
340
|
+
or lower.startswith("checked ") and " file" in lower
|
|
341
|
+
or "no fixes applied" in lower
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def is_oxlint_diagnostic_line(line: str) -> bool:
|
|
346
|
+
return _OXLINT_DIAGNOSTIC_RE.match(line.strip()) is not None
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def is_oxlint_frame_line(line: str) -> bool:
|
|
350
|
+
return _OXLINT_FRAME_RE.match(line.strip()) is not None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def is_oxlint_summary_line(line: str) -> bool:
|
|
354
|
+
lower = line.strip().lower()
|
|
355
|
+
return lower.startswith("found ") and ("warning" in lower or "error" in lower)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def is_shellcheck_header_line(line: str) -> bool:
|
|
359
|
+
return _SHELLCHECK_HEADER_RE.match(line.strip()) is not None
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def is_shellcheck_diagnostic_line(line: str) -> bool:
|
|
363
|
+
stripped = line.strip()
|
|
364
|
+
return (
|
|
365
|
+
is_shellcheck_gcc_diagnostic_line(stripped)
|
|
366
|
+
or _SHELLCHECK_CODE_RE.search(stripped) is not None
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def is_shellcheck_gcc_diagnostic_line(line: str) -> bool:
|
|
371
|
+
return _SHELLCHECK_GCC_RE.match(line.strip()) is not None
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def is_markdownlint_diagnostic_line(line: str) -> bool:
|
|
375
|
+
return _MARKDOWNLINT_RE.match(line.strip()) is not None
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def is_yamllint_diagnostic_line(line: str) -> bool:
|
|
379
|
+
stripped = line.strip()
|
|
380
|
+
return (
|
|
381
|
+
_YAMLLINT_PARSABLE_RE.match(stripped) is not None
|
|
382
|
+
or _YAMLLINT_GROUPED_RE.match(line) is not None
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def is_yamllint_file_header(line: str) -> bool:
|
|
387
|
+
stripped = line.strip()
|
|
388
|
+
return " " not in stripped and _YAMLLINT_FILE_HEADER_RE.match(stripped) is not None
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def is_golangci_lint_issue_line(line: str) -> bool:
|
|
392
|
+
match = _GOLANGCI_RE.match(line.strip())
|
|
393
|
+
return match is not None and match.group("linter").lower() in _GOLANGCI_LINTERS
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def parse_golangci_lint_issue_line(line: str) -> tuple[str, str, str] | None:
|
|
397
|
+
match = _GOLANGCI_RE.match(line.strip())
|
|
398
|
+
if match is None or match.group("linter").lower() not in _GOLANGCI_LINTERS:
|
|
399
|
+
return None
|
|
400
|
+
return match.group("path"), match.group("location"), match.group("detail")
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def is_golangci_lint_runner_error_line(line: str) -> bool:
|
|
404
|
+
return _GOLANGCI_RUNNER_RE.match(line.strip()) is not None
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def is_golangci_lint_summary_line(line: str) -> bool:
|
|
408
|
+
lower = line.strip().lower()
|
|
409
|
+
return lower.endswith("issues:") or lower.endswith("issue:") or "issues before processing" in lower
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def is_c_family_diagnostic_line(line: str) -> bool:
|
|
413
|
+
return _C_FAMILY_DIAGNOSTIC_RE.match(line.strip()) is not None
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def is_c_family_source_context_line(line: str) -> bool:
|
|
417
|
+
return re.match(r"^\s*(?:\d+\s*)?\|", line.rstrip()) is not None
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def is_go_build_package_header(line: str) -> bool:
|
|
421
|
+
return _GO_BUILD_PACKAGE_RE.match(line.strip()) is not None
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def is_go_build_diagnostic_line(line: str) -> bool:
|
|
425
|
+
return _GO_BUILD_DIAGNOSTIC_RE.match(line.strip()) is not None
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def is_msbuild_diagnostic_line(line: str) -> bool:
|
|
429
|
+
return _MSBUILD_DIAGNOSTIC_RE.match(line.strip()) is not None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def is_msbuild_summary_line(line: str) -> bool:
|
|
433
|
+
stripped = line.strip()
|
|
434
|
+
lower = stripped.lower()
|
|
435
|
+
return (
|
|
436
|
+
stripped.startswith("Build FAILED")
|
|
437
|
+
or stripped.startswith("Build succeeded")
|
|
438
|
+
or _MSBUILD_TOTAL_RE.match(stripped) is not None
|
|
439
|
+
or "time elapsed" in lower
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def is_maven_gradle_diagnostic_line(line: str) -> bool:
|
|
444
|
+
stripped = line.strip()
|
|
445
|
+
lower = stripped.lower()
|
|
446
|
+
return (
|
|
447
|
+
_MAVEN_GRADLE_ERROR_RE.match(stripped) is not None
|
|
448
|
+
or _GRADLE_TASK_FAILED_RE.match(stripped) is not None
|
|
449
|
+
or stripped.startswith(("FAILURE:", "* What went wrong:", "* Try:"))
|
|
450
|
+
or "build failed" in lower
|
|
451
|
+
or "build failure" in lower
|
|
452
|
+
or "execution failed for task" in lower
|
|
453
|
+
or "surefire-reports" in lower
|
|
454
|
+
or "test report" in lower
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def is_make_ninja_cmake_diagnostic_line(line: str) -> bool:
|
|
459
|
+
stripped = line.strip()
|
|
460
|
+
return _MAKE_NINJA_CMAKE_RE.match(stripped) is not None
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def is_nx_turbo_diagnostic_line(line: str) -> bool:
|
|
464
|
+
stripped = line.strip()
|
|
465
|
+
lower = stripped.lower()
|
|
466
|
+
return (
|
|
467
|
+
_NX_TURBO_FAILURE_RE.match(stripped) is not None
|
|
468
|
+
or stripped.startswith(("NX ", "• Running target", "Failed:"))
|
|
469
|
+
or (stripped.startswith("- ") and ("#" in stripped or ":" in stripped))
|
|
470
|
+
or "failed tasks:" in lower
|
|
471
|
+
or "command exited" in lower
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def is_swift_xcode_failure_line(line: str) -> bool:
|
|
476
|
+
stripped = line.strip()
|
|
477
|
+
return stripped == "** BUILD FAILED **" or stripped.startswith("The following build commands failed:")
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def is_lint_source_context_line(line: str) -> bool:
|
|
481
|
+
stripped = line.rstrip()
|
|
482
|
+
return (
|
|
483
|
+
_LINT_CONTEXT_RE.match(stripped) is not None
|
|
484
|
+
or is_rust_source_bar_line(stripped)
|
|
485
|
+
or is_oxlint_frame_line(stripped)
|
|
486
|
+
or is_c_family_source_context_line(line)
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def is_note_help_line(line: str) -> bool:
|
|
491
|
+
stripped = line.strip()
|
|
492
|
+
return _NOTE_HELP_RE.match(line) is not None or stripped.lower().startswith("did you mean:")
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def is_final_summary_line(line: str) -> bool:
|
|
496
|
+
return (
|
|
497
|
+
_FINAL_SUMMARY_RE.search(line) is not None
|
|
498
|
+
or is_eslint_summary_line(line)
|
|
499
|
+
or is_ruff_summary_line(line)
|
|
500
|
+
or is_pyright_summary_line(line)
|
|
501
|
+
or is_biome_summary_line(line)
|
|
502
|
+
or is_oxlint_summary_line(line)
|
|
503
|
+
or is_golangci_lint_summary_line(line)
|
|
504
|
+
or is_msbuild_summary_line(line)
|
|
505
|
+
or is_maven_gradle_diagnostic_line(line)
|
|
506
|
+
or is_make_ninja_cmake_diagnostic_line(line)
|
|
507
|
+
or is_nx_turbo_diagnostic_line(line)
|
|
508
|
+
or is_swift_xcode_failure_line(line)
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def is_progress_line(line: str) -> bool:
|
|
513
|
+
return _PROGRESS_RE.match(line) is not None
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def is_important_diagnostic_line(line: str) -> bool:
|
|
517
|
+
return (
|
|
518
|
+
is_rust_diagnostic_line(line)
|
|
519
|
+
or is_rust_span_line(line)
|
|
520
|
+
or is_rust_source_bar_line(line)
|
|
521
|
+
or is_tsc_diagnostic_line(line)
|
|
522
|
+
or is_eslint_issue_line(line)
|
|
523
|
+
or is_mypy_diagnostic_line(line)
|
|
524
|
+
or is_ruff_diagnostic_line(line)
|
|
525
|
+
or is_pyright_diagnostic_line(line)
|
|
526
|
+
or is_biome_diagnostic_line(line)
|
|
527
|
+
or is_oxlint_diagnostic_line(line)
|
|
528
|
+
or is_oxlint_frame_line(line)
|
|
529
|
+
or is_shellcheck_header_line(line)
|
|
530
|
+
or is_shellcheck_diagnostic_line(line)
|
|
531
|
+
or is_markdownlint_diagnostic_line(line)
|
|
532
|
+
or is_yamllint_diagnostic_line(line)
|
|
533
|
+
or is_golangci_lint_issue_line(line)
|
|
534
|
+
or is_golangci_lint_runner_error_line(line)
|
|
535
|
+
or is_c_family_diagnostic_line(line)
|
|
536
|
+
or is_go_build_package_header(line)
|
|
537
|
+
or is_go_build_diagnostic_line(line)
|
|
538
|
+
or is_msbuild_diagnostic_line(line)
|
|
539
|
+
or is_msbuild_summary_line(line)
|
|
540
|
+
or is_maven_gradle_diagnostic_line(line)
|
|
541
|
+
or is_make_ninja_cmake_diagnostic_line(line)
|
|
542
|
+
or is_nx_turbo_diagnostic_line(line)
|
|
543
|
+
or is_swift_xcode_failure_line(line)
|
|
544
|
+
or is_lint_source_context_line(line)
|
|
545
|
+
or is_note_help_line(line)
|
|
546
|
+
or is_final_summary_line(line)
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def is_diagnostic_file_header(kind: str, line: str) -> bool:
|
|
551
|
+
if kind == "eslint":
|
|
552
|
+
return is_eslint_file_header(line)
|
|
553
|
+
if kind == "pyright":
|
|
554
|
+
return is_pyright_file_header(line)
|
|
555
|
+
if kind == "yamllint":
|
|
556
|
+
return is_yamllint_file_header(line)
|
|
557
|
+
if kind == "go-build":
|
|
558
|
+
return is_go_build_package_header(line)
|
|
559
|
+
return False
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def diagnostic_context_budget(kind: str, line: str) -> int:
|
|
563
|
+
if is_tsc_diagnostic_line(line):
|
|
564
|
+
return 2
|
|
565
|
+
if kind == "shellcheck" and (
|
|
566
|
+
is_shellcheck_header_line(line) or is_note_help_line(line)
|
|
567
|
+
):
|
|
568
|
+
return 3 if is_shellcheck_header_line(line) else 1
|
|
569
|
+
if kind in {"ruff", "biome", "oxlint"} and (
|
|
570
|
+
is_ruff_diagnostic_line(line)
|
|
571
|
+
or is_biome_diagnostic_line(line)
|
|
572
|
+
or is_oxlint_diagnostic_line(line)
|
|
573
|
+
or is_oxlint_frame_line(line)
|
|
574
|
+
):
|
|
575
|
+
return 4
|
|
576
|
+
if is_c_family_diagnostic_line(line):
|
|
577
|
+
return 3
|
|
578
|
+
return 0
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def is_safe_snippet_line(line: str) -> bool:
|
|
582
|
+
stripped = line.strip()
|
|
583
|
+
if not stripped or len(stripped) > 240:
|
|
584
|
+
return False
|
|
585
|
+
if is_progress_line(stripped) or is_important_diagnostic_line(stripped):
|
|
586
|
+
return False
|
|
587
|
+
return True
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def has_error_signal(lines: list[str]) -> bool:
|
|
591
|
+
for line in lines:
|
|
592
|
+
lower = line.lower()
|
|
593
|
+
if _NONZERO_ERROR_COUNT_RE.search(line) is not None:
|
|
594
|
+
return True
|
|
595
|
+
if _ZERO_ERROR_WARNING_SUMMARY_RE.search(line) is not None:
|
|
596
|
+
continue
|
|
597
|
+
if _FAILURE_RE.search(line) is not None or "error[" in lower:
|
|
598
|
+
return True
|
|
599
|
+
if (
|
|
600
|
+
_ERROR_WORD_RE.search(line) is not None
|
|
601
|
+
and _WARNING_DIAGNOSTIC_RE.search(line) is None
|
|
602
|
+
):
|
|
603
|
+
return True
|
|
604
|
+
return False
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def has_warning_signal(lines: list[str]) -> bool:
|
|
608
|
+
return any(
|
|
609
|
+
_NONZERO_WARNING_COUNT_RE.search(line) is not None
|
|
610
|
+
or _WARNING_WORD_RE.search(line) is not None
|
|
611
|
+
or "warning[" in line.lower()
|
|
612
|
+
for line in lines
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def normalize_line(line: str) -> str:
|
|
617
|
+
return line.strip()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Detect conservative CI/job log shapes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from .summary import (
|
|
8
|
+
extract_group_step,
|
|
9
|
+
is_actions_marker,
|
|
10
|
+
is_error_annotation,
|
|
11
|
+
is_failure_line,
|
|
12
|
+
is_group_end,
|
|
13
|
+
is_warning_line,
|
|
14
|
+
parse_ci_line,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class CiLogSignal:
|
|
20
|
+
"""Detected CI log outcome signals."""
|
|
21
|
+
|
|
22
|
+
failed: bool
|
|
23
|
+
warning: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def detect_ci_job_log(lines: list[str]) -> CiLogSignal | None:
|
|
27
|
+
"""Return a strong GitHub Actions log signal, or ``None``."""
|
|
28
|
+
|
|
29
|
+
parsed_lines = [parse_ci_line(line) for line in lines if line.strip()]
|
|
30
|
+
if len(parsed_lines) < 3:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
prefixed_count = sum(1 for line in parsed_lines if line.had_ci_prefix)
|
|
34
|
+
tab_prefixed_count = sum(
|
|
35
|
+
1 for line in parsed_lines if line.had_ci_prefix and line.job and line.step
|
|
36
|
+
)
|
|
37
|
+
marker_count = sum(1 for line in parsed_lines if is_actions_marker(line.message))
|
|
38
|
+
group_count = sum(
|
|
39
|
+
1
|
|
40
|
+
for line in parsed_lines
|
|
41
|
+
if extract_group_step(line.message) is not None or is_group_end(line.message)
|
|
42
|
+
)
|
|
43
|
+
annotation_count = sum(
|
|
44
|
+
1
|
|
45
|
+
for line in parsed_lines
|
|
46
|
+
if is_error_annotation(line.message) or is_warning_line(line.message)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
has_strong_prefix_shape = prefixed_count >= 2 and marker_count >= 1
|
|
50
|
+
has_strong_tab_shape = tab_prefixed_count >= 2 and marker_count >= 1
|
|
51
|
+
has_workflow_command_shape = marker_count >= 2 and (
|
|
52
|
+
group_count >= 1 or annotation_count >= 1
|
|
53
|
+
)
|
|
54
|
+
if not (
|
|
55
|
+
has_strong_prefix_shape
|
|
56
|
+
or has_strong_tab_shape
|
|
57
|
+
or has_workflow_command_shape
|
|
58
|
+
):
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
return CiLogSignal(
|
|
62
|
+
failed=any(is_failure_line(line.message) for line in parsed_lines),
|
|
63
|
+
warning=any(is_warning_line(line.message) for line in parsed_lines),
|
|
64
|
+
)
|