refactor: split into web/crawler/go-parser and drop the 2017 archives

Move the frontend into web/, the repo's only npm workspace, and replace the
JS crawler with a Go module covering both remaining datasets. The crawler
writes to a .part file and renames on completion: writing straight to the
destination left truncated files that the skip-if-present check would then
skip forever.

Remove the 2017-old and 2017-old2 datasets. They were successive publications
of the same exam, kept side by side so the disagreement stayed inspectable;
the current 2017 supersedes them and they remain in git history.

Recover the 2016 crawler source from the Internet Archive's copy of the
aggregator article, whose original host no longer resolves. All 119 filenames
are verified against data/2016 in both directions, but no archive captured the
spreadsheets themselves, so the host still serving them is unconfirmed and
data/2016 remains the only confirmed copy.

Filenames are load-bearing throughout: go-parser sorts inputs bytewise and
inserts last-wins, so they decide which row survives a duplicate exam number.
This commit is contained in:
tiennm99 committed 2026-08-13 21:45:05 +07:00
1 parent 8530df0154
commit ceb694a747
163 files changed
+1317 -510

No files matched your search

+14 -6
View File
@@ -49,25 +49,33 @@ jobs:
- name: Test parser
run: npm run test:go
# The crawler is not part of the build — it only refreshes data/ by hand.
# It is still compiled and tested here so it cannot rot unnoticed, and
# because its filename test guards the parser: input filenames decide
# which row survives a duplicate exam number.
- name: Test crawler
run: npm run test:crawler
# excelize carries an open advisory, and the 2017 refresh runbook feeds
# network-downloaded spreadsheets straight into the parser.
- name: Vulnerability scan
working-directory: go-parser
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
"$(go env GOPATH)/bin/govulncheck" ./...
GOVULNCHECK="$(go env GOPATH)/bin/govulncheck"
(cd go-parser && "$GOVULNCHECK" ./...)
(cd crawler && "$GOVULNCHECK" ./...)
# One parser binary builds every dataset; build-db.js reads the dataset
# list from src/datasets.js, verifies each database against its known row
# list from web/src/datasets.js, verifies each database against its row
# count, then gzips it in place leaving no uncompressed file behind.
- name: Build databases
run: |
npm run build:go
npm run build:db
# One Vite build produces every page. scripts/assemble-site.js copies the
# emitted index.html to each dataset path (and the legacy nested URLs),
# then fails the job if any uncompressed database reached the artifact.
# One Vite build produces every page. web/scripts/assemble-site.js copies
# the emitted index.html to each dataset path (and the legacy nested
# URLs), then fails the job if an uncompressed database reached _site.
- name: Build and assemble site
run: npm run build:site