1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-08 03:31:59 +02:00
goreleaser/internal/pipe/git/git_test.go
Edward Maxwell-Lyte 18695c2687
fix: sort tags by version not day of the week (#2377)
* fix: tag sorting

When the HEAD commit has multiple tags, they are sorted in order to select the latest so that it can be released.

However, the existing sort would not work if there were multiple commits across a Wednesday and a Thursday.

For example:

```
git tag --points-at HEAD --format='%(creatordate)%09%(refname)'
Wed Jul 28 07:13:19 2021 +0000  refs/tags/v0.0.183
Thu Jul 29 13:31:07 2021 +0000  refs/tags/v0.0.184
Thu Jul 29 13:38:42 2021 +0000  refs/tags/v0.0.185
Thu Jul 29 13:57:44 2021 +0000  refs/tags/v0.0.186
```

When using the existing sort the `creatordate` field was targeted and reversed. Alphabetically Thursday comes before Wednesday, so that is reversed and the Wednesday release always comes first:
```
git tag --points-at HEAD --sort=-version:creatordate --format='%(creatordate)%09%(refname)'
Wed Jul 28 07:13:19 2021 +0000  refs/tags/v0.0.183
Thu Jul 29 13:57:44 2021 +0000  refs/tags/v0.0.186
Thu Jul 29 13:38:42 2021 +0000  refs/tags/v0.0.185
Thu Jul 29 13:31:07 2021 +0000  refs/tags/v0.0.184
```

This would make goreleaser attempt to release that existing tag again, and fail.

If we instead sort by reversed `refname` we get the tags ordered by their numeric value, which ignore the day of the week of release:
```
git tag --points-at HEAD --sort=-version:refname --format='%(creatordate)%09%(refname)'
Thu Jul 29 13:57:44 2021 +0000  refs/tags/v0.0.186
Thu Jul 29 13:38:42 2021 +0000  refs/tags/v0.0.185
Thu Jul 29 13:31:07 2021 +0000  refs/tags/v0.0.184
Wed Jul 28 07:13:19 2021 +0000  refs/tags/v0.0.183
```

Allowing the latest version, 0.0.186 in this case, to be targeted for release.

* corrected test case

* add space

* remove space
2021-08-02 16:20:09 +00:00

274 lines
7.5 KiB
Go

package git
import (
"os"
"os/exec"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/goreleaser/goreleaser/internal/testlib"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
)
func TestDescription(t *testing.T) {
require.NotEmpty(t, Pipe{}.String())
}
func TestNotAGitFolder(t *testing.T) {
testlib.Mktmp(t)
ctx := &context.Context{
Config: config.Project{},
}
require.EqualError(t, Pipe{}.Run(ctx), ErrNotRepository.Error())
}
func TestSingleCommit(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "commit1")
testlib.GitTag(t, "v0.0.1")
ctx := &context.Context{
Config: config.Project{},
}
require.NoError(t, Pipe{}.Run(ctx))
require.Equal(t, "v0.0.1", ctx.Git.CurrentTag)
}
func TestBranch(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "test-branch-commit")
testlib.GitTag(t, "test-branch-tag")
testlib.GitCheckoutBranch(t, "test-branch")
ctx := &context.Context{
Config: config.Project{},
}
require.NoError(t, Pipe{}.Run(ctx))
require.Equal(t, "test-branch", ctx.Git.Branch)
}
func TestNoRemote(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitCommit(t, "commit1")
testlib.GitTag(t, "v0.0.1")
ctx := &context.Context{
Config: config.Project{},
}
require.EqualError(t, Pipe{}.Run(ctx), "couldn't get remote URL: fatal: No remote configured to list refs from.")
}
func TestNewRepository(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
ctx := &context.Context{
Config: config.Project{},
}
// TODO: improve this error handling
require.Contains(t, Pipe{}.Run(ctx).Error(), `fatal: ambiguous argument 'HEAD'`)
}
// TestNoTagsNoSnapshot covers the situation where a repository
// only contains simple commits and no tags. In this case you have
// to set the --snapshot flag otherwise an error is returned.
func TestNoTagsNoSnapshot(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "first")
ctx := context.New(config.Project{})
ctx.Snapshot = false
require.EqualError(t, Pipe{}.Run(ctx), `git doesn't contain any tags. Either add a tag or use --snapshot`)
}
func TestDirty(t *testing.T) {
folder := testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
dummy, err := os.Create(filepath.Join(folder, "dummy"))
require.NoError(t, err)
require.NoError(t, dummy.Close())
testlib.GitAdd(t)
testlib.GitCommit(t, "commit2")
testlib.GitTag(t, "v0.0.1")
require.NoError(t, os.WriteFile(dummy.Name(), []byte("lorem ipsum"), 0o644))
t.Run("all checks up", func(t *testing.T) {
err := Pipe{}.Run(context.New(config.Project{}))
require.Error(t, err)
require.Contains(t, err.Error(), "git is currently in a dirty state")
})
t.Run("skip validate is set", func(t *testing.T) {
ctx := context.New(config.Project{})
ctx.SkipValidate = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
})
t.Run("snapshot", func(t *testing.T) {
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
})
}
func TestShallowClone(t *testing.T) {
folder := testlib.Mktmp(t)
require.NoError(
t,
exec.Command(
"git", "clone",
"--depth", "1",
"--branch", "v0.160.0",
"https://github.com/goreleaser/goreleaser",
folder,
).Run(),
)
t.Run("all checks up", func(t *testing.T) {
// its just a warning now
require.NoError(t, Pipe{}.Run(context.New(config.Project{})))
})
t.Run("skip validate is set", func(t *testing.T) {
ctx := context.New(config.Project{})
ctx.SkipValidate = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
})
t.Run("snapshot", func(t *testing.T) {
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
})
}
func TestTagIsNotLastCommit(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "commit3")
testlib.GitTag(t, "v0.0.1")
testlib.GitCommit(t, "commit4")
err := Pipe{}.Run(context.New(config.Project{}))
require.Error(t, err)
require.Contains(t, err.Error(), "git tag v0.0.1 was not made against commit")
}
func TestValidState(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "commit3")
testlib.GitTag(t, "v0.0.1")
testlib.GitCommit(t, "commit4")
testlib.GitTag(t, "v0.0.2")
ctx := context.New(config.Project{})
require.NoError(t, Pipe{}.Run(ctx))
require.Equal(t, "v0.0.2", ctx.Git.CurrentTag)
require.Equal(t, "git@github.com:foo/bar.git", ctx.Git.URL)
}
func TestSnapshotNoTags(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitAdd(t)
testlib.GitCommit(t, "whatever")
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
require.Equal(t, fakeInfo.CurrentTag, ctx.Git.CurrentTag)
}
func TestSnapshotNoCommits(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
require.Equal(t, fakeInfo, ctx.Git)
}
func TestSnapshotWithoutRepo(t *testing.T) {
testlib.Mktmp(t)
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
require.Equal(t, fakeInfo, ctx.Git)
}
func TestSnapshotDirty(t *testing.T) {
folder := testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitAdd(t)
testlib.GitCommit(t, "whatever")
testlib.GitTag(t, "v0.0.1")
require.NoError(t, os.WriteFile(filepath.Join(folder, "foo"), []byte("foobar"), 0o644))
ctx := context.New(config.Project{})
ctx.Snapshot = true
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
}
func TestGitNotInPath(t *testing.T) {
path := os.Getenv("PATH")
defer func() {
require.NoError(t, os.Setenv("PATH", path))
}()
require.NoError(t, os.Setenv("PATH", ""))
require.EqualError(t, Pipe{}.Run(context.New(config.Project{})), ErrNoGit.Error())
}
func TestTagFromCI(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommit(t, "commit1")
testlib.GitTag(t, "v0.0.1")
testlib.GitTag(t, "v0.0.2")
for _, tc := range []struct {
envs map[string]string
expected string
}{
{expected: "v0.0.2"},
{
envs: map[string]string{"GORELEASER_CURRENT_TAG": "v0.0.2"},
expected: "v0.0.2",
},
} {
for name, value := range tc.envs {
require.NoError(t, os.Setenv(name, value))
}
ctx := &context.Context{
Config: config.Project{},
}
require.NoError(t, Pipe{}.Run(ctx))
require.Equal(t, tc.expected, ctx.Git.CurrentTag)
for name := range tc.envs {
require.NoError(t, os.Setenv(name, ""))
}
}
}
func TestCommitDate(t *testing.T) {
// round to seconds since this is expressed in a Unix timestamp
commitDate := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second)
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:foo/bar.git")
testlib.GitCommitWithDate(t, "commit1", commitDate)
testlib.GitTag(t, "v0.0.1")
ctx := &context.Context{
Config: config.Project{},
}
require.NoError(t, Pipe{}.Run(ctx))
require.Equal(t, "v0.0.1", ctx.Git.CurrentTag)
require.True(t, commitDate.Equal(ctx.Git.CommitDate), "commit date does not match expected")
}