Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import de.psanetra.gitsemver.containers.GitSemverContainer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled;

import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -68,7 +67,6 @@ public void shouldReturnVersionsNotReachableFromHEAD() {
}

@Test
@Disabled("Skipping as it requires a specific version from fork source")
public void shouldReturnEmptyVersionOnRepoWithoutTags() {
try (var container = new GitSemverContainer()) {
container.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import de.psanetra.gitsemver.containers.GitSemverContainer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled;

import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -47,7 +46,6 @@ public void shouldReturnVersionsNotReachableFromHEAD() {


@Test
@Disabled("Skipping as it requires a specific version from fork source")
public void shouldReturnEmptyVersionOnRepoWithoutTags() {
try (var container = new GitSemverContainer()) {
container.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public void shouldConvertPrereleaseToRelease() {
/**
* In this case the next command can not calculate the next version based on the commits since the latest release.
*/
@Disabled("Skipping as it requires a specific version from fork source")
@Disabled("This fork intentionally does not assert that the latest version tag is reachable from HEAD")
@Test
public void shouldReturnErrorCodeIfLatestVersionNotReachableFromHEAD() throws IOException, InterruptedException {
try (var container = new GitSemverContainer()) {
Expand Down Expand Up @@ -151,7 +151,6 @@ public void shouldNotReturnErrorCodeIfLatestVersionReachableFromDetachedHEAD() t

}

@Disabled("Skipping as it requires a specific version from fork source")
@Test
public void shouldReturnFirstVersionOnRepoWithoutTags() {
try (var container = new GitSemverContainer()) {
Expand Down Expand Up @@ -252,7 +251,7 @@ public void shouldReturnNewVersionAfterMergeWithFeatureBranchPrallelToRelease()

}

@Disabled("Skipping as it requires a specific version from fork source")
@Disabled("This fork intentionally does not assert that the latest version tag is reachable from HEAD")
@Test
public void shouldPanicIfCommitIsMissingOnShallowClone() {
try (var container = new GitSemverContainer()) {
Expand Down
27 changes: 8 additions & 19 deletions latest/latest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/pkg/errors"
"io"
"os/exec"
"strings"
)

Expand Down Expand Up @@ -52,22 +51,8 @@ func FindLatestVersion(repo *git.Repository, majorVersionFilter int, preRelease
}

func findLatestVersionTag(repo *git.Repository, majorVersionFilter int, includePreReleases bool) (*plumbing.Reference, error) {
// Use git rev-list to get the latest tag from all branches, not just the current branch
cmd := exec.Command("git", "rev-list", "--tags", "--max-count=1")
worktree, err := repo.Worktree()
if err != nil {
return nil, err
}

cmd.Dir = worktree.Filesystem.Root() // Use the working directory for git commands
// We no longer need to capture the output of the command
_, err = cmd.Output()

if err != nil {
return nil, err
}

// Retrieve the tags from the repository
// Retrieve the tags from the repository. All tags are considered, not just the ones
// reachable from the current branch.
tagIter, err := repo.Tags()
if err != nil {
return nil, err
Expand Down Expand Up @@ -103,9 +88,13 @@ func findLatestVersionTag(repo *git.Repository, majorVersionFilter int, includeP
foundTags = append(foundTags, tag)
}

// If no valid tags were found
// If no valid tags were found the repository simply has no matching version yet.
// This is not an error: callers interpret a nil tag as "no previous version" and
// fall back to the empty version (0.0.0). Returning an error here would break
// repositories which have not been released yet, as well as repositories whose
// only tags are pre-releases when pre-releases are excluded.
if len(foundTags) == 0 {
return nil, errors.New("no matching semantic version tags found")
return nil, nil
}

// Find the highest version tag
Expand Down
92 changes: 92 additions & 0 deletions latest/latest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package latest

import (
"github.com/PandasWhoCode/git-semver/semver"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
"time"
)

func Test_tagNameToVersion_should_return_version(t *testing.T) {
Expand Down Expand Up @@ -35,3 +39,91 @@ func Test_tagNameToVersion_should_return_version_if_tag_has_v_prefix(t *testing.
version,
)
}

func Test_FindLatestVersion_should_return_no_version_if_repository_has_no_tags(t *testing.T) {
repo, _ := initRepo(t)

version, tag, err := FindLatestVersion(repo, -1, false)

require.NoError(t, err)
assert.Nil(t, version)
assert.Nil(t, tag)
}

func Test_FindLatestVersion_should_return_no_version_if_repository_only_has_pre_release_tags(t *testing.T) {
repo, _ := initRepo(t, "v0.1.0-alpha.1", "v0.1.0-alpha.2")

version, tag, err := FindLatestVersion(repo, -1, false)

require.NoError(t, err)
assert.Nil(t, version)
assert.Nil(t, tag)
}

func Test_FindLatestVersion_should_return_latest_pre_release_if_pre_releases_are_included(t *testing.T) {
repo, _ := initRepo(t, "v0.1.0-alpha.1", "v0.1.0-alpha.2")

version, tag, err := FindLatestVersion(repo, -1, true)

require.NoError(t, err)
assert.Equal(t, "0.1.0-alpha.2", version.ToString())
assert.Equal(t, "v0.1.0-alpha.2", tag.Name().Short())
}

func Test_FindLatestVersion_should_ignore_pre_release_tags_if_a_release_tag_exists(t *testing.T) {
repo, _ := initRepo(t, "v0.1.0", "v0.2.0-alpha.1")

version, tag, err := FindLatestVersion(repo, -1, false)

require.NoError(t, err)
assert.Equal(t, "0.1.0", version.ToString())
assert.Equal(t, "v0.1.0", tag.Name().Short())
}

func Test_Latest_should_return_empty_version_if_repository_has_no_tags(t *testing.T) {
_, workdir := initRepo(t)

version, err := Latest(LatestOptions{Workdir: workdir})

require.NoError(t, err)
assert.Equal(t, "0.0.0", version.ToString())
}

func Test_Latest_should_return_empty_version_if_repository_only_has_pre_release_tags(t *testing.T) {
_, workdir := initRepo(t, "v0.1.0-alpha.1")

version, err := Latest(LatestOptions{Workdir: workdir})

require.NoError(t, err)
assert.Equal(t, "0.0.0", version.ToString())
}

// initRepo creates a repository with a single commit which is tagged with all given tag names.
func initRepo(t *testing.T, tagNames ...string) (*git.Repository, string) {
t.Helper()

workdir := t.TempDir()

repo, err := git.PlainInit(workdir, false)
assert.NoError(t, err)

worktree, err := repo.Worktree()
assert.NoError(t, err)

commit, err := worktree.Commit("feat: initial commit", &git.CommitOptions{
Author: &object.Signature{
Name: "git-semver",
Email: "git-semver@example.com",
When: time.Now(),
},
AllowEmptyCommits: true,
})
assert.NoError(t, err)

for _, tagName := range tagNames {
_, err = repo.CreateTag(tagName, commit, nil)
assert.NoError(t, err)
}

return repo, workdir
}
103 changes: 103 additions & 0 deletions next/next_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package next

import (
"github.com/PandasWhoCode/git-semver/semver"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
"time"
)

func Test_Next_should_return_first_version_if_repository_has_no_tags(t *testing.T) {
workdir := initRepo(t, commit{message: "feat: initial commit"})

version, err := Next(NextOptions{
Workdir: workdir,
Stable: true,
MajorVersionFilter: -1,
})

require.NoError(t, err)
assert.Equal(t, "1.0.0", version.ToString())
}

func Test_Next_should_return_next_version_if_repository_only_has_pre_release_tags(t *testing.T) {
workdir := initRepo(
t,
commit{message: "feat: initial commit", tags: []string{"v0.1.0-alpha.1"}},
commit{message: "fix: a fix"},
)

version, err := Next(NextOptions{
Workdir: workdir,
Stable: false,
MajorVersionFilter: -1,
})

require.NoError(t, err)
assert.Equal(t, "0.1.0", version.ToString())
}

func Test_Next_should_increment_pre_release_counter_if_repository_only_has_pre_release_tags(t *testing.T) {
workdir := initRepo(
t,
commit{message: "feat: initial commit", tags: []string{"v0.1.0-alpha.1"}},
commit{message: "fix: a fix", tags: []string{"v0.1.0-alpha.2"}},
commit{message: "fix: another fix"},
)

version, err := Next(NextOptions{
Workdir: workdir,
Stable: false,
MajorVersionFilter: -1,
PreReleaseOptions: semver.PreReleaseOptions{
Label: "alpha",
AppendCounter: true,
},
})

require.NoError(t, err)
assert.Equal(t, "0.1.0-alpha.3", version.ToString())
}

type commit struct {
message string
tags []string
}

// initRepo creates a repository containing the given commits and returns its working directory.
func initRepo(t *testing.T, commits ...commit) string {
t.Helper()

workdir := t.TempDir()

repo, err := git.PlainInit(workdir, false)
assert.NoError(t, err)

worktree, err := repo.Worktree()
assert.NoError(t, err)

for i, c := range commits {
var hash plumbing.Hash

hash, err = worktree.Commit(c.message, &git.CommitOptions{
Author: &object.Signature{
Name: "git-semver",
Email: "git-semver@example.com",
When: time.Now().Add(time.Duration(i) * time.Second),
},
AllowEmptyCommits: true,
})
assert.NoError(t, err)

for _, tagName := range c.tags {
_, err = repo.CreateTag(tagName, hash, nil)
assert.NoError(t, err)
}
}

return workdir
}
Loading