Skip to content

ci(book): Eisvogel 3.4.0, and stop defining \chead twice - #148

Open
Kartikey1306 wants to merge 9 commits into
embeddedos-org:masterfrom
Kartikey1306:ci/book-pdf-eisvogel-port
Open

Kartikey1306 wants to merge 9 commits into
embeddedos-org:masterfrom
Kartikey1306:ci/book-pdf-eisvogel-port

Conversation

@Kartikey1306

Copy link
Copy Markdown
Contributor

Stacked on #132 (master does not build without it; a branch cut from master goes red on CI — ebuild for reasons unrelated to this change). Review the single commit at the head of this branch: one file, .github/workflows/book-build.yml, +47/−13. Land #132 first; this rebases cleanly and drops to its one commit.

Closes #147.

Build & Release Book PDF has never succeeded on this repository — every run back to the 2026-05-28 tag build dies at ! LaTeX Error: Command \chead already defined. (exit 43). The workflow is byte-identical to eBoot's pre-fix copy (diff against eBoot e152d8e is empty), and eBoot#135 diagnosed and fixed it there over three runs. This is that fix, ported unchanged.

What was wrong — five things, one of which is the cause

  1. The cause. The pandoc step injected \usepackage{fancyhdr} + \fancyhead/\fancyfoot through header-includes. Eisvogel builds its headers with KOMA's scrlayer-scrpage, which defines \chead; $header-includes$ sits before the template's own header block, so fancyhdr defined \chead first and the template's \newcommand failed. Both arrived in the same April commit, so no Eisvogel/pandoc pair has ever produced a green run. Headers and footer now go through the template's own variables: -V header-left=\leftmark -V header-right=<version> -V footer-center=….
  2. -V "author=… & …" is verbatim LaTeX, and & is an alignment tab. Removed; docs/book/book.md:3 carries the author in front matter, which pandoc escapes.
  3. Eisvogel 2.4.0 → 3.4.0 via EISVOGEL_VERSION. 2.4.0 predates pandoc 3.6. 3.5.x switched to sourcesans.sty, which the runner's texlive-fonts-extra does not ship (eBoot run 1 proved this) — 3.4.0 is the last release that works. The 3.x tarball extracts into Eisvogel-<v>/, so the cp names it.
  4. tar xzf … 2>/dev/null || true removed. A bad tarball must fail at the step that broke, not at the cp after it.
  5. The workflow is added to its own paths: filter (push and pull_request), so this PR gets the run that proves it. Before, a workflow-only change could not trigger the job at all.

eBoot's run 2 showed the version bump alone does not fix it — item 1 is load-bearing.

Verification

Structural, on the parsed document — not yaml.safe_load succeeding, which proves only that it is YAML:

assertion result
push.paths == ['docs/book/**', '.github/workflows/book-build.yml'], same for pull_request PASS
push.tags == ['v*'] — release trigger intact PASS
push.branches, release.types, workflow_dispatch unchanged PASS
env.EISVOGEL_VERSION == '3.4.0' PASS
no non-comment line in the pandoc step mentions fancyhdr / fancyhead / pagestyle{fancy} PASS
no -V author override remains PASS
no \-continued line is followed by a comment or blank (which silently ends the command) PASS
Eisvogel step has no || true; cp names Eisvogel-${EISVOGEL_VERSION}/ PASS
all 16 workflow files parse with a jobs map PASS
ruff check . PASS
pytest tests/ PASS — 683 passed, 1 skipped
evidence the fix works
eBoot#135 Build & Release Book PDF at abf0522 success, run 34937741463, eBoot-guide.pdf 48 pages — first green in that job's history
ebuild master copy vs eBoot pre-fix copy byte-identical
git apply --check of the eBoot diff against ebuild master clean

NOT RUN here: the PDF build itself. It needs the runner's TeX Live; this PR's own Build & Release Book PDF run is the proof for this repository, and it will fire because of item 5.

@codecov-commenter

codecov-commenter commented Sep 15, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Proven on this repository. The paths-filter change (item 5) fired this PR's own Build & Release Book PDF, and it is green — run 34938663921 at 66724a8:

Validate Book Source   success
Build PDF              success
Build Summary          success
Attach PDF to Release  skipped   (release-only, correct)
Update Dev PDF         skipped   (push-to-master-only, correct)

Eisvogel 3.4.0 template installed
PDF: docs/book/ebuild-guide.pdf (540K, 37 pages, 549733 bytes)

That is the first success in this job's history here — every prior run back to the 2026-05-28 tag build failed at \chead already defined. Every other check on this head is green too (CI — ebuild, CodeQL, Linked issue policy, Vendored core drift, Simulation Test).

The "NOT RUN here" in the body is now run: the PDF build itself is the evidence, on this repo's own runner, not inferred from eBoot.

No further push planned; head stays 66724a8. Land #132 first, as the body says.

CI -- ebuild has been red on master since the 09-08 batch merge, and the
first failing step (ruff) has hidden the ones behind it.

Lint (ruff, all nine Test legs):
- test_build_dir_resolution.py imported shutil twice (F811).
- test_package_recipe.py lost its trailing newline (W292).
- test_ci_gate.py had `import itertools` / `import re` two hundred lines
  down (E402) -- my own embeddedos-org#103, replayed onto a file that had moved.
  These three hunks are byte-identical to embeddedos-org#122's, so either PR merging
  first leaves the other clean.

Type check and tests (never reached on master since 09-08):
- ebuild/packages/index_sync.py calls PackageRecipe.to_dict(), which embeddedos-org#111
  defined and embeddedos-org#112 -- merged five minutes later from a base without it --
  deleted in its replay. mypy names it once; pytest fails nine
  test_index_sync cases with AttributeError. The method is restored verbatim
  from embeddedos-org#111 (cc90078): it emits the `package:`/`build:` keys parse_recipe()
  reads back, which an asdict() replacement would not.

Vendored core drift:
- embeddedos-org#109 (dba3d83) edited core/eos/docs/three-way-alignment.md, a vendored copy
  pinned to eos 5544c98, so drift went 44 -> 45 and the guard failed as
  designed. Reverted to the pinned content (blob 7f9c8c1, the same bytes as
  eos:docs/three-way-alignment.md at the pin). The alignment note belongs in
  ebuild's own docs or upstream in eos, not in the snapshot.

OSSF Scorecard:
- ossf/scorecard-action@v2.4.0 pulls gcr.io/openssf/scorecard-action, and
  gcr.io now refuses the pull ("requires billing to be enabled"). v2.4.3
  pulls from ghcr.io; eos already pins it and its Scorecard job is green.

Not in this PR: EoSim Sanity's Windows/macOS legs install a wheel that has
never been published; embeddedos-org#121 (srpatcha) already replaces that with the clone
the other legs use.

Verified locally: ruff clean, yamllint clean, mypy clean over 107 files,
680 passed / 1 skipped, scripts/check_vendor_drift.py 44/44 and 46/46.
…/3.11, yamllint on Windows

Both surfaced on this branch's first CI run, once ruff let the job get past
its first step.

- ebuild/plugins/__init__.py: on Python 3.10 and 3.11 the stubs type
  entry_points() as the deprecated mapping, and its .get() wants an
  EntryPoints default, so mypy fails with arg-type. The line carried a
  '# type: ignore[attr-defined]' -- the wrong error code, so it suppressed
  nothing. Spelled out with a cast, byte-identical to embeddedos-org#122's hunk (54605f0).

- .yamllint.yml: the Windows runners check out with core.autocrlf=true, so
  every YAML file arrives as CRLF and the default new-lines: unix rule
  rejected every line. The step was added on 09-03 and had never passed on
  that leg. new-lines: platform accepts the checkout's own convention.
…e checkout's line ending

new-lines: platform was the wrong fix. The Windows runners' autocrlf turns
LF files into CRLF -- except a file that already carries a stray CR, which
git leaves alone, and auto-assign.yml had one on its last line. So under
'platform' Windows expected CRLF and got LF on that file's first line, and
the leg was red again for the opposite reason.

Pin *.yml and *.yaml to eol=lf so every OS lints the same bytes, keep
yamllint's default unix rule, and drop the stray CR.
…h a round-trip test

PackageRecipe.to_dict() was written before install_args existed and was
never taught about it, so a recipe that went through index_sync came back
from the cache with install_args empty while every other field survived.
It also returned the recipe's own list objects, so a caller that appended
to what it got back edited the recipe behind its back.

install_args is now emitted after build_args, matching the order
parse_recipe() reads them, and every list field is copied on the way out.
index_sync's entry-to-recipe mapping carries install_args too; without
that the field could not arrive from an index at all.

The new round-trip test builds a recipe with every field set and asserts
parse_recipe(safe_load(safe_dump(to_dict()))) equals it; against the
previous to_dict() it fails on install_args. A second test checks the
lists are copies, and test_index_sync gains a case that an index entry's
install_args reaches the cached YAML.
actions/checkout, ossf/scorecard-action and codeql-action/upload-sarif
were referenced by moving tags. A tag can be re-pointed; a commit cannot,
and Scorecard itself flags unpinned actions. Each is now pinned to the
commit its tag resolved to on 2026-09-14, with the tag kept in a trailing
comment, the same shape linked-issue.yml already uses.
The changelog records what the master repair changed and why. CONTRIBUTING
gains a note for Windows contributors: .gitattributes now pins YAML to LF,
but the attribute governs future checkouts and commits, not files already
in a working tree, so an existing clone needs one git add --renormalize .
(or a fresh clone) before yamllint stops seeing CRLF.
…working tree

git add --renormalize . re-applies the clean filter to the index; it never
rewrites files, so a clone with CRLF YAML still has CRLF YAML afterwards
(reproduced in a scratch clone with core.autocrlf=true: two CRs before,
two after, status clean). What re-checks the files out is
git rm --cached -r . && git reset --hard HEAD, or a fresh clone. CONTRIBUTING
and the changelog now say that. The changelog entry also lists every file
it describes and the plugins/__init__.py type-check fix it had left out, and
the to_dict() comment no longer claims to follow parse_recipe()'s order.
The review at 43b0337 found the one-line assertion pinning install_args
next to build_args and, six lines above it, recipe.py's docstring saying
key order does not matter. Both are true: order is not a correctness
property, because parse_recipe() reads every key by name, and it is a
stability property, because index_sync writes the dict as cached YAML that
humans diff. The assertion now says which of the two it is guarding and
what to do when a reordering is deliberate, so a future failure reads as
intentional rather than as a mystery.

No behaviour change. ruff clean; pytest 683 passed, 1 skipped.
Build & Release Book PDF has never succeeded here. Every run back to the
2026-05-28 tag build dies with

    ! LaTeX Error: Command \chead already defined.

The workflow is byte-identical to eBoot's pre-fix copy, and eBoot#135
found the cause there, over three runs; this is that fix, ported unchanged.

The pandoc step injected \usepackage{fancyhdr} and \fancyhead/\fancyfoot
through header-includes. Eisvogel builds its headers with KOMA's
scrlayer-scrpage, which defines \chead, and $header-includes$ sits before
the template's own header block -- so fancyhdr defined \chead first and
the template's \newcommand failed. Both arrived in the same April commit,
which is why no Eisvogel or pandoc version ever made it green. Header and
footer now go through the template's own variables (header-left,
header-right, footer-center).

The -V author override is gone too: a -V value is verbatim LaTeX, and the
'&' in the author string is an alignment tab. The front matter in
docs/book/book.md carries the author and pandoc escapes metadata.

Eisvogel 2.4.0 -> 3.4.0: 2.4.0 predates pandoc 3.6; 3.5.x moved to
sourcesans.sty, which the runner's texlive-fonts-extra does not ship, so
3.4.0 is the last release that works. The 3.x tarball extracts into a
versioned directory, so the cp names it. tar no longer carries || true: a
bad tarball must fail at the step that broke, not at the cp after it.

The workflow is added to its own paths: filter, so this change gets the
run that proves it -- before, a workflow-only PR could not trigger the job.

Verified structurally on the parsed document, not by yaml.safe_load alone:
push.paths and pull_request.paths carry both entries, push.tags is still
['v*'], release.types unchanged, no non-comment line in the pandoc step
mentions fancyhdr, no continued line is followed by a comment. eBoot#135
is green twice at abf0522 (eBoot-guide.pdf, 48 pages).

Closes embeddedos-org#147.
@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Corrected the head. 2b4c426 was a merge commit — "Merge branch 'master' into ci/book-pdf-eisvogel-port" via GitHub's Update-branch, not pushed by me — which made the body's "review the single commit at the head" false (10 commits, one a merge). Replaced with a clean rebase: head 4678981 is exactly one commit over #132's 4054494, .github/workflows/book-build.yml +47/−13, cherry-picked from the proven 66724a8 unchanged. All 15 structural asserts re-run and pass (push.tags == ['v*'] intact, both paths entries, no fancyhdr outside comments).

Verified at this head with CI's exact invocations: ruff check . clean, yamllint . clean, mypy . --ignore-missing-imports --no-strict-optional --exclude '^(layers|core|promo)/' clean, pytest tests/ 680 passed, 1 skipped, 3 NOT RUN locally — the three tests that link a native C binary fail on this Mac's CommandLineTools SDK (ld: tapi error: malformed file … MacOSX27.0.sdk/usr/lib/libSystem.B.tbd: unknown architecture), identically on master c37e995; CI's ubuntu legs are the evidence for those three. Stated rather than omitted.

The Book PDF proof (run 34938663921, 37 pages) was at 66724a8; this head fires its own run via the paths filter and I'll confirm it.

@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Re-proven at 4678981: Build & Release Book PDF success on the clean-rebased head, alongside CI — ebuild, CodeQL, Linked issue policy, Vendored core drift and Simulation Test — 6/6. So the proof from 66724a8 holds after the restack and with the merge commit gone. Nothing further planned on this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Book PDF has never built: fancyhdr header-includes clash with Eisvogel's scrlayer-scrpage (\chead already defined)

2 participants