Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ builds:
archives:
-
name_template: >-
{{.Binary}}_{{.Version}}_{{.Os}}_{{.Arch}}
{{.Binary}}_{{.Version}}_
{{- if eq .Os "darwin"}}macOS
{{- else if eq .Os "linux"}}Linux
{{- else if eq .Os "windows"}}Windows
Expand Down
45 changes: 36 additions & 9 deletions magefiles/targets/targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,25 @@ func Install() error {
var releaseTag = regexp.MustCompile(`^v1\.\d+\.\d+$`)

// Release generates a new release. Expects a version tag in v1.x.x format.
func Release(tag string) (err error) {
mg.Deps(Tools)
// If dryRun is true, it runs goreleaser in snapshot mode without tagging or
// publishing, which can be used to verify the release artifacts.
func Release(tag string, dryRun *bool) (err error) {
if err := installTool("goreleaser"); err != nil {
return err
}

if !releaseTag.MatchString(tag) {
return errors.New("TAG environment variable must be in semver v1.x.x format, but was " + tag)
}

if dryRun != nil && *dryRun {
if err := sh.RunV("git", "tag", "-a", tag, "-m", tag); err != nil {
return err
}
defer func() { _ = sh.RunV("git", "tag", "--delete", tag) }()
return sh.RunV("goreleaser", "release", "--skip=publish", "--skip=validate")
}

if err := sh.RunV("git", "tag", "-a", tag, "-m", tag); err != nil {
return err
}
Expand All @@ -73,31 +85,46 @@ func Release(tag string) (err error) {
_ = sh.RunV("git", "push", "--delete", "origin", tag)
}
}()
return sh.RunV("goreleaser", "release")
return sh.RunV("goreleaser", "release", "--clean")
}

// Clean removes the temporarily generated files from Release.
func Clean() error {
return sh.Rm("dist")
}

var goTools = []string{
"github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.2",
"github.com/goreleaser/goreleaser/v2@v2.14.3",
var goTools = map[string]string{
"lint": "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.2",
"goreleaser": "github.com/goreleaser/goreleaser/v2@v2.14.3",
}

// Tools installs the dev tools used by mage, such as golangci-lint.
func Tools() error {
for _, tool := range goTools {
if err := sh.Run("go", "install", tool); err != nil {
return fmt.Errorf("failed to install %s: %w", tool, err)
if err := installTool(tool); err != nil {
return err
}
}
return nil
}

func installTool(tool string) error {
version, ok := goTools[tool]
if !ok {
return fmt.Errorf("unknown tool %q", tool)
}
if err := sh.Run("go", "install", version); err != nil {
return fmt.Errorf("failed to install %s: %w", version, err)
}
return nil
}

// Lint runs golangci-lint on the codebase.
func Lint() error {
mg.Deps(Tools)
err := installTool("lint")
if err != nil {
return err
}

return sh.RunV("golangci-lint", "run")
}
102 changes: 102 additions & 0 deletions magefiles/targets/targets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package targets

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)

// expectedReleaseFiles lists the release artifacts goreleaser should produce.
// Each entry uses %s as a placeholder for the version string.
var expectedReleaseFiles = []string{
"mage_%s_checksums.txt",
"mage_%s_DragonFlyBSD-64bit.tar.gz",
"mage_%s_FreeBSD-64bit.tar.gz",
"mage_%s_FreeBSD-ARM.tar.gz",
"mage_%s_FreeBSD-ARM64.tar.gz",
"mage_%s_Linux-64bit.tar.gz",
"mage_%s_Linux-ARM.tar.gz",
"mage_%s_Linux-ARM64.tar.gz",
"mage_%s_macOS-64bit.tar.gz",
"mage_%s_macOS-ARM64.tar.gz",
"mage_%s_NetBSD-64bit.tar.gz",
"mage_%s_NetBSD-ARM.tar.gz",
"mage_%s_NetBSD-ARM64.tar.gz",
"mage_%s_OpenBSD-64bit.tar.gz",
"mage_%s_OpenBSD-ARM64.tar.gz",
"mage_%s_Windows-64bit.zip",
"mage_%s_Windows-ARM64.zip",
}

func TestRelease(t *testing.T) {
if testing.Short() {
t.Skip("skipping release test in short mode")
}

// goreleaser must run from the repo root where .goreleaser.yml lives.
repoRoot, err := filepath.Abs(filepath.Join("..", ".."))
if err != nil {
t.Fatal(err)
}
origDir, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(repoRoot); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
err := os.RemoveAll("../../dist")
if err != nil {
t.Fatal(err)
}
})
t.Cleanup(func() { _ = os.Chdir(origDir) })

version := "v1.0.99"

dryRun := true
if err := Release(version, &dryRun); err != nil {
t.Fatal(err)
}

entries, err := os.ReadDir("dist")
if err != nil {
t.Fatal(err)
}

releaserVer := strings.TrimPrefix(version, "v")

// Build expected set, initially marking each as not found.
expected := make(map[string]bool, len(expectedReleaseFiles))
for _, pattern := range expectedReleaseFiles {
expected[fmt.Sprintf(pattern, releaserVer)] = false
}

// Walk dist/ and match release artifacts.
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
isArtifact := strings.HasSuffix(name, ".tar.gz") ||
strings.HasSuffix(name, ".zip") ||
strings.HasSuffix(name, "_checksums.txt")
if !isArtifact {
continue
}
if _, ok := expected[name]; ok {
expected[name] = true
} else {
t.Errorf("unexpected release artifact: %s", name)
}
}

for name, found := range expected {
if !found {
t.Errorf("expected release artifact not found: %s", name)
}
}
}
Loading