Skip to content

Commit 24ead28

Browse files
committed
fix(ci): retry refresh PR creation and compare validation to base
1 parent 06703a3 commit 24ead28

3 files changed

Lines changed: 69 additions & 26 deletions

File tree

‎.github/workflows/techapi-pr-validation-comment.yml‎

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,22 @@ jobs:
5151
repository: GetTechAPI/TechAPI
5252
ref: ${{ env.TECHAPI_HEAD_SHA }}
5353
path: TechAPI
54+
fetch-depth: 0
5455

55-
- name: Checkout TechAPI main
56+
- name: Checkout TechAPI PR base
5657
uses: actions/checkout@v4
5758
with:
5859
repository: GetTechAPI/TechAPI
59-
ref: main
60-
path: TechAPI-main
60+
ref: develop
61+
path: TechAPI-base
62+
fetch-depth: 0
63+
64+
- name: Pin PR base to the merge base
65+
shell: bash
66+
run: |
67+
git -C TechAPI fetch --no-tags origin develop
68+
base_sha="$(git -C TechAPI merge-base origin/develop HEAD)"
69+
git -C TechAPI-base checkout --detach "$base_sha"
6170
6271
- uses: actions/setup-python@v6
6372
with:
@@ -100,7 +109,7 @@ jobs:
100109
return files
101110
102111
head = rel_site_files(Path("TechAPI"))
103-
base = rel_site_files(Path("TechAPI-main"))
112+
base = rel_site_files(Path("TechAPI-base"))
104113
added = sorted(set(head) - set(base))
105114
deleted = sorted(set(base) - set(head))
106115
modified = sorted(key for key in set(head) & set(base) if digest(head[key]) != digest(base[key]))
@@ -135,11 +144,26 @@ jobs:
135144
136145
{
137146
echo
138-
echo "## integrity_check.py --strict"
139-
python integrity_check.py TechAPI/data --strict
140-
echo "integrity_status=$?"
147+
echo "## integrity_check.py (PR head compared with PR base)"
148+
python integrity_check.py TechAPI/data --hard-report head-hard.json
149+
echo "head_integrity_scan_status=$?"
150+
python integrity_check.py TechAPI-base/data --hard-report base-hard.json > baseline-integrity.log
151+
echo "base_integrity_scan_status=$?"
141152
} >> validation.log 2>&1
142-
integrity_status=$(grep "integrity_status=" validation.log | tail -n 1 | cut -d= -f2)
153+
integrity_status=$(python - <<'PY'
154+
import json
155+
from pathlib import Path
156+
157+
head = set(json.loads(Path("head-hard.json").read_text(encoding="utf-8")))
158+
base = set(json.loads(Path("base-hard.json").read_text(encoding="utf-8")))
159+
introduced = sorted(head - base)
160+
with Path("validation.log").open("a", encoding="utf-8") as log:
161+
log.write(f"\nBaseline hard anomalies: {len(base)}; PR head: {len(head)}; introduced: {len(introduced)}\n")
162+
for anomaly in introduced:
163+
log.write(f"NEW HARD ANOMALY: {anomaly}\n")
164+
print("1" if introduced else "0")
165+
PY
166+
)
143167
144168
sed -i '/_status=/d' validation.log
145169
@@ -173,7 +197,6 @@ jobs:
173197
- name: Build data quality summary
174198
shell: bash
175199
run: |
176-
git -C TechAPI fetch --no-tags --depth=1 origin main
177200
python - <<'PY'
178201
from __future__ import annotations
179202
@@ -186,7 +209,7 @@ jobs:
186209
from typing import Any
187210
188211
HEAD = Path("TechAPI/data")
189-
BASE = Path("TechAPI-main/data")
212+
BASE = Path("TechAPI-base/data")
190213
CATEGORIES = (
191214
"brand",
192215
"soc",
@@ -227,8 +250,7 @@ jobs:
227250
"diff",
228251
"--name-status",
229252
"--no-renames",
230-
"FETCH_HEAD",
231-
"HEAD",
253+
"origin/develop...HEAD",
232254
"--",
233255
"data",
234256
],
@@ -469,7 +491,7 @@ jobs:
469491
site_lines.append("## Changed site")
470492
site_lines.append("")
471493
head_site = rel_site_files(Path("TechAPI"))
472-
base_site = rel_site_files(Path("TechAPI-main"))
494+
base_site = rel_site_files(Path("TechAPI-base"))
473495
site_added = sorted(set(head_site) - set(base_site))
474496
site_deleted = sorted(set(base_site) - set(head_site))
475497
site_modified = sorted(
@@ -573,12 +595,13 @@ jobs:
573595
continue
574596
if current_section and line.startswith(" "):
575597
section_counts[current_section] += 1
576-
if any(token in line for token in ("DUP ", "slug!=file", " > ")):
577-
hard_lines.append(line)
598+
if line.startswith("NEW HARD ANOMALY: "):
599+
hard_lines.append(line.removeprefix("NEW HARD ANOMALY: "))
578600
579601
out: list[str] = []
580602
out.append("## Validation notes")
581603
out.append("")
604+
out.append("- The integrity gate blocks only hard anomalies introduced by this PR relative to its merge base.")
582605
out.append("- Full advisory outlier listings are suppressed on successful runs because they are dataset-wide and mostly stable between PRs.")
583606
out.append("- Failure runs still include a detailed log excerpt for debugging.")
584607
if key_lines:
@@ -596,7 +619,7 @@ jobs:
596619
out.append(f"| {name} | {count} |")
597620
if hard_lines:
598621
out.append("")
599-
out.append("Potential blocking lines:")
622+
out.append("New blocking anomalies:")
600623
out.append("")
601624
out.append("```text")
602625
out.extend(hard_lines[:80])
@@ -654,7 +677,7 @@ jobs:
654677
echo "| Check | Result |"
655678
echo "| --- | --- |"
656679
echo "| \`python -m app.validate\` | $([ "${{ steps.validate.outputs.app_status }}" = "0" ] && echo PASS || echo FAIL) |"
657-
echo "| \`python integrity_check.py TechAPI/data --strict\` | $([ "${{ steps.validate.outputs.integrity_status }}" = "0" ] && echo PASS || echo FAIL) |"
680+
echo "| New hard integrity anomalies vs PR base | $([ "${{ steps.validate.outputs.integrity_status }}" = "0" ] && echo PASS || echo FAIL) |"
658681
if [ "${site_changed}" = "true" ]; then
659682
echo "| \`cd TechAPI/site && npm ci && npm run build\` | $([ "${site_build_status}" = "0" ] && echo PASS || echo FAIL) |"
660683
fi

‎.github/workflows/weekly-refresh.yml‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,21 @@ jobs:
220220
env:
221221
GH_TOKEN: ${{ env.TECHAPI_WRITE_TOKEN }}
222222
run: |
223-
pr_count="$(gh pr list --repo GetTechAPI/TechAPI --head refresh/${{ steps.meta.outputs.date }} --base main --state open --json number --jq length)"
224-
if [ "$pr_count" -gt 0 ]; then
225-
exit 0
226-
fi
227-
228-
echo "::error::Refresh PR refresh/${{ steps.meta.outputs.date }} was not created."
223+
set -euo pipefail
224+
branch="refresh/${{ steps.meta.outputs.date }}"
225+
title="chore(data): weekly refresh ${{ steps.meta.outputs.date }}"
226+
for attempt in 1 2 3; do
227+
if gh pr list --repo GetTechAPI/TechAPI --head "$branch" --base main \
228+
--state open --json number --jq 'length' | grep -qx '[1-9][0-9]*'; then
229+
exit 0
230+
fi
231+
# create-pull-request may have pushed the branch before its API call
232+
# failed. Retry just the PR API operation without repeating the scrape.
233+
if gh pr create --repo GetTechAPI/TechAPI --head "$branch" --base main \
234+
--title "$title" --body-file pr-body.md; then
235+
exit 0
236+
fi
237+
if [ "$attempt" -lt 3 ]; then sleep 15; fi
238+
done
239+
echo "::error::Refresh PR $branch was not created after three attempts."
229240
exit 1

‎integrity_check.py‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
Usage::
99
10-
python integrity_check.py [DATA_ROOT] [--strict]
10+
python integrity_check.py [DATA_ROOT] [--strict] [--hard-report PATH]
1111
1212
By default it prints every flagged item and exits 0 (human-review mode). With
1313
``--strict`` it additionally exits non-zero when any *hard* anomaly is found —
@@ -16,6 +16,7 @@
1616
The statistical cross-source/era outliers stay advisory (a heterogeneous catalog
1717
of server + desktop + mobile parts legitimately produces many ratio outliers), so
1818
they are printed for review but never fail the gate.
19+
``--hard-report`` writes a JSON list of hard anomalies for baseline comparison.
1920
"""
2021
from __future__ import annotations
2122
import os, json, math, re, statistics, sys
@@ -28,6 +29,10 @@
2829

2930
_argv = sys.argv[1:]
3031
STRICT = "--strict" in _argv
32+
_report_index = _argv.index("--hard-report") if "--hard-report" in _argv else -1
33+
HARD_REPORT = _argv[_report_index + 1] if _report_index >= 0 else None
34+
if _report_index >= 0:
35+
del _argv[_report_index:_report_index + 2]
3136
_positional = [a for a in _argv if not a.startswith("-")]
3237
ROOT = _positional[0] if _positional else r"C:\Users\29\Desktop\TechAPI\data"
3338

@@ -70,9 +75,9 @@ def section(t): print(f"\n### {t}")
7075
if d.get("slug") != fn:
7176
hard(f" [{comp}] slug!=file: {fn} slug={d.get('slug')}")
7277
for s, fl in slugs.items():
73-
if len(fl) > 1: hard(f" [{comp}] DUP slug {s}: {fl}")
78+
if len(fl) > 1: hard(f" [{comp}] DUP slug {s}: {sorted(fl)}")
7479
for n, fl in names.items():
75-
if len(fl) > 1: hard(f" [{comp}] DUP name {n!r}: {fl}")
80+
if len(fl) > 1: hard(f" [{comp}] DUP name {n!r}: {sorted(fl)}")
7681

7782
# --- 2. AMD Ryzen line vs DESKTOP model tier-digit (2nd digit); APU/mobile excepted ---
7883
section("CPU name/tier consistency (desktop mainstream only)")
@@ -130,6 +135,10 @@ def collect(recs, fa, fb):
130135

131136
print("\n(no lines under a section = clean)")
132137

138+
if HARD_REPORT:
139+
with open(HARD_REPORT, "w", encoding="utf-8") as report:
140+
json.dump(sorted(set(HARD)), report, ensure_ascii=False, indent=2)
141+
133142
if STRICT and HARD:
134143
print(f"\n❌ integrity gate: {len(HARD)} hard anomaly(ies) — blocking refresh.")
135144
sys.exit(1)

0 commit comments

Comments
 (0)