mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-08 03:31:59 +02:00
18695c2687
* 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
198 lines
4.5 KiB
Go
198 lines
4.5 KiB
Go
package git
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/apex/log"
|
|
|
|
"github.com/goreleaser/goreleaser/internal/git"
|
|
"github.com/goreleaser/goreleaser/internal/pipe"
|
|
"github.com/goreleaser/goreleaser/pkg/context"
|
|
)
|
|
|
|
// Pipe that sets up git state.
|
|
type Pipe struct{}
|
|
|
|
func (Pipe) String() string {
|
|
return "getting and validating git state"
|
|
}
|
|
|
|
// Run the pipe.
|
|
func (Pipe) Run(ctx *context.Context) error {
|
|
if _, err := exec.LookPath("git"); err != nil {
|
|
return ErrNoGit
|
|
}
|
|
info, err := getInfo(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx.Git = info
|
|
log.WithField("commit", info.Commit).WithField("latest tag", info.CurrentTag).Info("building...")
|
|
ctx.Version = strings.TrimPrefix(ctx.Git.CurrentTag, "v")
|
|
return validate(ctx)
|
|
}
|
|
|
|
// nolint: gochecknoglobals
|
|
var fakeInfo = context.GitInfo{
|
|
Branch: "none",
|
|
CurrentTag: "v0.0.0",
|
|
Commit: "none",
|
|
ShortCommit: "none",
|
|
FullCommit: "none",
|
|
}
|
|
|
|
func getInfo(ctx *context.Context) (context.GitInfo, error) {
|
|
if !git.IsRepo() && ctx.Snapshot {
|
|
log.Warn("accepting to run without a git repo because this is a snapshot")
|
|
return fakeInfo, nil
|
|
}
|
|
if !git.IsRepo() {
|
|
return context.GitInfo{}, ErrNotRepository
|
|
}
|
|
info, err := getGitInfo()
|
|
if err != nil && ctx.Snapshot {
|
|
log.WithError(err).Warn("ignoring errors because this is a snapshot")
|
|
if info.Commit == "" {
|
|
info = fakeInfo
|
|
}
|
|
return info, nil
|
|
}
|
|
return info, err
|
|
}
|
|
|
|
func getGitInfo() (context.GitInfo, error) {
|
|
branch, err := getBranch()
|
|
if err != nil {
|
|
return context.GitInfo{}, fmt.Errorf("couldn't get current branch: %w", err)
|
|
}
|
|
short, err := getShortCommit()
|
|
if err != nil {
|
|
return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
|
|
}
|
|
full, err := getFullCommit()
|
|
if err != nil {
|
|
return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
|
|
}
|
|
date, err := getCommitDate()
|
|
if err != nil {
|
|
return context.GitInfo{}, fmt.Errorf("couldn't get commit date: %w", err)
|
|
}
|
|
url, err := getURL()
|
|
if err != nil {
|
|
return context.GitInfo{}, fmt.Errorf("couldn't get remote URL: %w", err)
|
|
}
|
|
tag, err := getTag()
|
|
if err != nil {
|
|
return context.GitInfo{
|
|
Branch: branch,
|
|
Commit: full,
|
|
FullCommit: full,
|
|
ShortCommit: short,
|
|
CommitDate: date,
|
|
URL: url,
|
|
CurrentTag: "v0.0.0",
|
|
}, ErrNoTag
|
|
}
|
|
return context.GitInfo{
|
|
Branch: branch,
|
|
CurrentTag: tag,
|
|
Commit: full,
|
|
FullCommit: full,
|
|
ShortCommit: short,
|
|
CommitDate: date,
|
|
URL: url,
|
|
}, nil
|
|
}
|
|
|
|
func validate(ctx *context.Context) error {
|
|
if ctx.Snapshot {
|
|
return pipe.ErrSnapshotEnabled
|
|
}
|
|
if ctx.SkipValidate {
|
|
return pipe.ErrSkipValidateEnabled
|
|
}
|
|
if _, err := os.Stat(".git/shallow"); err == nil {
|
|
log.Warn("running against a shallow clone - check your CI documentation at https://goreleaser.com/ci")
|
|
}
|
|
if err := CheckDirty(); err != nil {
|
|
return err
|
|
}
|
|
_, err := git.Clean(git.Run("describe", "--exact-match", "--tags", "--match", ctx.Git.CurrentTag))
|
|
if err != nil {
|
|
return ErrWrongRef{
|
|
commit: ctx.Git.Commit,
|
|
tag: ctx.Git.CurrentTag,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckDirty returns an error if the current git repository is dirty.
|
|
func CheckDirty() error {
|
|
out, err := git.Run("status", "--porcelain")
|
|
if strings.TrimSpace(out) != "" || err != nil {
|
|
return ErrDirty{status: out}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getBranch() (string, error) {
|
|
return git.Clean(git.Run("rev-parse", "--abbrev-ref", "HEAD", "--quiet"))
|
|
}
|
|
|
|
func getCommitDate() (time.Time, error) {
|
|
ct, err := git.Clean(git.Run("show", "--format='%ct'", "HEAD", "--quiet"))
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
if ct == "" {
|
|
return time.Time{}, nil
|
|
}
|
|
i, err := strconv.ParseInt(ct, 10, 64)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
t := time.Unix(i, 0).UTC()
|
|
return t, nil
|
|
}
|
|
|
|
func getShortCommit() (string, error) {
|
|
return git.Clean(git.Run("show", "--format='%h'", "HEAD", "--quiet"))
|
|
}
|
|
|
|
func getFullCommit() (string, error) {
|
|
return git.Clean(git.Run("show", "--format='%H'", "HEAD", "--quiet"))
|
|
}
|
|
|
|
func getTag() (string, error) {
|
|
var tag string
|
|
var err error
|
|
for _, fn := range []func() (string, error){
|
|
func() (string, error) {
|
|
return os.Getenv("GORELEASER_CURRENT_TAG"), nil
|
|
},
|
|
func() (string, error) {
|
|
return git.Clean(git.Run("tag", "--points-at", "HEAD", "--sort", "-version:refname"))
|
|
},
|
|
func() (string, error) {
|
|
return git.Clean(git.Run("describe", "--tags", "--abbrev=0"))
|
|
},
|
|
} {
|
|
tag, err = fn()
|
|
if tag != "" || err != nil {
|
|
return tag, err
|
|
}
|
|
}
|
|
|
|
return tag, err
|
|
}
|
|
|
|
func getURL() (string, error) {
|
|
return git.Clean(git.Run("ls-remote", "--get-url"))
|
|
}
|