Skip to content

Commit df2d47a

Browse files
authored
Merge pull request #29 from spring-cloud/update-antora-ui-bundle-on-docs-build-branch
Update anotra ui bundle on docs-build branch
2 parents 56f4ddb + 1279969 commit df2d47a

4 files changed

Lines changed: 96 additions & 9 deletions

File tree

‎.github/scripts/__tests__/antora-ui-bundle.test.js‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { PLAYBOOK_PATH, extractBundle, rewrite, cmp } = require('../antora-ui-bundle');
1+
const { PLAYBOOK_PATH, DOCS_BUILD_BRANCH, DOCS_BUILD_PLAYBOOK_PATH, playbookCandidates, extractBundle, rewrite, cmp } = require('../antora-ui-bundle');
22

33
// The exact shape confirmed live on spring-cloud-commons's main, 4.2.x, 4.3.x and 5.0.x
44
// branches (and their -commercial counterparts).
@@ -28,6 +28,41 @@ describe('PLAYBOOK_PATH', () => {
2828
});
2929
});
3030

31+
describe('docs-build playbook', () => {
32+
const DOCS_BUILD = [
33+
'content:',
34+
' sources:',
35+
' - url: https://github.com/spring-cloud/spring-cloud-function',
36+
' branches: [ main, 4.3.x, 4.2.x ]',
37+
'ui:',
38+
' bundle:',
39+
' url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip',
40+
' snapshot: true',
41+
'',
42+
].join('\n');
43+
44+
it('has the repo-wide branch and root playbook path', () => {
45+
expect(DOCS_BUILD_BRANCH).toBe('docs-build');
46+
expect(DOCS_BUILD_PLAYBOOK_PATH).toBe('antora-playbook.yml');
47+
});
48+
49+
it('extracts and rewrites the bundle url without touching the rest', () => {
50+
expect(extractBundle(DOCS_BUILD).tag).toBe('v0.4.18');
51+
const out = rewrite(DOCS_BUILD, { repo: 'spring-io/antora-ui-spring', tag: 'v0.4.26' });
52+
expect(out).toContain('releases/download/v0.4.26/ui-bundle.zip');
53+
expect(out.replace('v0.4.26', 'v0.4.18')).toBe(DOCS_BUILD);
54+
});
55+
});
56+
57+
describe('playbookCandidates', () => {
58+
it('tries .yml then .yaml, whichever the configured path uses', () => {
59+
expect(playbookCandidates('antora-playbook.yml'))
60+
.toEqual(['antora-playbook.yml', 'antora-playbook.yaml']);
61+
expect(playbookCandidates('docs/antora-playbook.yaml'))
62+
.toEqual(['docs/antora-playbook.yaml', 'docs/antora-playbook.yml']);
63+
});
64+
});
65+
3166
describe('extractBundle', () => {
3267
it('reads the repo, tag and full url from a real playbook', () => {
3368
expect(extractBundle(PLAYBOOK)).toEqual({

‎.github/scripts/antora-ui-bundle.js‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@ const { cmp } = require('./version-cmp');
88
// such file at all.
99
const PLAYBOOK_PATH = 'docs/antora-playbook.yml';
1010

11+
// Every Antora project also keeps a repo-wide `docs-build` branch whose root
12+
// antora-playbook.yml pins the same UI bundle URL (same shape, so extractBundle/rewrite apply
13+
// unchanged). It is one branch per repository rather than one per maintained branch.
14+
const DOCS_BUILD_BRANCH = 'docs-build';
15+
const DOCS_BUILD_PLAYBOOK_PATH = 'antora-playbook.yml';
16+
1117
// Matches the UI bundle download URL wherever it appears in the file - matched by shape (a
1218
// GitHub release asset download link) rather than by indentation under ui:/bundle:/url:,
1319
// the same way currentMaven() in maven-wrapper-properties.js matches distributionUrl by
1420
// shape rather than by position.
1521
const BUNDLE_URL =
1622
/https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/releases\/download\/([^/\s]+)\/([^\s'"]+)/;
1723

24+
// The path itself, then the same path with the other YAML extension - a playbook may be
25+
// named either antora-playbook.yml or antora-playbook.yaml.
26+
function playbookCandidates(path) {
27+
const alt = path.endsWith('.yml') ? path.replace(/\.yml$/, '.yaml') : path.replace(/\.yaml$/, '.yml');
28+
return alt === path ? [path] : [path, alt];
29+
}
30+
1831
// { repo, tag, url } for the UI bundle release this playbook currently points at, or null
1932
// when no release-download URL is present at all.
2033
function extractBundle(text) {
@@ -33,4 +46,4 @@ function rewrite(text, { repo, tag }) {
3346
return text.replace(current.url, newUrl);
3447
}
3548

36-
module.exports = { PLAYBOOK_PATH, extractBundle, rewrite, cmp };
49+
module.exports = { PLAYBOOK_PATH, DOCS_BUILD_BRANCH, DOCS_BUILD_PLAYBOOK_PATH, playbookCandidates, extractBundle, rewrite, cmp };

‎.github/workflows/README-update-antora-ui-bundle.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ rather than there being one canonical file on the default branch. Branches that
4343
Antora adoption have no such file at all; those are reported as `no-playbook` rather than an
4444
error.
4545

46+
## The `docs-build` playbook
47+
48+
Every Antora project also keeps a repo-wide `docs-build` branch whose root
49+
`antora-playbook.yml` pins the same UI bundle URL. It is one branch per repository (not one
50+
per maintained branch), so `setup` adds one extra matrix entry per repository —
51+
`branch: docs-build`, `playbook_path: antora-playbook.yml` — and the `update` job handles it
52+
through the same PR flow, statuses, auto-merge and notification as the per-branch playbooks.
53+
The PR head is `antora-ui-bundle-update/docs-build-<tag>`. Either `.yml` or `.yaml` is accepted (`.yml` first), for both kinds of playbook. A repository with no
54+
playbook on `docs-build` is reported as `no-playbook`. The `-internal` exclusion
55+
does not apply.
56+
4657
### What is out of scope
4758

4859
- **`-internal` branches are skipped**, for the same reason

‎.github/workflows/update-antora-ui-bundle.yml‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ run-name: "${{ (github.event_name != 'schedule' && inputs.dry_run != false) && '
1717
# predate Antora adoption have no such file at all, which this discovers rather than assumes
1818
# (confirmed: spring-cloud-commons's 3.1.x has none, while main/4.2.x/4.3.x/5.0.x do).
1919
#
20+
# Each repository's repo-wide docs-build branch keeps its own antora-playbook.yml (at the
21+
# root) pinning the same bundle, so it gets one extra matrix entry per repository and is
22+
# bumped through exactly the same PR flow.
23+
#
2024
# This is the same shape of problem update-maven-wrapper.yml solves for the Maven wrapper -
2125
# an upstream artifact versions independently of every project, and nothing else notices when
2226
# it moves - and shares that workflow's gh-cli.js, git-pr-helpers.js, merge-if-green.js and
@@ -129,10 +133,12 @@ jobs:
129133
PROJECTS_FILTER: ${{ inputs.projects }}
130134
REPO_TYPE: ${{ inputs.repo_type || 'both' }}
131135
MATRIX_LIB: ${{ github.workspace }}/.github/scripts/project-branch-matrix.js
136+
BUNDLE_LIB: ${{ github.workspace }}/.github/scripts/antora-ui-bundle.js
132137
run: |
133138
node - << 'JSEOF'
134139
const fs = require('fs');
135-
const { buildBranchMatrix } = require(process.env.MATRIX_LIB);
140+
const { buildBranchMatrix, walkProjects } = require(process.env.MATRIX_LIB);
141+
const A = require(process.env.BUNDLE_LIB);
136142
137143
const projects = JSON.parse(fs.readFileSync('config/projects.json', 'utf8'));
138144
@@ -147,6 +153,18 @@ jobs:
147153
repoType: process.env.REPO_TYPE,
148154
});
149155
156+
// Plus one docs-build entry per repository: that branch is repo-wide (not one per
157+
// maintained branch) and keeps its playbook at the root, so the entry carries its
158+
// own path. A repo with no docs-build playbook is reported as no-playbook.
159+
walkProjects(projects, {
160+
filter: process.env.PROJECTS_FILTER,
161+
repoType: process.env.REPO_TYPE,
162+
}, ({ projectKey, typeKey, repo }) => {
163+
entries.push({ project: projectKey, repo, type: typeKey,
164+
branch: A.DOCS_BUILD_BRANCH, playbook_path: A.DOCS_BUILD_PLAYBOOK_PATH });
165+
});
166+
entries.sort((a, b) => a.repo.localeCompare(b.repo) || a.branch.localeCompare(b.branch));
167+
150168
console.log(`Repo/branch combinations to check: ${entries.length}`);
151169
for (const e of entries) console.log(` ${e.repo}@${e.branch}`);
152170
console.log(`Excluded (-internal): ${excluded.length}`);
@@ -189,6 +207,7 @@ jobs:
189207
BRANCH: ${{ matrix.branch }}
190208
PROJECT: ${{ matrix.project }}
191209
TYPE: ${{ matrix.type }}
210+
PLAYBOOK_PATH: ${{ matrix.playbook_path }}
192211
TARGET_TAG: ${{ needs.versions.outputs.tag }}
193212
# A scheduled run has no inputs, so `inputs.dry_run != false` would be true and the
194213
# weekly job would never actually do anything. Schedule therefore acts, while a
@@ -206,6 +225,10 @@ jobs:
206225
const BRANCH = process.env.BRANCH;
207226
const TARGET = process.env.TARGET_TAG;
208227
const DRY = process.env.DRY_RUN !== 'false';
228+
// docs/antora-playbook.yml on a maintained branch; antora-playbook.yml at the root
229+
// of the repo-wide docs-build branch (the matrix entry says which).
230+
// Either extension is accepted (.yml preferred), as update-antora-playbook does.
231+
const CONFIGURED = process.env.PLAYBOOK_PATH || A.PLAYBOOK_PATH;
209232
210233
const safe = `${REPO}@${BRANCH}`.replace(/[/@]/g, '-');
211234
const finish = (status, detail, extra = {}) => {
@@ -227,11 +250,16 @@ jobs:
227250
const enc = encodeURIComponent(BRANCH);
228251
229252
// ── Read the playbook at this branch ─────────────────────────────────────────────
230-
const got = PR.readFileAt(REPO, A.PLAYBOOK_PATH, enc);
231-
if (!got) finish('no-playbook', `no ${A.PLAYBOOK_PATH} on this branch`);
253+
let PLAYBOOK = CONFIGURED;
254+
let got = null;
255+
for (const candidate of A.playbookCandidates(CONFIGURED)) {
256+
got = PR.readFileAt(REPO, candidate, enc);
257+
if (got) { PLAYBOOK = candidate; break; }
258+
}
259+
if (!got) finish('no-playbook', `no ${CONFIGURED} (or .yaml) on this branch`);
232260
233261
const bundle = A.extractBundle(got.text);
234-
if (!bundle) finish('unparsed', `no UI bundle release-download url in ${A.PLAYBOOK_PATH}`);
262+
if (!bundle) finish('unparsed', `no UI bundle release-download url in ${PLAYBOOK}`);
235263
236264
const extras = { current: bundle.tag, target: TARGET };
237265
@@ -273,7 +301,7 @@ jobs:
273301
// Read the PR's own head branch: it may already be at the target, or behind it
274302
// (a newer release has shipped since it was opened).
275303
const headRef = encodeURIComponent(pr.head.ref);
276-
const onHead = PR.readFileAt(REPO, A.PLAYBOOK_PATH, headRef);
304+
const onHead = PR.readFileAt(REPO, PLAYBOOK, headRef);
277305
const headBundle = onHead && A.extractBundle(onHead.text);
278306
279307
if (!headBundle || A.cmp(headBundle.tag, TARGET) >= 0) {
@@ -288,7 +316,7 @@ jobs:
288316
// PR per branch, always at the current target, rather than a growing stack.
289317
const headUpdated = A.rewrite(onHead.text, { repo: headBundle.repo, tag: TARGET });
290318
const bump = PR.commitFilesToRef(REPO, pr.head.ref,
291-
[{ path: A.PLAYBOOK_PATH, content: headUpdated }], message, author);
319+
[{ path: PLAYBOOK, content: headUpdated }], message, author);
292320
if (!bump.ok) finish('error', `could not update #${pr.number}: ${bump.err}`);
293321
finish('pr-updated', `#${pr.number} moved up to ${TARGET}`,
294322
{ ...extras, prUrl: pr.html_url, prNumber: pr.number });
@@ -307,7 +335,7 @@ jobs:
307335
308336
const updatedText = A.rewrite(got.text, { repo: bundle.repo, tag: TARGET });
309337
const put = PR.commitFilesToRef(REPO, head,
310-
[{ path: A.PLAYBOOK_PATH, content: updatedText }], message, author);
338+
[{ path: PLAYBOOK, content: updatedText }], message, author);
311339
if (!put.ok) finish('error', `could not commit: ${put.err}`);
312340
313341
const opened = PR.openPr(REPO, {

0 commit comments

Comments
 (0)