From bfc30dfe66feafc92fa6999a1b78efd16a4628a5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 17:55:41 +0000 Subject: [PATCH] Fix extraction races and churn; pin deps; bump actions - extract-pdf-text: queue runs (concurrency group), check out latest main, and rebase-and-retry the push, so merges landing close together no longer fail with a rejected push. - extract_pdf_text.py: skip files whose extracted text is unchanged instead of rewriting every file to bump the extracted: date. - Pin pyyaml==6.0.3 and pypdf==6.21.4 (reproduces the committed _extracted/ text exactly), so output only changes when a PDF does. validate-corpus now installs from requirements.txt too. - actions/checkout v4 -> v5, actions/setup-python v5 -> v6 (Node 24). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015bzez2N6vdSh6qXV7Y44P9 --- .github/workflows/extract-pdf-text.yaml | 22 +++++++++++++++++++--- .github/workflows/process-inbox.yaml | 4 ++-- .github/workflows/validate-corpus.yaml | 7 ++++--- scripts/extract_pdf_text.py | 16 +++++++++++++--- scripts/requirements.txt | 4 ++-- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/.github/workflows/extract-pdf-text.yaml b/.github/workflows/extract-pdf-text.yaml index a76796b..1097196 100644 --- a/.github/workflows/extract-pdf-text.yaml +++ b/.github/workflows/extract-pdf-text.yaml @@ -7,20 +7,30 @@ on: - "corpus/**/*.pdf" - "corpus/MANIFEST.yaml" - "scripts/extract_pdf_text.py" + - "scripts/requirements.txt" workflow_dispatch: {} permissions: contents: write +# Merges that land close together would otherwise race to push to main and +# all but one would fail. Queue runs instead; each one checks out the latest +# main, so the last run always covers everything merged before it. +concurrency: + group: extract-pdf-text + cancel-in-progress: false + jobs: extract: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 + with: + ref: main - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.11" @@ -39,5 +49,11 @@ jobs: echo "No changes to commit." else git commit -m "Extract PDF text for search index ($(date -u +%Y-%m-%d))" - git push + # A push can still lose to a merge that lands mid-run; rebase onto + # it and retry. _extracted/ is only written here, so no conflicts. + for i in 1 2 3; do + git push && exit 0 + git pull --rebase origin main + done + exit 1 fi diff --git a/.github/workflows/process-inbox.yaml b/.github/workflows/process-inbox.yaml index fe05d60..e32a241 100644 --- a/.github/workflows/process-inbox.yaml +++ b/.github/workflows/process-inbox.yaml @@ -24,7 +24,7 @@ jobs: # be written to, so they get a dry run and the report in the job summary. SAME_REPO: ${{ github.event.pull_request.head.repo.full_name == github.repository }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} @@ -34,7 +34,7 @@ jobs: # processed commit. token: ${{ secrets.CORPUS_BOT_TOKEN || github.token }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.11" diff --git a/.github/workflows/validate-corpus.yaml b/.github/workflows/validate-corpus.yaml index 3be6df5..fc60c6b 100644 --- a/.github/workflows/validate-corpus.yaml +++ b/.github/workflows/validate-corpus.yaml @@ -6,18 +6,19 @@ on: - "corpus/**" - "inbox/**" - "scripts/validate_corpus.py" + - "scripts/requirements.txt" jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: "3.11" - name: Install dependencies - run: pip install pyyaml + run: pip install -r scripts/requirements.txt - name: Validate corpus manifest run: python scripts/validate_corpus.py --repo-root . diff --git a/scripts/extract_pdf_text.py b/scripts/extract_pdf_text.py index febd13b..ad9d598 100644 --- a/scripts/extract_pdf_text.py +++ b/scripts/extract_pdf_text.py @@ -63,10 +63,20 @@ def extract_one(repo_root: str, pdf_path: str, resource_id: str, license_: str) f"\n" ) + body = "\n\n".join(pages) + "\n" + + # Leave unchanged files alone; otherwise every run rewrites every file + # just to bump the `extracted:` date in the header. + if os.path.isfile(out_abs): + with open(out_abs, "r", encoding="utf-8") as f: + old = f.read() + if old.startswith(header.split("# extracted:")[0]) and old.endswith("\n\n" + body): + print(f"[extract_pdf_text] unchanged {out_rel}") + return False + with open(out_abs, "w", encoding="utf-8") as f: f.write(header) - f.write("\n\n".join(pages)) - f.write("\n") + f.write(body) print(f"[extract_pdf_text] wrote {out_rel} ({len(pages)} pages)") return True @@ -111,7 +121,7 @@ def main() -> None: if extract_one(repo_root, pdf_path, r["id"], r.get("license", "UNKNOWN")): wrote += 1 - print(f"[extract_pdf_text] done: {wrote} file(s) extracted") + print(f"[extract_pdf_text] done: {wrote} file(s) written") if __name__ == "__main__": diff --git a/scripts/requirements.txt b/scripts/requirements.txt index aefe988..d9ec199 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,2 +1,2 @@ -pyyaml -pypdf +pyyaml==6.0.3 +pypdf==6.21.4