@antora/content-aggregator 3.1.1 → 3.2.0-alpha.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.
@@ -188,8 +188,7 @@ async function loadRepository (url, opts) {
188
188
  await git
189
189
  .fetch(fetchOpts)
190
190
  .then(() => {
191
- const credentialManager = gitPlugins.credentialManager
192
- authStatus = credentials ? 'auth-embedded' : credentialManager.status({ url }) ? 'auth-required' : undefined
191
+ authStatus = identifyAuthStatus(gitPlugins.credentialManager, credentials, url)
193
192
  return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
194
193
  })
195
194
  .catch((fetchErr) => {
@@ -210,8 +209,7 @@ async function loadRepository (url, opts) {
210
209
  .clone(fetchOpts)
211
210
  .then(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo)))
212
211
  .then(() => {
213
- const credentialManager = gitPlugins.credentialManager
214
- authStatus = credentials ? 'auth-embedded' : credentialManager.status({ url }) ? 'auth-required' : undefined
212
+ authStatus = identifyAuthStatus(gitPlugins.credentialManager, credentials, url)
215
213
  return git.setConfig(Object.assign({ path: 'remote.origin.private', value: authStatus }, repo))
216
214
  })
217
215
  .catch((cloneErr) => {
@@ -223,8 +221,15 @@ async function loadRepository (url, opts) {
223
221
  .then(() => fetchOpts.onProgress && fetchOpts.onProgress.finish())
224
222
  }
225
223
  } else if (await isDirectory((dir = expandPath(url, { dot: opts.startDir })))) {
226
- const gitdir = ospath.join(dir, '.git')
227
- repo = (await isDirectory(gitdir)) ? { cache, dir, fs, gitdir } : { cache, dir, fs, gitdir: dir, noCheckout: true }
224
+ const dotgit = ospath.join(dir, '.git')
225
+ const dotgitStat = await fsp.stat(dotgit).catch(() => ({ isFile: invariably.false, isDirectory: invariably.false }))
226
+ if (dotgitStat.isDirectory()) {
227
+ repo = { cache, dir, fs, gitdir: dotgit }
228
+ } else if (dotgitStat.isFile()) {
229
+ repo = await resolveRepositoryFromWorktree({ cache, dir, fs, gitdir: dotgit })
230
+ } else {
231
+ repo = { cache, dir, fs, gitdir: dir, noCheckout: true }
232
+ }
228
233
  try {
229
234
  await git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo))
230
235
  } catch {
@@ -273,7 +278,7 @@ async function collectFilesFromSource (source, repo, remoteName, authStatus) {
273
278
 
274
279
  // QUESTION should we resolve HEAD to a ref eagerly to avoid having to do a match on it?
275
280
  async function selectReferences (source, repo, remote) {
276
- let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns = '.' } = source
281
+ let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns } = source
277
282
  const isBare = repo.noCheckout
278
283
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
279
284
  const noWorktree = repo.url ? undefined : false
@@ -293,6 +298,12 @@ async function selectReferences (source, repo, remote) {
293
298
  }
294
299
  }
295
300
  if (!branchPatterns) return [...refs.values()]
301
+ branchPatterns = Array.isArray(branchPatterns)
302
+ ? branchPatterns.map((pattern) => String(pattern))
303
+ : splitRefPatterns(String(branchPatterns))
304
+ if (!branchPatterns.length) return [...refs.values()]
305
+ const worktreeName = repo.worktreeName // possibly switch to worktree property ({ name, dir}) in future
306
+ if (worktreeName) branchPatterns = branchPatterns.map((it) => (it === 'HEAD' ? 'HEAD@' + worktreeName : it))
296
307
  if (worktreePatterns) {
297
308
  if (worktreePatterns === '.') {
298
309
  worktreePatterns = ['.']
@@ -302,10 +313,12 @@ async function selectReferences (source, repo, remote) {
302
313
  worktreePatterns = Array.isArray(worktreePatterns)
303
314
  ? worktreePatterns.map((pattern) => String(pattern))
304
315
  : splitRefPatterns(String(worktreePatterns))
316
+ if (worktreeName) worktreePatterns = worktreePatterns.map((it) => (it === '@' ? worktreeName : it))
305
317
  }
318
+ } else {
319
+ worktreePatterns = worktreePatterns === undefined ? [worktreeName || '.'] : []
306
320
  }
307
- const branchPatternsString = String(branchPatterns)
308
- if (branchPatternsString === 'HEAD' || branchPatternsString === '.') {
321
+ if (branchPatterns.length === 1 && (branchPatterns[0] === 'HEAD' || branchPatterns[0] === '.')) {
309
322
  const currentBranch = await getCurrentBranchName(repo, remote)
310
323
  if (currentBranch) {
311
324
  branchPatterns = [currentBranch]
@@ -317,11 +330,7 @@ async function selectReferences (source, repo, remote) {
317
330
  refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
318
331
  return [...refs.values()]
319
332
  }
320
- } else if (
321
- (branchPatterns = Array.isArray(branchPatterns)
322
- ? branchPatterns.map((pattern) => String(pattern))
323
- : splitRefPatterns(branchPatternsString)).length
324
- ) {
333
+ } else {
325
334
  let headBranchIdx
326
335
  // NOTE we can assume at least two entries if HEAD or . are present
327
336
  if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
@@ -346,8 +355,6 @@ async function selectReferences (source, repo, remote) {
346
355
  branchPatterns.splice(headBranchIdx, 1)
347
356
  }
348
357
  }
349
- } else {
350
- return [...refs.values()]
351
358
  }
352
359
  // NOTE isomorphic-git includes HEAD in list of remote branches (see https://isomorphic-git.org/docs/listBranches)
353
360
  const remoteBranches = (await git.listBranches(Object.assign({ remote }, repo))).filter((it) => it !== 'HEAD')
@@ -362,8 +369,20 @@ async function selectReferences (source, repo, remote) {
362
369
  const localBranches = await git.listBranches(repo)
363
370
  if (localBranches.length) {
364
371
  const worktrees = await findWorktrees(repo, worktreePatterns)
365
- for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
366
- const head = worktrees.get(shortname) || noWorktree
372
+ let onMatch
373
+ if ((worktreePatterns.join('') || '.') !== '.') {
374
+ const symbolicNames = new Map()
375
+ worktrees.forEach(({ name, symbolicName = 'HEAD@' + name }, shortname) => {
376
+ localBranches.push(symbolicName)
377
+ symbolicNames.set(symbolicName, shortname)
378
+ })
379
+ onMatch = (candidate, { pattern }) => {
380
+ const shortname = symbolicNames.get(candidate)
381
+ return shortname ? (pattern.startsWith('HEAD@') ? shortname : undefined) : candidate
382
+ }
383
+ }
384
+ for (const shortname of filterRefs(localBranches, branchPatterns, patternCache, onMatch)) {
385
+ const head = (worktrees.get(shortname) || { head: noWorktree }).head
367
386
  refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
368
387
  }
369
388
  }
@@ -382,15 +401,13 @@ async function selectReferences (source, repo, remote) {
382
401
  * Returns the current branch name unless the HEAD is detached.
383
402
  */
384
403
  function getCurrentBranchName (repo, remote) {
385
- let refPromise
386
- if (repo.noCheckout) {
387
- refPromise = git
388
- .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
389
- .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
390
- } else {
391
- refPromise = git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
392
- }
393
- return refPromise.then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
404
+ return (
405
+ repo.noCheckout && remote
406
+ ? git
407
+ .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
408
+ .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
409
+ : git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
410
+ ).then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
394
411
  }
395
412
 
396
413
  async function collectFilesFromReference (source, repo, remoteName, authStatus, ref, originUrl) {
@@ -823,11 +840,14 @@ function onGitComplete (err) {
823
840
  }
824
841
 
825
842
  function resolveCredentials (credentialsFromUrlHolder, url, auth) {
826
- const credentialsFromUrl = credentialsFromUrlHolder.get()
843
+ const credentialsFromUrl = credentialsFromUrlHolder.get() || {}
827
844
  if ('Authorization' in auth.headers) {
828
- if (!credentialsFromUrl) return this.rejected({ url, auth })
829
- credentialsFromUrlHolder.clear()
830
- } else if (credentialsFromUrl) {
845
+ if ('username' in credentialsFromUrl) {
846
+ credentialsFromUrlHolder.clear()
847
+ } else {
848
+ return this.rejected({ url, auth })
849
+ }
850
+ } else if ('username' in credentialsFromUrl) {
831
851
  return credentialsFromUrl
832
852
  } else {
833
853
  auth = undefined
@@ -839,6 +859,15 @@ function resolveCredentials (credentialsFromUrlHolder, url, auth) {
839
859
  )
840
860
  }
841
861
 
862
+ function identifyAuthStatus (credentialManager, credentials, url) {
863
+ const status = credentialManager.status({ url })
864
+ if (credentials) {
865
+ return typeof status === 'string' && status.startsWith('requested,') ? 'auth-required' : 'auth-embedded'
866
+ } else if (status != null) {
867
+ return 'auth-required'
868
+ }
869
+ }
870
+
842
871
  /**
843
872
  * Generates a safe, unique folder name for a git URL.
844
873
  *
@@ -883,7 +912,7 @@ function resolveRemoteUrl (repo, remoteName) {
883
912
  * Checks whether the specified URL matches a directory on the local filesystem.
884
913
  *
885
914
  * @param {String} url - The URL to check.
886
- * @return {Boolean} A flag indicating whether the URL matches a directory on the local filesystem.
915
+ * @returns {Boolean} A flag indicating whether the URL matches a directory on the local filesystem.
887
916
  */
888
917
  function isDirectory (url) {
889
918
  return fsp.stat(url).then((stat) => stat.isDirectory(), invariably.false)
@@ -1006,39 +1035,54 @@ function coerceToString (value) {
1006
1035
  return value == null ? '' : String(value)
1007
1036
  }
1008
1037
 
1038
+ async function resolveRepositoryFromWorktree (repo) {
1039
+ return fsp
1040
+ .readFile(repo.gitdir, 'utf8')
1041
+ .then((contents) => contents.trimRight().substr(8))
1042
+ .then((worktreeGitdir) => {
1043
+ const worktreeName = ospath.basename(worktreeGitdir)
1044
+ return fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8').then(
1045
+ (contents) => {
1046
+ const gitdir = ospath.join(worktreeGitdir, contents.trimRight())
1047
+ return ospath.basename(gitdir) === '.git'
1048
+ ? Object.assign(repo, { dir: ospath.dirname(gitdir), gitdir, worktreeName })
1049
+ : Object.assign(repo, { dir: gitdir, gitdir, worktreeName })
1050
+ },
1051
+ () => repo
1052
+ )
1053
+ })
1054
+ }
1055
+
1009
1056
  function findWorktrees (repo, patterns) {
1010
1057
  if (!patterns.length) return new Map()
1011
- const linkedOnly = patterns[0] === '.' ? !(patterns = patterns.slice(1)) : true
1012
- let worktreesDir
1058
+ const mainWorktree =
1059
+ patterns[0] === '.' && (patterns = patterns.slice(1))
1060
+ ? getCurrentBranchName(repo).then((branch) => (branch ? [branch, { head: repo.dir, name: '.' }] : undefined))
1061
+ : Promise.resolve()
1062
+ const worktreesDir = patterns.length ? ospath.join(repo.dir, '.git', 'worktrees') : undefined
1013
1063
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
1014
1064
  return (
1015
- patterns.length
1065
+ worktreesDir
1016
1066
  ? fsp
1017
- .readdir((worktreesDir = ospath.join(repo.dir, '.git', 'worktrees')))
1067
+ .readdir(worktreesDir)
1018
1068
  .then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
1019
1069
  .then((worktreeNames) =>
1020
- worktreeNames.length
1021
- ? Promise.all(
1022
- worktreeNames.map((worktreeName) => {
1023
- const gitdir = ospath.resolve(worktreesDir, worktreeName)
1024
- // NOTE uses name of worktree as branch name if HEAD is detached
1025
- return git
1026
- .currentBranch(Object.assign({}, repo, { gitdir }))
1027
- .then((branch = worktreeName) =>
1028
- fsp
1029
- .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1030
- .then((contents) => ({ branch, dir: ospath.dirname(contents.trimRight()) }))
1031
- )
1032
- })
1033
- ).then((entries) => entries.reduce((accum, it) => accum.set(it.branch, it.dir), new Map()))
1034
- : new Map()
1070
+ Promise.all(
1071
+ worktreeNames.map((worktreeName) => {
1072
+ const gitdir = ospath.resolve(worktreesDir, worktreeName)
1073
+ // NOTE branch name defaults to worktree name if HEAD is detached
1074
+ return git
1075
+ .currentBranch(Object.assign({}, repo, { gitdir }))
1076
+ .then((branch = worktreeName) =>
1077
+ fsp
1078
+ .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1079
+ .then((contents) => [branch, { head: ospath.dirname(contents.trimRight()), name: worktreeName }])
1080
+ )
1081
+ })
1082
+ )
1035
1083
  )
1036
- : Promise.resolve(new Map())
1037
- ).then((worktrees) =>
1038
- linkedOnly
1039
- ? worktrees
1040
- : git.currentBranch(repo).then((branch) => (branch ? worktrees.set(branch, repo.dir) : worktrees))
1041
- )
1084
+ : Promise.resolve()
1085
+ ).then((entries = []) => mainWorktree.then((entry) => new Map(entry ? entries.push(entry) && entries : entries)))
1042
1086
  }
1043
1087
 
1044
1088
  module.exports = aggregateContent
@@ -4,7 +4,7 @@ const { posix: path } = require('path')
4
4
  const posixify = require('./posixify')
5
5
  const removeGitSuffix = require('./remove-git-suffix')
6
6
 
7
- const EDIT_URL_TEMPLATE_VAR_RX = /\{(web_url|ref(?:hash|name|type)|path)\}/g
7
+ const EDIT_URL_TEMPLATE_VAR_RX = /\{(web_url|ref(?:hash|name|type)?|path)\}/g
8
8
  const HOSTED_GIT_REPO_RX = /^(?:https?:\/\/|.+@)(git(?:hub|lab)\.com|bitbucket\.org|pagure\.io)[/:](.+?)(?:\.git)?$/
9
9
 
10
10
  function computeOrigin (url, authStatus, gitdir, ref, startPath, worktreePath = undefined, editUrl = true) {
@@ -41,6 +41,7 @@ function computeOrigin (url, authStatus, gitdir, ref, startPath, worktreePath =
41
41
  } else if (editUrl) {
42
42
  const vars = {
43
43
  path: () => (startPath ? path.join(startPath, '%s') : '%s'),
44
+ ref: () => 'refs/' + (reftype === 'branch' ? 'heads' : reftype) + '/' + refname,
44
45
  refhash: () => refhash,
45
46
  reftype: () => reftype,
46
47
  refname: () => refname,
@@ -4,9 +4,11 @@ const { makeMatcherRx, refMatcherOpts: getMatcherOpts, MATCH_ALL_RX } = require(
4
4
 
5
5
  function compileRx (pattern, opts) {
6
6
  if (pattern === '*' || pattern === '**') return MATCH_ALL_RX
7
- return pattern.charAt() === '!' // do our own negate
8
- ? Object.defineProperty(makeMatcherRx(pattern.substr(1), opts), 'negated', { value: true })
9
- : makeMatcherRx(pattern, opts)
7
+ const rx =
8
+ pattern.charAt() === '!' // we handle negate ourselves
9
+ ? Object.defineProperty(makeMatcherRx((pattern = pattern.substr(1)), opts), 'negated', { value: true })
10
+ : makeMatcherRx(pattern, opts)
11
+ return Object.defineProperty(rx, 'pattern', { value: pattern })
10
12
  }
11
13
 
12
14
  function createMatcher (patterns, cache, opts) {
@@ -15,8 +17,8 @@ function createMatcher (patterns, cache, opts) {
15
17
  cache.get(pattern) || cache.set(pattern, compileRx(pattern, opts || (opts = getMatcherOpts(cache)))).get(pattern)
16
18
  )
17
19
  if (rxs[0].negated) rxs.unshift(MATCH_ALL_RX)
18
- return (candidate) => {
19
- let matched
20
+ return (candidate, onMatch) => {
21
+ let matched, symbolic
20
22
  for (const rx of rxs) {
21
23
  let voteIfMatched = true
22
24
  if (matched) {
@@ -25,16 +27,22 @@ function createMatcher (patterns, cache, opts) {
25
27
  } else if (rx.negated) {
26
28
  continue
27
29
  }
28
- if (rx.test(candidate)) matched = voteIfMatched
30
+ if (rx.test(candidate) || (symbolic && rx.test(symbolic) && (candidate = symbolic))) {
31
+ if (onMatch) {
32
+ if (!(matched = onMatch(candidate, rx))) continue
33
+ ;[symbolic, candidate] = [candidate, matched]
34
+ }
35
+ matched = voteIfMatched && candidate
36
+ }
29
37
  }
30
38
  return matched
31
39
  }
32
40
  }
33
41
 
34
- function filterRefs (candidates, patterns, cache = Object.assign(new Map(), { braces: new Map() })) {
35
- const isMatch = createMatcher(patterns, cache)
42
+ function filterRefs (candidates, patterns, cache = Object.assign(new Map(), { braces: new Map() }), onMatch) {
43
+ const match = createMatcher(patterns, cache)
36
44
  return candidates.reduce((accum, candidate) => {
37
- if (isMatch(candidate)) accum.push(candidate)
45
+ if ((candidate = match(candidate, onMatch))) accum.push(candidate)
38
46
  return accum
39
47
  }, [])
40
48
  }
@@ -86,11 +86,11 @@ class GitCredentialManagerStore {
86
86
  }
87
87
 
88
88
  async approved ({ url }) {
89
- this.urls[url] = 'approved'
89
+ this.urls[url] = (url in this.urls ? this.urls[url] + ',' : '') + 'approved'
90
90
  }
91
91
 
92
92
  async rejected ({ url, auth }) {
93
- this.urls[url] = 'rejected'
93
+ this.urls[url] = (url in this.urls ? this.urls[url] + ',' : '') + 'rejected'
94
94
  const statusCode = 401
95
95
  const statusMessage = 'HTTP Basic: Access Denied'
96
96
  const err = new Error(`HTTP Error: ${statusCode} ${statusMessage}`)
package/lib/matcher.js CHANGED
@@ -22,7 +22,7 @@ function makeMatcherRx (input, opts) {
22
22
  }
23
23
 
24
24
  module.exports = {
25
- MATCH_ALL_RX: { test: () => true },
25
+ MATCH_ALL_RX: Object.defineProperty({ test: () => true }, 'pattern', { value: '*' }),
26
26
  expandBraces,
27
27
  makeMatcherRx,
28
28
  pathMatcherOpts: Object.assign({}, BASE_OPTS, { dot: false }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/content-aggregator",
3
- "version": "3.1.1",
3
+ "version": "3.2.0-alpha.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)",
@@ -29,13 +29,13 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@antora/expand-path-helper": "~2.0",
32
- "@antora/logger": "3.1.1",
32
+ "@antora/logger": "3.2.0-alpha.1",
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
- "hpagent": "~1.0",
38
- "isomorphic-git": "~1.19",
37
+ "hpagent": "~1.1",
38
+ "isomorphic-git": "~1.21",
39
39
  "js-yaml": "~4.1",
40
40
  "multi-progress": "~4.0",
41
41
  "picomatch": "~2.3",