github.1git.de/goreleaser/goreleaser@v0.92.0/internal/pipe/brew/brew.go (about) 1 package brew 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "path/filepath" 9 "strings" 10 "text/template" 11 12 "github.com/apex/log" 13 "github.com/goreleaser/goreleaser/internal/artifact" 14 "github.com/goreleaser/goreleaser/internal/client" 15 "github.com/goreleaser/goreleaser/internal/pipe" 16 "github.com/goreleaser/goreleaser/internal/tmpl" 17 "github.com/goreleaser/goreleaser/pkg/config" 18 "github.com/goreleaser/goreleaser/pkg/context" 19 ) 20 21 // ErrNoDarwin64Build when there is no build for darwin_amd64 22 var ErrNoDarwin64Build = errors.New("brew tap requires one darwin amd64 build") 23 24 // ErrTooManyDarwin64Builds when there are too many builds for darwin_amd64 25 var ErrTooManyDarwin64Builds = errors.New("brew tap requires at most one darwin amd64 build") 26 27 // Pipe for brew deployment 28 type Pipe struct{} 29 30 func (Pipe) String() string { 31 return "homebrew formula" 32 } 33 34 // Publish brew formula 35 func (Pipe) Publish(ctx *context.Context) error { 36 client, err := client.NewGitHub(ctx) 37 if err != nil { 38 return err 39 } 40 return doRun(ctx, client) 41 } 42 43 // Default sets the pipe defaults 44 func (Pipe) Default(ctx *context.Context) error { 45 if ctx.Config.Brew.Install == "" { 46 var installs []string 47 for _, build := range ctx.Config.Builds { 48 if !isBrewBuild(build) { 49 continue 50 } 51 installs = append( 52 installs, 53 fmt.Sprintf(`bin.install "%s"`, build.Binary), 54 ) 55 } 56 ctx.Config.Brew.Install = strings.Join(installs, "\n") 57 } 58 59 if ctx.Config.Brew.CommitAuthor.Name == "" { 60 ctx.Config.Brew.CommitAuthor.Name = "goreleaserbot" 61 } 62 if ctx.Config.Brew.CommitAuthor.Email == "" { 63 ctx.Config.Brew.CommitAuthor.Email = "goreleaser@carlosbecker.com" 64 } 65 if ctx.Config.Brew.Name == "" { 66 ctx.Config.Brew.Name = ctx.Config.ProjectName 67 } 68 return nil 69 } 70 71 func isBrewBuild(build config.Build) bool { 72 for _, ignore := range build.Ignore { 73 if ignore.Goos == "darwin" && ignore.Goarch == "amd64" { 74 return false 75 } 76 } 77 return contains(build.Goos, "darwin") && contains(build.Goarch, "amd64") 78 } 79 80 func contains(ss []string, s string) bool { 81 for _, zs := range ss { 82 if zs == s { 83 return true 84 } 85 } 86 return false 87 } 88 89 func doRun(ctx *context.Context, client client.Client) error { 90 if ctx.Config.Brew.GitHub.Name == "" { 91 return pipe.Skip("brew section is not configured") 92 } 93 if getFormat(ctx) == "binary" { 94 return pipe.Skip("archive format is binary") 95 } 96 97 var archives = ctx.Artifacts.Filter( 98 artifact.And( 99 artifact.ByGoos("darwin"), 100 artifact.ByGoarch("amd64"), 101 artifact.ByGoarm(""), 102 artifact.ByType(artifact.UploadableArchive), 103 ), 104 ).List() 105 if len(archives) == 0 { 106 return ErrNoDarwin64Build 107 } 108 if len(archives) > 1 { 109 return ErrTooManyDarwin64Builds 110 } 111 112 content, err := buildFormula(ctx, archives[0]) 113 if err != nil { 114 return err 115 } 116 117 var filename = ctx.Config.Brew.Name + ".rb" 118 var path = filepath.Join(ctx.Config.Dist, filename) 119 log.WithField("formula", path).Info("writing") 120 if err := ioutil.WriteFile(path, content.Bytes(), 0644); err != nil { 121 return err 122 } 123 124 if ctx.Config.Brew.SkipUpload { 125 return pipe.Skip("brew.skip_upload is set") 126 } 127 if ctx.SkipPublish { 128 return pipe.ErrSkipPublishEnabled 129 } 130 if ctx.Config.Release.Draft { 131 return pipe.Skip("release is marked as draft") 132 } 133 134 path = filepath.Join(ctx.Config.Brew.Folder, filename) 135 log.WithField("formula", path). 136 WithField("repo", ctx.Config.Brew.GitHub.String()). 137 Info("pushing") 138 139 var msg = fmt.Sprintf("Brew formula update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag) 140 return client.CreateFile(ctx, ctx.Config.Brew.CommitAuthor, ctx.Config.Brew.GitHub, content, path, msg) 141 } 142 143 func getFormat(ctx *context.Context) string { 144 for _, override := range ctx.Config.Archive.FormatOverrides { 145 if strings.HasPrefix("darwin", override.Goos) { 146 return override.Format 147 } 148 } 149 return ctx.Config.Archive.Format 150 } 151 152 func buildFormula(ctx *context.Context, artifact artifact.Artifact) (bytes.Buffer, error) { 153 data, err := dataFor(ctx, artifact) 154 if err != nil { 155 return bytes.Buffer{}, err 156 } 157 return doBuildFormula(data) 158 } 159 160 func doBuildFormula(data templateData) (out bytes.Buffer, err error) { 161 t, err := template.New(data.Name).Parse(formulaTemplate) 162 if err != nil { 163 return out, err 164 } 165 err = t.Execute(&out, data) 166 return 167 } 168 169 func dataFor(ctx *context.Context, artifact artifact.Artifact) (result templateData, err error) { 170 sum, err := artifact.Checksum() 171 if err != nil { 172 return 173 } 174 var cfg = ctx.Config.Brew 175 176 if ctx.Config.Brew.URLTemplate == "" { 177 ctx.Config.Brew.URLTemplate = fmt.Sprintf("%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}", 178 ctx.Config.GitHubURLs.Download, 179 ctx.Config.Release.GitHub.Owner, 180 ctx.Config.Release.GitHub.Name) 181 } 182 url, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).Apply(ctx.Config.Brew.URLTemplate) 183 if err != nil { 184 return 185 } 186 187 return templateData{ 188 Name: formulaNameFor(ctx.Config.Brew.Name), 189 DownloadURL: url, 190 Desc: cfg.Description, 191 Homepage: cfg.Homepage, 192 Version: ctx.Version, 193 Caveats: split(cfg.Caveats), 194 SHA256: sum, 195 Dependencies: cfg.Dependencies, 196 Conflicts: cfg.Conflicts, 197 Plist: cfg.Plist, 198 Install: split(cfg.Install), 199 Tests: split(cfg.Test), 200 DownloadStrategy: cfg.DownloadStrategy, 201 }, nil 202 } 203 204 func split(s string) []string { 205 strings := strings.Split(strings.TrimSpace(s), "\n") 206 if len(strings) == 1 && strings[0] == "" { 207 return []string{} 208 } 209 return strings 210 } 211 212 func formulaNameFor(name string) string { 213 name = strings.Replace(name, "-", " ", -1) 214 name = strings.Replace(name, "_", " ", -1) 215 return strings.Replace(strings.Title(name), " ", "", -1) 216 }