[#1147] Fail the doc build when a page leaves an AsciiDoc attribute unresolved (#1148)
Fixes #1147
### Problem
A page that uses an attribute it does not define, or a verbatim block
that uses an attribute without attribute subs, publishes `{name}`
literally, and the build stays green. The only render in the build is
the PDF book, where an attribute defined in an earlier chapter also
resolves in the later ones; the site renders every chapter as a page of
its own, so a page can be broken on the site while the PDF is fine
(`chap-uninstall.adoc` before #1140). The `antora` goal of
`doc-maven-plugin` only rewrites the `.adoc` files, it renders nothing.
### Change
- New `check-attribute-references` execution of
`asciidoctor-maven-plugin` in the `man-pages` profile (phase `verify`,
so it runs on the ubuntu legs of the PR build). It renders every `.adoc`
of `target/asciidoc/source` on its own to throwaway HTML, with each
file's own directory as the base, and fails on any WARN that mentions an
attribute:
1. an attribute that is not defined: `attribute-missing=warn`;
2. an attribute in a listing, literal or passthrough block without
attribute subs, or in a literal table cell (`l|`, `cols="1l"`): the new
`literal-attribute-references.rb` tree processor. A reference counts
when its name is an attribute at that point of the page, an intrinsic
one such as `{nbsp}`, or one that a `:name:` entry anywhere in the doc
sources sets - the last catches a page that neither defines nor
substitutes the attribute, and since the page is among those sources,
its own entry counts even where it has unset the attribute. Braces
around any other name (`{SSHA}` values, `{cn}` in MakeLDIF templates)
are left alone. Without attribute subs a backslash does not escape, so
`\{name}` is reported too, as published with its backslash.
The advice in the warning is `subs="+attributes"`, which keeps the
block's default subs; a literal cell takes no subs, so it is told to
become an `a|` cell with such a listing.
- `LiteralAttributeReferencesTest` reads the execution's configuration
from the pom - the required extensions, the attributes, the `failIf` -
renders pages with it, and checks which of them would fail the build: 13
that must fail, 9 that must pass. Its sources directory sits under
`target` and holds one of the pages, as the build's does, and it asserts
that `sourceDirectory` and `literal-attribute-sources` are the same
directory. The pom module binds `testCompile` and `test` for it, and the
plugin and the test share `asciidoctorj` `${asciidoctorj.version}`
(2.5.11, the plugin's own).
- The 16 blocks of the guides with `subs="attributes"` take
`subs="+attributes"`. The replacing form had dropped
`specialcharacters`, so `chap-writing-plugins` published `(build
<unknown>, revision <unknown>)` as raw tags, which a browser hides; the
`>`, `>>>>` and `&&` of the other blocks were raw as well. The rendered
HTML of the six pages differs only in that escaping.
- The two literal placeholders the check found on master are escaped
like the generated reference does since #1142: `{options}` in the
`windows-service` synopsis and `{path}` in the password policy chapter
(both were shown with braces, but by accident).
Asciidoctor resets the document attributes to the header before tree
processors run, so the extension replays the attribute entries of the
body in document order, as the converter does, on a copy of each
document's attributes. The document itself is left alone: playing the
entries back on it would also hand the compat mode of the last
`compat-mode` entry to the converter.
### Limits
- `asciidoctor-maven-plugin` 2.2.6 evaluates `failIf` after each file,
so the build stops at the first page that fails; fix it and run `mvn
-Pdistribution-unix,man-pages -pl opendj-doc-generated-ref
asciidoctor:process-asciidoc@check-attribute-references` again for the
next one (both profiles activate by themselves only on Linux). This is
noted in the pom.
- Asciidoctor names no file for a missing attribute; it is the page of
the `Converted` line that follows the warning.
- The plugin makes every Maven project property an attribute
(`product.name` is `{product-name}`), and the site defines none of them,
so a page that uses one passes the check and still shows the braces on
the site. The plugin has no switch for it; no page uses one today. This
is noted in the pom.
- The `partials` directory is rendered as well (234 of the 512 files),
although Antora only includes its files and never publishes them as
pages; the plugin has no exclusion, and an `_` prefix would break the
includes. So the man pages are checked twice, and a partial that relies
on an attribute of the chapter that includes it would fail here although
the site resolves it; none of the 11 partials that exist only there
holds an attribute reference. This is noted in the pom.
### Verification
- Current master (with #1145 and #1146), with the extension of this
round: 512 pages, no warning at all, `BUILD SUCCESS`; the check itself
took 43 s in the CI job of the previous round. The hand-written pages
were pre-processed again from this branch; the generated reference pages
come from my last module build.
- Before #1140 it reported exactly what #1140 fixed: the undefined
`{opendj-version}` in `chap-uninstall.adoc` and the listing without subs
in `chap-monitoring.adoc`.
- Three mutants on the pre-processed copies each fail the build:
`:opendj-version:` removed from `chap-uninstall`; `subs="attributes"`
removed from a listing in `chap-monitoring`; both the subs and the
definition removed from `chap-monitoring` (caught only through the
`:name:` entries of the other pages).
- `LiteralAttributeReferencesTest`: 22 green. Each of 16 mutants turns
it red - in the extension: no report at all, the subs guard inverted, no
literal-cell branch, no intrinsic names, no replay of body entries, no
unset in the replay, the replay on the document instead of a copy, the
sources path back in the glob pattern, `\{name}` skipped, the old
`subs="attributes"` advice; in the pom: a typo in `containsText`,
`failIf` on `ERROR`, no `attribute-missing`, no
`literal-attribute-sources`, the extension not required,
`sourceDirectory` narrowed to `man-pages`. The previous round's
extension, which played the entries back on the document, fails the two
compat-mode rows.
- The HTML rendered with and without the extension is identical on all
512 pages (only the `Last updated` line set aside), so the extension
changes nothing in the output.
- Edge cases checked by hand: header and body attributes, unset
attributes, compat mode set or unset in the body, literal paragraphs,
passthrough blocks, listings inside AsciiDoc table cells, literal cells,
`{SSHA}`/`{givenName}` braces, a sources path with `{` and `[`.
- Not run locally: the full module build with the PDF (killed for memory
on my machine); the check ran on the sources the build had already
pre-processed. CI runs the whole path.