Skip to content

Make a non-default Branch of ConfigTester resolve - #809

Merged
alexander-yevsyukov merged 6 commits into
masterfrom
fix-config-tester-branch
Oct 19, 2026
Merged

alexander-yevsyukov merged 6 commits into
masterfrom
fix-config-tester-branch

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Follows #805, which repaired four defects in buildDependants. This is the
fifth, found while using the repaired task to verify an unmerged consumer fix.

The defect

The Branch override has never worked, so there was no way to run
buildDependants against an unmerged branch of a consumer repository — which is
exactly when one needs it. config-tester.gradle.kts documents the feature:

// This is how one builds a specific branch of some repository:
// .addRepo(SpineRepos.coreJvm, Branch("grpc-concurrency-fixes"))

Using it failed with:

org.eclipse.jgit.api.errors.RefNotFoundException: Ref <name> cannot be resolved

GitRepository.checkout cloned, and then checked the branch out as a second
step. Grgit.checkout resolves a name against the clone's local ref
database, which right after a clone holds only the default branch — every other
branch is there solely as a remote-tracking ref. So the commented-out example
was the one thing in that file that could not work, which is why it went
unnoticed for so long.

The fix

Pass the ref to the clone instead of checking it out afterwards:

Grgit.clone(
    mapOf(
        "dir" to preparedFolder,
        "uri" to uri,
        "refToCheckout" to branch.name
    )
).close()

CloneOp.refToCheckout maps onto JGit's CloneCommand.setBranch, which
resolves against the refs advertised by the remote, links local HEAD to
the branch and adds its merge config. The branch thus lands as a proper local
branch tracking its remote counterpart.

This was preferred over adding createBranch + startPoint to the checkout
call, which would have needed an "is this already a local ref?" special case to
keep the default Branch("master") working. One code path now serves both.

The .close() releases the JGit repository handle, which the discarded Grgit
of the two-step code leaked on every checkout.

Two things worth knowing

  • Branch("origin/<name>") was the pre-fix workaround and still resolves, but
    as a detached HEAD. So "the right files are present" does not prove a
    branch was checked out properly, and the spec asserts the clone's current
    branch
    for that reason.
  • A misspelled branch now fails faster and more clearly:
    TransportException: Remote branch 'x' not found in upstream origin. It does
    not clone an empty working tree, which would have resurfaced the confusing
    "does not contain a Gradle build" failure that Fix the buildDependants task #805 removed.

Tests

GitRepositorySpec — 3 cases, no network access. It Grgit.inits a throwaway
repository in @TempDir with two branches and clones it over its file:// URI.
JGit honors init.defaultBranch, so the fixture reads the initial branch name
rather than assuming master.

Against the unfixed checkout, check out a branch other than the default one
fails with the reported RefNotFoundException, while check out the default branch still passes — the suite is vacuous in neither direction.

Path.createFile(), which both specs of the package need to lay out a folder,
moves out of the companion of ClonedRepoSpec into a top-level extension of its
own in PathExts.kt, instead of being copied into the new spec. The name is an
overload of kotlin.io.path.createFile(), not a clash: that one takes file
attributes, this one a relative path and the content.

Verification

./gradlew detekt passes, as does the whole buildSrc test suite.

./gradlew clean buildDependants was run against the real remotes, and every
checkout was inspected with git branch -vv:

repository branch asked for HEAD of the clone consumer build
base default master master -> origin/master green
base-types default master master -> origin/master green
core-jvm default master master -> origin/master fails, see below
base-types fix-publishing-docs fix-publishing-docs -> origin/fix-publishing-docs fails, see below

So both paths resolve, and both land a local branch tracking its remote
counterpart rather than a detached HEAD. The non-default run used a temporary
edit to config-tester.gradle.kts that is not committed — the example there
stays a comment.

Both consumer build failures are unrelated to this change, and both occur well
after a successful checkout:

  • core-jvm at master failed on Unresolved reference 'OpenTelemetryKotlin',
    which Declare OpenTelemetryKotlin in buildSrc #808 has since declared in buildSrc.
  • base-types at fix-publishing-docs fails compiling its own build script on
    Unresolved reference 'pluginLib' — that branch predates the current
    config. This is exactly the drift buildDependants exists to surface.

An earlier run also confirmed the fast-fail path against a real remote: a branch
deleted after its PR merged was reported as
Remote branch '...' not found in upstream origin, rather than silently cloning
an empty tree.

🤖 Generated with Claude Code

The `Branch` override has never worked, so there was no way to run
`buildDependants` against an unmerged branch of a consumer repository —
which is exactly when one needs it. The feature that
`config-tester.gradle.kts` documents in a comment,

    .addRepo(SpineRepos.coreJvm, Branch("grpc-concurrency-fixes"))

died with

    org.eclipse.jgit.api.errors.RefNotFoundException: Ref <name> cannot
    be resolved

`GitRepository.checkout` cloned, and then checked the branch out as
a second step. `Grgit.checkout` resolves a name against the clone's
local ref database, which right after a clone holds only the default
branch; every other branch is there solely as a remote-tracking ref. So
the commented-out example was the one thing in that file that could not
work, which is why it went unnoticed.

Pass the ref to the clone instead. `CloneOp.refToCheckout` maps onto
JGit's `CloneCommand.setBranch`, which resolves against the refs
advertised by the remote, links local `HEAD` to the branch and adds its
merge config. The branch thus lands as a proper local branch tracking
its remote counterpart.

This is preferred over adding `createBranch` and `startPoint` to the
`checkout` call, which would need an "is this already a local ref?"
special case to keep the default `Branch("master")` working. One code
path now serves both.

The added `close()` releases the JGit repository handle, which the
discarded `Grgit` of the two-step code leaked on every checkout.

A misspelled branch now fails faster and more clearly, with
`TransportException: Remote branch 'x' not found in upstream origin`.
It does not clone an empty working tree, which would have resurfaced
the confusing "does not contain a Gradle build" failure that #805
removed.

`GitRepositorySpec` covers the three cases without network access, by
cloning a throwaway repository over its `file://` URI. It asserts the
`HEAD` of the clone, along with the branch it tracks, because
`Branch("origin/<name>")` — the pre-fix workaround — holds the right
files too, but as a detached `HEAD`.

`Path.put()`, which both specs of the package need to lay out a folder,
moves from the companion of `ClonedRepoSpec` to a top-level extension
of its own, instead of being copied into the new spec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T15:25:56.843109Z 608ceae New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@alexander-yevsyukov alexander-yevsyukov self-assigned this Oct 19, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to In Review in v2.0 Oct 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The focused fix has targeted regression coverage and no identified blocking issues; tests were not executed in this review.

No open issues 🎉

What changed in this PR

Fixes non-default branch checkout in ConfigTester, extending the buildDependants repairs from #805 to support unmerged consumer branches.

Changes:

  • Selects the branch during cloning and closes the Git repository handle.
  • Adds local regression tests for default, non-default, and missing branches.
  • Shares the test file-creation helper and clarifies branch documentation.
File Description
buildSrc/​src/​test/​kotlin/​io/​spine/​gradle/​PathExtensions.kt Extracts the shared file-creation helper.
buildSrc/​src/​test/​kotlin/​io/​spine/​gradle/​GitRepositorySpec.kt Tests branch selection, tracking, and missing-branch failure.
buildSrc/​src/​test/​kotlin/​io/​spine/​gradle/​ClonedRepoSpec.kt Uses the extracted helper.
buildSrc/​src/​main/​kotlin/​io/​spine/​gradle/​ConfigTester.kt Resolves branches during cloning, closes the handle, and updates documentation.

Review effort: Balanced

alexander-yevsyukov and others added 4 commits October 19, 2026 16:19
`put()` says where the file goes but not what happens there, so the
extension both specs of the package use to lay out a folder becomes
`createFile()`, and its file becomes `PathExts.kt`.

The name is an overload of `kotlin.io.path.createFile()` rather than a
clash: that one takes file attributes, this one a relative path and the
content. Neither spec imports the standard-library extension.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov merged commit 6d1c793 into master Oct 19, 2026
2 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the fix-config-tester-branch branch October 19, 2026 15:25
@github-project-automation github-project-automation Bot moved this from In Review to ✅ Done in v2.0 Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants