Skip to content

Commit ca67e1b

Browse files
chore: sync workflow templates from Workflows repo (#62)
Automated sync from stranske/Workflows Template hash: 69bf45f7ea5a Changes synced from sync-manifest.yml Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent e548700 commit ca67e1b

8 files changed

Lines changed: 32 additions & 110 deletions

File tree

.github/scripts/decode_raw_input.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818

1919
def _parse_args() -> argparse.Namespace:
2020
parser = argparse.ArgumentParser(description="Decode or normalize topic input")
21-
parser.add_argument(
22-
"--passthrough", action="store_true", help="Read plain text from --in file"
23-
)
21+
parser.add_argument("--passthrough", action="store_true", help="Read plain text from --in file")
2422
parser.add_argument("--in", dest="in_file", help="Input file for passthrough mode")
2523
parser.add_argument(
2624
"--source",
@@ -33,9 +31,7 @@ def _parse_args() -> argparse.Namespace:
3331
def main() -> None:
3432
args = _parse_args()
3533
text: str = ""
36-
source_used = args.source_tag or (
37-
"passthrough" if args.passthrough else "raw_input"
38-
)
34+
source_used = args.source_tag or ("passthrough" if args.passthrough else "raw_input")
3935
if args.passthrough:
4036
if not args.in_file:
4137
return
@@ -118,9 +114,7 @@ def apply_section_headers(s: str) -> str:
118114
rebuilt = s
119115
for m in markers:
120116
# replace occurrences not already preceded by newline
121-
rebuilt = re.sub(
122-
rf"(?<!\n){re.escape(m)}", "\n" + m.strip() + "\n", rebuilt
123-
)
117+
rebuilt = re.sub(rf"(?<!\n){re.escape(m)}", "\n" + m.strip() + "\n", rebuilt)
124118
if rebuilt != s:
125119
applied.append("sections")
126120
return rebuilt
@@ -170,9 +164,7 @@ def extract_enumerators(s: str) -> tuple[list[str], list[str]]:
170164
if text.strip():
171165
OUT_FILE.write_text(text + "\n", encoding="utf-8")
172166
# Always write diagnostics when debug artifact collection might happen
173-
Path("decode_debug.json").write_text(
174-
json.dumps(diagnostics, indent=2), encoding="utf-8"
175-
)
167+
Path("decode_debug.json").write_text(json.dumps(diagnostics, indent=2), encoding="utf-8")
176168

177169

178170
if __name__ == "__main__":

.github/scripts/parse_chatgpt_topics.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
def _load_text() -> str:
2626
try:
2727
text = INPUT_PATH.read_text(encoding="utf-8").strip()
28-
except (
29-
FileNotFoundError
30-
) as exc: # pragma: no cover - guardrail for workflow execution
28+
except FileNotFoundError as exc: # pragma: no cover - guardrail for workflow execution
3129
raise SystemExit("No input.txt found to parse.") from exc
3230
if not text:
3331
raise SystemExit("No topic content provided.")
@@ -46,9 +44,7 @@ def _split_numbered_items(text: str) -> list[dict[str, str | list[str] | bool]]:
4644
Each returned item dict includes:
4745
title, lines, enumerator, continuity_break (bool)
4846
"""
49-
pattern = re.compile(
50-
r"^\s*(?P<enum>(?:\d+|[A-Za-z]\d+|[A-Za-z]))[\).:\-]\s+(?P<title>.+)$"
51-
)
47+
pattern = re.compile(r"^\s*(?P<enum>(?:\d+|[A-Za-z]\d+|[A-Za-z]))[\).:\-]\s+(?P<title>.+)$")
5248
items: list[dict[str, str | list[str] | bool]] = []
5349
current: dict[str, str | list[str] | bool] | None = None
5450
style: str | None = None # 'numeric' | 'alpha' | 'alphanum'
@@ -174,9 +170,7 @@ def _join_section(lines: list[str]) -> str:
174170
return "\n".join(lines).strip()
175171

176172

177-
def parse_text(
178-
text: str, *, allow_single_fallback: bool = False
179-
) -> list[dict[str, object]]:
173+
def parse_text(text: str, *, allow_single_fallback: bool = False) -> list[dict[str, object]]:
180174
"""Parse raw *text* into topic dictionaries.
181175
182176
Parameters

scripts/ci_coverage_delta.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,7 @@ def _build_payload(
5454
*,
5555
fail_on_drop: bool,
5656
) -> tuple[dict[str, Any], bool]:
57-
timestamp = (
58-
_dt.datetime.now(_dt.UTC)
59-
.replace(microsecond=0)
60-
.isoformat()
61-
.replace("+00:00", "Z")
62-
)
57+
timestamp = _dt.datetime.now(_dt.UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
6358
drop = max(0.0, baseline - current) if baseline > 0 else 0.0
6459
delta = current - baseline
6560
status: str
@@ -94,9 +89,7 @@ def main() -> int:
9489
baseline = _parse_float(
9590
os.environ.get("BASELINE_COVERAGE"), "BASELINE_COVERAGE", _DEFAULT_BASELINE
9691
)
97-
alert_drop = _parse_float(
98-
os.environ.get("ALERT_DROP"), "ALERT_DROP", _DEFAULT_ALERT_DROP
99-
)
92+
alert_drop = _parse_float(os.environ.get("ALERT_DROP"), "ALERT_DROP", _DEFAULT_ALERT_DROP)
10093
fail_on_drop = _truthy(os.environ.get("FAIL_ON_DROP"))
10194

10295
try:
@@ -105,13 +98,9 @@ def main() -> int:
10598
print(str(exc), file=sys.stderr)
10699
return 1
107100

108-
payload, should_fail = _build_payload(
109-
current, baseline, alert_drop, fail_on_drop=fail_on_drop
110-
)
101+
payload, should_fail = _build_payload(current, baseline, alert_drop, fail_on_drop=fail_on_drop)
111102

112-
output_path.write_text(
113-
json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8"
114-
)
103+
output_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
115104
print(f"Coverage delta written to {output_path}")
116105

117106
return 1 if should_fail else 0

scripts/ci_history.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,7 @@ def _build_history_record(
5454
metrics_path: Path,
5555
metrics_from_file: bool,
5656
) -> dict[str, Any]:
57-
timestamp = (
58-
_dt.datetime.now(_dt.UTC)
59-
.replace(microsecond=0)
60-
.isoformat()
61-
.replace("+00:00", "Z")
62-
)
57+
timestamp = _dt.datetime.now(_dt.UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
6358
summary = metrics.get("summary", {})
6459
failures = metrics.get("failures", [])
6560

@@ -85,12 +80,7 @@ def _build_history_record(
8580

8681

8782
def _build_classification_payload(metrics: dict[str, Any]) -> dict[str, Any]:
88-
timestamp = (
89-
_dt.datetime.now(_dt.UTC)
90-
.replace(microsecond=0)
91-
.isoformat()
92-
.replace("+00:00", "Z")
93-
)
83+
timestamp = _dt.datetime.now(_dt.UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
9484
failures = metrics.get("failures", []) or []
9585
counts = Counter(entry.get("status", "unknown") for entry in failures)
9686
payload: dict[str, Any] = {
@@ -119,9 +109,7 @@ def main() -> int:
119109
if classification_env is None:
120110
classification_env = os.environ.get("ENABLE_CLASSIFICATION_FLAG")
121111
classification_flag = _truthy(classification_env)
122-
classification_out = Path(
123-
os.environ.get("CLASSIFICATION_OUT", _DEFAULT_CLASSIFICATION)
124-
)
112+
classification_out = Path(os.environ.get("CLASSIFICATION_OUT", _DEFAULT_CLASSIFICATION))
125113

126114
if not junit_path.is_file():
127115
print(f"JUnit report not found: {junit_path}", file=sys.stderr)

scripts/ci_metrics.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,7 @@ def build_metrics(
205205

206206
payload: dict[str, Any] = {
207207
"generated_at": (
208-
_dt.datetime.now(_dt.UTC)
209-
.replace(microsecond=0)
210-
.isoformat()
211-
.replace("+00:00", "Z")
208+
_dt.datetime.now(_dt.UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
212209
),
213210
"junit_path": str(junit_path),
214211
"summary": summary,
@@ -226,19 +223,15 @@ def main() -> int:
226223
junit_path = Path(os.environ.get("JUNIT_PATH", _DEFAULT_JUNIT))
227224
output_path = Path(os.environ.get("OUTPUT_PATH", _DEFAULT_OUTPUT))
228225
top_n = _parse_int(os.environ.get("TOP_N"), "TOP_N", _DEFAULT_TOP_N)
229-
min_seconds = _parse_float(
230-
os.environ.get("MIN_SECONDS"), "MIN_SECONDS", _DEFAULT_MIN_SECONDS
231-
)
226+
min_seconds = _parse_float(os.environ.get("MIN_SECONDS"), "MIN_SECONDS", _DEFAULT_MIN_SECONDS)
232227

233228
try:
234229
payload = build_metrics(junit_path, top_n=top_n, min_seconds=min_seconds)
235230
except FileNotFoundError as exc:
236231
print(str(exc), file=sys.stderr)
237232
return 1
238233

239-
output_path.write_text(
240-
json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8"
241-
)
234+
output_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
242235
print(f"Metrics written to {output_path}")
243236
return 0
244237

scripts/sync_dev_dependencies.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,7 @@ def find_project_section_end(content: str) -> int | None:
131131
return len(content)
132132

133133

134-
def create_dev_dependencies_section(
135-
pins: dict[str, str], use_exact_pins: bool = True
136-
) -> str:
134+
def create_dev_dependencies_section(pins: dict[str, str], use_exact_pins: bool = True) -> str:
137135
"""Create a new dev dependencies section with core tools."""
138136
op = "==" if use_exact_pins else ">="
139137
deps = []
@@ -158,9 +156,7 @@ def extract_dependencies(section: str) -> list[tuple[str, str, str]]:
158156
deps = []
159157
# Match patterns like "package>=1.0.0" or "package==1.0.0" or just "package"
160158
# Be precise: package name followed by optional version specifier
161-
pattern = re.compile(
162-
r'"([a-zA-Z0-9_-]+)(?:(>=|==|~=|>|<|<=|!=)([^"\[\]]+))?(?:\[.*?\])?"'
163-
)
159+
pattern = re.compile(r'"([a-zA-Z0-9_-]+)(?:(>=|==|~=|>|<|<=|!=)([^"\[\]]+))?(?:\[.*?\])?"')
164160

165161
for match in pattern.finditer(section):
166162
package = match.group(1)
@@ -240,20 +236,12 @@ def sync_pyproject(
240236
opt_deps_pos = find_optional_dependencies_section(content)
241237
if opt_deps_pos is not None:
242238
# Add after [project.optional-dependencies] header
243-
content = (
244-
content[:opt_deps_pos]
245-
+ "\n"
246-
+ new_section
247-
+ "\n"
248-
+ content[opt_deps_pos:]
249-
)
239+
content = content[:opt_deps_pos] + "\n" + new_section + "\n" + content[opt_deps_pos:]
250240
else:
251241
# Need to add [project.optional-dependencies] section
252242
insert_pos = find_project_section_end(content)
253243
if insert_pos is None:
254-
return [], [
255-
"Could not find [project] section to add optional-dependencies"
256-
]
244+
return [], ["Could not find [project] section to add optional-dependencies"]
257245

258246
section_to_add = "\n[project.optional-dependencies]\n" + new_section + "\n"
259247
content = content[:insert_pos] + section_to_add + content[insert_pos:]

scripts/sync_test_dependencies.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,7 @@ def _read_local_modules() -> set[str]:
211211

212212
def get_project_modules() -> set[str]:
213213
"""Return the full set of project modules (static + dynamically detected + local)."""
214-
return (
215-
_BASE_PROJECT_MODULES | _detect_local_project_modules() | _read_local_modules()
216-
)
214+
return _BASE_PROJECT_MODULES | _detect_local_project_modules() | _read_local_modules()
217215

218216

219217
# For backward compatibility - will be populated on first use
@@ -366,9 +364,7 @@ def add_dependencies_to_pyproject(missing: set[str], fix: bool = False) -> bool:
366364
dev_group.multiline(True)
367365
optional[DEV_EXTRA] = dev_group
368366

369-
existing_normalised = {
370-
_normalise_package_name(str(item).split("[")[0]) for item in dev_group
371-
}
367+
existing_normalised = {_normalise_package_name(str(item).split("[")[0]) for item in dev_group}
372368

373369
added = False
374370
for package in sorted(missing):
@@ -387,9 +383,7 @@ def add_dependencies_to_pyproject(missing: set[str], fix: bool = False) -> bool:
387383

388384
def main(argv: list[str] | None = None) -> int:
389385
"""Main entry point."""
390-
parser = argparse.ArgumentParser(
391-
description="Sync test dependencies to pyproject.toml"
392-
)
386+
parser = argparse.ArgumentParser(description="Sync test dependencies to pyproject.toml")
393387
parser.add_argument(
394388
"--fix",
395389
action="store_true",

tools/coverage_trend.py

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -93,27 +93,15 @@ def main(args: list[str] | None = None) -> int:
9393
parser.add_argument("--coverage-xml", type=Path, help="Path to coverage.xml")
9494
parser.add_argument("--coverage-json", type=Path, help="Path to coverage.json")
9595
parser.add_argument("--baseline", type=Path, help="Path to baseline JSON")
96-
parser.add_argument(
97-
"--summary-path", type=Path, help="Path to output summary markdown"
98-
)
96+
parser.add_argument("--summary-path", type=Path, help="Path to output summary markdown")
9997
parser.add_argument("--job-summary", type=Path, help="Path to GITHUB_STEP_SUMMARY")
100-
parser.add_argument(
101-
"--artifact-path", type=Path, help="Path to output trend artifact"
102-
)
98+
parser.add_argument("--artifact-path", type=Path, help="Path to output trend artifact")
10399
parser.add_argument("--github-output", type=Path, help="Path to write env file")
100+
parser.add_argument("--minimum", type=float, default=70.0, help="Minimum coverage threshold")
101+
parser.add_argument("--hotspot-limit", type=int, default=15, help="Max hotspot files to show")
102+
parser.add_argument("--low-threshold", type=float, default=50.0, help="Low coverage threshold")
104103
parser.add_argument(
105-
"--minimum", type=float, default=70.0, help="Minimum coverage threshold"
106-
)
107-
parser.add_argument(
108-
"--hotspot-limit", type=int, default=15, help="Max hotspot files to show"
109-
)
110-
parser.add_argument(
111-
"--low-threshold", type=float, default=50.0, help="Low coverage threshold"
112-
)
113-
parser.add_argument(
114-
"--soft",
115-
action="store_true",
116-
help="Soft gate mode - report only, always exit 0",
104+
"--soft", action="store_true", help="Soft gate mode - report only, always exit 0"
117105
)
118106
parsed = parser.parse_args(args)
119107

@@ -161,9 +149,7 @@ def main(args: list[str] | None = None) -> int:
161149
"hotspots": hotspots,
162150
"low_coverage_files": low_coverage,
163151
}
164-
parsed.artifact_path.write_text(
165-
json.dumps(artifact_data, indent=2), encoding="utf-8"
166-
)
152+
parsed.artifact_path.write_text(json.dumps(artifact_data, indent=2), encoding="utf-8")
167153

168154
status = "✅ Pass" if passes_minimum else "❌ Below minimum"
169155
summary = f"""## Coverage Trend
@@ -180,9 +166,7 @@ def main(args: list[str] | None = None) -> int:
180166

181167
# Add hotspot tables if we have coverage data
182168
if hotspots:
183-
summary += _format_hotspot_table(
184-
hotspots, "Top Coverage Hotspots (lowest coverage)"
185-
)
169+
summary += _format_hotspot_table(hotspots, "Top Coverage Hotspots (lowest coverage)")
186170

187171
if low_coverage:
188172
summary += _format_hotspot_table(

0 commit comments

Comments
 (0)