Commit 6a1b8fb6 authored by Dan Allen's avatar Dan Allen
Browse files

merge !1081

resolves #1111 support linked worktree attached to bare repository
parents 84d2cae2 fa5f625d
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ This project utilizes semantic versioning.
* *playbook-builder*: Rework `buildPlaybook` so it only processes args and env once
* *playbook-builder*: Use YAML serialization internally for map and primitive-map values read from arguments (i.e., CLI options)
* *content-aggregator*: Skip check for remote branches in non-managed repository with no remote (#1179)
* *content-aggregator*: Skip check for local branches in managed repository (#1111)
* *page-composer*: `resolvePage` and `resolvePageURL` helpers resolve current page when called with no arguments (#1166)
* *page-composer*: Populate breadcrumbs for orphan page when component version has no navigation (#1176)
* *site-generator*: Log warning (instead of crashing) if Antora extension in playbook is missing require key (#1177)
@@ -27,6 +28,8 @@ This project utilizes semantic versioning.

=== Fixed

* *content-aggregator*: Discover linked worktree attached to bare repository when worktree is used as content source (#1111)
* *content-aggregator*: Discover linked worktree attached to bare repository when bare repository is used as content source (#1111)
* *content-aggregator*: Coerce version value to string in refname projection; coerce null value to empty string (#1170)
* *page-composer*: Fix crash if `resolvePage` or `resolvePageURL` helpers are called with no arguments
* *asciidoc-loader*: Restore support for relative path token for include file in example or partial family (#1173)
+22 −29
Original line number Diff line number Diff line
@@ -383,14 +383,12 @@ async function selectReferences (source, repo, remote) {
      refs.set(shortname, { shortname, fullname, type: 'branch', remote, head: noWorktree })
    }
  }
  // NOTE only consider local branches if repo has a worktree or there are no remote tracking branches
  if (!isBare) {
  if (!managed) {
    const localBranches = await git.listBranches(repo).then((branches) => {
      if (branches.length) return branches
      if (currentBranch == null) return getCurrentBranchName(repo).then((branch) => (branch ? [branch] : []))
      return currentBranch ? [currentBranch] : []
      if (branches.length || isBare) return branches
      if (currentBranch != null) return [currentBranch]
      return getCurrentBranchName(repo).then((branch) => (branch ? [branch] : []))
    })
    if (localBranches.length) {
    const worktrees = await findWorktrees(repo, worktreePatterns)
    let onMatch
    if ((worktreePatterns.join('') || '.') !== '.') {
@@ -404,19 +402,14 @@ async function selectReferences (source, repo, remote) {
        return shortname ? (pattern.startsWith('HEAD@') ? shortname : undefined) : candidate
      }
    }
    if (localBranches.length) {
      const preferRemote = isBare && remoteBranches.length > 0
      for (const shortname of filterRefs(localBranches, branchPatterns, patternCache, onMatch)) {
        const head = (worktrees.get(shortname) || { head: noWorktree }).head
        if (preferRemote && refs.has(shortname)) continue
        const head = (worktrees.get(shortname) || { head: false }).head
        refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
      }
    }
  } else if (!managed || !remoteBranches.length) {
    const localBranches = await git.listBranches(repo)
    if (localBranches.length) {
      for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
        if (refs.has(shortname)) continue // NOTE prefer remote branches in bare repository
        refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head: noWorktree })
      }
    }
  }
  return [...refs.values()]
}
@@ -1077,11 +1070,11 @@ function resolveRepositoryFromWorktree (repo) {
function findWorktrees (repo, patterns) {
  if (!patterns.length) return new Map()
  const mainWorktree =
    patterns[0] === '.' && (patterns = patterns.slice(1))
    patterns[0] === '.' && (patterns = patterns.slice(1)) && !repo.noCheckout
      ? getCurrentBranchName(repo).then((branch) => branch && [branch, { head: repo.dir, name: '.' }])
      : Promise.resolve()
  if (!patterns.length) return mainWorktree.then((entry) => new Map(entry && [entry]))
  const worktreesDir = ospath.join(repo.dir, '.git', 'worktrees')
  const worktreesDir = ospath.join(repo.dir, repo.dir === repo.gitdir ? '' : '.git', 'worktrees')
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
  return fsp
    .readdir(worktreesDir)
+40 −7
Original line number Diff line number Diff line
@@ -4788,16 +4788,20 @@ describe('aggregateContent()', () => {
        worktreeBranch = 'v1.2.x',
        worktreeName = worktreeBranch,
        checkout,
        bare,
      } = {}) => {
        const repoBuilder = new RepositoryBuilder(CONTENT_REPOS_DIR, FIXTURES_DIR)
        const repoBuilder = new RepositoryBuilder(CONTENT_REPOS_DIR, FIXTURES_DIR, { bare })
        const repoName = 'the-component'
        const version = worktreeBranch.replace(/v?(\d+\.\d+)(?:\.x.*)?/, '$1')
        const dir = ospath.join(repoBuilder.repoBase, repoName)
        const linkedWorktreeRepoBuilder = new RepositoryBuilder(CONTENT_REPOS_DIR, FIXTURES_DIR)
        const linkedWorktreeRepoName = `the-component-${worktreeName}`
        const linkedWorktreeDir = ospath.join(linkedWorktreeRepoBuilder.repoBase, linkedWorktreeRepoName)
        const linkedWorktreeDotgit = ospath.join(linkedWorktreeDir, '.git')
        const linkedWorktreeGitdir = ospath.join(dir, `.git/worktrees/${worktreeName}`)
        const linkedWorktreeGitdir =
          bare === 'flat'
            ? ospath.join(repoBuilder.repoBase, repoName + '.git', 'worktrees', worktreeName)
            : ospath.join(repoBuilder.repoBase, repoName, '.git', 'worktrees', worktreeName)
        const worktreesDirFromWorktree = bare === 'flat' ? `../${repoName}.git/worktrees` : '.git/worktrees'
        const pageOnePath = 'modules/ROOT/pages/page-one.adoc'
        await initRepoWithFilesAndWorktree(repoBuilder, { version }, () =>
          repoBuilder
@@ -4805,12 +4809,17 @@ describe('aggregateContent()', () => {
            .then((self) =>
              checkout
                ? (checkout === true ? self.getHeadCommit() : Promise.resolve(`ref: refs/heads/${checkout}`)).then(
                    (ref) => self.addToWorktree(`.git/worktrees/${worktreeName}/HEAD`, ref + '\n')
                    (ref) => self.addToWorktree(`${worktreesDirFromWorktree}/${worktreeName}/HEAD`, ref + '\n')
                  )
                : self.addToWorktree(
                    `${worktreesDirFromWorktree}/${worktreeName}/HEAD`,
                    `ref: refs/heads/${worktreeBranch}\n`
                  )
            )
                : self.addToWorktree(`.git/worktrees/${worktreeName}/HEAD`, `ref: refs/heads/${worktreeBranch}\n`)
            .then((self) => self.addToWorktree(`${worktreesDirFromWorktree}/${worktreeName}/commondir`, '../..\n'))
            .then((self) =>
              self.addToWorktree(`${worktreesDirFromWorktree}/${worktreeName}/gitdir`, linkedWorktreeDotgit + '\n')
            )
            .then((self) => self.addToWorktree(`.git/worktrees/${worktreeName}/commondir`, '../..\n'))
            .then((self) => self.addToWorktree(`.git/worktrees/${worktreeName}/gitdir`, linkedWorktreeDotgit + '\n'))
            .then((self) => (typeof checkout === 'string' ? self.checkoutBranch(checkout) : self))
            .then((self) => self.checkoutBranch('main'))
            .then((self) => self.addComponentDescriptorToWorktree({ name: 'the-component', version: '2.0' }))
@@ -5085,6 +5094,30 @@ describe('aggregateContent()', () => {
        )
      })

      it('should resolve worktree from bare repository if content source is linked worktree', async () => {
        const { linkedWorktreeRepoBuilder } = await initRepoWithFilesAndMultipleWorktrees({ bare: 'flat' })
        playbookSpec.content.sources.push({ url: linkedWorktreeRepoBuilder.url, branches: 'HEAD' })
        const aggregate = await aggregateContent(playbookSpec)
        expect(aggregate).to.have.lengthOf(1)
        expect(aggregate[0]).to.include({ name: 'the-component', version: '1.2' })
        const files = aggregate[0].files
        const pageOne = files.find((it) => it.relative === 'modules/ROOT/pages/page-one.adoc')
        expect(pageOne.contents.toString()).to.include('= Page One (Linked Worktree)\n')
        expect(pageOne.src.origin.refname).to.equal('v1.2.x')
      })

      it('should resolve linked worktree if content source is bare repository', async () => {
        const { repoBuilder } = await initRepoWithFilesAndMultipleWorktrees({ bare: 'flat' })
        playbookSpec.content.sources.push({ url: repoBuilder.url, branches: 'v1.2.x', worktrees: true })
        const aggregate = await aggregateContent(playbookSpec)
        expect(aggregate).to.have.lengthOf(1)
        expect(aggregate[0]).to.include({ name: 'the-component', version: '1.2' })
        const files = aggregate[0].files
        const pageOne = files.find((it) => it.relative === 'modules/ROOT/pages/page-one.adoc')
        expect(pageOne.contents.toString()).to.include('= Page One (Linked Worktree)\n')
        expect(pageOne.src.origin.refname).to.equal('v1.2.x')
      })

      it('should not use worktree if content source is linked worktree and worktrees is .', async () => {
        const { linkedWorktreeRepoBuilder } = await initRepoWithFilesAndMultipleWorktrees()
        playbookSpec.content.sources.push({ url: linkedWorktreeRepoBuilder.url, branches: 'v1.2.x', worktrees: '.' })
+3 −2
Original line number Diff line number Diff line
@@ -41,14 +41,15 @@ class RepositoryBuilder {
      this.repoPath += '.git'
      this.url = `${this.gitServerProtocol}//localhost:${this.gitServerPort}/${repoName}.git`
    } else if (this.bare) {
      this.url += ospath.sep + '.git'
      this.url += (this.bare === 'flat' ? '' : ospath.sep) + '.git'
    } else {
      this.local = ospath.join(this.repoPath, '.git')
    }
    const dir = this.repoPath
    const gitdir = ospath.join(dir, '.git')
    const gitdir = this.bare === 'flat' ? dir + '.git' : ospath.join(dir, '.git')
    this.repository = { cache: {}, defaultBranch: opts.branch || 'main', dir, fs, gitdir, http }
    await git.init(this.repository)
    if (this.bare === 'flat') await fsp.mkdir(dir)
    if (opts.empty) return this
    await (await this.addToWorktree('.gitignore')).addToWorktree('.gitattributes', '* text=auto eol=lf')
    // NOTE isomorphic-git doesn't require any commits to set up the default branch, but tests still rely on these files