@antora/content-aggregator 3.2.0-alpha.8 → 3.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/aggregate-content.js +254 -151
- package/lib/compute-origin.js +1 -1
- package/lib/filter-refs.js +6 -1
- package/lib/resolve-path-globs.js +1 -1
- package/package.json +6 -6
package/lib/aggregate-content.js
CHANGED
|
@@ -87,35 +87,41 @@ const URL_PORT_CLEANER_RX = /^([^/]+):[0-9]+(?=\/)/
|
|
|
87
87
|
*/
|
|
88
88
|
function aggregateContent (playbook) {
|
|
89
89
|
const startDir = playbook.dir || '.'
|
|
90
|
-
const { branches, editUrl, tags, sources } = playbook.content
|
|
91
|
-
const sourceDefaults = { branches, editUrl, tags }
|
|
90
|
+
const { branches, editUrl, tags, sources, worktrees } = playbook.content
|
|
91
|
+
const sourceDefaults = { branches, editUrl, tags, worktrees }
|
|
92
92
|
const { cacheDir: requestedCacheDir, fetch, quiet } = playbook.runtime
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
const READABLE_STREAM_DISABLED = process.env.READABLE_STREAM === 'disable'
|
|
94
|
+
if (!READABLE_STREAM_DISABLED) process.env.READABLE_STREAM = 'disable' // force use of Node.js stream APIs
|
|
95
|
+
return ensureCacheDir(requestedCacheDir, startDir)
|
|
96
|
+
.then((cacheDir) => {
|
|
97
|
+
const gitConfig = Object.assign({ ensureGitSuffix: true }, playbook.git)
|
|
98
|
+
const gitPlugins = loadGitPlugins(gitConfig, playbook.network || {}, startDir)
|
|
99
|
+
const concurrency = {
|
|
100
|
+
fetch: Math.max(gitConfig.fetchConcurrency || Infinity, 1),
|
|
101
|
+
read: Math.max(gitConfig.readConcurrency || Infinity, 1),
|
|
102
|
+
}
|
|
103
|
+
const sourcesByUrl = sources.reduce((accum, source) => {
|
|
104
|
+
return accum.set(source.url, [...(accum.get(source.url) || []), Object.assign({}, sourceDefaults, source)])
|
|
105
|
+
}, new Map())
|
|
106
|
+
const progress = quiet ? undefined : createProgress(sourcesByUrl.keys(), process.stdout)
|
|
107
|
+
const refPatternCache = Object.assign(new Map(), { braces: new Map() })
|
|
108
|
+
const fetchConfig = { always: fetch, depth: Math.max(0, gitConfig.fetchDepth ?? 1) }
|
|
109
|
+
const loadOpts = { cacheDir, fetch: fetchConfig, gitPlugins, progress, startDir, refPatternCache }
|
|
110
|
+
return collectFiles(sourcesByUrl, loadOpts, concurrency).then(buildAggregate, (err) => {
|
|
111
|
+
progress?.terminate()
|
|
112
|
+
throw err
|
|
113
|
+
})
|
|
110
114
|
})
|
|
111
|
-
|
|
115
|
+
.finally(() => READABLE_STREAM_DISABLED || delete process.env.READABLE_STREAM)
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls = []) {
|
|
115
119
|
const loadTasks = [...sourcesByUrl.entries()].map(([url, sources]) => {
|
|
116
120
|
const loadOptsForUrl = Object.assign({}, loadOpts)
|
|
117
|
-
if (loadOpts.fetch.always && fetchedUrls.length && fetchedUrls.
|
|
121
|
+
if (loadOpts.fetch.always && fetchedUrls.length && ~fetchedUrls.indexOf(url)) loadOptsForUrl.fetch.always = false
|
|
118
122
|
if (tagsSpecified(sources)) loadOptsForUrl.fetch.tags = true
|
|
123
|
+
const commits = commitsRequested(sources)
|
|
124
|
+
if (commits) loadOptsForUrl.fetch.commits = commits
|
|
119
125
|
return loadRepository.bind(null, url, loadOptsForUrl, { url, sources })
|
|
120
126
|
})
|
|
121
127
|
return gracefulPromiseAllWithLimit(loadTasks, concurrency.fetch).then(([results, rejections]) => {
|
|
@@ -132,7 +138,7 @@ async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls =
|
|
|
132
138
|
}
|
|
133
139
|
return Promise.all(
|
|
134
140
|
results.map(({ repo, authStatus, sources }) =>
|
|
135
|
-
selectStartPathsForRepository(repo,
|
|
141
|
+
selectStartPathsForRepository(repo, sources).then((startPaths) =>
|
|
136
142
|
collectFilesFromStartPaths.bind(null, startPaths, repo, authStatus)
|
|
137
143
|
)
|
|
138
144
|
)
|
|
@@ -140,9 +146,10 @@ async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls =
|
|
|
140
146
|
})
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
function buildAggregate (
|
|
144
|
-
const
|
|
145
|
-
|
|
149
|
+
function buildAggregate (componentVersionBatches) {
|
|
150
|
+
const contentAggregate = []
|
|
151
|
+
const entries = new Map()
|
|
152
|
+
for (const batchesForOrigin of componentVersionBatches) {
|
|
146
153
|
for (const batch of batchesForOrigin) {
|
|
147
154
|
let key, entry
|
|
148
155
|
if ((entry = entries.get((key = batch.version + '@' + batch.name)))) {
|
|
@@ -151,11 +158,12 @@ function buildAggregate (componentVersionBuckets) {
|
|
|
151
158
|
;(batch.origins = entry.origins).push(origins[0])
|
|
152
159
|
Object.assign(entry, batch)
|
|
153
160
|
} else {
|
|
154
|
-
entries.set(key, batch)
|
|
161
|
+
entries.set(key, batch)
|
|
162
|
+
contentAggregate.push(batch)
|
|
155
163
|
}
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
|
-
return
|
|
166
|
+
return contentAggregate
|
|
159
167
|
}
|
|
160
168
|
|
|
161
169
|
async function loadRepository (url, opts, result = {}) {
|
|
@@ -177,6 +185,7 @@ async function loadRepository (url, opts, result = {}) {
|
|
|
177
185
|
const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetch, 'fetch')
|
|
178
186
|
await git
|
|
179
187
|
.fetch(fetchOpts)
|
|
188
|
+
.then(() => ensureOids(fetchOpts))
|
|
180
189
|
.then(() => {
|
|
181
190
|
authStatus = identifyAuthStatus(credentialManager, credentials, url)
|
|
182
191
|
return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
|
|
@@ -192,12 +201,13 @@ async function loadRepository (url, opts, result = {}) {
|
|
|
192
201
|
authStatus = await git.getConfig(Object.assign({ path: 'remote.origin.private' }, repo))
|
|
193
202
|
}
|
|
194
203
|
} catch (gitErr) {
|
|
195
|
-
await fsp
|
|
204
|
+
await fsp.rm(dir, { recursive: true, force: true })
|
|
196
205
|
if (gitErr.rethrow) throw transformGitCloneError(gitErr, displayUrl)
|
|
197
206
|
const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetch, 'clone')
|
|
198
207
|
await git
|
|
199
208
|
.clone(fetchOpts)
|
|
200
209
|
.then(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo)))
|
|
210
|
+
.then(() => ensureOids(fetchOpts))
|
|
201
211
|
.then(() => {
|
|
202
212
|
authStatus = identifyAuthStatus(credentialManager, credentials, url)
|
|
203
213
|
return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
|
|
@@ -216,6 +226,7 @@ async function loadRepository (url, opts, result = {}) {
|
|
|
216
226
|
if (dotgitStat.isDirectory()) {
|
|
217
227
|
repo = { cache, dir, fs, gitdir: dotgit }
|
|
218
228
|
} else if (dotgitStat.isFile()) {
|
|
229
|
+
// NOTE isomorphic-git will discover the gitdir, but we must do it eagerly to process worktree patterns correctly
|
|
219
230
|
repo = await resolveRepositoryFromWorktree({ cache, dir, fs, gitdir: dotgit })
|
|
220
231
|
} else {
|
|
221
232
|
repo = { cache, dir, fs, gitdir: dir, noCheckout: true }
|
|
@@ -245,33 +256,46 @@ function extractCredentials (url) {
|
|
|
245
256
|
const credentials = username ? { username, password: password || '' } : {}
|
|
246
257
|
return { displayUrl, url, credentials }
|
|
247
258
|
}
|
|
248
|
-
if (url.startsWith('git@')) return { displayUrl: url, url: 'https://' + url.
|
|
259
|
+
if (url.startsWith('git@')) return { displayUrl: url, url: 'https://' + url.substring(4).replace(':', '/') }
|
|
249
260
|
return { displayUrl: url, url }
|
|
250
261
|
}
|
|
251
262
|
|
|
252
|
-
async function selectStartPathsForRepository (repo,
|
|
263
|
+
async function selectStartPathsForRepository (repo, sources) {
|
|
253
264
|
const startPaths = []
|
|
254
265
|
const originUrls = {}
|
|
255
266
|
for (const source of sources) {
|
|
256
267
|
const { version, editUrl } = source
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
268
|
+
let remoteName, originUrl
|
|
269
|
+
if (repo.url) {
|
|
270
|
+
remoteName = 'origin' // NOTE if repository is managed (has url property), we can assume remote name is origin
|
|
271
|
+
originUrl = repo.url
|
|
272
|
+
} else {
|
|
273
|
+
remoteName = source.remote || 'origin'
|
|
274
|
+
originUrl =
|
|
275
|
+
remoteName in originUrls
|
|
276
|
+
? originUrls[remoteName]
|
|
277
|
+
: (originUrls[remoteName] = await resolveRemoteUrl(repo, remoteName))
|
|
278
|
+
if (!originUrl) {
|
|
279
|
+
remoteName = undefined
|
|
280
|
+
if ((originUrl = posixify ? 'file:///' + posixify(repo.dir) : 'file://' + repo.dir).indexOf(' ')) {
|
|
281
|
+
originUrl = originUrl.replace(SPACE_RX, '%20')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
261
285
|
const refs = await selectReferences(source, repo, remoteName)
|
|
262
286
|
if (refs.length) {
|
|
263
287
|
for (const ref of refs) {
|
|
264
|
-
for (const startPath of await selectStartPaths(source, repo,
|
|
288
|
+
for (const startPath of await selectStartPaths(source, repo, ref)) {
|
|
265
289
|
startPaths.push({ startPath, ref, originUrl, editUrl, version })
|
|
266
290
|
}
|
|
267
291
|
}
|
|
268
292
|
} else {
|
|
269
|
-
const { url, branches, tags } = source
|
|
293
|
+
const { url, branches, tags, commits } = source
|
|
270
294
|
const startPathInfo =
|
|
271
295
|
'startPaths' in source
|
|
272
296
|
? { 'start paths': source.startPaths || undefined }
|
|
273
297
|
: { 'start path': source.startPath || undefined }
|
|
274
|
-
const sourceInfo = yaml.dump({ url, branches, tags, ...startPathInfo }, { flowLevel: 1 }).trimRight()
|
|
298
|
+
const sourceInfo = yaml.dump({ url, branches, tags, commits, ...startPathInfo }, { flowLevel: 1 }).trimRight()
|
|
275
299
|
logger.info(`No matching references found for content source entry (${sourceInfo.replace(NEWLINE_RX, ' | ')})`)
|
|
276
300
|
}
|
|
277
301
|
}
|
|
@@ -280,11 +304,12 @@ async function selectStartPathsForRepository (repo, authStatus, sources) {
|
|
|
280
304
|
|
|
281
305
|
// QUESTION should we resolve HEAD to a ref eagerly to avoid having to do a match on it?
|
|
282
306
|
async function selectReferences (source, repo, remote) {
|
|
283
|
-
let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns } = source
|
|
307
|
+
let { branches: branchPatterns, tags: tagPatterns, commits, worktrees: worktreePatterns } = source
|
|
284
308
|
const managed = 'url' in repo
|
|
285
309
|
const isBare = managed || repo.noCheckout
|
|
286
310
|
const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
|
|
287
311
|
const noWorktree = managed ? undefined : false
|
|
312
|
+
const isLinkedWorktree = repo.worktree?.name
|
|
288
313
|
const refs = new Map()
|
|
289
314
|
if (
|
|
290
315
|
tagPatterns &&
|
|
@@ -295,11 +320,20 @@ async function selectReferences (source, repo, remote) {
|
|
|
295
320
|
const tags = await git.listTags(repo)
|
|
296
321
|
if (tags.length) {
|
|
297
322
|
for (const shortname of filterRefs(tags, tagPatterns, patternCache)) {
|
|
298
|
-
// NOTE tags are stored using
|
|
299
|
-
refs.set(
|
|
323
|
+
// NOTE tags are stored using Buffer keys to distinguish them from commits and branches
|
|
324
|
+
refs.set(Buffer.from(shortname), { shortname, fullname: 'tags/' + shortname, type: 'tag', head: noWorktree })
|
|
300
325
|
}
|
|
301
326
|
}
|
|
302
327
|
}
|
|
328
|
+
if (
|
|
329
|
+
commits &&
|
|
330
|
+
(commits = Array.isArray(commits) ? commits.map((commit) => String(commit)) : commits.split(CSV_RX)).length
|
|
331
|
+
) {
|
|
332
|
+
for (const oid of commits) {
|
|
333
|
+
// NOTE commits are stored using Symbol keys to distinguish them from tags and branches
|
|
334
|
+
refs.set(Symbol(oid), { oid, shortname: oid, fullname: 'commits/' + oid, type: 'commit' })
|
|
335
|
+
}
|
|
336
|
+
}
|
|
303
337
|
if (
|
|
304
338
|
!branchPatterns ||
|
|
305
339
|
!(branchPatterns = Array.isArray(branchPatterns)
|
|
@@ -308,38 +342,54 @@ async function selectReferences (source, repo, remote) {
|
|
|
308
342
|
) {
|
|
309
343
|
return [...refs.values()]
|
|
310
344
|
}
|
|
311
|
-
|
|
312
|
-
if (
|
|
313
|
-
if (worktreePatterns) {
|
|
345
|
+
let useWorktree = false
|
|
346
|
+
if (!managed && (useWorktree = {})) {
|
|
314
347
|
if (worktreePatterns === '.') {
|
|
315
|
-
|
|
348
|
+
isLinkedWorktree ? (useWorktree.linked = isLinkedWorktree) : isBare || (useWorktree.main = true)
|
|
349
|
+
worktreePatterns = []
|
|
350
|
+
} else if (!worktreePatterns) {
|
|
351
|
+
worktreePatterns = []
|
|
316
352
|
} else if (worktreePatterns === true) {
|
|
317
|
-
|
|
353
|
+
if (!isBare) useWorktree.main = true
|
|
354
|
+
// NOTE if we don't start at a linked worktree, linked worktree cannot be current worktree
|
|
355
|
+
if (isLinkedWorktree) useWorktree.linked = isLinkedWorktree
|
|
356
|
+
worktreePatterns = ['*']
|
|
357
|
+
} else if (worktreePatterns === '/.') {
|
|
358
|
+
if (!isBare) useWorktree.main = true
|
|
359
|
+
worktreePatterns = []
|
|
318
360
|
} else {
|
|
319
|
-
worktreePatterns =
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
361
|
+
worktreePatterns = (
|
|
362
|
+
Array.isArray(worktreePatterns)
|
|
363
|
+
? worktreePatterns.map((pattern) => String(pattern))
|
|
364
|
+
: splitRefPatterns(String(worktreePatterns))
|
|
365
|
+
).reduce((accum, it) => {
|
|
366
|
+
if (it === '/.') return (isBare || (useWorktree.main = true)) && accum
|
|
367
|
+
if (it === '.') {
|
|
368
|
+
isLinkedWorktree ? (useWorktree.linked = isLinkedWorktree) : isBare || (useWorktree.main = true)
|
|
369
|
+
} else {
|
|
370
|
+
accum.push(it)
|
|
371
|
+
}
|
|
372
|
+
return accum
|
|
373
|
+
}, [])
|
|
323
374
|
}
|
|
324
|
-
|
|
325
|
-
worktreePatterns = worktreePatterns === undefined ? [worktreeName || '.'] : []
|
|
375
|
+
if (!(useWorktree.main || useWorktree.linked)) useWorktree = false
|
|
326
376
|
}
|
|
327
377
|
let currentBranch
|
|
328
|
-
if (
|
|
329
|
-
if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
|
|
330
|
-
branchPatterns = [currentBranch]
|
|
331
|
-
} else if (isBare) {
|
|
332
|
-
return [...refs.values()]
|
|
333
|
-
} else {
|
|
334
|
-
// NOTE current branch is undefined when HEAD is detached
|
|
335
|
-
const head = worktreePatterns[0] === '.' ? repo.dir : noWorktree
|
|
336
|
-
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
337
|
-
return [...refs.values()]
|
|
338
|
-
}
|
|
339
|
-
} else {
|
|
378
|
+
if (!isLinkedWorktree) {
|
|
340
379
|
let headBranchIdx
|
|
341
|
-
|
|
342
|
-
|
|
380
|
+
if (branchPatterns.length === 1 && (branchPatterns[0] === 'HEAD' || branchPatterns[0] === '.')) {
|
|
381
|
+
if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
|
|
382
|
+
branchPatterns = [currentBranch]
|
|
383
|
+
} else if (isBare) {
|
|
384
|
+
return [...refs.values()]
|
|
385
|
+
} else {
|
|
386
|
+
// NOTE current branch is undefined when HEAD is detached
|
|
387
|
+
const head = useWorktree.main ? repo.dir : noWorktree
|
|
388
|
+
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
389
|
+
return [...refs.values()]
|
|
390
|
+
}
|
|
391
|
+
} else if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
|
|
392
|
+
// NOTE we can assume at least two entries if HEAD or . are present
|
|
343
393
|
if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
|
|
344
394
|
if (~branchPatterns.indexOf(currentBranch)) {
|
|
345
395
|
branchPatterns.splice(headBranchIdx, 1)
|
|
@@ -349,11 +399,7 @@ async function selectReferences (source, repo, remote) {
|
|
|
349
399
|
} else if (isBare) {
|
|
350
400
|
branchPatterns.splice(headBranchIdx, 1)
|
|
351
401
|
} else {
|
|
352
|
-
|
|
353
|
-
if (worktreePatterns[0] === '.') {
|
|
354
|
-
worktreePatterns = worktreePatterns.slice(1)
|
|
355
|
-
head = repo.dir
|
|
356
|
-
}
|
|
402
|
+
const head = useWorktree.main ? repo.dir : noWorktree
|
|
357
403
|
// NOTE current branch is undefined when HEAD is detached
|
|
358
404
|
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
359
405
|
branchPatterns.splice(headBranchIdx, 1)
|
|
@@ -361,45 +407,54 @@ async function selectReferences (source, repo, remote) {
|
|
|
361
407
|
}
|
|
362
408
|
}
|
|
363
409
|
// NOTE isomorphic-git includes HEAD in list of remote branches (see https://isomorphic-git.org/docs/listBranches)
|
|
364
|
-
const remoteBranches =
|
|
410
|
+
const remoteBranches = remote
|
|
411
|
+
? (await git.listBranches(Object.assign({ remote }, repo))).filter((it) => it !== 'HEAD')
|
|
412
|
+
: []
|
|
365
413
|
if (remoteBranches.length) {
|
|
366
414
|
for (const shortname of filterRefs(remoteBranches, branchPatterns, patternCache)) {
|
|
367
415
|
const fullname = 'remotes/' + remote + '/' + shortname
|
|
368
416
|
refs.set(shortname, { shortname, fullname, type: 'branch', remote, head: noWorktree })
|
|
369
417
|
}
|
|
370
418
|
}
|
|
371
|
-
|
|
372
|
-
if (!isBare) {
|
|
419
|
+
if (!managed) {
|
|
373
420
|
const localBranches = await git.listBranches(repo).then((branches) => {
|
|
374
|
-
if (branches.length) return branches
|
|
375
|
-
if (currentBranch
|
|
376
|
-
return
|
|
421
|
+
if (branches.length || isBare) return branches
|
|
422
|
+
if (currentBranch != null) return [currentBranch]
|
|
423
|
+
return getCurrentBranchName(repo).then((branch) => (branch ? [branch] : []))
|
|
377
424
|
})
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return shortname ? (pattern.startsWith('HEAD@') ? shortname : undefined) : candidate
|
|
425
|
+
let onMatch, worktrees
|
|
426
|
+
if (
|
|
427
|
+
(useWorktree || worktreePatterns.length) &&
|
|
428
|
+
(worktrees = await findWorktrees(repo, worktreePatterns, useWorktree)).size
|
|
429
|
+
) {
|
|
430
|
+
const headNames = new Map()
|
|
431
|
+
worktrees.forEach(({ name, symbolicNames }, shortname) => {
|
|
432
|
+
if (name) {
|
|
433
|
+
const headName = 'HEAD@' + name
|
|
434
|
+
localBranches.push(headName)
|
|
435
|
+
headNames.set(headName, shortname)
|
|
390
436
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
437
|
+
if (symbolicNames) {
|
|
438
|
+
for (const symbolicName of symbolicNames) {
|
|
439
|
+
const symbolicHeadName = symbolicName === 'HEAD' ? symbolicName : 'HEAD@' + symbolicName
|
|
440
|
+
localBranches.push(symbolicHeadName)
|
|
441
|
+
headNames.set(symbolicHeadName, shortname)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
onMatch = (candidate, { pattern }) => {
|
|
446
|
+
const shortname = headNames.get(candidate)
|
|
447
|
+
if (!shortname) return candidate
|
|
448
|
+
if (pattern === 'HEAD' || pattern.startsWith('HEAD@')) return shortname
|
|
395
449
|
}
|
|
396
450
|
}
|
|
397
|
-
} else if (!managed || !remoteBranches.length) {
|
|
398
|
-
const localBranches = await git.listBranches(repo)
|
|
399
451
|
if (localBranches.length) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
refs.
|
|
452
|
+
const preferRemote = isBare && remoteBranches.length > 0
|
|
453
|
+
for (const shortname of filterRefs(localBranches, branchPatterns, patternCache, onMatch)) {
|
|
454
|
+
if (preferRemote && refs.has(shortname)) continue
|
|
455
|
+
worktrees ??= await findWorktrees(repo, worktreePatterns, useWorktree)
|
|
456
|
+
const head = (worktrees.get(shortname) || { head: false }).head
|
|
457
|
+
refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
|
|
403
458
|
}
|
|
404
459
|
}
|
|
405
460
|
}
|
|
@@ -419,14 +474,16 @@ function getCurrentBranchName (repo, remote) {
|
|
|
419
474
|
).then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
|
|
420
475
|
}
|
|
421
476
|
|
|
422
|
-
async function selectStartPaths (source, repo,
|
|
477
|
+
async function selectStartPaths (source, repo, ref) {
|
|
423
478
|
const url = repo.url
|
|
424
479
|
const displayUrl = url || repo.dir
|
|
425
480
|
const worktreePath = ref.head
|
|
426
481
|
if (!worktreePath) {
|
|
427
|
-
ref.oid =
|
|
428
|
-
Object.assign(
|
|
429
|
-
|
|
482
|
+
ref.oid = ref.oid
|
|
483
|
+
? await git.expandOid(Object.assign({ oid: ref.oid }, repo)).catch(() => ref.oid)
|
|
484
|
+
: await git.resolveRef(
|
|
485
|
+
Object.assign(ref.detached ? { ref: 'HEAD', depth: 1 } : { ref: 'refs/' + ref.fullname }, repo)
|
|
486
|
+
)
|
|
430
487
|
}
|
|
431
488
|
if ('startPaths' in source) {
|
|
432
489
|
let startPaths
|
|
@@ -461,7 +518,9 @@ function collectFilesFromStartPath (startPath, repo, authStatus, ref, originUrl,
|
|
|
461
518
|
return (worktreePath ? readFilesFromWorktree(origin) : readFilesFromGitTree(repo, ref.oid, startPath))
|
|
462
519
|
.then((files) => {
|
|
463
520
|
const batch = deepClone((origin.descriptor = loadComponentDescriptor(files, ref, version)))
|
|
464
|
-
if ('nav' in batch && Array.isArray(batch.nav))
|
|
521
|
+
if ('nav' in batch && Array.isArray(batch.nav)) {
|
|
522
|
+
Object.defineProperty(batch.nav, 'origin', { configurable: true, value: origin, writable: true })
|
|
523
|
+
}
|
|
465
524
|
batch.files = files.map((file) => assignFileProperties(file, origin))
|
|
466
525
|
batch.origins = [origin]
|
|
467
526
|
return batch
|
|
@@ -648,10 +707,10 @@ function readGitSymlink (repo, root, parent, { oid, path: name }, following) {
|
|
|
648
707
|
let target
|
|
649
708
|
let targetParent = root
|
|
650
709
|
if (dirname) {
|
|
651
|
-
if (!(target = path.join('/', dirname, symlink).
|
|
710
|
+
if (!(target = path.join('/', dirname, symlink).substring(1)) || target === dirname) {
|
|
652
711
|
target = '.'
|
|
653
712
|
} else if (target.startsWith(dirname + '/')) {
|
|
654
|
-
target = target.
|
|
713
|
+
target = target.substring(dirname.length + 1) // join doesn't remove trailing separator
|
|
655
714
|
targetParent = parent
|
|
656
715
|
}
|
|
657
716
|
} else {
|
|
@@ -734,19 +793,19 @@ function loadComponentDescriptor (files, ref, version) {
|
|
|
734
793
|
if (!version) {
|
|
735
794
|
if (version === undefined) throw new Error(`${COMPONENT_DESC_FILENAME} is missing a version`)
|
|
736
795
|
if (version === false) throw new Error(`${COMPONENT_DESC_FILENAME} has an invalid version`)
|
|
737
|
-
version =
|
|
796
|
+
version = typeof version === 'number' ? '' + version : ''
|
|
738
797
|
} else if (version === true) {
|
|
739
798
|
version = ref.shortname.replace(PATH_SEPARATOR_RX, '-')
|
|
740
799
|
} else if (version.constructor === Object) {
|
|
741
800
|
const refname = ref.shortname
|
|
742
801
|
let matched
|
|
743
802
|
if (refname in version) {
|
|
744
|
-
matched = version[refname]
|
|
803
|
+
matched = '' + (version[refname] ?? '')
|
|
745
804
|
} else if (
|
|
746
805
|
!Object.entries(version).some(([pattern, replacement]) => {
|
|
747
|
-
const result = refname.replace(makeMatcherRx(pattern, VERSION_MATCHER_OPTS), '\0' + replacement)
|
|
748
|
-
if (result === refname) return false
|
|
749
|
-
matched = result.
|
|
806
|
+
const result = refname.replace(makeMatcherRx(pattern, VERSION_MATCHER_OPTS), '\0' + (replacement ?? ''))
|
|
807
|
+
if (result === refname) return false // no match
|
|
808
|
+
matched = result.substring(1)
|
|
750
809
|
return true
|
|
751
810
|
})
|
|
752
811
|
) {
|
|
@@ -793,6 +852,7 @@ function buildFetchOptions (repo, progress, displayUrl, credentialsFromUrl, gitP
|
|
|
793
852
|
} else if (!fetch.tags) {
|
|
794
853
|
opts.noTags = true
|
|
795
854
|
}
|
|
855
|
+
if (fetch.commits) opts.oids = fetch.commits
|
|
796
856
|
return opts
|
|
797
857
|
}
|
|
798
858
|
|
|
@@ -827,14 +887,17 @@ function createProgressListener (progress, progressLabel, operation) {
|
|
|
827
887
|
// NOTE leave room for indeterminate progress at end of bar; this isn't strictly needed for a bare clone
|
|
828
888
|
progressBar.scaleFactor = Math.max(0, (ticks - 1) / ticks)
|
|
829
889
|
progressBar.tick(0)
|
|
830
|
-
return Object.assign(onGitProgress.bind(progressBar), {
|
|
890
|
+
return Object.assign(onGitProgress.bind(progressBar), {
|
|
891
|
+
finish: onGitComplete.bind(progressBar),
|
|
892
|
+
reset: () => progressBar.update(0),
|
|
893
|
+
})
|
|
831
894
|
}
|
|
832
895
|
|
|
833
896
|
function formatProgressBar (label, maxLabelWidth, operation) {
|
|
834
897
|
const paddingSize = maxLabelWidth - label.length
|
|
835
898
|
let padding = ''
|
|
836
899
|
if (paddingSize < 0) {
|
|
837
|
-
label = '...' + label.
|
|
900
|
+
label = '...' + label.substring(-paddingSize + 3)
|
|
838
901
|
} else if (paddingSize) {
|
|
839
902
|
padding = ' '.repeat(paddingSize)
|
|
840
903
|
}
|
|
@@ -914,21 +977,18 @@ function generateCloneFolderName (url) {
|
|
|
914
977
|
*
|
|
915
978
|
* @param {Repository} repo - The repository on which to operate.
|
|
916
979
|
* @param {String} remoteName - The name of the remote to resolve.
|
|
917
|
-
* @returns {String} The URL of the specified remote, if defined
|
|
980
|
+
* @returns {String} The URL of the specified remote, if defined
|
|
918
981
|
*/
|
|
919
982
|
function resolveRemoteUrl (repo, remoteName) {
|
|
920
983
|
return git.getConfig(Object.assign({ path: 'remote.' + remoteName + '.url' }, repo)).then((url) => {
|
|
921
|
-
if (url)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
984
|
+
if (!url) return
|
|
985
|
+
if (url.startsWith('https://') || url.startsWith('http://')) {
|
|
986
|
+
return ~url.indexOf('@') ? url.replace(URL_AUTH_CLEANER_RX, '$1') : url
|
|
987
|
+
}
|
|
988
|
+
if (url.startsWith('git@')) return 'https://' + url.substring(4).replace(':', '/')
|
|
989
|
+
if (url.startsWith('ssh://')) {
|
|
990
|
+
return 'https://' + url.substring(url.indexOf('@') + 1 || 6).replace(URL_PORT_CLEANER_RX, '$1')
|
|
929
991
|
}
|
|
930
|
-
url = posixify ? 'file:///' + posixify(repo.dir) : 'file://' + repo.dir
|
|
931
|
-
return ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url
|
|
932
992
|
})
|
|
933
993
|
}
|
|
934
994
|
|
|
@@ -946,6 +1006,16 @@ function tagsSpecified (sources) {
|
|
|
946
1006
|
return sources.some(({ tags }) => tags && (Array.isArray(tags) ? tags.length : true))
|
|
947
1007
|
}
|
|
948
1008
|
|
|
1009
|
+
function commitsRequested (sources) {
|
|
1010
|
+
if (!sources.some(({ commits }) => commits && (Array.isArray(commits) ? commits.length : true))) return
|
|
1011
|
+
const result = new Set()
|
|
1012
|
+
for (const { commits } of sources) {
|
|
1013
|
+
if (!commits) continue
|
|
1014
|
+
for (const commit of Array.isArray(commits) ? commits : commits.split(CSV_RX)) result.add(String(commit))
|
|
1015
|
+
}
|
|
1016
|
+
return [...result]
|
|
1017
|
+
}
|
|
1018
|
+
|
|
949
1019
|
function loadGitPlugins (gitConfig, networkConfig, startDir) {
|
|
950
1020
|
const plugins = new Map((git.cores || git.default.cores || new Map()).get(GIT_CORE))
|
|
951
1021
|
for (const [name, request] of Object.entries(gitConfig.plugins || {})) {
|
|
@@ -1023,7 +1093,7 @@ function transformGitCloneError (err, displayUrl, authRequested) {
|
|
|
1023
1093
|
}
|
|
1024
1094
|
|
|
1025
1095
|
function splitRefPatterns (str) {
|
|
1026
|
-
return ~str.indexOf('{') ?
|
|
1096
|
+
return str.split(~str.indexOf('{') ? VENTILATED_CSV_RX : CSV_RX)
|
|
1027
1097
|
}
|
|
1028
1098
|
|
|
1029
1099
|
function camelCaseKeys (o, stopPaths = [], p = '') {
|
|
@@ -1049,47 +1119,57 @@ function coerceToString (value) {
|
|
|
1049
1119
|
function resolveRepositoryFromWorktree (repo) {
|
|
1050
1120
|
return fsp
|
|
1051
1121
|
.readFile(repo.gitdir, 'utf8')
|
|
1052
|
-
.then((contents) => contents.
|
|
1122
|
+
.then((contents) => contents.substring(8).trimEnd())
|
|
1053
1123
|
.then((worktreeGitdir) =>
|
|
1054
1124
|
fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8').then(
|
|
1055
1125
|
(contents) => {
|
|
1056
1126
|
const gitdir = ospath.join(worktreeGitdir, contents.trimEnd())
|
|
1057
1127
|
const dir = ospath.basename(gitdir) === '.git' ? ospath.dirname(gitdir) : gitdir
|
|
1058
|
-
|
|
1128
|
+
const name = ospath.basename(worktreeGitdir)
|
|
1129
|
+
return Object.assign(repo, { dir, gitdir, worktree: { gitdir: worktreeGitdir, name } })
|
|
1059
1130
|
},
|
|
1060
1131
|
() => repo
|
|
1061
1132
|
)
|
|
1062
1133
|
)
|
|
1063
1134
|
}
|
|
1064
1135
|
|
|
1065
|
-
function findWorktrees (repo, patterns) {
|
|
1066
|
-
|
|
1067
|
-
const mainWorktree =
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1136
|
+
function findWorktrees (repo, patterns, useWorktree) {
|
|
1137
|
+
const useLinkedWorktree = !!useWorktree.linked
|
|
1138
|
+
const mainWorktree = useWorktree.main
|
|
1139
|
+
? getCurrentBranchName(repo).then((branch) => {
|
|
1140
|
+
if (!branch) return
|
|
1141
|
+
return [branch, { head: repo.dir, name: undefined, symbolicNames: useLinkedWorktree ? ['/.'] : ['/.', '.'] }]
|
|
1142
|
+
})
|
|
1143
|
+
: Promise.resolve()
|
|
1144
|
+
if (!(useLinkedWorktree || patterns.length)) return mainWorktree.then((entry) => new Map(entry && [entry]))
|
|
1145
|
+
const worktreesDir = ospath.join(repo.dir, repo.dir === repo.gitdir ? '' : '.git', 'worktrees')
|
|
1072
1146
|
const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
.
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1147
|
+
const scanWorktrees = patterns.length
|
|
1148
|
+
? fsp
|
|
1149
|
+
.readdir(worktreesDir)
|
|
1150
|
+
.then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
|
|
1151
|
+
.then((worktreeNames) => {
|
|
1152
|
+
if (useLinkedWorktree && !~worktreeNames.indexOf(useWorktree.linked)) worktreeNames.push(useWorktree.linked)
|
|
1153
|
+
return worktreeNames
|
|
1154
|
+
})
|
|
1155
|
+
: Promise.resolve(useLinkedWorktree ? [useWorktree.linked] : [])
|
|
1156
|
+
return scanWorktrees
|
|
1157
|
+
.then((worktreeNames) =>
|
|
1158
|
+
Promise.all(
|
|
1159
|
+
worktreeNames.map((name) => {
|
|
1160
|
+
const symbolicNames = useLinkedWorktree && name === useWorktree.linked ? ['.', 'HEAD'] : undefined
|
|
1161
|
+
const gitdir = ospath.resolve(worktreesDir, name)
|
|
1162
|
+
// NOTE branch name defaults to worktree name if HEAD is detached
|
|
1163
|
+
return getCurrentBranchName(Object.assign({}, repo, { gitdir })).then((branch = name) =>
|
|
1164
|
+
fsp
|
|
1165
|
+
.readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
|
|
1166
|
+
.then((contents) => [branch, { head: ospath.dirname(contents.trimEnd()), name, symbolicNames }])
|
|
1090
1167
|
)
|
|
1091
|
-
|
|
1092
|
-
|
|
1168
|
+
})
|
|
1169
|
+
)
|
|
1170
|
+
)
|
|
1171
|
+
.then((entries) => new Map(entries))
|
|
1172
|
+
.then((worktrees) => mainWorktree.then((result) => (result ? worktrees.set(result[0], result[1]) : worktrees)))
|
|
1093
1173
|
}
|
|
1094
1174
|
|
|
1095
1175
|
async function gracefulPromiseAllWithLimit (tasks, limit = Infinity) {
|
|
@@ -1125,4 +1205,27 @@ async function promiseAllWithLimit (tasks, limit = Infinity) {
|
|
|
1125
1205
|
return Promise.all(started)
|
|
1126
1206
|
}
|
|
1127
1207
|
|
|
1208
|
+
async function ensureOids (opts) {
|
|
1209
|
+
if (!opts.oids) return
|
|
1210
|
+
let prevShallowCommits = await getShallowCommits(opts)
|
|
1211
|
+
if (prevShallowCommits == null) return
|
|
1212
|
+
let oids = opts.oids.slice()
|
|
1213
|
+
const deepenOpts = Object.assign({}, opts, { relative: true })
|
|
1214
|
+
const format = 'deflated'
|
|
1215
|
+
while (oids.length) {
|
|
1216
|
+
deepenOpts.onProgress?.reset()
|
|
1217
|
+
await git.fetch(deepenOpts)
|
|
1218
|
+
const shallowCommits = await getShallowCommits(opts)
|
|
1219
|
+
if (shallowCommits == null || shallowCommits === prevShallowCommits) break
|
|
1220
|
+
prevShallowCommits = shallowCommits
|
|
1221
|
+
oids = await Promise.all(
|
|
1222
|
+
oids.map((oid) => git.readObject(Object.assign({ oid, format }, opts)).then(invariably.void, () => oid))
|
|
1223
|
+
).then((results) => results.filter((it) => it))
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function getShallowCommits ({ gitdir }) {
|
|
1228
|
+
return fsp.readFile(ospath.join(gitdir, 'shallow'), 'utf8').catch(invariably.void)
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1128
1231
|
module.exports = aggregateContent
|
package/lib/compute-origin.js
CHANGED
|
@@ -42,7 +42,7 @@ function computeOrigin (url, authStatus, gitdir, ref, startPath, worktreePath =
|
|
|
42
42
|
} else if (editUrl) {
|
|
43
43
|
const vars = {
|
|
44
44
|
path: () => (startPath ? path.join(startPath, '%s') : '%s'),
|
|
45
|
-
ref: () => 'refs/' + (reftype === 'branch' ? '
|
|
45
|
+
ref: () => 'refs/' + (reftype === 'branch' ? 'head' : reftype) + 's/' + refname,
|
|
46
46
|
refhash: () => refhash,
|
|
47
47
|
reftype: () => reftype,
|
|
48
48
|
refname: () => refname,
|
package/lib/filter-refs.js
CHANGED
|
@@ -6,7 +6,7 @@ function compileRx (pattern, opts) {
|
|
|
6
6
|
if (pattern === '*' || pattern === '**') return MATCH_ALL_RX
|
|
7
7
|
const rx =
|
|
8
8
|
pattern.charAt() === '!' // we handle negate ourselves
|
|
9
|
-
? Object.defineProperty(makeMatcherRx((pattern = pattern.
|
|
9
|
+
? Object.defineProperty(makeMatcherRx((pattern = pattern.substring(1)), opts), 'negated', { value: true })
|
|
10
10
|
: makeMatcherRx(pattern, opts)
|
|
11
11
|
return Object.defineProperty(rx, 'pattern', { value: pattern })
|
|
12
12
|
}
|
|
@@ -39,6 +39,7 @@ function createMatcher (patterns, cache = Object.assign(new Map(), { braces: new
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function filterRefs (candidates, patterns, cache, onMatch) {
|
|
42
|
+
if (!(patterns = patterns.filter(compact)).length) return []
|
|
42
43
|
const match = createMatcher(patterns, cache)
|
|
43
44
|
return candidates.reduce((accum, candidate) => {
|
|
44
45
|
if ((candidate = match(candidate, onMatch))) accum.push(candidate)
|
|
@@ -46,4 +47,8 @@ function filterRefs (candidates, patterns, cache, onMatch) {
|
|
|
46
47
|
}, [])
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function compact (str) {
|
|
51
|
+
return str !== ''
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
module.exports = filterRefs
|
|
@@ -14,7 +14,7 @@ function resolvePathGlobs (base, patterns, listDirents, retrievePath, tree = { p
|
|
|
14
14
|
if (pattern.charAt() === '!') {
|
|
15
15
|
return paths.then((resolvedPaths) => {
|
|
16
16
|
if (resolvedPaths.length) {
|
|
17
|
-
const rx = makeMatcherRx(pattern.
|
|
17
|
+
const rx = makeMatcherRx(pattern.substring(1), MATCHER_OPTS)
|
|
18
18
|
return resolvedPaths.filter((it) => !rx.test(it))
|
|
19
19
|
}
|
|
20
20
|
return resolvedPaths
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-aggregator",
|
|
3
|
-
"version": "3.2.0-
|
|
3
|
+
"version": "3.2.0-rc.1",
|
|
4
4
|
"description": "Fetches and aggregates content from distributed sources for use in an Antora documentation pipeline.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@antora/expand-path-helper": "~3.0",
|
|
36
|
-
"@antora/logger": "3.2.0-
|
|
36
|
+
"@antora/logger": "3.2.0-rc.1",
|
|
37
37
|
"@antora/user-require-helper": "~3.0",
|
|
38
38
|
"braces": "~3.0",
|
|
39
39
|
"cache-directory": "~2.0",
|
|
40
40
|
"fast-glob": "~3.3",
|
|
41
41
|
"hpagent": "~1.2",
|
|
42
|
-
"isomorphic-git": "~1.
|
|
43
|
-
"js-yaml": "~4.
|
|
42
|
+
"isomorphic-git": "~1.38",
|
|
43
|
+
"js-yaml": "~4.2",
|
|
44
44
|
"multi-progress": "~4.0",
|
|
45
45
|
"picomatch": "~4.0",
|
|
46
46
|
"progress": "~2.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"vinyl": "~3.0"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
|
-
"node": ">=
|
|
52
|
+
"node": ">=20.0.0"
|
|
53
53
|
},
|
|
54
54
|
"files": [
|
|
55
55
|
"lib/"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"web publishing"
|
|
66
66
|
],
|
|
67
67
|
"scripts": {
|
|
68
|
-
"test": "
|
|
68
|
+
"test": "node --test",
|
|
69
69
|
"prepublishOnly": "npx -y downdoc@latest --prepublish",
|
|
70
70
|
"postpublish": "npx -y downdoc@latest --postpublish"
|
|
71
71
|
}
|