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