From 40e46adcaf9afe13053f89ea577a3ee3f4010f68 Mon Sep 17 00:00:00 2001 From: Dan Allen Date: Tue, 23 Jul 2024 14:58:11 -0600 Subject: [PATCH] resolves #22 allow keep worktree behavior to be configured per origin --- CHANGELOG.adoc | 3 +- .../ROOT/pages/configuration-keys.adoc | 22 ++++++++- packages/collector-extension/lib/index.js | 49 ++++++++++--------- .../test/collector-extension-test.js | 36 +++++++++----- 4 files changed, 73 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c29f09c..cf14e5a 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -10,7 +10,8 @@ For a detailed view of what's changed, refer to the {url-repo}/commits[commit hi * allow target bucket to be specified using `into` key on scan operation (#3) * allow different target bucket to be specified in scanned antora.yml when `create` key is true (#3) -* allow checkout of worktree to be bypassed by setting `worktree.false` on the collector config (#8) +* allow checkout of worktree to be bypassed by setting `worktree.checkout` on the collector config (#8) +* allow keep worktree behavior to be configured per origin by setting `worktree.keep` on the collector config (#22) == 1.0.0-alpha.6 (2024-07-17) diff --git a/docs/modules/ROOT/pages/configuration-keys.adoc b/docs/modules/ROOT/pages/configuration-keys.adoc index 2191c56..ccf685e 100644 --- a/docs/modules/ROOT/pages/configuration-keys.adoc +++ b/docs/modules/ROOT/pages/configuration-keys.adoc @@ -56,7 +56,7 @@ The `worktree` key must be nested under the `collector` key. If the `collector` key is an array, the `worktree` key must be specified in the first entry of the array. The `worktree` key accepts map. -Currently, the only key recognized in that map is `checkout`. +Acceptable keys are `checkout` and `keep`. By default, Collector will checkout the git reference into a temporary worktree if the content source does not already have a worktree. To turn off the default behavior and run collector in an empty directory, set the value of this key to `false`. @@ -82,6 +82,26 @@ When the worktree checkout behavior is turned off, the command to run must be av The command cannot be a file in the git repository (unless the command does its own checkout). The purpose of this option is to avoid the overhead of checking out the worktree when the Collector run does not need any of those files. +The decision to keep the worktree can be configured per origin by setting `worktree.keep` on the collector configuration. + +.antora.yml +[,yaml] +---- +name: colorado +title: Colorado +version: '5.6.0' +ext: + collector: + worktree: + keep: until:exit + run: + - command: generate-files + scan: + dir: generated +---- + +This `worktree.keep` setting overrides the global setting on the extension. + [#clean-key] == clean key diff --git a/packages/collector-extension/lib/index.js b/packages/collector-extension/lib/index.js index b5b6f17..71aed8e 100644 --- a/packages/collector-extension/lib/index.js +++ b/packages/collector-extension/lib/index.js @@ -36,15 +36,16 @@ module.exports.register = function ({ config: { keepWorktrees = false } }) { let worktreeDir = worktree let checkoutWorktree if (!worktree) { - worktreeDir = ospath.join(cacheDir, generateWorktreeFolderName(origin, keepWorktrees)) const worktreeConfig = collectorConfig[0].worktree || {} checkoutWorktree = worktreeConfig.checkout !== false + const keepWorktree = 'keep' in worktreeConfig ? worktreeConfig.keep : keepWorktrees + worktreeDir = ospath.join(cacheDir, generateWorktreeFolderName(origin, keepWorktree)) if (managedWorktrees.has(worktreeDir)) { - managedWorktrees.get(worktreeDir).add(origin) + managedWorktrees.get(worktreeDir).origins.add(origin) if (!checkoutWorktree) await fsp.rm(worktreeDir, { force: true, recursive: true }) } else { - managedWorktrees.set(worktreeDir, new Set([origin])) - if (!checkoutWorktree || keepWorktrees !== true) await fsp.rm(worktreeDir, { force: true, recursive: true }) + managedWorktrees.set(worktreeDir, { origins: new Set([origin]), keep: keepWorktree }) + if (!checkoutWorktree || keepWorktree !== true) await fsp.rm(worktreeDir, { force: true, recursive: true }) } origin.collectorWorktree = worktreeDir } @@ -167,29 +168,24 @@ module.exports.register = function ({ config: { keepWorktrees = false } }) { } } } - if (!keepWorktrees) { - for (const [collectorWorktree, origins] of managedWorktrees) { - for (const origin of origins) delete origin.collectorWorktree - await fsp.rm(collectorWorktree, { recursive: true }) + const deferredWorktreeRemovals = new Map() + for (const [worktreeDir, { origins, keep }] of managedWorktrees) { + if (keep === true) continue + if (typeof keep === 'string' && keep.startsWith('until:')) { + const eventName = keep === 'until:exit' ? 'contextClosed' : keep.slice(6) + const removal = { worktreeDir, origins } + const removals = deferredWorktreeRemovals.get(eventName) + removals ? removals.push(removal) : deferredWorktreeRemovals.set(eventName, [removal]) + continue } + await removeWorktree(worktreeDir, origins) + } + for (const [eventName, removals] of deferredWorktreeRemovals) { + this.once(eventName, () => + Promise.all(removals.map(({ worktreeDir, origins }) => removeWorktree(worktreeDir, origins))) + ) } }) - - if (typeof keepWorktrees === 'string' && keepWorktrees.startsWith('until:')) { - const eventName = keepWorktrees.slice(6) - this.once(eventName === 'exit' ? 'contextClosed' : eventName, async ({ contentCatalog }) => { - // TODO get origins from component version once implemented in Antora core - const collectorWorktrees = contentCatalog.getFiles().reduce((accum, { src }) => { - const collectorWorktree = src.origin?.collectorWorktree - if (collectorWorktree) { - accum.add(collectorWorktree) - delete src.origin.collectorWorktree - } - return accum - }, new Set()) - for (const collectorWorktree of collectorWorktrees) await fsp.rm(collectorWorktree, { recursive: true }) - }) - } } /** @@ -237,6 +233,11 @@ async function prepareWorktree (repo) { } } +async function removeWorktree (worktreeDir, origins) { + for (const origin of origins) delete origin.collectorWorktree + await fsp.rm(worktreeDir, { recursive: true }) +} + function generateWorktreeFolderName ({ url, gitdir, refname, worktree }, keepWorktrees) { const refnameQualifier = keepWorktrees ? '@' + refname.replace(/[/]/g, '-') : undefined if (worktree === undefined) { diff --git a/packages/collector-extension/test/collector-extension-test.js b/packages/collector-extension/test/collector-extension-test.js index 82ac333..74083e6 100644 --- a/packages/collector-extension/test/collector-extension-test.js +++ b/packages/collector-extension/test/collector-extension-test.js @@ -166,17 +166,7 @@ describe('collector extension', () => { ext.register.call(generatorContext, registerVars) await generatorContext.contentAggregated({ playbook, contentAggregate }) if (after) isAsync(after) ? await after(contentAggregate) : after(contentAggregate) - if (typeof generatorContext.contextClosed === 'function') { - const contentCatalog = { - getFiles () { - return contentAggregate.reduce((accum, { origins = [] }) => { - origins.forEach((origin) => accum.push({ src: { origin } })) - return accum - }, []) - }, - } - await generatorContext.contextClosed({ contentCatalog }) - } + if (typeof generatorContext.contextClosed === 'function') await generatorContext.contextClosed() } it('should not allocate worktree for reference in git tree if collector is not configured', async () => { @@ -264,6 +254,30 @@ describe('collector extension', () => { expect(collectorWorktree).to.not.be.a.path() }) + it('should keep temporary worktree until specified event when specified on collector config', async () => { + const collectorConfig = { + worktree: { keep: 'until:exit' }, + run: { command: 'node .gen-start-page.js' }, + scan: { dir: 'build' }, + } + let collectorWorktree + await runScenario({ + repoName: 'test-at-root', + collectorConfig, + before: (contentAggregate) => { + expect(contentAggregate).to.have.lengthOf(1) + expect(contentAggregate[0].files).to.be.empty() + }, + after: (contentAggregate) => { + expect(contentAggregate[0].files).to.have.lengthOf(1) + collectorWorktree = contentAggregate[0].files[0].src.origin.collectorWorktree + expect(collectorWorktree).to.be.a.directory() + expect(ospath.basename(collectorWorktree)).to.include('@main') + }, + }) + expect(collectorWorktree).to.not.be.a.path() + }) + it('should populate properties of file collected from temporary worktree', async () => { const collectorConfig = { run: { command: 'node .gen-start-page.js' }, -- GitLab