Skip to content

Commit generated PDFs, rebuild on dependency changes, and support Markdown documents - #21

Merged
smiths merged 3 commits into
smiths:mainfrom
CSchank:pdf-pipeline
Sep 14, 2026
Merged

smiths merged 3 commits into
smiths:mainfrom
CSchank:pdf-pipeline

Conversation

@CSchank

@CSchank CSchank commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

This started as a fix for one problem and grew into four related changes to
the PDF build. Happy to split it up, drop parts of it, or rework anything —
feedback very welcome, particularly on the first and third items, which are
the most opinionated.

What this changes

Generated PDFs are committed to the repository.
Every compiled PDF is written to a pdfs/ directory and committed back to
main, so the documents can be read directly in the repository without
downloading build artifacts or installing LaTeX. The published site is
unchanged; this adds an in-repository copy alongside it.

Documents are rebuilt when the files they depend on change.
Previously only a document whose own file changed was rebuilt. Because the
shared includes are named .text rather than .tex, editing
docs/Common.text — which fourteen documents read — rebuilt nothing at all
and still reported success, and refs/References.bib, read by eight
documents, could not even start the workflow. Rather than maintain a list of
dependencies by hand, pdflatex now runs with -recorder and the build
reads the resulting .fls, which reports every file the document actually
opened. That picks up figures and transitively included files for free.

Deliverables can be written in Markdown.
Any .md file under docs/ is compiled to PDF with pandoc, apart from
README.md and Expectations*.md, which are instructor-owned. Where a
folder contains both Foo.md and Foo.tex, the Markdown version is used.

Builds are faster, and the index page shows timestamps.
TeX Live and pandoc are cached rather than reinstalled on every run, taking
that step from about 126 seconds to about 30. The actions GitHub flagged as
running on the deprecated Node 20 runtime are updated. Each PDF on the index
page now shows when it was last updated, rendered in the reader's own
timezone. The deploy job is also restricted to pushes, since a pull request
has a read-only token and could never deploy.

Please treat the Markdown support as experimental

It has compiled exactly one test document. Pandoc's LaTeX template requires
packages the .tex documents never use — lmodern was missing on the first
attempt — so a team using Markdown features that have not been exercised,
images in particular, may hit a missing package. That surfaces as a failed
build with a clear LaTeX error rather than a quietly wrong PDF.

Markdown dependency tracking is also weaker than the LaTeX equivalent, since
pandoc discards the .fls and those dependencies come from scanning the
source instead. Anything missed falls back to rebuilding everything, so the
result stays correct, just less precise. There is one known gap on the LaTeX
side too: .fls records what was read on a given run, so a conditional
\input only registers the branch actually taken.

More generally, the dependency rule is deliberately fail-safe. A changed file
that is neither a document, nor a recorded dependency, nor explicitly
ignorable triggers a full rebuild, so a missing record makes a build slower
rather than wrong.

What is not in this pull request

The generated pdfs/ directory and the dependency manifest are left out, to
keep the diff reviewable. Both regenerate on the first run: with no manifest
recorded, every document is rebuilt.

How it was tested

Everything here has been running on CSchank/capTemplate, where the site is
published at https://cschank.github.io/capTemplate/ with all 29 documents.
This branch was also run as a pull request first, to confirm the build passes
and the deploy job is correctly skipped.

Worth noting that the Markdown support failed twice before it worked, once on
a missing lmodern and once on font maps that the package cache had not
rebuilt, so the verification step now compiles a small Markdown file before
any real document is built.

🤖 Generated with Claude Code

CSchank and others added 3 commits September 12, 2026 19:39
Three changes to how documents are built, plus a reduction in build time.

Generated PDFs are written to pdfs/ and committed back to main, so they
can be read directly in the repository without downloading build
artifacts or installing LaTeX. build-pdf/ is seeded from that directory
before compiling, which makes the repository rather than the Actions
cache the source of truth: previously a cache miss combined with the
rsync --delete would have removed every generated PDF.

Documents are rebuilt when the files they read change. Only documents
whose own file changed were rebuilt before, and because the shared
includes are named .text rather than .tex, editing docs/Common.text,
which fourteen documents read, rebuilt nothing and still reported
success. refs/References.bib, read by eight documents, could not even
start the workflow. Rather than maintain a list of dependencies by hand,
pdflatex now runs with -recorder and record_deps.py reads the resulting
.fls, which reports every file the document opened, including figures and
transitively included files. .bib files come from the \bibdata entries in
the .aux, since pdflatex reads the generated .bbl and never the
bibliography source. select_docs.py reverses that graph to choose what to
rebuild, and falls back to a full rebuild for any changed file it cannot
account for, so a missing record makes a build slower rather than wrong.

Markdown documents under docs/ are compiled with pandoc, using the
pdflatex already installed for the .tex rule. README.md and
Expectations*.md are excluded as instructor-owned. Where a folder holds
both Foo.md and Foo.tex the Makefile lists the Markdown rule first, so
Markdown wins.

TeX Live and pandoc are cached rather than reinstalled each run, which
takes that step from about 126 seconds to about 30. Almost none of that
time was downloading, so the installed files are cached rather than the
.deb archives. Restoring files skips their postinst scripts, so mktexlsr
and updmap-sys are run afterwards to rebuild the filename and font map
databases, and a short verification step compiles a small Markdown file
before any real document is built. The actions GitHub reported as running
on the deprecated Node 20 runtime are updated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A reader had no way to tell whether a PDF reflected the current source.
Each entry now shows when its PDF was last committed, and the page
records when it was generated.

The times come from git rather than file mtimes, because the PDFs are
restored from a cache or checked out fresh and their mtimes are the time
of the run. They are written as UTC in a <time> element and rewritten by
a small script into the reader's own timezone, falling back to the UTC
text when JavaScript is unavailable.

Generating the list moves from awk to a script, which also fixes the
markup: the placeholder sat inside a <ul>, so the generated headings and
lists were nested inside a list item.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A pull request runs with a read-only token, so the deploy job failed on
every pull request, including ones from forks that cannot deploy by
definition. Restrict it to pushes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CSchank

CSchank commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Hi @smiths, what are your thoughts on this?

@smiths
smiths merged commit 46f0824 into smiths:main Sep 14, 2026
2 checks passed
@smiths

smiths commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Looks excellent @CSchank. I would have been fine if we didn't support md files, but this is so much better that you have found a way to support them. Great! The way you have handled dependencies is so much nicer than the heavy-handed way I have been doing it. I like that you have the pdf folder for you, and the github.io page for me. 😄 We now support multiple ways of interacting with the system. Does the github page link to the pdf folder? That would be efficient, but my guess is that isn't how GitHub works.

I only have two comments that come to mind:

  1. The testing is likely not enough. That isn't a criticism. You did a great job with the time available. I just know that students are going to invent use-cases and error-cases we haven't thought of. We don't need it perfect when we start. What we had before certainly wasn't perfect. 😄 We can work with the teams to make it better, and if we don't have the time, the teams themselves can make it better. It isn't unreasonable to expect senior software engineering students to work with a CI/CD system.
  2. It isn't a big deal, but it would be nice for the files on the GitHub.io page to be listed in a logical order, like I did for a few of my JOSS projects, like SynthEddy. If we listed the documents in the waterfall order (not the order we create them), it would reinforce faking a rational design process. It would also make the documentation more useful for people outside of the capstone course.

@smiths

smiths commented Sep 14, 2026

Copy link
Copy Markdown
Owner

I did a small test after merging the PR and I'm very impressed. I added some text to the problem statement template and pushed. The build was triggered and it took 1m+10s compared to the 4m+11s for the initial build. That is a nice savings! The updated file appeared in the correct places and with the change I made reflected. Very nice!

@CSchank

CSchank commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

These are good points. For testing, hopefully yes if something goes wrong, it'll be a good exercise for them to write a good bug report!

I can work on the logical ordering in another PR, after I get the paper submitted tonight!

@CSchank

CSchank commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Great! I'm glad the cache worked well for you. I can't take much of the credit for it, but it seems to work well. GitHub will keep the cache warm for 7 days, and it gets reset every time they push. So for bursty last-minute pushes before deadlines it will be slow only the first time in any 7-day period (if the cache goes cold). For our use case this means most of the time they will be fast builds.

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.

2 participants