1//+build mage
2
3// This is the build script for Mage. The install target is all you really need.
4// The release target is for generating official releases and is really only
5// useful to project admins.
6package main
7
8import (
9	"errors"
10	"fmt"
11	"os"
12	"path/filepath"
13	"regexp"
14	"runtime"
15	"strings"
16	"time"
17
18	"github.com/magefile/mage/mg"
19	"github.com/magefile/mage/sh"
20)
21
22var Aliases = map[string]interface{}{
23	"Speak": Say,
24}
25
26// Say says something.
27func Say(msg string, i int, b bool, d time.Duration) error {
28	_, err := fmt.Printf("%v(%T) %v(%T) %v(%T) %v(%T)\n", msg, msg, i, i, b, b, d, d)
29	return err
30}
31
32// Runs "go install" for mage.  This generates the version info the binary.
33func Install() error {
34	name := "mage"
35	if runtime.GOOS == "windows" {
36		name += ".exe"
37	}
38
39	gocmd := mg.GoCmd()
40	// use GOBIN if set in the environment, otherwise fall back to first path
41	// in GOPATH environment string
42	bin, err := sh.Output(gocmd, "env", "GOBIN")
43	if err != nil {
44		return fmt.Errorf("can't determine GOBIN: %v", err)
45	}
46	if bin == "" {
47		gopath, err := sh.Output(gocmd, "env", "GOPATH")
48		if err != nil {
49			return fmt.Errorf("can't determine GOPATH: %v", err)
50		}
51		paths := strings.Split(gopath, string([]rune{os.PathListSeparator}))
52		bin = filepath.Join(paths[0], "bin")
53	}
54	// specifically don't mkdirall, if you have an invalid gopath in the first
55	// place, that's not on us to fix.
56	if err := os.Mkdir(bin, 0700); err != nil && !os.IsExist(err) {
57		return fmt.Errorf("failed to create %q: %v", bin, err)
58	}
59	path := filepath.Join(bin, name)
60
61	// we use go build here because if someone built with go get, then `go
62	// install` turns into a no-op, and `go install -a` fails on people's
63	// machines that have go installed in a non-writeable directory (such as
64	// normal OS installs in /usr/bin)
65	return sh.RunV(gocmd, "build", "-o", path, "-ldflags="+flags(), "github.com/magefile/mage")
66}
67
68var releaseTag = regexp.MustCompile(`^v1\.[0-9]+\.[0-9]+$`)
69
70// Generates a new release. Expects a version tag in v1.x.x format.
71func Release(tag string) (err error) {
72	if !releaseTag.MatchString(tag) {
73		return errors.New("TAG environment variable must be in semver v1.x.x format, but was " + tag)
74	}
75
76	if err := sh.RunV("git", "tag", "-a", tag, "-m", tag); err != nil {
77		return err
78	}
79	if err := sh.RunV("git", "push", "origin", tag); err != nil {
80		return err
81	}
82	defer func() {
83		if err != nil {
84			sh.RunV("git", "tag", "--delete", "$TAG")
85			sh.RunV("git", "push", "--delete", "origin", "$TAG")
86		}
87	}()
88	return sh.RunV("goreleaser")
89}
90
91// Remove the temporarily generated files from Release.
92func Clean() error {
93	return sh.Rm("dist")
94}
95
96func flags() string {
97	timestamp := time.Now().Format(time.RFC3339)
98	hash := hash()
99	tag := tag()
100	if tag == "" {
101		tag = "dev"
102	}
103	return fmt.Sprintf(`-X "github.com/magefile/mage/mage.timestamp=%s" -X "github.com/magefile/mage/mage.commitHash=%s" -X "github.com/magefile/mage/mage.gitTag=%s"`, timestamp, hash, tag)
104}
105
106// tag returns the git tag for the current branch or "" if none.
107func tag() string {
108	s, _ := sh.Output("git", "describe", "--tags")
109	return s
110}
111
112// hash returns the git hash for the current repo or "" if none.
113func hash() string {
114	hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
115	return hash
116}
117