From 9100fdadfaf6e2a5bc92ed3d44710db1edda1adc Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Wed, 30 Sep 2026 22:46:59 +0300 Subject: [PATCH 1/3] Publish the last release of each previous major line as an older version - antora-playbook.yml: content sources for the tags OpenAM-15.2.2, OpenDJ-4.10.2, OpenIDM-6.3.0 and OpenIG-5.3.2 - lib/versions-from-tags.js: extracts antora.yml and modules/ of each tag and publishes it as version . (e.g. /openam/15.2/); the branch stays the unversioned latest version, so URLs do not change - /antora.yml: display_version of the branch (16.x, 5.x, 7.x, 6.x) - add-pdf-link.js: PDF links only on the latest version - docsearch/config.json: do not index the older versions Fixes #26 --- antora-playbook.yml | 24 ++++++++++ docsearch/config.json | 3 +- lib/add-pdf-link.js | 18 +++++++- lib/versions-from-tags.js | 93 +++++++++++++++++++++++++++++++++++++++ openam/antora.yml | 1 + opendj/antora.yml | 15 +++++++ openidm/antora.yml | 15 +++++++ openig/antora.yml | 15 +++++++ 8 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 lib/versions-from-tags.js diff --git a/antora-playbook.yml b/antora-playbook.yml index 87141ef60b2..065cd5ca72d 100644 --- a/antora-playbook.yml +++ b/antora-playbook.yml @@ -41,6 +41,29 @@ content: - url: ./ start_path: openidm edit_url: https://github.com/OpenIdentityPlatform/OpenIDM/edit/master/openidm-doc/src/main/asciidoc + # Older versions: the last release of each previous major line, from the tags the product release + # workflows put on this repository. One tag per source; lib/versions-from-tags.js reads the tag + # (fetching it from origin if missing) and turns it into the version: OpenAM-15.2.2 -> 15.2. + - url: ./ + start_path: opendj + branches: ~ + tags: OpenDJ-4.10.2 + edit_url: false + - url: ./ + start_path: openig + branches: ~ + tags: OpenIG-5.3.2 + edit_url: false + - url: ./ + start_path: openam + branches: ~ + tags: OpenAM-15.2.2 + edit_url: false + - url: ./ + start_path: openidm + branches: ~ + tags: OpenIDM-6.3.0 + edit_url: false ui: bundle: url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable @@ -52,6 +75,7 @@ output: antora: extensions: + - ./lib/versions-from-tags.js - ./lib/add-pdf-link.js diff --git a/docsearch/config.json b/docsearch/config.json index 7c8c40c280c..18f94a128b4 100644 --- a/docsearch/config.json +++ b/docsearch/config.json @@ -39,7 +39,8 @@ "https://doc.openidentityplatform.org/openig/apidocs", "https://doc.openidentityplatform.org/commons/apidocs", "https://doc.openidentityplatform.org/openicf/apidocs", - "https://doc.openidentityplatform.org/openidm/apidocs" + "https://doc.openidentityplatform.org/openidm/apidocs", + "https://doc.openidentityplatform.org/[a-z]+/[0-9]+\\.[0-9]+/" ], "selectors": { "default": { diff --git a/lib/add-pdf-link.js b/lib/add-pdf-link.js index a6503129767..2b32c18062c 100644 --- a/lib/add-pdf-link.js +++ b/lib/add-pdf-link.js @@ -1,3 +1,18 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Portions Copyright 2026 3A Systems, LLC. + */ module.exports.register = function () { this.on('navigationBuilt', ({contentCatalog}) => { @@ -9,7 +24,8 @@ module.exports.register = function () { }); contentCatalog.getFiles().forEach((file) => { - if(file.asciidoc && file.asciidoc.attributes) { + // the PDFs are built from the current sources, so older versions get no PDF link + if(file.asciidoc && file.asciidoc.attributes && file.src.version === contentCatalog.getComponent(file.src.component)?.latest?.version) { if(compomentsPdfs[file.src.component] && compomentsPdfs[file.src.component][file.src.module]) { const pdfLink = compomentsPdfs[file.src.component][file.src.module]; file.asciidoc.attributes['page-pdflink'] = pdfLink; diff --git a/lib/versions-from-tags.js b/lib/versions-from-tags.js new file mode 100644 index 00000000000..cea0913b95a --- /dev/null +++ b/lib/versions-from-tags.js @@ -0,0 +1,93 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2026 3A Systems, LLC. + */ + +'use strict' + +// Publishes release tags of this repository (OpenAM-15.2.2 etc.) as older versions of a component. +// +// A content source that names a tag is not read by Antora from the git tree, for two reasons: +// - the antora.yml of every tag declares `version: ~`, and Antora gives the version in antora.yml +// precedence over the version of the content source, so the tag would be merged into the +// unversioned component version built from the branch; +// - Antora reads every file under the start path of a git tree, and the start path of a tag also +// holds the API docs of that time (18,672 files for OpenAM-15.2.2, against 330 pages), which +// makes the build several times slower. +// Instead, only antora.yml and modules/ of the tag are extracted with `git archive` into a +// temporary repository, which is aggregated apart from the other sources, and the result is given +// the version of the tag: OpenAM-15.2.2 becomes version 15.2, displayed as 15.2.2. The branch +// stays the unversioned (and latest) version, so its URLs do not change. + +const { execFileSync } = require('child_process') +const fs = require('fs') +const os = require('os') +const ospath = require('path') + +const TAG_RX = /^[^-]+-(\d+)\.(\d+)\.(\d+)$/ + +module.exports.register = function () { + const logger = this.getLogger('versions-from-tags') + this.replaceFunctions({ + async aggregateContent (playbook) { + const aggregateContent = this.require('@antora/content-aggregator') + const sources = playbook.content.sources + const withSources = (subset) => ({ ...playbook, content: { ...playbook.content, sources: subset } }) + const aggregate = await aggregateContent(withSources(sources.filter((source) => !source.tags))) + const tmpdir = fs.mkdtempSync(ospath.join(os.tmpdir(), 'antora-tags-')) + try { + for (const source of sources.filter((source) => source.tags)) { + const tag = source.tags + const match = typeof tag === 'string' && tag.match(TAG_RX) + if (!match) throw new Error(`content source ${source.startPath}: tags must name one release tag, got ${tag}`) + const worktree = ospath.join(tmpdir, tag) + extractTag(ospath.resolve(playbook.dir, source.url), tag, source.startPath, worktree, logger) + const tagSource = { url: worktree, branches: 'HEAD', startPath: source.startPath, editUrl: source.editUrl } + for (const componentVersion of await aggregateContent(withSources([tagSource]))) { + componentVersion.version = `${match[1]}.${match[2]}` + componentVersion.displayVersion = `${match[1]}.${match[2]}.${match[3]}` + logger.info('%s %s from tag %s', componentVersion.name, componentVersion.version, tag) + aggregate.push(componentVersion) + } + } + } finally { + fs.rmSync(tmpdir, { recursive: true, force: true }) + } + return aggregate + }, + }) +} + +// Extracts antora.yml and modules/ of startPath at tag into a new single-commit repository. +// Fetches the tag from origin first if the repository does not have it, as in a CI checkout. +function extractTag (repo, tag, startPath, worktree, logger) { + const git = (dir, ...args) => execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8', stdio: 'pipe' }) + try { + git(repo, 'rev-parse', '-q', '--verify', `refs/tags/${tag}^{commit}`) + } catch { + logger.info('fetching tag %s', tag) + const shallow = git(repo, 'rev-parse', '--is-shallow-repository').trim() === 'true' + git(repo, 'fetch', '-q', '--no-tags', ...(shallow ? ['--depth=1'] : []), 'origin', `+refs/tags/${tag}:refs/tags/${tag}`) + } + fs.mkdirSync(worktree) + const paths = [`${startPath}/antora.yml`, `${startPath}/modules`] + const archive = execFileSync('git', ['-C', repo, 'archive', '--format=tar', tag, '--', ...paths], { + maxBuffer: 1024 * 1024 * 1024, + }) + execFileSync('tar', ['-x', '-C', worktree], { input: archive }) + git(worktree, 'init', '-q') + git(worktree, 'add', '-A') + git(worktree, '-c', 'user.name=antora', '-c', 'user.email=antora@localhost', '-c', 'commit.gpgsign=false', + 'commit', '-q', '-m', tag) +} diff --git a/openam/antora.yml b/openam/antora.yml index c4c5f4ee64b..b5f956252c8 100644 --- a/openam/antora.yml +++ b/openam/antora.yml @@ -15,6 +15,7 @@ name: openam title: 'OpenAM: Access Management' version: ~ +display_version: '16.x' asciidoc: attributes: pdfs: diff --git a/opendj/antora.yml b/opendj/antora.yml index 38d5826055e..14154ade69e 100644 --- a/opendj/antora.yml +++ b/opendj/antora.yml @@ -1,6 +1,21 @@ +# The contents of this file are subject to the terms of the Common Development and +# Distribution License (the License). You may not use this file except in compliance with the +# License. +# +# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the +# specific language governing permission and limitations under the License. +# +# When distributing Covered Software, include this CDDL Header Notice in each file and include +# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL +# Header, with the fields enclosed by brackets [] replaced by your own identifying +# information: "Portions copyright [year] [name of copyright owner]". +# +# Portions Copyright 2026 3A Systems, LLC. + name: opendj title: 'OpenDJ: Directory Services' version: ~ +display_version: '5.x' asciidoc: attributes: pdfs: diff --git a/openidm/antora.yml b/openidm/antora.yml index c6ab768d8eb..d3ec27c746d 100644 --- a/openidm/antora.yml +++ b/openidm/antora.yml @@ -1,6 +1,21 @@ +# The contents of this file are subject to the terms of the Common Development and +# Distribution License (the License). You may not use this file except in compliance with the +# License. +# +# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the +# specific language governing permission and limitations under the License. +# +# When distributing Covered Software, include this CDDL Header Notice in each file and include +# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL +# Header, with the fields enclosed by brackets [] replaced by your own identifying +# information: "Portions copyright [year] [name of copyright owner]". +# +# Portions Copyright 2026 3A Systems, LLC. + name: openidm title: 'OpenIDM: Identity Management' version: ~ +display_version: '7.x' asciidoc: attributes: pdfs: diff --git a/openig/antora.yml b/openig/antora.yml index d8f2c2b04d8..9ab0b18f936 100644 --- a/openig/antora.yml +++ b/openig/antora.yml @@ -1,6 +1,21 @@ +# The contents of this file are subject to the terms of the Common Development and +# Distribution License (the License). You may not use this file except in compliance with the +# License. +# +# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the +# specific language governing permission and limitations under the License. +# +# When distributing Covered Software, include this CDDL Header Notice in each file and include +# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL +# Header, with the fields enclosed by brackets [] replaced by your own identifying +# information: "Portions copyright [year] [name of copyright owner]". +# +# Portions Copyright 2026 3A Systems, LLC. + name: openig title: 'OpenIG: Identity Gateway' version: ~ +display_version: '6.x' #start_page: gateway-guide:preface.adoc asciidoc: attributes: From 142053220a5be6564a34a804578e871060dd24e6 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Thu, 1 Oct 2026 15:35:05 +0300 Subject: [PATCH 2/3] Address review of #31: stable temp path, clearer missing-tag error, no hooks, tar -f - - lib/versions-from-tags.js: extract the tags into build/antora-tags/ instead of a random mkdtemp directory, so that the file paths Antora logs for tag content are stable and can be listed in .github/build-baseline (#28); name the temp branch after the tag - fail with a hint when a tag is neither local nor on origin (e.g. a fork created before the tags were pushed) - commit in the temp repository with core.hooksPath=/dev/null, so global hooks of the developer cannot abort the build - tar -x -f -: read the archive from stdin explicitly instead of $TAPE --- lib/versions-from-tags.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/versions-from-tags.js b/lib/versions-from-tags.js index cea0913b95a..a5c4cd3c1a4 100644 --- a/lib/versions-from-tags.js +++ b/lib/versions-from-tags.js @@ -29,10 +29,11 @@ // temporary repository, which is aggregated apart from the other sources, and the result is given // the version of the tag: OpenAM-15.2.2 becomes version 15.2, displayed as 15.2.2. The branch // stays the unversioned (and latest) version, so its URLs do not change. +// The temporary repositories are kept at a fixed path, build/antora-tags/, because Antora logs +// the files of a tag with that path, and .github/build-baseline keys the known errors by it. const { execFileSync } = require('child_process') const fs = require('fs') -const os = require('os') const ospath = require('path') const TAG_RX = /^[^-]+-(\d+)\.(\d+)\.(\d+)$/ @@ -45,7 +46,9 @@ module.exports.register = function () { const sources = playbook.content.sources const withSources = (subset) => ({ ...playbook, content: { ...playbook.content, sources: subset } }) const aggregate = await aggregateContent(withSources(sources.filter((source) => !source.tags))) - const tmpdir = fs.mkdtempSync(ospath.join(os.tmpdir(), 'antora-tags-')) + const tmpdir = ospath.join(playbook.dir, 'build', 'antora-tags') + fs.rmSync(tmpdir, { recursive: true, force: true }) + fs.mkdirSync(tmpdir, { recursive: true }) try { for (const source of sources.filter((source) => source.tags)) { const tag = source.tags @@ -78,16 +81,25 @@ function extractTag (repo, tag, startPath, worktree, logger) { } catch { logger.info('fetching tag %s', tag) const shallow = git(repo, 'rev-parse', '--is-shallow-repository').trim() === 'true' - git(repo, 'fetch', '-q', '--no-tags', ...(shallow ? ['--depth=1'] : []), 'origin', `+refs/tags/${tag}:refs/tags/${tag}`) + try { + git(repo, 'fetch', '-q', '--no-tags', ...(shallow ? ['--depth=1'] : []), 'origin', `+refs/tags/${tag}:refs/tags/${tag}`) + } catch (err) { + // e.g. a clone of a fork created before the tag was pushed + throw new Error(`tag ${tag} is neither in ${repo} nor on its origin; run ` + + `git fetch https://github.com/OpenIdentityPlatform/doc.openidentityplatform.org tag ${tag}\n${err.stderr || err.message}`) + } } fs.mkdirSync(worktree) const paths = [`${startPath}/antora.yml`, `${startPath}/modules`] const archive = execFileSync('git', ['-C', repo, 'archive', '--format=tar', tag, '--', ...paths], { maxBuffer: 1024 * 1024 * 1024, }) - execFileSync('tar', ['-x', '-C', worktree], { input: archive }) - git(worktree, 'init', '-q') + // -f -: without it tar reads the archive from $TAPE or a system-dependent default device + execFileSync('tar', ['-x', '-f', '-', '-C', worktree], { input: archive }) + // the branch is named after the tag, so that Antora logs the tag as the refname of its files + git(worktree, 'init', '-q', '-b', tag) git(worktree, 'add', '-A') + // no hooks: a global core.hooksPath or init.templateDir hook of the developer may reject the commit git(worktree, '-c', 'user.name=antora', '-c', 'user.email=antora@localhost', '-c', 'commit.gpgsign=false', - 'commit', '-q', '-m', tag) + '-c', 'core.hooksPath=/dev/null', 'commit', '-q', '-m', tag) } From 01b07661cb5e3f022ee1acbf42c1acddea21fc6c Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Thu, 1 Oct 2026 17:13:20 +0300 Subject: [PATCH 3/3] Address review of #31: neutral tag-fetch error, 3A copyright in header-less files - lib/versions-from-tags.js: a failed tag fetch no longer claims the tag is missing on origin; the cause (missing tag, no network) is left to git's error on the next line - lib/add-pdf-link.js, /antora.yml: "Copyright 2024-2026 3A Systems, LLC." instead of "Portions Copyright" - these files had no earlier copyright holder, as for the playbook in #29 --- lib/add-pdf-link.js | 2 +- lib/versions-from-tags.js | 4 ++-- openam/antora.yml | 2 +- opendj/antora.yml | 2 +- openidm/antora.yml | 2 +- openig/antora.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/add-pdf-link.js b/lib/add-pdf-link.js index 2b32c18062c..eed729a475f 100644 --- a/lib/add-pdf-link.js +++ b/lib/add-pdf-link.js @@ -11,7 +11,7 @@ * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * - * Portions Copyright 2026 3A Systems, LLC. + * Copyright 2024-2026 3A Systems, LLC. */ module.exports.register = function () { diff --git a/lib/versions-from-tags.js b/lib/versions-from-tags.js index a5c4cd3c1a4..875d2541e4d 100644 --- a/lib/versions-from-tags.js +++ b/lib/versions-from-tags.js @@ -84,8 +84,8 @@ function extractTag (repo, tag, startPath, worktree, logger) { try { git(repo, 'fetch', '-q', '--no-tags', ...(shallow ? ['--depth=1'] : []), 'origin', `+refs/tags/${tag}:refs/tags/${tag}`) } catch (err) { - // e.g. a clone of a fork created before the tag was pushed - throw new Error(`tag ${tag} is neither in ${repo} nor on its origin; run ` + + // e.g. a clone of a fork created before the tag was pushed, or no network + throw new Error(`cannot fetch tag ${tag} from the origin of ${repo}; if the tag is missing there, run ` + `git fetch https://github.com/OpenIdentityPlatform/doc.openidentityplatform.org tag ${tag}\n${err.stderr || err.message}`) } } diff --git a/openam/antora.yml b/openam/antora.yml index b5f956252c8..dadacfee15f 100644 --- a/openam/antora.yml +++ b/openam/antora.yml @@ -10,7 +10,7 @@ # Header, with the fields enclosed by brackets [] replaced by your own identifying # information: "Portions copyright [year] [name of copyright owner]". # -# Portions Copyright 2026 3A Systems, LLC. +# Copyright 2024-2026 3A Systems, LLC. name: openam title: 'OpenAM: Access Management' diff --git a/opendj/antora.yml b/opendj/antora.yml index 14154ade69e..f1a2bf9381b 100644 --- a/opendj/antora.yml +++ b/opendj/antora.yml @@ -10,7 +10,7 @@ # Header, with the fields enclosed by brackets [] replaced by your own identifying # information: "Portions copyright [year] [name of copyright owner]". # -# Portions Copyright 2026 3A Systems, LLC. +# Copyright 2024-2026 3A Systems, LLC. name: opendj title: 'OpenDJ: Directory Services' diff --git a/openidm/antora.yml b/openidm/antora.yml index d3ec27c746d..7df0719c8fc 100644 --- a/openidm/antora.yml +++ b/openidm/antora.yml @@ -10,7 +10,7 @@ # Header, with the fields enclosed by brackets [] replaced by your own identifying # information: "Portions copyright [year] [name of copyright owner]". # -# Portions Copyright 2026 3A Systems, LLC. +# Copyright 2024-2026 3A Systems, LLC. name: openidm title: 'OpenIDM: Identity Management' diff --git a/openig/antora.yml b/openig/antora.yml index 9ab0b18f936..13360ebbece 100644 --- a/openig/antora.yml +++ b/openig/antora.yml @@ -10,7 +10,7 @@ # Header, with the fields enclosed by brackets [] replaced by your own identifying # information: "Portions copyright [year] [name of copyright owner]". # -# Portions Copyright 2026 3A Systems, LLC. +# Copyright 2024-2026 3A Systems, LLC. name: openig title: 'OpenIG: Identity Gateway'