@antora/content-aggregator 3.2.0-alpha.2 → 3.2.0-alpha.4
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/README.md +1 -1
- package/lib/aggregate-content.js +154 -113
- package/lib/git-plugin-http.js +5 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -9,4 +9,4 @@ Its site generator aggregates documents from versioned content repositories and
|
|
|
9
9
|
|
|
10
10
|
Copyright (C) 2017-present [OpenDevise Inc.](https://opendevise.com) and the [Antora Project](https://antora.org).
|
|
11
11
|
|
|
12
|
-
Use of this software is granted under the terms of the [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0).
|
|
12
|
+
Use of this software is granted under the terms of the [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0).
|
package/lib/aggregate-content.js
CHANGED
|
@@ -5,7 +5,6 @@ const { createHash } = require('crypto')
|
|
|
5
5
|
const createGitHttpPlugin = require('./git-plugin-http')
|
|
6
6
|
const decodeUint8Array = require('./decode-uint8-array')
|
|
7
7
|
const deepClone = require('./deep-clone')
|
|
8
|
-
const deepFlatten = require('./deep-flatten')
|
|
9
8
|
const EventEmitter = require('events')
|
|
10
9
|
const expandPath = require('@antora/expand-path-helper')
|
|
11
10
|
const File = require('./file')
|
|
@@ -17,6 +16,7 @@ const GitCredentialManagerStore = require('./git-credential-manager-store')
|
|
|
17
16
|
const git = require('./git')
|
|
18
17
|
const { NotFoundError, ObjectTypeError, UnknownTransportError, UrlParseError } = git.Errors
|
|
19
18
|
const globStream = require('glob-stream')
|
|
19
|
+
const { inspect } = require('util')
|
|
20
20
|
const invariably = require('./invariably')
|
|
21
21
|
const logger = require('./logger')
|
|
22
22
|
const { makeMatcherRx, versionMatcherOpts: VERSION_MATCHER_OPTS } = require('./matcher')
|
|
@@ -93,83 +93,71 @@ function aggregateContent (playbook) {
|
|
|
93
93
|
return ensureCacheDir(requestedCacheDir, startDir).then((cacheDir) => {
|
|
94
94
|
const gitConfig = Object.assign({ ensureGitSuffix: true }, playbook.git)
|
|
95
95
|
const gitPlugins = loadGitPlugins(gitConfig, playbook.network || {}, startDir)
|
|
96
|
-
const
|
|
96
|
+
const concurrency = {
|
|
97
|
+
fetch: Math.max(gitConfig.fetchConcurrency || Infinity, 1),
|
|
98
|
+
read: Math.max(gitConfig.readConcurrency || Infinity, 1),
|
|
99
|
+
}
|
|
97
100
|
const sourcesByUrl = sources.reduce((accum, source) => {
|
|
98
101
|
return accum.set(source.url, [...(accum.get(source.url) || []), Object.assign({}, sourceDefaults, source)])
|
|
99
102
|
}, new Map())
|
|
100
103
|
const progress = !quiet && createProgress(sourcesByUrl.keys(), process.stdout)
|
|
101
104
|
const refPatternCache = Object.assign(new Map(), { braces: new Map() })
|
|
102
105
|
const loadOpts = { cacheDir, fetch, gitPlugins, progress, startDir, refPatternCache }
|
|
103
|
-
return collectFiles(sourcesByUrl, loadOpts,
|
|
106
|
+
return collectFiles(sourcesByUrl, loadOpts, concurrency).then(buildAggregate, (err) => {
|
|
104
107
|
progress && progress.terminate()
|
|
105
108
|
throw err
|
|
106
109
|
})
|
|
107
110
|
})
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
async function collectFiles (sourcesByUrl, loadOpts, concurrency) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const runTask = (primary, continuation, idx) =>
|
|
129
|
-
primary().then((value) => {
|
|
130
|
-
if (!rejection) startedContinuations[idx] = continuation(value).catch(recordRejection)
|
|
131
|
-
}, recordRejection)
|
|
132
|
-
if (tasks.length > concurrency) {
|
|
133
|
-
started = []
|
|
134
|
-
const pending = []
|
|
135
|
-
for (const [primary, continuation] of tasks) {
|
|
136
|
-
const current = runTask(primary, continuation, started.length).finally(() =>
|
|
137
|
-
pending.splice(pending.indexOf(current), 1)
|
|
138
|
-
)
|
|
139
|
-
started.push(current)
|
|
140
|
-
if (pending.push(current) < concurrency) continue
|
|
141
|
-
await Promise.race(pending)
|
|
142
|
-
if (rejection) break
|
|
113
|
+
async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls) {
|
|
114
|
+
const loadTasks = [...sourcesByUrl.entries()].map(([url, sources]) => {
|
|
115
|
+
const loadOptsForUrl = Object.assign({}, loadOpts)
|
|
116
|
+
if (loadOpts.fetch && fetchedUrls && fetchedUrls.length && fetchedUrls.includes(url)) loadOptsForUrl.fetch = false
|
|
117
|
+
if (tagsSpecified(sources)) loadOptsForUrl.fetchTags = true
|
|
118
|
+
return loadRepository.bind(null, url, loadOptsForUrl, { url, sources })
|
|
119
|
+
})
|
|
120
|
+
return gracefulPromiseAllWithLimit(loadTasks, concurrency.fetch).then(([results, rejections]) => {
|
|
121
|
+
if (rejections.length) {
|
|
122
|
+
if (concurrency.fetch > 1 && results.length > 1 && rejections.every(({ recoverable }) => recoverable)) {
|
|
123
|
+
if (loadOpts.progress) loadOpts.progress.terminate() // reset cursor position and allow it be reused
|
|
124
|
+
const msg0 = 'An unexpected error occurred while fetching content sources concurrently.'
|
|
125
|
+
const msg1 = 'Retrying with git.fetch_concurrency value of 1.'
|
|
126
|
+
logger.warn(rejections[0], msg0 + ' ' + msg1)
|
|
127
|
+
const fulfilledUrls = results.map((it) => it && it.repo.url && it.url).filter((it) => it)
|
|
128
|
+
return collectFiles(sourcesByUrl, loadOpts, Object.assign(concurrency, { fetch: 1 }), fulfilledUrls)
|
|
129
|
+
}
|
|
130
|
+
throw rejections[0]
|
|
143
131
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
132
|
+
return Promise.all(
|
|
133
|
+
results.map(({ repo, authStatus, sources }) =>
|
|
134
|
+
selectStartPathsForRepository(repo, authStatus, sources).then((startPaths) =>
|
|
135
|
+
collectFilesFromStartPaths.bind(null, startPaths, repo, authStatus)
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
).then((collectTasks) => promiseAllWithLimit(collectTasks, concurrency.read))
|
|
139
|
+
})
|
|
153
140
|
}
|
|
154
141
|
|
|
155
142
|
function buildAggregate (componentVersionBuckets) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (!entry) return accum.set(key, batch)
|
|
143
|
+
const entries = Object.assign(new Map(), { accum: [] })
|
|
144
|
+
for (const batchesForOrigin of componentVersionBuckets) {
|
|
145
|
+
for (const batch of batchesForOrigin) {
|
|
146
|
+
let key, entry
|
|
147
|
+
if ((entry = entries.get((key = batch.version + '@' + batch.name)))) {
|
|
162
148
|
const { files, origins } = batch
|
|
163
149
|
;(batch.files = entry.files).push(...files)
|
|
164
150
|
;(batch.origins = entry.origins).push(origins[0])
|
|
165
151
|
Object.assign(entry, batch)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
152
|
+
} else {
|
|
153
|
+
entries.set(key, batch).accum.push(batch)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return entries.accum
|
|
170
158
|
}
|
|
171
159
|
|
|
172
|
-
async function loadRepository (url, opts) {
|
|
160
|
+
async function loadRepository (url, opts, result = {}) {
|
|
173
161
|
let authStatus, dir, repo
|
|
174
162
|
const cache = { [REF_PATTERN_CACHE_KEY]: opts.refPatternCache }
|
|
175
163
|
if (~url.indexOf(':') && GIT_URI_DETECTOR_RX.test(url)) {
|
|
@@ -179,6 +167,7 @@ async function loadRepository (url, opts) {
|
|
|
179
167
|
dir = ospath.join(cacheDir, generateCloneFolderName(displayUrl))
|
|
180
168
|
// NOTE the presence of the url property on the repo object implies the repository is remote
|
|
181
169
|
repo = { cache, dir, fs, gitdir: dir, noCheckout: true, url }
|
|
170
|
+
const { credentialManager } = gitPlugins
|
|
182
171
|
const validStateFile = ospath.join(dir, VALID_STATE_FILENAME)
|
|
183
172
|
try {
|
|
184
173
|
await fsp.access(validStateFile)
|
|
@@ -188,7 +177,7 @@ async function loadRepository (url, opts) {
|
|
|
188
177
|
await git
|
|
189
178
|
.fetch(fetchOpts)
|
|
190
179
|
.then(() => {
|
|
191
|
-
authStatus = identifyAuthStatus(
|
|
180
|
+
authStatus = identifyAuthStatus(credentialManager, credentials, url)
|
|
192
181
|
return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
|
|
193
182
|
})
|
|
194
183
|
.catch((fetchErr) => {
|
|
@@ -209,13 +198,13 @@ async function loadRepository (url, opts) {
|
|
|
209
198
|
.clone(fetchOpts)
|
|
210
199
|
.then(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo)))
|
|
211
200
|
.then(() => {
|
|
212
|
-
authStatus = identifyAuthStatus(
|
|
201
|
+
authStatus = identifyAuthStatus(credentialManager, credentials, url)
|
|
213
202
|
return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
|
|
214
203
|
})
|
|
215
204
|
.catch((cloneErr) => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
throw transformGitCloneError(cloneErr, displayUrl)
|
|
205
|
+
if (fetchOpts.onProgress) fetchOpts.onProgress.finish(cloneErr)
|
|
206
|
+
const authRequested = credentialManager.status({ url }) === 'requested'
|
|
207
|
+
throw transformGitCloneError(cloneErr, displayUrl, authRequested)
|
|
219
208
|
})
|
|
220
209
|
.then(() => fsp.writeFile(validStateFile, '').catch(invariably.void))
|
|
221
210
|
.then(() => fetchOpts.onProgress && fetchOpts.onProgress.finish())
|
|
@@ -239,7 +228,7 @@ async function loadRepository (url, opts) {
|
|
|
239
228
|
} else {
|
|
240
229
|
throw new Error(`Local content source does not exist: ${dir}${url !== dir ? ' (url: ' + url + ')' : ''}`)
|
|
241
230
|
}
|
|
242
|
-
return { repo, authStatus }
|
|
231
|
+
return Object.assign(result, { repo, authStatus })
|
|
243
232
|
}
|
|
244
233
|
|
|
245
234
|
function extractCredentials (url) {
|
|
@@ -261,27 +250,42 @@ function extractCredentials (url) {
|
|
|
261
250
|
}
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
async function
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
253
|
+
async function selectStartPathsForRepository (repo, authStatus, sources) {
|
|
254
|
+
const startPaths = []
|
|
255
|
+
const originUrls = {}
|
|
256
|
+
for (const source of sources) {
|
|
257
|
+
const { version, editUrl } = source
|
|
258
|
+
// NOTE if repository is managed (has a url property), we can assume the remote name is origin
|
|
259
|
+
// TODO if the repo has no remotes, then remoteName should be undefined
|
|
260
|
+
const remoteName = repo.url ? 'origin' : source.remote || 'origin'
|
|
261
|
+
const originUrl = repo.url || (originUrls[remoteName] ||= await resolveRemoteUrl(repo, remoteName))
|
|
262
|
+
const refs = await selectReferences(source, repo, remoteName)
|
|
263
|
+
if (refs.length) {
|
|
264
|
+
for (const ref of refs) {
|
|
265
|
+
for (const startPath of await selectStartPaths(source, repo, remoteName, ref)) {
|
|
266
|
+
startPaths.push({ startPath, ref, originUrl, editUrl, version })
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
const { url, branches, tags } = source
|
|
269
271
|
const startPathInfo =
|
|
270
|
-
'startPaths' in source
|
|
272
|
+
'startPaths' in source
|
|
273
|
+
? { 'start paths': source.startPaths || undefined }
|
|
274
|
+
: { 'start path': source.startPath || undefined }
|
|
271
275
|
const sourceInfo = yaml.dump({ url, branches, tags, ...startPathInfo }, { flowLevel: 1 }).trimRight()
|
|
272
276
|
logger.info(`No matching references found for content source entry (${sourceInfo.replace(NEWLINE_RX, ' | ')})`)
|
|
273
|
-
return []
|
|
274
277
|
}
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
}
|
|
279
|
+
return startPaths
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
// QUESTION should we resolve HEAD to a ref eagerly to avoid having to do a match on it?
|
|
280
283
|
async function selectReferences (source, repo, remote) {
|
|
281
284
|
let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns } = source
|
|
282
|
-
const
|
|
285
|
+
const managed = 'url' in repo
|
|
286
|
+
const isBare = managed || repo.noCheckout
|
|
283
287
|
const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
|
|
284
|
-
const noWorktree =
|
|
288
|
+
const noWorktree = managed ? undefined : false
|
|
285
289
|
const refs = new Map()
|
|
286
290
|
if (
|
|
287
291
|
tagPatterns &&
|
|
@@ -389,10 +393,11 @@ async function selectReferences (source, repo, remote) {
|
|
|
389
393
|
refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
|
|
390
394
|
}
|
|
391
395
|
}
|
|
392
|
-
} else if (!remoteBranches.length) {
|
|
396
|
+
} else if (!managed || !remoteBranches.length) {
|
|
393
397
|
const localBranches = await git.listBranches(repo)
|
|
394
398
|
if (localBranches.length) {
|
|
395
399
|
for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
|
|
400
|
+
if (refs.has(shortname)) continue // NOTE prefer remote branches in bare repository
|
|
396
401
|
refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head: noWorktree })
|
|
397
402
|
}
|
|
398
403
|
}
|
|
@@ -413,10 +418,9 @@ function getCurrentBranchName (repo, remote) {
|
|
|
413
418
|
).then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
|
|
414
419
|
}
|
|
415
420
|
|
|
416
|
-
async function
|
|
421
|
+
async function selectStartPaths (source, repo, remoteName, ref) {
|
|
417
422
|
const url = repo.url
|
|
418
423
|
const displayUrl = url || repo.dir
|
|
419
|
-
const { version, editUrl } = source
|
|
420
424
|
const worktreePath = ref.head
|
|
421
425
|
if (!worktreePath) {
|
|
422
426
|
ref.oid = await git.resolveRef(
|
|
@@ -436,17 +440,22 @@ async function collectFilesFromReference (source, repo, remoteName, authStatus,
|
|
|
436
440
|
const flag = worktreePath ? ' <worktree>' : ref.remote && worktreePath === false ? ` <remotes/${ref.remote}>` : ''
|
|
437
441
|
throw new Error(`no start paths found in ${where} (${ref.type}: ${ref.shortname}${flag})`)
|
|
438
442
|
}
|
|
439
|
-
return
|
|
440
|
-
startPaths.map((startPath) =>
|
|
441
|
-
collectFilesFromStartPath(startPath, repo, authStatus, ref, worktreePath, originUrl, editUrl, version)
|
|
442
|
-
)
|
|
443
|
-
)
|
|
443
|
+
return startPaths
|
|
444
444
|
}
|
|
445
|
-
|
|
446
|
-
return collectFilesFromStartPath(startPath, repo, authStatus, ref, worktreePath, originUrl, editUrl, version)
|
|
445
|
+
return [cleanStartPath(coerceToString(source.startPath))]
|
|
447
446
|
}
|
|
448
447
|
|
|
449
|
-
function
|
|
448
|
+
async function collectFilesFromStartPaths (startPaths, repo, authStatus) {
|
|
449
|
+
const buckets = []
|
|
450
|
+
for (const { startPath, ref, originUrl, editUrl, version } of startPaths) {
|
|
451
|
+
buckets.push(await collectFilesFromStartPath(startPath, repo, authStatus, ref, originUrl, editUrl, version))
|
|
452
|
+
}
|
|
453
|
+
repo.cache = undefined
|
|
454
|
+
return buckets
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function collectFilesFromStartPath (startPath, repo, authStatus, ref, originUrl, editUrl, version) {
|
|
458
|
+
const worktreePath = ref.head
|
|
450
459
|
const origin = computeOrigin(originUrl, authStatus, repo.gitdir, ref, startPath, worktreePath, editUrl)
|
|
451
460
|
return (worktreePath ? readFilesFromWorktree(origin) : readFilesFromGitTree(repo, ref.oid, startPath))
|
|
452
461
|
.then((files) =>
|
|
@@ -526,26 +535,25 @@ function srcFs (cwd, origin) {
|
|
|
526
535
|
}
|
|
527
536
|
|
|
528
537
|
function readFilesFromGitTree (repo, oid, startPath) {
|
|
529
|
-
return git
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
getGitTreeAtStartPath(repo, oid, startPath).then((start) =>
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
538
|
+
return git.readTree(Object.assign({ oid }, repo)).then((root) => {
|
|
539
|
+
Object.assign(root, { dirname: '' })
|
|
540
|
+
return startPath
|
|
541
|
+
? getGitTreeAtStartPath(repo, oid, startPath).then((start) => {
|
|
542
|
+
Object.assign(start, { dirname: startPath })
|
|
543
|
+
return srcGitTree(repo, root, start)
|
|
544
|
+
})
|
|
545
|
+
: srcGitTree(repo, root)
|
|
546
|
+
})
|
|
536
547
|
}
|
|
537
548
|
|
|
538
549
|
function getGitTreeAtStartPath (repo, oid, startPath) {
|
|
539
|
-
return git.readTree(Object.assign({ oid, filepath: startPath }, repo)).
|
|
540
|
-
|
|
541
|
-
(
|
|
542
|
-
|
|
543
|
-
throw new Error(`the start path '${startPath}' ${m}`)
|
|
544
|
-
}
|
|
545
|
-
)
|
|
550
|
+
return git.readTree(Object.assign({ oid, filepath: startPath }, repo)).catch((err) => {
|
|
551
|
+
const m = err instanceof ObjectTypeError && err.data.expected === 'tree' ? 'is not a directory' : 'does not exist'
|
|
552
|
+
throw new Error(`the start path '${startPath}' ${m}`)
|
|
553
|
+
})
|
|
546
554
|
}
|
|
547
555
|
|
|
548
|
-
function srcGitTree (repo, root, start) {
|
|
556
|
+
function srcGitTree (repo, root, start = root) {
|
|
549
557
|
return new Promise((resolve, reject) => {
|
|
550
558
|
const files = []
|
|
551
559
|
createGitTreeWalker(repo, root, filterGitEntry, gitEntryToFile)
|
|
@@ -832,7 +840,6 @@ function onGitProgress ({ phase, loaded, total }) {
|
|
|
832
840
|
|
|
833
841
|
function onGitComplete (err) {
|
|
834
842
|
if (err) {
|
|
835
|
-
// TODO could use progressBar.interrupt() to replace bar with message instead
|
|
836
843
|
this.chars.incomplete = '?'
|
|
837
844
|
this.update(0)
|
|
838
845
|
// NOTE force progress bar to update regardless of throttle setting
|
|
@@ -982,8 +989,8 @@ function ensureCacheDir (preferredCacheDir, startDir) {
|
|
|
982
989
|
)
|
|
983
990
|
}
|
|
984
991
|
|
|
985
|
-
function transformGitCloneError (err, displayUrl) {
|
|
986
|
-
let wrappedMsg, trimMessage
|
|
992
|
+
function transformGitCloneError (err, displayUrl, authRequested) {
|
|
993
|
+
let wrappedMsg, recoverable, trimMessage
|
|
987
994
|
if (HTTP_ERROR_CODE_RX.test(err.code)) {
|
|
988
995
|
switch (err.data.statusCode) {
|
|
989
996
|
case 401:
|
|
@@ -992,25 +999,26 @@ function transformGitCloneError (err, displayUrl) {
|
|
|
992
999
|
: 'Content repository not found or requires credentials'
|
|
993
1000
|
break
|
|
994
1001
|
case 404:
|
|
995
|
-
wrappedMsg =
|
|
1002
|
+
wrappedMsg = authRequested
|
|
1003
|
+
? 'Content repository not found or credentials were rejected'
|
|
1004
|
+
: 'Content repository not found'
|
|
996
1005
|
break
|
|
997
1006
|
default:
|
|
998
1007
|
wrappedMsg = err.message
|
|
999
|
-
trimMessage = true
|
|
1008
|
+
recoverable = trimMessage = true
|
|
1000
1009
|
}
|
|
1001
1010
|
} else if (err instanceof UrlParseError || err instanceof UnknownTransportError) {
|
|
1002
1011
|
wrappedMsg = 'Content source uses an unsupported transport protocol'
|
|
1003
1012
|
} else if (err.code === 'ENOTFOUND') {
|
|
1004
1013
|
wrappedMsg = `Content repository host could not be resolved: ${err.hostname}`
|
|
1005
1014
|
} else {
|
|
1006
|
-
wrappedMsg =
|
|
1007
|
-
trimMessage = true
|
|
1008
|
-
}
|
|
1009
|
-
if (trimMessage) {
|
|
1010
|
-
wrappedMsg = ~(wrappedMsg = wrappedMsg.trimRight()).indexOf('. ') ? wrappedMsg : wrappedMsg.replace(/\.$/, '')
|
|
1015
|
+
wrappedMsg = err.message || String(err)
|
|
1016
|
+
recoverable = trimMessage = true
|
|
1011
1017
|
}
|
|
1018
|
+
if (trimMessage && !~(wrappedMsg = wrappedMsg.trimEnd()).indexOf('. ')) wrappedMsg = wrappedMsg.replace(/\.$/, '')
|
|
1012
1019
|
const errWrapper = new Error(`${wrappedMsg} (url: ${displayUrl})`)
|
|
1013
|
-
errWrapper.stack += `\nCaused by: ${err.stack
|
|
1020
|
+
errWrapper.stack += `\nCaused by: ${err.stack ? inspect(err).replace(/^Error \[(.+?)\](?=: )/, '$1') : err}`
|
|
1021
|
+
if (recoverable) Object.defineProperty(errWrapper, 'recoverable', { value: true })
|
|
1014
1022
|
return errWrapper
|
|
1015
1023
|
}
|
|
1016
1024
|
|
|
@@ -1041,11 +1049,11 @@ function coerceToString (value) {
|
|
|
1041
1049
|
function resolveRepositoryFromWorktree (repo) {
|
|
1042
1050
|
return fsp
|
|
1043
1051
|
.readFile(repo.gitdir, 'utf8')
|
|
1044
|
-
.then((contents) => contents.
|
|
1052
|
+
.then((contents) => contents.substr(8).trimEnd())
|
|
1045
1053
|
.then((worktreeGitdir) =>
|
|
1046
1054
|
fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8').then(
|
|
1047
1055
|
(contents) => {
|
|
1048
|
-
const gitdir = ospath.join(worktreeGitdir, contents.
|
|
1056
|
+
const gitdir = ospath.join(worktreeGitdir, contents.trimEnd())
|
|
1049
1057
|
const dir = ospath.basename(gitdir) === '.git' ? ospath.dirname(gitdir) : gitdir
|
|
1050
1058
|
return Object.assign(repo, { dir, gitdir, worktreeName: ospath.basename(worktreeGitdir) })
|
|
1051
1059
|
},
|
|
@@ -1077,7 +1085,7 @@ function findWorktrees (repo, patterns) {
|
|
|
1077
1085
|
.then((branch = worktreeName) =>
|
|
1078
1086
|
fsp
|
|
1079
1087
|
.readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
|
|
1080
|
-
.then((contents) => [branch, { head: ospath.dirname(contents.
|
|
1088
|
+
.then((contents) => [branch, { head: ospath.dirname(contents.trimEnd()), name: worktreeName }])
|
|
1081
1089
|
)
|
|
1082
1090
|
})
|
|
1083
1091
|
)
|
|
@@ -1086,4 +1094,37 @@ function findWorktrees (repo, patterns) {
|
|
|
1086
1094
|
).then((entries = []) => mainWorktree.then((entry) => new Map(entry ? entries.push(entry) && entries : entries)))
|
|
1087
1095
|
}
|
|
1088
1096
|
|
|
1097
|
+
async function gracefulPromiseAllWithLimit (tasks, limit = Infinity) {
|
|
1098
|
+
const rejections = []
|
|
1099
|
+
const recordRejection = (err) => rejections.push(err) && undefined
|
|
1100
|
+
const started = []
|
|
1101
|
+
if (tasks.length <= limit) {
|
|
1102
|
+
for (const task of tasks) started.push(task().catch(recordRejection))
|
|
1103
|
+
} else {
|
|
1104
|
+
const pending = []
|
|
1105
|
+
for (const task of tasks) {
|
|
1106
|
+
const current = task()
|
|
1107
|
+
.catch(recordRejection)
|
|
1108
|
+
.finally(() => pending.splice(pending.indexOf(current), 1))
|
|
1109
|
+
started.push(current)
|
|
1110
|
+
if (pending.push(current) < limit) continue
|
|
1111
|
+
await Promise.race(pending)
|
|
1112
|
+
if (rejections.length) break
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return Promise.all(started).then((results) => [results, rejections])
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
async function promiseAllWithLimit (tasks, limit = Infinity) {
|
|
1119
|
+
if (tasks.length <= limit) return Promise.all(tasks.map((task) => task()))
|
|
1120
|
+
const started = []
|
|
1121
|
+
const pending = []
|
|
1122
|
+
for (const task of tasks) {
|
|
1123
|
+
const current = task().finally(() => pending.splice(pending.indexOf(current), 1))
|
|
1124
|
+
started.push(current)
|
|
1125
|
+
if (pending.push(current) >= limit) await Promise.race(pending)
|
|
1126
|
+
}
|
|
1127
|
+
return Promise.all(started)
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1089
1130
|
module.exports = aggregateContent
|
package/lib/git-plugin-http.js
CHANGED
|
@@ -49,11 +49,12 @@ module.exports = ({ headers: extraHeaders, httpProxy, httpsProxy, noProxy } = {}
|
|
|
49
49
|
}
|
|
50
50
|
return {
|
|
51
51
|
async request ({ url, method, headers, body }) {
|
|
52
|
-
headers = mergeHeaders(headers, extraHeaders)
|
|
52
|
+
headers = Object.assign(mergeHeaders(headers, extraHeaders), { connection: 'close' })
|
|
53
53
|
body = await mergeBuffers(body)
|
|
54
|
-
return new Promise((resolve, reject) =>
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const opts = { url, method, headers, body, timeout: 0, keepAlive: false }
|
|
56
|
+
return get(opts, (err, res) => (err ? reject(err) : resolve(distillResponse(res))))
|
|
57
|
+
})
|
|
57
58
|
},
|
|
58
59
|
}
|
|
59
60
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-aggregator",
|
|
3
|
-
"version": "3.2.0-alpha.
|
|
3
|
+
"version": "3.2.0-alpha.4",
|
|
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)",
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@antora/expand-path-helper": "~2.0",
|
|
32
|
-
"@antora/logger": "3.2.0-alpha.
|
|
32
|
+
"@antora/logger": "3.2.0-alpha.4",
|
|
33
33
|
"@antora/user-require-helper": "~2.0",
|
|
34
34
|
"braces": "~3.0",
|
|
35
35
|
"cache-directory": "~2.0",
|
|
36
36
|
"glob-stream": "~7.0",
|
|
37
37
|
"hpagent": "~1.2",
|
|
38
|
-
"isomorphic-git": "~1.
|
|
38
|
+
"isomorphic-git": "~1.25",
|
|
39
39
|
"js-yaml": "~4.1",
|
|
40
40
|
"multi-progress": "~4.0",
|
|
41
41
|
"picomatch": "~2.3",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
],
|
|
63
63
|
"scripts": {
|
|
64
64
|
"test": "_mocha",
|
|
65
|
-
"prepublishOnly": "
|
|
66
|
-
"postpublish": "
|
|
65
|
+
"prepublishOnly": "npx -y downdoc@latest --prepublish",
|
|
66
|
+
"postpublish": "npx -y downdoc@latest --postpublish"
|
|
67
67
|
}
|
|
68
68
|
}
|