Skip to content

feat: replace config-to-docs with a config-sample-to-adoc Antora extension - #160

Open
LukasHirt wants to merge 1 commit into
mainfrom
feat/config-sample-to-adoc-extension
Open

LukasHirt wants to merge 1 commit into
mainfrom
feat/config-sample-to-adoc-extension

Conversation

@LukasHirt

Copy link
Copy Markdown
Contributor

What

Ports the owncloud/config-to-docs converter to Node and embeds it in this repo, so the server config parameter pages (config_sample_php_parameters.adoc, config_apps_sample_php_parameters.adoc) no longer depend on a separate PHP repo. This takes a different route than owncloud/config-to-docs#32 (Laminas/Twig upgrade): with the converter in the docs repo, config-to-docs can be retired and the Twig alerts go with it.

Changes

  • antora-extensions/lib/config-sample-to-adoc.js: dependency-free converter. Output is byte-identical to the PHP tool, quirks included, so the published pages do not change. Carries the original MIT notice.
  • antora-extensions/config-sample-to-adoc.js: maintainer-only extension, enabled: false in site.yml. Config: pairs (source_path, target_path), report_only, stop_after_processing. Runs on playbookBuilt. Unlike the PHP tool it fails loudly on bad paths, unknown config keys, a missing // header end marker and a missing $CONFIG array.
  • config-docs-in-sync now runs on Node 22 against the new converter (via scripts/config-sample-to-adoc.js), with no PHP container, config-to-docs checkout or Composer install.
  • content/server/AGENTS.md (+ CLAUDE.md symlink, listed in the root AGENTS.md) holds the sample-comment authoring rules that lived in the config-to-docs README.
  • Tests in extension-tests/config-sample-to-adoc.test.js. The golden fixture was generated by the PHP tool, not by this code.

Verification

  • Ran the old PHP tool (PHP 7.4 in Docker) and the new converter over the core samples for 10.16 and master: all four pages identical, and identical to what is committed.
  • Ran the extension in real Antora in report_only and write mode, and with a bad path.
  • Ran the workflow's regenerate and diff steps locally against live core: 4 pages across 2 versions match.
  • npm test passes. Local runs were on Node 24; CI will be the first run on 22.

Note: Antora camelCases extension config keys, so the YAML uses source_path etc. while the code reads sourcePath. Unknown keys are rejected so a misspelled report_only cannot silently turn a dry run into a write.

Follow-ups (not in this PR)

…nsion

Port the owncloud/config-to-docs converter to Node and embed it here, so
the server config parameter pages no longer depend on a separate PHP repo
(PHP/Twig/Laminas dependency problem, see owncloud/config-to-docs#32).

- antora-extensions/lib/config-sample-to-adoc.js: dependency-free
  converter; reproduces the PHP tool's output byte for byte, quirks
  included, so the published pages do not change
- antora-extensions/config-sample-to-adoc.js: maintainer-only extension,
  disabled in site.yml; pairs of source_path/target_path, report_only,
  stop_after_processing. Fails loudly on bad paths, unknown keys, a
  missing header marker or a missing $CONFIG array
- config-docs-in-sync: run on Node 22 with the new converter instead of
  cloning config-to-docs and installing PHP 7.4 + Composer
- content/server/AGENTS.md: the sample-comment authoring rules that
  previously lived in the config-to-docs README

Output was verified against the PHP tool for the 10.16 and master core
samples, and a quirk fixture generated by the PHP tool is the golden
test.

Signed-off-by: Lukas Hirt <info@hirt.cz>

@oc-tmueller oc-tmueller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff against the PHP tool it replaces: its Twig template and the pinned phpdocumentor/reflection-docblock 4.3.4. No blocking issue. The new tests pass (9/9), and the current pages convert identically. The inline comments are latent divergences from the PHP output plus a few smaller points. I confirmed the first two by running the PHP regexes in php:7.4-cli next to the new code.

Two points on lines outside the diff:

  • .github/workflows/config-docs-in-sync.yml:108: $sample is missing in core $ref - the converter would report success and change nothing described the PHP tool. The Node script throws ENOENT and exits non-zero on a missing input, so this message is now wrong. The pre-check is still useful for a clearer error. Please update the wording and the step comment to say so.
  • extension-tests/static-files.test.js:245: the comment still lists the AGENTS.md/CLAUDE.md locations as (content/, content/ocis/, ui/). This PR adds content/server/ and updates the root AGENTS.md list, so the comment is now stale.

Comment on lines +48 to +49
[new RegExp(`^([^\\n]${WS}*)(?=-)`, 'gm'), ''],
[new RegExp(`[^\\n][^\\n] warning::${WS}*`, 'gm'), 'WARNING: '],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP ran these filters without /u, so [^\n] matched a single byte. In JS it matches a whole character (UTF-16 unit), so the two differ as soon as a line starts with a non-ASCII character. Checked against PHP 7.4:

  • é-mail here: PHP leaves it unchanged (é is two bytes, so the lookahead does not hit -), JS outputs -mail here
  • ü warning:: x: PHP outputs WARNING: x (the [^\n][^\n] pair is the two bytes of ü), JS leaves it unchanged

No current sample triggers this, but the next one that does will silently publish something other than what the PHP tool would have. One way to match PHP is to run these filters over a latin1 view of the UTF-8 bytes (Buffer.from(s, "utf8").toString("latin1"), then convert back). Alternatively, add a test that pins the intended behaviour, if the divergence is acceptable.

function splitDocBlock (comment) {
// A docblock that starts with a tag has no summary and no description.
if (comment.startsWith('@')) return { summary: '', description: '' }
const match = SPLIT.exec(comment.replace(/[ \t]+$/gm, ''))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reflection-docblock 4.3.4 strips trailing whitespace with \h*$ under /u, which also covers NBSP (U+00A0) and the other Unicode horizontal spaces. [ \t]+$ only covers space and tab.

The difference matters in renderDescription. A description line holding only an NBSP is emptied by PHP and skipped. In JS it survives, isn't phpTrim-empty (NBSP isn't in PHP's trim list), and counts as a line with zero indentation. That forces strip to 0, so the whole description keeps its indentation and may render as a literal block where PHP dedented it. /[\t \u00a0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000]+$/gm would match \h.

}
if (strip > 0 && strip !== Infinity) description = [lines[0], ...lines.slice(1).map((l) => l.slice(strip))].join('\n')
}
if (/\{@(?!\})/.test(description)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws on any {@ not followed by }. PHP's DescriptionFactory only treats well-formed {@tag ...} as an inline tag and renders it as text. Prose such as use {@ ... or a {@link https://...} passes through without error.

If core ever adds one of these, the required config-docs-in-sync check (nightly and on every docs PR) fails with a stack trace instead of a diff, and stays red until someone rewrites core's comment. Could this render inline tags the way PHP did, or at least match only well-formed {@tag and report the sample and line?


// PHP's trim() default character list.
const PHP_WS = '[ \\t\\n\\r\\0\\x0B]'
const phpTrim = (s) => s.replace(new RegExp(`^${PHP_WS}+|${PHP_WS}+$`, 'g'), '')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: phpTrim compiles a new RegExp on every call, and renderDescription builds another inside its per-line loop (line 96). With hundreds of blocks per sample, that adds up to a lot of identical compiles. Hoisting both to module-level constants, as SUMMARY_FILTER/CODE_FILTER already are, avoids that. The cost is small.

console.error('usage: node scripts/config-sample-to-adoc.js <config.sample.php> <page.adoc>')
process.exit(2)
}
fs.writeFileSync(target, convert(fs.readFileSync(source, 'utf8'), fs.readFileSync(target, 'utf8')))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repeats the extension's read → convert → write path without its error wrapping. A missing target in CI gives a bare ENOENT stack, where the extension says cannot read target_path .... Any future change to that pipeline (an unchanged/updated report, for example) would also need making in two places. A shared regeneratePage(source, target) in lib/, used by both entry points, would keep them aligned.

if (!Array.isArray(pairs) || !pairs.length) {
throw new Error('config-sample-to-adoc: `pairs` is required and must be a non-empty list')
}
const log = (message) => console.log(`[config-sample-to-adoc] ${message}`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other extensions here (llms-txt, sitemap-cleanup, prerelease-attribute, load-global-site-attributes) log through this.getLogger("config-sample-to-adoc"). Using console.log bypasses --log-level, --log-format json and --log-failure-level, so this output looks different from the rest of the build and can't be silenced.

This branch has not been deployed

No deployments
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.

Discuss: replace abandoned zend-filter/zend-inputfilter with laminas/laminas-filter, laminas/laminas-inputfilter

2 participants