bazel-ebook
v3.0.1
published 4 hours ago
12 stars
2 forks
2 watchers
Apache License 2.0
public
2 assets
453 downloads
316 KB
v3.0.1
September 13, 2026
[expand for release notes]

README.md

Workflow Status
Build Build
Publish to my custom Bazel registry Publish to my custom Bazel registry
Publish to official Bazel Central Registry Publish to official Bazel Central Registry
Release to GitHub Pages Release to GitHub Pages
Release to GitHub Tag and release

Introduction

This repository is a set of bazel build rules that allow you to write a moderately complex book in the Markdown text format, and produce EPUB and Kindle's AZW3 formats from them. You can also produce a PDF format book, which allows you to preview the results slightly more convenient than by reading the resulting books.

The documentation is available in the following formats:

The build rules currently support pure Markdown formatting, LaTeX-style equations (though not cross-referencing, and in general the amount of LaTeX supported is somewhat limited).

This is not an officially supported Google product. Even though Google owns the copyright. I just happened to work there while I worked on this tool.

Usage

bazel_ebook and several of its dependencies are published in my Bazel registry rather than the Bazel Central Registry, so a project that depends on it must declare both registries.

Add the registry to your .bazelrc file.

common --registry=https://raw.githubusercontent.com/filmil/bazel-registry/main
common --registry=https://bcr.bazel.build

Bazel consults registries in the order listed and takes the first one that has the module, so the custom registry goes first: that lets it override a module that also exists in the Bazel Central Registry. This matches this repo's own .bazelrc and integration/.bazelrc.

Then declare the dependency in MODULE.bazel:

bazel_dep(name = "bazel_ebook", version = "2.0.15")

bazel_ebook_extension = use_extension(
    "@bazel_ebook//:extensions.bzl",
    "bazel_ebook_extension",
)
use_repo(
    bazel_ebook_extension,
    "pandoc_crossref",
    "pandoc_ext_include_files",
)

Without the extra --registry line the build fails at module resolution with module bazel_ebook@... not found in registries.

Prerequisites

Quick start

Build

If you are impatient to see the rules in action, check out an example book in the integration repository.

The easiest way to dig in is to run the following one-liner:

cd integration && bazel build //...

This will build all the examples for you to appreciate.

Examine results

Check out a built example here.

API Documentation

Detailed, automatically generated documentation for all public API items in the .bzl files is available below:

Defined build rules

The build rules are defined in the file build/rules.bzl. A quick list is here:

Rule Description
asymptote(name, srcs, deps, output) This build rule converts Asymptote source files into images that can be included in the book. This rule can take any *.asy file in srcs and can depend on any asymptote rule in deps.
dot_png(name, srcs, deps, output) This build rule converts a Graphviz source files into PNG images that can be included in the book. This rule can take any *.dot file in srcs and can depend on any rule in deps. The .dot file is laid out using the graphviz program dot.
drawtiming_png(name, srcs, deps, output, args) Typeset a timing diagram using drawtiming.
ebook_epub(name, deps, metadata_xml, title_yaml, args, additional_inputs) This build rule assembles all markdown_lib rules in sequece and produces a book named [name].epub
ebook_kindle(name, deps, metadata_xml, title_yaml, args) This build rule assembles all markdown_lib rules in sequence and produces a book named [name].azw3
ebook_pdf(name, deps, metadata_xml, title_yaml, args, additional_inputs) This build rule assembles all markdown_lib rules in sequence and produces a book named [name].pdf
markdown_lib(name, srcs, deps, additional_inputs) This build rule makes a library out of */md files. deps may be any markdown_lib or asymptote or other such rule, and those will be used correctly.
neato_png(name, srcs, deps, output) This build rule converts a Graphviz source files into PNG images that can be included in the book. This rule can take any *.dot file in srcs and can depend on any rule in deps. The .dot file is laid out using the graphviz program neato.
pandoc_chunked_html(name, deps, metadata, toc, title, args, filters, additional_inputs) Use pandoc to convert the markdown_lib deps listed into a set of chaptered HTML resources. This is probably the best way to generate a set of self-contained files.
pandoc_standalone_html(name, deps, metadata, toc, title, args, filters, additional_inputs) Use pandoc to convert the markdown_lib deps listed into a standalone HTML file.
plantuml_png(name, srcs, deps, output) This build rule converts a PlantUML source files into PNG images that can be included in the book. This rule can take any PlantUML-formatted *.txt file in srcs and can depend on any rule in deps.

Common parameters

  • args: (list[string]): verbatim arguments to be passed to the underlying program.
  • deps: (list[Label]): dependency labels, can be any generated targets.
  • filters: (list[Label]): a list of pandoc filters to apply, in the order that they need to appear in the pandoc command line.
  • toc: (bool): whether to generate a table of contents.
  • metadata: (Label): A label representing a YAML metadata file. Note that quite a few of these may be specified as preamble to regular pandoc markdown.

Underlying software

These build rules, of course, only explain to bazel how the ebook is to be built. The actual workhorses for building are Docker, pandoc, calibre, LaTeX, Graphviz, Asymptote, drawtiming and PlantUML.

I used automated coding assistance (Claude Code) to rework my prior setup requiring docker, into a plain, fully hermetic setup.

Releasing and publishing

Tag and Release in .github/workflows/tag-and-release.yml runs weekly and on workflow_dispatch. It releases only when a commit landed since the last tag, computes the version from the conventional-commit titles since that tag, and pushes it. The release itself goes through bazel-contrib's release_ruleset.yaml: it runs bazel test //... at the tag, has .github/workflows/release_prep.sh build bazel-ebook-<tag>.zip and the release notes, and attests the archive's provenance. A release then lists the archive and bazel-ebook-<tag>.zip.intoto.jsonl.

The same run publishes the release to my Bazel registry as a pull request, through .github/workflows/publish-my-bcr.yml. When the repository variable PUBLISH_TO_BCR is true, it also opens a pull request against the Bazel Central Registry through .github/workflows/publish-official-bcr.yml, with attested MODULE.bazel and source.json, which is what the BCR presubmit verifies with slsa-verifier. Only that publish attests: two attesting publishes would overwrite each other's attestation files on the release. The variable stays false until every dependency is on the Bazel Central Registry.

To check a release the way the BCR does, with the archive downloaded from the release:

slsa-verifier verify-github-attestation \
  --attestation-path bazel-ebook-<tag>.zip.intoto.jsonl \
  --source-uri github.com/filmil/bazel-ebook \
  --builder-id https://github.com/bazel-contrib/.github/.github/workflows/release_ruleset.yaml \
  bazel-ebook-<tag>.zip

Limitations

There are a few constraints to note however:

  1. Hermeticized build rules are hefty. You need to have your caching set up well to avoid recompiling the build environment as much as possible. Otherwise, you may pay for costly downloads and infra recompiles.