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
3 changes: 2 additions & 1 deletion content/momentum/4/eol-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ Momentum version 5 became GA on March 1, 2025. Therefore:
| Momentum 4.8.x | 2024/10/17 | 2026/3/1 | 2027/3/1 |
| Momentum 5.0.x | 2025/3/1 | 2026/7/1 | TBD |
| Momentum 5.1.x | 2025/7/1 | 2027/1/31 | TBD |
| **Momentum 5.2.x** | 2026/1/31 | TBD | TBD |
| Momentum 5.2.x. | 2026/1/31 | 2027/7/28 | TBD |
| **Momentum 5.3.x** | 2026/7/28 | TBD | TBD |

> ¹ Momentum 4.4.x was superseded by 4.6, which was the last version supporting CentOS 7.
>
Expand Down
90 changes: 69 additions & 21 deletions pdf-build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
# Usage (run from this directory):
# make # build a PDF for every manual
# make list # show what would be built
# make build/installation-manual.pdf # build one manual
# make build/Momentum_5.3_Installation.pdf # build one manual
# make clean # remove generated PDFs

PANDOC ?= pandoc
SRC_DIR := ../content/momentum/manuals
# PDF-only manuals: Markdown sources here get a PDF like any other manual but
# are NOT rendered online (the site only serves pages under content/).
PRIV_DIR := private
TEMPLATE := templates/bird-manual.typ
SHARED_META := templates/metadata.yaml
BUILD_DIR := build
Expand All @@ -26,8 +29,39 @@ FONT_DIR := assets/fonts
export TYPST_FONT_PATHS := $(FONT_DIR)

# Every top-level Markdown file is a manual, except the section landing page.
SOURCES := $(filter-out $(SRC_DIR)/index.md,$(wildcard $(SRC_DIR)/*.md))
PDFS := $(patsubst $(SRC_DIR)/%.md,$(BUILD_DIR)/%.pdf,$(SOURCES))
SOURCES := $(filter-out $(SRC_DIR)/index.md,$(wildcard $(SRC_DIR)/*.md))
PRIV_SOURCES := $(wildcard $(PRIV_DIR)/*.md)

# ---------------------------------------------------------------------------
# PDF naming: Momentum_<version>_<Suffix>.pdf
#
# The 5.3 manuals were originally produced from DOCX exports named
# Momentum_5.3_<Suffix>.docx, and the customer-facing PDFs must keep following
# that pattern. From 5.4 on there are no DOCX sources anymore — the PDFs are
# generated straight from edits to the .md files — so this table is the
# durable record of each manual's historical name suffix. The <version> part
# is read from each manual's own `version:` frontmatter, so bumping the
# version in the .md automatically renames the PDF for the next release.
#
# When adding a new manual, add its slug -> suffix entry here; a slug with no
# entry (or no version) falls back to <slug>.pdf.
PDF_SUFFIX_installation-manual := Installation
PDF_SUFFIX_upgrade-manual := Upgrade
PDF_SUFFIX_enabling-apis-message-generation := Enabling_TransAPI-MsgGen
PDF_SUFFIX_enabling-webhooks := Enabling_Webhooks
PDF_SUFFIX_upgrading-webhooks := Upgrading_Webhooks
PDF_SUFFIX_release-notes := Release_Notes
PDF_SUFFIX_rocky9-installation-manual := Rocky9_Installation

# $(call md_version,<file>): the version: "X.Y" value from the frontmatter.
md_version = $(shell sed -n 's/^version:[[:space:]]*"\([^"]*\)".*/\1/p' $(1) | head -1)
# $(call pdf_name,<slug>,<version>): the PDF basename (no extension).
pdf_name = $(if $(and $(PDF_SUFFIX_$(1)),$(2)),Momentum_$(2)_$(PDF_SUFFIX_$(1)),$(1))
# $(call pdf_for,<file>): full PDF path for a manual source file.
pdf_for = $(BUILD_DIR)/$(call pdf_name,$(basename $(notdir $(1))),$(call md_version,$(1))).pdf
# ---------------------------------------------------------------------------

PDFS := $(foreach src,$(SOURCES) $(PRIV_SOURCES),$(call pdf_for,$(src)))

# Shared inputs every PDF depends on. Listing the logos, fonts, and the Makefile
# itself means `make` rebuilds when any of them change -- no manual `clean`.
Expand All @@ -37,35 +71,49 @@ DEPS := $(TEMPLATE) $(SHARED_META) $(ASSETS) $(MAKEFILE_LIST)
.PHONY: all list clean
all: $(PDFS)

# Intermediate Typst file for the manual being built (named after the .md
# slug, not the PDF, so it stays predictable). Expanded per-recipe via $<.
TYP = $(basename $(notdir $<)).typ

# Two-step build: Pandoc -> Typst source, then compile with the typst CLI.
# The intermediate .typ is emitted here in pdf-build/ (not build/) so the
# template's relative `assets/...` logo paths resolve. It is removed after.
#
# `-tex_math_dollars`: disable $...$ math parsing so literal shell/config vars
# like $NGINX_ROOT or $PATH in prose and tables are never treated as math.
$(BUILD_DIR)/%.pdf: $(SRC_DIR)/%.md $(DEPS) | $(BUILD_DIR)
$(PANDOC) "$<" \
--from=markdown-tex_math_dollars \
--to=typst \
--template=$(TEMPLATE) \
--metadata-file=$(SHARED_META) \
--toc --toc-depth=3 \
--resource-path=".:$(SRC_DIR)" \
--output="$*.typ"
@# Pandoc gives tables fixed percentage column widths (always full-width);
@# rewrite them to auto so the template can detect a table's natural width
@# and rotate genuinely wide ones onto their own landscape page.
@# Use a temp file + mv (not `sed -i`, whose syntax differs on BSD vs GNU).
sed -E '/^[[:space:]]*columns: \(/ s/[0-9.]+%/auto/g' "$*.typ" > "$*.typ.cols" && mv "$*.typ.cols" "$*.typ"
typst compile --font-path "$(FONT_DIR)" "$*.typ" "$@"
@rm -f "$*.typ"
@echo "Built $@"
# Canned recipe shared by every manual rule generated below.
define BUILD_PDF
$(PANDOC) "$<" \
--from=markdown-tex_math_dollars \
--to=typst \
--template=$(TEMPLATE) \
--metadata-file=$(SHARED_META) \
--toc --toc-depth=3 \
--resource-path=".:$(dir $<)" \
--output="$(TYP)"
@# Pandoc gives tables fixed percentage column widths (always full-width);
@# rewrite them to auto so the template can detect a table's natural width
@# and rotate genuinely wide ones onto their own landscape page.
@# Use a temp file + mv (not `sed -i`, whose syntax differs on BSD vs GNU).
sed -E '/^[[:space:]]*columns: \(/ s/[0-9.]+%/auto/g' "$(TYP)" > "$(TYP).cols" && mv "$(TYP).cols" "$(TYP)"
typst compile --font-path "$(FONT_DIR)" "$(TYP)" "$@"
@rm -f "$(TYP)"
@echo "Built $@"
endef

# The PDF basename is unrelated to the .md basename, so a pattern rule cannot
# express the mapping — generate one explicit rule per manual instead.
define MANUAL_RULE
$(call pdf_for,$(1)): $(1) $(DEPS) | $(BUILD_DIR)
$$(BUILD_PDF)
endef
$(foreach src,$(SOURCES) $(PRIV_SOURCES),$(eval $(call MANUAL_RULE,$(src))))

$(BUILD_DIR):
@mkdir -p $(BUILD_DIR)

list:
@echo "Sources:"; for s in $(SOURCES); do echo " $$s"; done
@echo "Sources:"; for s in $(SOURCES) $(PRIV_SOURCES); do echo " $$s"; done
@echo "PDFs:"; for p in $(PDFS); do echo " $$p"; done

clean:
Expand Down
35 changes: 33 additions & 2 deletions pdf-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ content/momentum/manuals/*.md ──► online pages (Next.js site)
One manual = one Markdown file = one online page = one PDF. There is a single
source of truth; the PDF is a build artifact.

Exception: manuals under `private/` in this directory are **PDF-only** — they
get a branded PDF like any other manual but are never rendered online (the
site only serves pages under `content/`).

## Toolchain

Pandoc (≥ 3.x) and the Typst CLI. These are not npm packages — install once per
Expand All @@ -29,12 +33,34 @@ brew install pandoc typst # macOS
cd pdf-build
make # build every manual into build/
make list # show what would be built
make build/installation-manual.pdf # build a single manual
make build/Momentum_5.3_Installation.pdf # build a single manual
make clean # remove generated PDFs
```

Generated PDFs land in `build/` (git-ignored).

### PDF naming

PDFs are named `Momentum_<version>_<Suffix>.pdf` — the pattern of the original
5.3 DOCX exports the manuals were converted from. The DOCX files are no longer
part of the flow (from 5.4 on, changes are made directly in the `.md` sources),
so the `PDF_SUFFIX_*` table in the `Makefile` is the durable record of each
manual's historical suffix:

| `.md` slug | PDF suffix |
| --- | --- |
| `installation-manual` | `Installation` |
| `upgrade-manual` | `Upgrade` |
| `enabling-apis-message-generation` | `Enabling_TransAPI-MsgGen` |
| `enabling-webhooks` | `Enabling_Webhooks` |
| `upgrading-webhooks` | `Upgrading_Webhooks` |
| `release-notes` | `Release_Notes` |
| `rocky9-installation-manual` | `Rocky9_Installation` |

The `<version>` part is read from each manual's `version:` frontmatter, so
bumping the version in the `.md` automatically renames the PDF for the next
release. A slug with no table entry falls back to `<slug>.pdf`.

The build is incremental: `make` rebuilds a manual when its `.md` changes, and
rebuilds **all** manuals when a shared input changes (the template,
`metadata.yaml`, a logo/font in `assets/`, or this `Makefile`). You only need
Expand All @@ -46,6 +72,7 @@ rebuilds **all** manuals when a shared input changes (the template,
| Path | Purpose |
| --- | --- |
| `../content/momentum/manuals/*.md` | Manual sources (also served online) |
| `private/*.md` | PDF-only manual sources (never served online) |
| `templates/bird-manual.typ` | Pandoc→Typst template with the Bird-branded cover, header/footer, and styling |
| `templates/metadata.yaml` | Brand/layout defaults shared by every manual |
| `assets/` | Logo and (optional) brand fonts |
Expand Down Expand Up @@ -92,10 +119,14 @@ date: "June 2026"
not nested inside a list item or blockquote (Typst cannot start a new page
inside a container, and the build will error). If a wide table belongs
under a step, lift it out to its own paragraph.
- **PDF-only manuals**: save the `.md` under `pdf-build/private/` instead
of `content/momentum/manuals/`, and skip step 2.
2. Add the page to the menu: a sub-item under the **Online manuals** node in
`content/momentum/navigation.yml`, and a link in
`content/momentum/manuals/index.md`.
3. `make build/<name>.pdf` and review the output.
3. Add the manual's slug → suffix entry to the `PDF_SUFFIX_*` table in the
`Makefile` (see [PDF naming](#pdf-naming)), then `make` and review the
output.

## Branding

Expand Down
Loading
Loading