Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ The plain-git decision does not *dissolve* the raw-vs-media trap for the repoint

That must return nothing. The scoping is not cosmetic: the form quoted here before was `… repos/`, which could never pass, because it matched this document's own occurrences of the string. Any restatement of this check must exclude the rules that describe it.

This is **not** covered by CI. The strict audit's URL-form check catches only the `github.com/*/raw/` form, and only in `lecture-wasm` ([#48](https://github.com/QuantEcon/data-lectures/pull/48)). A media-host reference to a data-lectures path classifies as `pattern: data-lectures`, so it **passes the audit while 404ing at read time** — verified end to end: a tree with all 12 `.md` reads left on the media host and `migration.yml` flipped to `repointed` exits `--strict` with code 0. `scripts/build_audit.py:196` already computes `lfs_media` per reference and nothing asserts on it; closing that is the first gate on the fold.
This **is** covered by CI now, for the repos the audit scans. A reference classified `pattern: data-lectures` fails the strict audit if it is on `media.githubusercontent.com`, and separately if its `(ref, path)` is anything but `main` + `lectures/<file>`, or if that file is not committed here yet. Those are two independent assertions on purpose: a media URL parses to exactly the same `ref` and `path` as the raw URL beside it, only the host differs, so neither check can stand in for the other. Before this, `scripts/build_audit.py` computed `lfs_media` per reference and asserted on it nowhere — a tree with all 12 `.md` reads left on the media host and `migration.yml` flipped to `repointed` exited `--strict` with code 0, verified end to end.

Note the three `_static/…/inequality/data.ipynb` reads are builder notebooks, which the audit does not scan as data reads at all *and* which the translation sync never carries (it is `.md`-only). They must be changed by hand in all three repos and checked by hand.
**What CI still cannot see is 9 of the fold's 21 reads.** `SCAN_REPOS` is the eight Python-family repos, so `lecture-intro.zh-cn`'s seven reads are outside it; `lectures/_static/**` is excluded by design, so the three `data.ipynb` copies are too. Those are builder notebooks, which the translation sync never carries either (it is `.md`-only), so they must be changed by hand in all three repos and checked by hand. The pre-merge grep in `lecture-python-intro` and `lecture-wasm` ([workspace-lectures#23](https://github.com/QuantEcon/workspace-lectures/issues/23) gate 2) does cover both of those repos' `_static` notebooks, which leaves `lecture-intro.zh-cn` as the only consumer with no automated check of any kind.

## Migration tracks

Expand Down
48 changes: 46 additions & 2 deletions scripts/build_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,9 @@ def scan(repos_dir: Path):

# Repoint rule 5 (PLAN): lecture-wasm executes code cells in the reader's
# browser, where the github.com/*/raw/ redirect fails CORS before it is
# followed — wasm reads must use raw.githubusercontent.com, or
# media.githubusercontent.com for LFS files. Only code-cell reads are
# followed — wasm reads must use raw.githubusercontent.com. (The media
# host is CORS-clean too, but it is never a valid target for a
# data-lectures path — see repoint rule 6 below.) Only code-cell reads are
# scanned, so {download} and prose links (navigations, CORS-exempt) can
# never trip this.
for d in datasets:
Expand All @@ -472,6 +473,49 @@ def scan(repos_dir: Path):
f"{r['url_form']} — fails CORS in the browser "
f"(repoint rule 5)")

# Repoint rule 6 (PLAN): media.githubusercontent.com is the LFS *media*
# endpoint. It routes per path, not per repo — it serves a file only where
# that path is LFS-tracked in the repo the URL names, and 404s otherwise.
# Everything this repo publishes is plain git, so the media host is never
# a valid way to read a data-lectures path, from any runtime. This has to
# be its own assertion: a media URL parses to exactly the same (ref, path)
# as the raw URL beside it, so the resolvability check below cannot see
# it, and rule 5 above only looks at url_form prefixes in lecture-wasm.
for d in datasets:
for r in d["refs"]:
if r["pattern"] == "data-lectures" and r.get("lfs_media"):
mig_problems.append(
f"{d['file']}: {r['repo']} {r['lecture']} reads it via "
f"media.githubusercontent.com — the LFS media host 404s "
f"plain-git files, and everything published here is plain "
f"git (repoint rule 6)")

# Does the URL resolve to a file this repo actually publishes? `pattern`
# is derived from org and repo alone (classify_url), so a repoint to a ref
# that does not exist, to a directory this repo does not use, or ahead of
# the bytes landing here, all classify as data-lectures and count as
# migrated. Each 404s for every reader while the audit stays green, so
# assert the two halves `pattern` does not carry: how the URL is spelled,
# and whether the file is in the published tree.
for d in datasets:
for r in d["refs"]:
if r["pattern"] != "data-lectures":
continue
where = f"{r['repo']} {r['lecture']}"
expected = f"lectures/{d['file']}"
if r.get("ref") != "main":
mig_problems.append(
f"{d['file']}: {where} pins ref {r.get('ref')!r} — this "
f"repo serves from main, and a stale ref 404s silently")
elif r.get("path") != expected:
mig_problems.append(
f"{d['file']}: {where} reads {r.get('path')!r}, but the "
f"published tree is flat — expected {expected!r}")
elif not (LECTURES / d["file"]).exists():
mig_problems.append(
f"{d['file']}: {where} already reads it from data-lectures "
f"but {expected} is not committed here yet")

audit = {
"generated": date.today().isoformat(),
"repos": {n: {"sha": repos[n]["sha"],
Expand Down
Loading