gd-tools-cli 0.2.0__tar.gz → 0.3.0__tar.gz
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.
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/PKG-INFO +1 -1
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/pyproject.toml +2 -2
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/gd-tools-coverage/pre_run_hook.gd +34 -3
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/orchestrator.py +41 -23
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/reporter.py +2 -1
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/errors.py +34 -1
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/PKG-INFO +1 -1
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/README.md +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/setup.cfg +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/__init__.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/__main__.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/__init__.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/gd-tools-coverage/coverage.gd +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/gd-tools-coverage/post_run_hook.gd +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/cli.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/config.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/__init__.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/cobertura_reporter.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/html_reporter.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/lcov_reporter.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/plan_generator.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/templates/file.html +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/templates/index.html +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/coverage/terminal_reporter.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/doctor.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/file_discovery.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/format_runner.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/godot.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/init.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/lint_runner.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/test_runner.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/update_check.py +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/SOURCES.txt +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/dependency_links.txt +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/entry_points.txt +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/requires.txt +0 -0
- {gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gd-tools-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "A modern development workflow CLI for GDScript projects in Godot 4.5+"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -99,7 +99,7 @@ target-version = ["py310"]
|
|
|
99
99
|
|
|
100
100
|
[tool.commitizen]
|
|
101
101
|
name = "cz_conventional_commits"
|
|
102
|
-
version = "0.
|
|
102
|
+
version = "0.3.0"
|
|
103
103
|
version_files = ["pyproject.toml:version"]
|
|
104
104
|
changelog_file = "CHANGELOG.md"
|
|
105
105
|
tag_format = "v$version"
|
{gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/gd-tools-coverage/pre_run_hook.gd
RENAMED
|
@@ -112,6 +112,19 @@ static func _extract_indent(line: String) -> String:
|
|
|
112
112
|
return indent
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
static func _detect_body_indent(source_lines: PackedStringArray, pattern_index: int) -> String:
|
|
116
|
+
# Scan lines after the pattern line to find the next non-empty line
|
|
117
|
+
# and use its indentation as the body indent.
|
|
118
|
+
var i: int = pattern_index + 1
|
|
119
|
+
while i < source_lines.size():
|
|
120
|
+
var line: String = source_lines[i]
|
|
121
|
+
if not line.strip_edges().is_empty():
|
|
122
|
+
return _extract_indent(line)
|
|
123
|
+
i += 1
|
|
124
|
+
# Fallback: if no non-empty line found, use the pattern indent plus one tab.
|
|
125
|
+
return _extract_indent(source_lines[pattern_index]) + "\t"
|
|
126
|
+
|
|
127
|
+
|
|
115
128
|
static func _inject_trackers(source: String, file_id: int, lines: Array) -> String:
|
|
116
129
|
var source_lines: PackedStringArray = source.split("\n")
|
|
117
130
|
var sorted_lines: Array = lines.duplicate(true)
|
|
@@ -123,9 +136,25 @@ static func _inject_trackers(source: String, file_id: int, lines: Array) -> Stri
|
|
|
123
136
|
if target_index < 0 or target_index >= source_lines.size():
|
|
124
137
|
continue
|
|
125
138
|
var target_line: String = source_lines[target_index]
|
|
126
|
-
var
|
|
139
|
+
var branch_type = entry.get("branch_type", "")
|
|
140
|
+
if branch_type == null:
|
|
141
|
+
branch_type = ""
|
|
142
|
+
var insert_index: int
|
|
143
|
+
var indent: String
|
|
144
|
+
if branch_type in ["match_case", "if_false", "elif_true"]:
|
|
145
|
+
# Inject AFTER the branch line (inside the body).
|
|
146
|
+
# match_case patterns, else: and elif: lines must have trackers
|
|
147
|
+
# placed inside their body — injecting before these lines would
|
|
148
|
+
# insert a statement between the if/elif/else keywords, breaking
|
|
149
|
+
# the GDScript block structure (orphaned else/elif = syntax error).
|
|
150
|
+
insert_index = target_index + 1
|
|
151
|
+
indent = _detect_body_indent(source_lines, target_index)
|
|
152
|
+
else:
|
|
153
|
+
# Inject BEFORE the tracked line (existing behavior)
|
|
154
|
+
insert_index = target_index
|
|
155
|
+
indent = _extract_indent(target_line)
|
|
127
156
|
var tracker_call: String = "%s%s.hit(%d, %d)" % [indent, TRACKER_NAME, file_id, line_id]
|
|
128
|
-
source_lines.insert(
|
|
157
|
+
source_lines.insert(insert_index, tracker_call)
|
|
129
158
|
return "\n".join(source_lines)
|
|
130
159
|
|
|
131
160
|
|
|
@@ -232,4 +261,6 @@ func _validate_file_entry(file_entry: Variant) -> bool:
|
|
|
232
261
|
|
|
233
262
|
|
|
234
263
|
func _log_error(what: String, cause: String, fix: String) -> void:
|
|
235
|
-
push_error(
|
|
264
|
+
push_error(
|
|
265
|
+
"[gd-tools] [Error] " + what + "\n\n" + " Cause: " + cause + "\n" + " Fix: " + fix
|
|
266
|
+
)
|
|
@@ -115,14 +115,16 @@ def run_coverage_test(
|
|
|
115
115
|
|
|
116
116
|
min_threshold = min_percent / 100 if min_percent is not None else None
|
|
117
117
|
try:
|
|
118
|
-
reporter.generate_report(
|
|
118
|
+
report_result = reporter.generate_report(
|
|
119
119
|
plan,
|
|
120
120
|
data,
|
|
121
121
|
output_dir,
|
|
122
122
|
config.coverage.format,
|
|
123
123
|
min_threshold=min_threshold,
|
|
124
124
|
)
|
|
125
|
-
except CoverageThresholdError:
|
|
125
|
+
except CoverageThresholdError as exc:
|
|
126
|
+
if exc.report_result is not None:
|
|
127
|
+
_print_coverage_table(exc.report_result.summary)
|
|
126
128
|
if test_error is not None:
|
|
127
129
|
raise test_error
|
|
128
130
|
raise
|
|
@@ -130,6 +132,9 @@ def run_coverage_test(
|
|
|
130
132
|
if test_error is not None:
|
|
131
133
|
raise test_error
|
|
132
134
|
|
|
135
|
+
# Print coverage summary table on success.
|
|
136
|
+
_print_coverage_table(report_result.summary)
|
|
137
|
+
|
|
133
138
|
if result is None:
|
|
134
139
|
raise CoveragePlanError(
|
|
135
140
|
"Coverage generation returned no result — "
|
|
@@ -229,6 +234,39 @@ def merge_coverage_files(
|
|
|
229
234
|
return merged
|
|
230
235
|
|
|
231
236
|
|
|
237
|
+
def _print_coverage_table(summary: CoverageSummary) -> None:
|
|
238
|
+
"""Print a Rich coverage summary table to stdout.
|
|
239
|
+
|
|
240
|
+
Renders a table with Lines and Branches rows, showing Found,
|
|
241
|
+
Hit, and Rate columns. Used by both :func:`show_coverage_summary`
|
|
242
|
+
and :func:`run_coverage_test` to ensure a consistent table format.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
summary: The coverage summary to display.
|
|
246
|
+
"""
|
|
247
|
+
console = Console()
|
|
248
|
+
table = Table(title="Coverage Summary")
|
|
249
|
+
table.add_column("Metric", style="cyan")
|
|
250
|
+
table.add_column("Found", justify="right")
|
|
251
|
+
table.add_column("Hit", justify="right")
|
|
252
|
+
table.add_column("Rate", justify="right", style="green")
|
|
253
|
+
|
|
254
|
+
table.add_row(
|
|
255
|
+
"Lines",
|
|
256
|
+
str(summary.total_lines),
|
|
257
|
+
str(summary.covered_lines),
|
|
258
|
+
f"{summary.line_rate * 100:.1f}%",
|
|
259
|
+
)
|
|
260
|
+
table.add_row(
|
|
261
|
+
"Branches",
|
|
262
|
+
str(summary.total_branches),
|
|
263
|
+
str(summary.covered_branches),
|
|
264
|
+
f"{summary.branch_rate * 100:.1f}%",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
console.print(table)
|
|
268
|
+
|
|
269
|
+
|
|
232
270
|
def show_coverage_summary(
|
|
233
271
|
config: GdToolsConfig,
|
|
234
272
|
min_percent: int | None = None,
|
|
@@ -265,27 +303,7 @@ def show_coverage_summary(
|
|
|
265
303
|
summary = reporter.compute_summary(plan, data)
|
|
266
304
|
|
|
267
305
|
# Print Rich terminal table.
|
|
268
|
-
|
|
269
|
-
table = Table(title="Coverage Summary")
|
|
270
|
-
table.add_column("Metric", style="cyan")
|
|
271
|
-
table.add_column("Found", justify="right")
|
|
272
|
-
table.add_column("Hit", justify="right")
|
|
273
|
-
table.add_column("Rate", justify="right", style="green")
|
|
274
|
-
|
|
275
|
-
table.add_row(
|
|
276
|
-
"Lines",
|
|
277
|
-
str(summary.total_lines),
|
|
278
|
-
str(summary.covered_lines),
|
|
279
|
-
f"{summary.line_rate * 100:.1f}%",
|
|
280
|
-
)
|
|
281
|
-
table.add_row(
|
|
282
|
-
"Branches",
|
|
283
|
-
str(summary.total_branches),
|
|
284
|
-
str(summary.covered_branches),
|
|
285
|
-
f"{summary.branch_rate * 100:.1f}%",
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
console.print(table)
|
|
306
|
+
_print_coverage_table(summary)
|
|
289
307
|
|
|
290
308
|
# Threshold check.
|
|
291
309
|
if min_percent is not None and summary.line_rate * 100 < min_percent:
|
|
@@ -512,7 +512,8 @@ def generate_report(
|
|
|
512
512
|
f" Cause: Line coverage {summary.line_rate:.2%} is "
|
|
513
513
|
f"below minimum {min_threshold:.2%}.\n"
|
|
514
514
|
f" Fix: Increase test coverage or lower the minimum "
|
|
515
|
-
"threshold."
|
|
515
|
+
"threshold.",
|
|
516
|
+
report_result=result,
|
|
516
517
|
)
|
|
517
518
|
|
|
518
519
|
return result
|
|
@@ -12,6 +12,13 @@ Exit-code convention:
|
|
|
12
12
|
threshold). These are "your code has a problem" errors.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from gd_tools.coverage.reporter import ReportResult
|
|
21
|
+
|
|
15
22
|
|
|
16
23
|
class GdToolsError(Exception):
|
|
17
24
|
"""Base exception for all gd-tools errors.
|
|
@@ -55,10 +62,36 @@ class CoveragePlanError(GdToolsError):
|
|
|
55
62
|
|
|
56
63
|
|
|
57
64
|
class CoverageThresholdError(GdToolsError):
|
|
58
|
-
"""Raised when coverage falls below the required threshold.
|
|
65
|
+
"""Raised when coverage falls below the required threshold.
|
|
66
|
+
|
|
67
|
+
Attributes:
|
|
68
|
+
exit_code: Process exit code (1 for threshold failures).
|
|
69
|
+
report_result: The :class:`ReportResult` computed before the
|
|
70
|
+
threshold check failed, if available. Callers can use
|
|
71
|
+
this to display coverage information without recomputation.
|
|
72
|
+
"""
|
|
59
73
|
|
|
60
74
|
exit_code: int = 1
|
|
61
75
|
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
message: str = "",
|
|
79
|
+
*,
|
|
80
|
+
exit_code: int | None = None,
|
|
81
|
+
report_result: ReportResult | None = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Initialize the error.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
message: The error message.
|
|
87
|
+
exit_code: Override the class default exit code. If None,
|
|
88
|
+
the class-level ``exit_code`` is used.
|
|
89
|
+
report_result: The :class:`ReportResult` containing the
|
|
90
|
+
coverage summary computed before the threshold check.
|
|
91
|
+
"""
|
|
92
|
+
super().__init__(message, exit_code=exit_code)
|
|
93
|
+
self.report_result = report_result
|
|
94
|
+
|
|
62
95
|
|
|
63
96
|
class TestFailureError(GdToolsError):
|
|
64
97
|
"""Raised when tests fail."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gd_tools_cli-0.2.0 → gd_tools_cli-0.3.0}/src/gd_tools/addons/gd-tools-coverage/post_run_hook.gd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|