Skip to content

Commit 848786f

Browse files
committed
fix: minor fix tested against real logs
1 parent abf032d commit 848786f

1 file changed

Lines changed: 53 additions & 32 deletions

File tree

.github/scripts/dep_table.py

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
44
Usage: dep_table.py BASELINE.zip AFTER.zip
55
6-
Each archive is a GitHub Actions run-log zip. The `deps` CI job prints two
7-
marker-delimited boostdep reports into its log:
6+
Each archive is a GitHub Actions run-log zip. The `deps` job of the
7+
dependency-report workflow prints two marker-delimited boostdep reports into
8+
its log:
89
910
===DEP-BRIEF-START=== boostdep --brief graph ===DEP-BRIEF-END===
1011
===DEP-PRIMARY-START=== boostdep graph (primary) ===DEP-PRIMARY-END===
1112
1213
From those it derives two metrics and prints their delta vs the baseline:
13-
- the set of transitive Boost modules graph depends on (from --brief), and
14+
- the set of transitive Boost modules graph depends on (from --brief:
15+
Primary + Secondary sections = the full transitive closure), and
1416
- the header-inclusion weight of each direct dependency (from the primary
15-
report: how many distinct boost/graph/* headers pull that dependency in).
17+
report: how many distinct boost/graph files pull that dependency in).
1618
Weights are the live signal, they drop toward zero as coupling is removed;
1719
the module count is the coarse target that only moves when a dep hits zero.
1820
"""
@@ -22,61 +24,80 @@
2224

2325
BRIEF = ("===DEP-BRIEF-START===", "===DEP-BRIEF-END===")
2426
PRIMARY = ("===DEP-PRIMARY-START===", "===DEP-PRIMARY-END===")
27+
# Module names as boostdep prints them, e.g. "numeric~conversion".
28+
MODULE = re.compile(r"[A-Za-z0-9_.~-]+")
29+
TIMESTAMP = re.compile(r"^\d{4}-\d\d-\d\dT[\d:.]+Z\s?") # GitHub log line prefix
30+
ANSI = re.compile(r"\x1b\[[0-9;]*m")
2531

2632

27-
def read_logs(zip_path):
28-
"""Concatenate the top-level per-job .txt logs from a run-log archive."""
29-
out = []
33+
def read_clean_lines(zip_path):
34+
"""Top-level per-job logs, with GitHub timestamp and ANSI prefixes stripped."""
35+
lines = []
3036
with zipfile.ZipFile(zip_path) as z:
3137
for entry in z.namelist():
3238
if "/" in entry or not entry.endswith(".txt"):
3339
continue
34-
out.append(z.read(entry).decode("utf-8", "replace"))
35-
return "\n".join(out)
36-
37-
38-
def block(text, markers):
39-
m = re.search(re.escape(markers[0]) + r"(.*?)" + re.escape(markers[1]), text, re.S)
40-
return m.group(1) if m else ""
40+
for ln in z.read(entry).decode("utf-8", "replace").splitlines():
41+
lines.append(TIMESTAMP.sub("", ANSI.sub("", ln)))
42+
return lines
43+
44+
45+
def block(lines, markers):
46+
"""Lines strictly between the marker lines, matched exactly.
47+
48+
Exact matching is what skips the echoed `echo "<marker>"` command lines
49+
GitHub prepends to a run step, so we capture boostdep's real stdout.
50+
"""
51+
start, end = markers
52+
out, capturing = [], False
53+
for ln in lines:
54+
s = ln.strip()
55+
if s == start:
56+
capturing = True
57+
continue
58+
if s == end and capturing:
59+
break
60+
if capturing:
61+
out.append(ln)
62+
return out
4163

4264

43-
def parse_brief(text):
65+
def parse_brief(lines):
4466
"""--brief output -> set of module names (one bare name per line)."""
4567
mods = set()
46-
for line in text.splitlines():
47-
s = line.strip()
68+
for ln in lines:
69+
s = ln.strip()
4870
if not s or s.startswith("#") or s.lower().startswith("brief dependency"):
4971
continue
50-
if re.fullmatch(r"[A-Za-z0-9_.-]+", s):
72+
if MODULE.fullmatch(s):
5173
mods.add(s)
5274
return mods
5375

5476

55-
def parse_weights(text):
56-
"""Primary report -> {dependency: number of distinct boost/graph headers that pull it in}."""
77+
def parse_weights(lines):
78+
"""Primary report -> {dependency: number of distinct graph files that pull it in}."""
5779
weights, cur = {}, None
58-
for line in text.splitlines():
59-
head = re.match(r"^([A-Za-z0-9_.-]+):\s*$", line) # "module:" at column 0
80+
for ln in lines:
81+
head = re.match(r"^([A-Za-z0-9_.~-]+):\s*$", ln) # "module:" at column 0
6082
if head:
6183
cur = head.group(1)
6284
weights.setdefault(cur, set())
6385
continue
64-
frm = re.match(r"^\s+from\s+(.+?)\s*$", line)
86+
frm = re.match(r"^\s+from\s+(.+?)\s*$", ln)
6587
if frm and cur is not None:
6688
weights[cur].add(frm.group(1))
6789
return {k: len(v) for k, v in weights.items()}
6890

6991

7092
def main(base_zip, pr_zip):
71-
base, pr = read_logs(base_zip), read_logs(pr_zip)
72-
base_mods = parse_brief(block(base, BRIEF))
73-
pr_mods = parse_brief(block(pr, BRIEF))
74-
base_w = parse_weights(block(base, PRIMARY))
75-
pr_w = parse_weights(block(pr, PRIMARY))
76-
77-
# Header-inclusion weights: the live signal. Show only rows that changed,
78-
# most-reduced first.
79-
print("**Header-inclusion weights** (graph headers pulling each direct dependency in):")
93+
bl, pl = read_clean_lines(base_zip), read_clean_lines(pr_zip)
94+
base_mods = parse_brief(block(bl, BRIEF))
95+
pr_mods = parse_brief(block(pl, BRIEF))
96+
base_w = parse_weights(block(bl, PRIMARY))
97+
pr_w = parse_weights(block(pl, PRIMARY))
98+
99+
# Header-inclusion weights: the live signal. Only rows that changed, most-reduced first.
100+
print("**Header-inclusion weights** (graph files pulling each direct dependency in):")
80101
print()
81102
changed = []
82103
for dep in base_w.keys() | pr_w.keys():

0 commit comments

Comments
 (0)