Skip to content

Commit 4e52f7e

Browse files
Make the "ahead of remote" check more explicit (both in docs and error message) (#931)
1 parent 0af2214 commit 4e52f7e

File tree

8 files changed

+86
-25
lines changed

8 files changed

+86
-25
lines changed

docs/configuration/checks.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,15 @@ You can disable this check using either
4242

4343
## Ahead of remote check
4444

45-
This check tries to verify if all local commits have been pushed to
46-
remote. In order to do so, it needs full information about remote and
45+
This check verifies that:
46+
47+
- all local commits have been pushed to remote
48+
- no new remote commits appeared that haven't been pulled to local
49+
50+
For long-running CI builds, it may happen that build for older merge commit will finish after the newer merge commit.
51+
In this case, the versions would be mixed (older commit would be tagged with a newer version).
52+
53+
It needs full information about remote and
4754
HEAD commit, which might be lacking in CI environment (for more on CI
4855
build read [CI Servers](ci_servers.md)).
4956

src/main/groovy/pl/allegro/tech/build/axion/release/VerifyReleaseTask.groovy

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,13 @@ abstract class VerifyReleaseTask extends BaseAxionTask {
4848
boolean remoteAttached = repository.remoteAttached(versionConfig.repository.remote.get())
4949
def localOnly = localOnlyResolver.localOnly(remoteAttached)
5050
if (versionConfig.checks.checkAheadOfRemote().get() && !localOnly) {
51-
boolean aheadOfRemote = repository.checkAheadOfRemote()
52-
logger.quiet("Checking if branch is ahead of remote.. ${aheadOfRemote ? 'FAILED' : ''}")
53-
if (aheadOfRemote && !dryRun) {
54-
throw new IllegalStateException("Current branch is ahead of remote counterpart - can't release.")
51+
int aheadOrBehindCommits = repository.numberOfCommitsAheadOrBehindRemote()
52+
53+
if (aheadOrBehindCommits > 0 && !dryRun) {
54+
throw new IllegalStateException("Current branch is $aheadOrBehindCommits commits ahead of remote - can't release.")
55+
}
56+
if (aheadOrBehindCommits < 0 && !dryRun) {
57+
throw new IllegalStateException("Current branch is ${-aheadOrBehindCommits} commits behind remote - can't release.")
5558
}
5659
} else {
5760
logger.quiet("Skipping ahead of remote check")

src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DummyRepository.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ class DummyRepository implements ScmRepository {
100100
}
101101

102102
@Override
103-
boolean checkAheadOfRemote() {
103+
int numberOfCommitsAheadOrBehindRemote() {
104104
log('check ahead of remote')
105-
return false
105+
return 0
106106
}
107107

108108
@Override

src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/NoOpRepository.groovy

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ package pl.allegro.tech.build.axion.release.infrastructure
22

33
import org.gradle.api.logging.Logger
44
import org.gradle.api.logging.Logging
5-
import pl.allegro.tech.build.axion.release.domain.scm.*
5+
import pl.allegro.tech.build.axion.release.domain.scm.ScmIdentity
6+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPosition
7+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushOptions
8+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushResult
9+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushResultOutcome
10+
import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository
11+
import pl.allegro.tech.build.axion.release.domain.scm.TagsOnCommit
612

713
import java.util.regex.Pattern
814

@@ -95,8 +101,8 @@ class NoOpRepository implements ScmRepository {
95101
}
96102

97103
@Override
98-
boolean checkAheadOfRemote() {
99-
boolean aheadOfRemote = delegateRepository.checkAheadOfRemote()
104+
int numberOfCommitsAheadOrBehindRemote() {
105+
int aheadOfRemote = delegateRepository.numberOfCommitsAheadOrBehindRemote()
100106
log("ahead of remote: $aheadOfRemote")
101107
return aheadOfRemote
102108
}

src/main/java/pl/allegro/tech/build/axion/release/domain/scm/ScmRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public interface ScmRepository {
3434

3535
boolean checkUncommittedChanges();
3636

37-
boolean checkAheadOfRemote();
37+
int numberOfCommitsAheadOrBehindRemote();
3838

3939
boolean isLegacyDefTagnameRepo();
4040

src/main/java/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepository.java

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,56 @@
11
package pl.allegro.tech.build.axion.release.infrastructure.git;
22

3-
import org.eclipse.jgit.api.*;
3+
import org.eclipse.jgit.api.AddCommand;
4+
import org.eclipse.jgit.api.FetchCommand;
5+
import org.eclipse.jgit.api.Git;
6+
import org.eclipse.jgit.api.LogCommand;
7+
import org.eclipse.jgit.api.PushCommand;
8+
import org.eclipse.jgit.api.Status;
49
import org.eclipse.jgit.api.errors.GitAPIException;
510
import org.eclipse.jgit.api.errors.NoHeadException;
611
import org.eclipse.jgit.diff.DiffFormatter;
712
import org.eclipse.jgit.errors.RepositoryNotFoundException;
8-
import org.eclipse.jgit.lib.*;
13+
import org.eclipse.jgit.lib.AnyObjectId;
14+
import org.eclipse.jgit.lib.BranchTrackingStatus;
15+
import org.eclipse.jgit.lib.Config;
16+
import org.eclipse.jgit.lib.Constants;
17+
import org.eclipse.jgit.lib.ObjectId;
18+
import org.eclipse.jgit.lib.Ref;
19+
import org.eclipse.jgit.lib.Repository;
20+
import org.eclipse.jgit.lib.StoredConfig;
921
import org.eclipse.jgit.revwalk.RevCommit;
1022
import org.eclipse.jgit.revwalk.RevSort;
1123
import org.eclipse.jgit.revwalk.RevWalk;
12-
import org.eclipse.jgit.transport.*;
24+
import org.eclipse.jgit.transport.PushResult;
25+
import org.eclipse.jgit.transport.RemoteConfig;
26+
import org.eclipse.jgit.transport.RemoteRefUpdate;
27+
import org.eclipse.jgit.transport.TagOpt;
28+
import org.eclipse.jgit.transport.URIish;
1329
import org.eclipse.jgit.treewalk.filter.PathFilter;
1430
import org.eclipse.jgit.util.SystemReader;
1531
import org.eclipse.jgit.util.io.DisabledOutputStream;
1632
import org.gradle.api.logging.Logger;
1733
import org.gradle.api.logging.Logging;
18-
import pl.allegro.tech.build.axion.release.domain.scm.*;
34+
import pl.allegro.tech.build.axion.release.domain.scm.ScmException;
35+
import pl.allegro.tech.build.axion.release.domain.scm.ScmIdentity;
36+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPosition;
37+
import pl.allegro.tech.build.axion.release.domain.scm.ScmProperties;
38+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushOptions;
39+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushResult;
40+
import pl.allegro.tech.build.axion.release.domain.scm.ScmPushResultOutcome;
41+
import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository;
42+
import pl.allegro.tech.build.axion.release.domain.scm.ScmRepositoryUnavailableException;
43+
import pl.allegro.tech.build.axion.release.domain.scm.TagsOnCommit;
1944

2045
import java.io.File;
2146
import java.io.IOException;
2247
import java.net.URISyntaxException;
23-
import java.util.*;
48+
import java.util.ArrayList;
49+
import java.util.HashMap;
50+
import java.util.List;
51+
import java.util.Map;
52+
import java.util.Optional;
53+
import java.util.Set;
2454
import java.util.regex.Pattern;
2555
import java.util.stream.StreamSupport;
2656

@@ -554,7 +584,7 @@ public boolean checkUncommittedChanges() {
554584
}
555585

556586
@Override
557-
public boolean checkAheadOfRemote() {
587+
public int numberOfCommitsAheadOrBehindRemote() {
558588
try {
559589
String branchName = jgitRepository.getRepository().getFullBranch();
560590
BranchTrackingStatus status = BranchTrackingStatus.of(jgitRepository.getRepository(), branchName);
@@ -563,7 +593,14 @@ public boolean checkAheadOfRemote() {
563593
throw new ScmException("Branch " + branchName + " is not set to track another branch");
564594
}
565595

566-
return status.getAheadCount() != 0 || status.getBehindCount() != 0;
596+
if (status.getAheadCount() > 0) {
597+
return status.getAheadCount();
598+
}
599+
if (status.getBehindCount() > 0) {
600+
return -status.getBehindCount();
601+
}
602+
return 0;
603+
567604
} catch (IOException e) {
568605
throw new ScmException(e);
569606
}

src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepositoryTest.groovy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ class GitRepositoryTest extends Specification {
463463

464464
def "should pass ahead of remote check when in sync with remote"() {
465465
expect:
466-
!repository.checkAheadOfRemote()
466+
!repository.numberOfCommitsAheadOrBehindRemote()
467467
}
468468

469469
def "should fail ahead of remote check when repository behind remote"() {
@@ -472,15 +472,15 @@ class GitRepositoryTest extends Specification {
472472
repository.fetchTags(ScmIdentity.defaultIdentityWithoutAgents(), "origin")
473473

474474
expect:
475-
repository.checkAheadOfRemote()
475+
repository.numberOfCommitsAheadOrBehindRemote()
476476
}
477477

478478
def "should fail ahead of remote check when repository has local commits"() {
479479
given:
480480
repository.commit(["*"], "local commit")
481481

482482
expect:
483-
repository.checkAheadOfRemote()
483+
repository.numberOfCommitsAheadOrBehindRemote()
484484
}
485485

486486
def "should fail ahead of remote check when on branch with no remote tracking"() {
@@ -496,7 +496,7 @@ class GitRepositoryTest extends Specification {
496496
rawRepository.branch.change(name: 'new-branch', startPoint: 'new-branch')
497497

498498
when:
499-
noRemoteRepository.checkAheadOfRemote()
499+
noRemoteRepository.numberOfCommitsAheadOrBehindRemote()
500500

501501
then:
502502
thrown(ScmException)

src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/NoOpRepositoryTest.groovy

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,18 @@ class NoOpRepositoryTest extends Specification {
5959

6060
def "should check ahead of remote on real scm"() {
6161
given:
62-
scm.checkAheadOfRemote() >> true
62+
scm.numberOfCommitsAheadOrBehindRemote() >> 1
6363

6464
expect:
65-
dryRepository.checkAheadOfRemote()
65+
dryRepository.numberOfCommitsAheadOrBehindRemote() == 1
66+
}
67+
68+
def "should check behind remote on real scm"() {
69+
given:
70+
scm.numberOfCommitsAheadOrBehindRemote() >> -1
71+
72+
expect:
73+
dryRepository.numberOfCommitsAheadOrBehindRemote() == -1
6674
}
6775

6876
}

0 commit comments

Comments
 (0)