Skip to content

Commit 24b858d

Browse files
robsymejorgee
andauthored
Fix fetching new remote branch in multi-revision mode (nextflow-io#6733)
Co-authored-by: jorgee <jorge.ejarque@seqera.io>
1 parent 73e5075 commit 24b858d

2 files changed

Lines changed: 109 additions & 9 deletions

File tree

modules/nextflow/src/main/groovy/nextflow/scm/MultiRevisionRepositoryStrategy.groovy

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package nextflow.scm
1818

1919
import groovy.transform.CompileStatic
20+
import groovy.transform.Memoized
2021
import groovy.transform.PackageScope
2122
import groovy.util.logging.Slf4j
2223
import nextflow.config.Manifest
@@ -333,20 +334,35 @@ class MultiRevisionRepositoryStrategy extends AbstractRepositoryStrategy {
333334
}
334335

335336
private RefSpec refSpecForName(String revision) {
336-
// Is it a local branch?
337-
Ref branch = getBareGit().getRepository().findRef("refs/heads/" + revision)
337+
// First, check if it's a local branch
338+
final branchName = "refs/heads/$revision".toString()
339+
final branch = getBareGit().getRepository().findRef(branchName)
338340
if( branch != null ) {
339-
return new RefSpec("refs/heads/" + revision + ":refs/heads/" + revision)
341+
return new RefSpec("$branchName:$branchName")
340342
}
341343

342-
// Is it a tag?
343-
Ref tag = getBareGit().getRepository().findRef("refs/tags/" + revision)
344+
// Check if it's a local tag
345+
final tagName = "refs/tags/$revision".toString()
346+
final tag = getBareGit().getRepository().findRef(tagName)
344347
if( tag != null ) {
345-
return new RefSpec("refs/tags/" + revision + ":refs/tags/" + revision)
348+
return new RefSpec("$tagName:$tagName")
346349
}
347350

348-
// It is a commit
349-
return new RefSpec(revision + ":refs/tags/" + revision)
351+
// Not found locally - check remote refs
352+
final remoteRefs = lsRemote(false)
353+
354+
// Is it a remote branch?
355+
if( remoteRefs.containsKey(branchName) ) {
356+
return new RefSpec("$branchName:$branchName")
357+
}
358+
359+
// Is it a remote tag?
360+
if( remoteRefs.containsKey(tagName) ) {
361+
return new RefSpec("$tagName:$tagName")
362+
}
363+
364+
// Assume it's a commit SHA
365+
return new RefSpec("$revision:$tagName")
350366
}
351367

352368
@Override
@@ -422,7 +438,7 @@ class MultiRevisionRepositoryStrategy extends AbstractRepositoryStrategy {
422438
@Override
423439
Map<String, Ref> lsRemote(boolean tags) {
424440
final cmd = getBareGitWithLegacyFallback()?.lsRemote()?.setTags(tags)
425-
if( provider.hasCredentials() )
441+
if( provider?.hasCredentials() )
426442
cmd?.setCredentialsProvider(provider.getGitCredentials())
427443
return cmd?.callAsMap() ?: [:]
428444
}

modules/nextflow/src/test/groovy/nextflow/scm/MultiRevisionRepositoryStrategyTest.groovy

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package nextflow.scm
1818

1919
import nextflow.config.Manifest
20+
import org.eclipse.jgit.api.Git
2021

2122
import static MultiRevisionRepositoryStrategy.BARE_REPO
2223
import static MultiRevisionRepositoryStrategy.REVISION_SUBDIR
@@ -127,4 +128,87 @@ class MultiRevisionRepositoryStrategyTest extends Specification {
127128
folder.resolve(REPOS_SUBDIR + '/nextflow-io/hello/' + REVISION_SUBDIR + '/1c3e9e7404127514d69369cd87f8036830f5cf64/.git/objects/info/alternates').text == folder.resolve(REPOS_SUBDIR + '/nextflow-io/hello/' + BARE_REPO + '/objects').toAbsolutePath().toString()
128129
}
129130

131+
def 'should create correct RefSpec for branches tags and commits'() {
132+
given:
133+
def folder = tempDir.getRoot()
134+
// Create a bare repo manually for testing
135+
def bareRepoPath = folder.resolve(REPOS_SUBDIR + '/test/project/' + BARE_REPO).toFile()
136+
bareRepoPath.mkdirs()
137+
def git = Git.init().setDirectory(bareRepoPath).setBare(true).call()
138+
// Configure origin to point to itself so lsRemote works (returns empty refs)
139+
def config = git.getRepository().getConfig()
140+
config.setString("remote", "origin", "url", bareRepoPath.absolutePath)
141+
config.save()
142+
git.close()
143+
144+
def strategy = new MultiRevisionRepositoryStrategy('test/project')
145+
146+
when:
147+
// Access the private method via Groovy's metaclass
148+
def refSpec = strategy.invokeMethod('refSpecForName', 'some-revision')
149+
150+
then:
151+
// When revision is not found locally or remotely, it's treated as a commit
152+
refSpec.toString() == 'some-revision:refs/tags/some-revision'
153+
}
154+
155+
@IgnoreIf({ System.getenv('NXF_SMOKE') })
156+
@Requires({ System.getenv('NXF_GITHUB_ACCESS_TOKEN') })
157+
def 'should fetch new remote branch not in local bare repo'() {
158+
given:
159+
def folder = tempDir.getRoot()
160+
def token = System.getenv('NXF_GITHUB_ACCESS_TOKEN')
161+
// Use a test repo with known branches
162+
def strategy = createStrategy('nextflow-io/hello', token)
163+
def manifest = Mock(Manifest) {
164+
getDefaultBranch() >> 'master'
165+
getRecurseSubmodules() >> false
166+
}
167+
168+
when:
169+
// First, create bare repo with default branch only
170+
strategy.checkBareRepo(manifest)
171+
172+
then:
173+
folder.resolve(REPOS_SUBDIR + '/nextflow-io/hello/' + BARE_REPO).isDirectory()
174+
175+
when:
176+
// Now try to download 'mybranch' which exists remotely but wasn't fetched initially
177+
// Clear the local branch ref to simulate it not existing locally
178+
def bareGit = Git.open(strategy.getBareRepo())
179+
def localBranchRef = bareGit.getRepository().findRef("refs/heads/mybranch")
180+
// If mybranch was fetched during clone, delete it to simulate fresh fetch scenario
181+
if (localBranchRef) {
182+
bareGit.branchDelete().setBranchNames("mybranch").setForce(true).call()
183+
}
184+
bareGit.close()
185+
186+
// This should succeed by querying remote refs
187+
strategy.download('mybranch', 1, manifest)
188+
189+
then:
190+
noExceptionThrown()
191+
// mybranch commit is 1c3e9e7404127514d69369cd87f8036830f5cf64
192+
folder.resolve(REPOS_SUBDIR + '/nextflow-io/hello/' + REVISION_SUBDIR + '/1c3e9e7404127514d69369cd87f8036830f5cf64/.git').isDirectory()
193+
194+
when:
195+
// Now try to download tag 'v1.2' which exists remotely but wasn't fetched initially
196+
// Clear the local tag ref to simulate it not existing locally
197+
bareGit = Git.open(strategy.getBareRepo())
198+
def localTagRef = bareGit.getRepository().findRef("refs/tags/v1.2")
199+
// If v1.2 was fetched during clone, delete it to simulate fresh fetch scenario
200+
if (localTagRef) {
201+
bareGit.tagDelete().setTags("v1.2").call()
202+
}
203+
bareGit.close()
204+
205+
// This should succeed by querying remote refs
206+
strategy.download('v1.2', 1, manifest)
207+
208+
then:
209+
noExceptionThrown()
210+
// v1.2 ref is 1b420d060d3fad67027154ac48e3bdea06f058
211+
folder.resolve(REPOS_SUBDIR + '/nextflow-io/hello/' + REVISION_SUBDIR + '/1b420d060d3fad67027154ac48e3bdea06f058da/.git').isDirectory()
212+
}
213+
130214
}

0 commit comments

Comments
 (0)