mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-06 03:13:48 +02:00
ec2db4a727
<!-- Hi, thanks for contributing! Please make sure you read our CONTRIBUTING guide. Also, add tests and the respective documentation changes as well. --> <!-- If applied, this commit will... --> ... <!-- Why is this change being made? --> ... <!-- # Provide links to any relevant tickets, URLs or other resources --> ... --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
405 lines
9.6 KiB
Go
405 lines
9.6 KiB
Go
package aur
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/caarlos0/log"
|
|
"github.com/goreleaser/goreleaser/v2/internal/artifact"
|
|
"github.com/goreleaser/goreleaser/v2/internal/client"
|
|
"github.com/goreleaser/goreleaser/v2/internal/commitauthor"
|
|
"github.com/goreleaser/goreleaser/v2/internal/pipe"
|
|
"github.com/goreleaser/goreleaser/v2/internal/skips"
|
|
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/config"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/context"
|
|
)
|
|
|
|
const (
|
|
aurExtra = "AURConfig"
|
|
defaultCommitMsg = "Update to {{ .Tag }}"
|
|
)
|
|
|
|
var ErrNoArchivesFound = errors.New("no linux archives found")
|
|
|
|
// Pipe for arch linux's AUR pkgbuild.
|
|
type Pipe struct{}
|
|
|
|
func (Pipe) String() string { return "arch user repositories" }
|
|
func (Pipe) ContinueOnError() bool { return true }
|
|
func (Pipe) Skip(ctx *context.Context) bool {
|
|
return skips.Any(ctx, skips.AUR) || len(ctx.Config.AURs) == 0
|
|
}
|
|
|
|
func (Pipe) Default(ctx *context.Context) error {
|
|
for i := range ctx.Config.AURs {
|
|
pkg := &ctx.Config.AURs[i]
|
|
|
|
pkg.CommitAuthor = commitauthor.Default(pkg.CommitAuthor)
|
|
if pkg.CommitMessageTemplate == "" {
|
|
pkg.CommitMessageTemplate = defaultCommitMsg
|
|
}
|
|
if pkg.Name == "" {
|
|
pkg.Name = ctx.Config.ProjectName
|
|
}
|
|
if !strings.HasSuffix(pkg.Name, "-bin") {
|
|
pkg.Name += "-bin"
|
|
}
|
|
if len(pkg.Conflicts) == 0 {
|
|
pkg.Conflicts = []string{ctx.Config.ProjectName}
|
|
}
|
|
if len(pkg.Provides) == 0 {
|
|
pkg.Provides = []string{ctx.Config.ProjectName}
|
|
}
|
|
if pkg.Rel == "" {
|
|
pkg.Rel = "1"
|
|
}
|
|
if pkg.Goamd64 == "" {
|
|
pkg.Goamd64 = "v1"
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (Pipe) Run(ctx *context.Context) error {
|
|
cli, err := client.NewReleaseClient(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return runAll(ctx, cli)
|
|
}
|
|
|
|
func runAll(ctx *context.Context, cli client.ReleaseURLTemplater) error {
|
|
for _, aur := range ctx.Config.AURs {
|
|
err := doRun(ctx, aur, cli)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doRun(ctx *context.Context, aur config.AUR, cl client.ReleaseURLTemplater) error {
|
|
if err := tmpl.New(ctx).ApplyAll(
|
|
&aur.Name,
|
|
&aur.Directory,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
filters := []artifact.Filter{
|
|
artifact.ByGoos("linux"),
|
|
artifact.Or(
|
|
artifact.And(
|
|
artifact.ByGoarch("amd64"),
|
|
artifact.ByGoamd64(aur.Goamd64),
|
|
),
|
|
artifact.ByGoarch("arm64"),
|
|
artifact.ByGoarch("386"),
|
|
artifact.And(
|
|
artifact.ByGoarch("arm"),
|
|
artifact.Or(
|
|
artifact.ByGoarm("7"),
|
|
),
|
|
),
|
|
),
|
|
artifact.Or(
|
|
artifact.ByType(artifact.UploadableArchive),
|
|
artifact.ByType(artifact.UploadableBinary),
|
|
),
|
|
}
|
|
if len(aur.IDs) > 0 {
|
|
filters = append(filters, artifact.ByIDs(aur.IDs...))
|
|
}
|
|
|
|
archives := ctx.Artifacts.Filter(artifact.And(filters...)).List()
|
|
if len(archives) == 0 {
|
|
return ErrNoArchivesFound
|
|
}
|
|
|
|
pkg, err := tmpl.New(ctx).Apply(aur.Package)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(pkg) == "" {
|
|
art := archives[0]
|
|
switch art.Type {
|
|
case artifact.UploadableBinary:
|
|
name := art.Name
|
|
bin := artifact.ExtraOr(*art, artifact.ExtraBinary, art.Name)
|
|
pkg = fmt.Sprintf(`install -Dm755 "./%s "${pkgdir}/usr/bin/%s"`, name, bin)
|
|
case artifact.UploadableArchive:
|
|
folder := artifact.ExtraOr(*art, artifact.ExtraWrappedIn, ".")
|
|
for _, bin := range artifact.ExtraOr(*art, artifact.ExtraBinaries, []string{}) {
|
|
path := filepath.ToSlash(filepath.Clean(filepath.Join(folder, bin)))
|
|
pkg = fmt.Sprintf(`install -Dm755 "./%s" "${pkgdir}/usr/bin/%s"`, path, bin)
|
|
break
|
|
}
|
|
}
|
|
log.Warnf("guessing package to be %q", pkg)
|
|
}
|
|
aur.Package = pkg
|
|
|
|
for _, info := range []struct {
|
|
name, tpl, ext string
|
|
kind artifact.Type
|
|
}{
|
|
{
|
|
name: "PKGBUILD",
|
|
tpl: aurTemplateData,
|
|
ext: ".pkgbuild",
|
|
kind: artifact.PkgBuild,
|
|
},
|
|
{
|
|
name: ".SRCINFO",
|
|
tpl: srcInfoTemplate,
|
|
ext: ".srcinfo",
|
|
kind: artifact.SrcInfo,
|
|
},
|
|
} {
|
|
pkgContent, err := buildPkgFile(ctx, aur, cl, archives, info.tpl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
path := filepath.Join(ctx.Config.Dist, "aur", aur.Name+info.ext)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return fmt.Errorf("failed to write %s: %w", info.kind, err)
|
|
}
|
|
log.WithField("file", path).Info("writing")
|
|
if err := os.WriteFile(path, []byte(pkgContent), 0o644); err != nil { //nolint: gosec
|
|
return fmt.Errorf("failed to write %s: %w", info.kind, err)
|
|
}
|
|
|
|
ctx.Artifacts.Add(&artifact.Artifact{
|
|
Name: info.name,
|
|
Path: path,
|
|
Type: info.kind,
|
|
Extra: map[string]interface{}{
|
|
aurExtra: aur,
|
|
artifact.ExtraID: aur.Name,
|
|
},
|
|
})
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func buildPkgFile(ctx *context.Context, pkg config.AUR, client client.ReleaseURLTemplater, artifacts []*artifact.Artifact, tpl string) (string, error) {
|
|
data, err := dataFor(ctx, pkg, client, artifacts)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return applyTemplate(ctx, tpl, data)
|
|
}
|
|
|
|
func fixLines(s string) string {
|
|
lines := strings.Split(s, "\n")
|
|
var result []string
|
|
for _, line := range lines {
|
|
l := strings.TrimSpace(line)
|
|
if l == "" {
|
|
result = append(result, "")
|
|
continue
|
|
}
|
|
result = append(result, " "+l)
|
|
}
|
|
return strings.Join(result, "\n")
|
|
}
|
|
|
|
func applyTemplate(ctx *context.Context, tpl string, data templateData) (string, error) {
|
|
t := template.Must(
|
|
template.New(data.Name).
|
|
Funcs(template.FuncMap{
|
|
"fixLines": fixLines,
|
|
"pkgArray": toPkgBuildArray,
|
|
}).
|
|
Parse(tpl),
|
|
)
|
|
|
|
var out bytes.Buffer
|
|
if err := t.Execute(&out, data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
content, err := tmpl.New(ctx).Apply(out.String())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out.Reset()
|
|
|
|
// Sanitize the template output and get rid of trailing whitespace.
|
|
var (
|
|
r = strings.NewReader(content)
|
|
s = bufio.NewScanner(r)
|
|
)
|
|
for s.Scan() {
|
|
l := strings.TrimRight(s.Text(), " ")
|
|
_, _ = out.WriteString(l)
|
|
_ = out.WriteByte('\n')
|
|
}
|
|
if err := s.Err(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return out.String(), nil
|
|
}
|
|
|
|
func toPkgBuildArray(ss []string) string {
|
|
result := make([]string, 0, len(ss))
|
|
for _, s := range ss {
|
|
result = append(result, fmt.Sprintf("'%s'", s))
|
|
}
|
|
return strings.Join(result, " ")
|
|
}
|
|
|
|
func toPkgBuildArch(arch string) string {
|
|
switch arch {
|
|
case "amd64":
|
|
return "x86_64"
|
|
case "386":
|
|
return "i686"
|
|
case "arm64":
|
|
return "aarch64"
|
|
case "arm6":
|
|
return "armv6h"
|
|
case "arm7":
|
|
return "armv7h"
|
|
default:
|
|
return "invalid" // should never get here
|
|
}
|
|
}
|
|
|
|
func dataFor(ctx *context.Context, cfg config.AUR, cl client.ReleaseURLTemplater, artifacts []*artifact.Artifact) (templateData, error) {
|
|
result := templateData{
|
|
Name: cfg.Name,
|
|
Desc: cfg.Description,
|
|
Homepage: cfg.Homepage,
|
|
Version: fmt.Sprintf("%d.%d.%d", ctx.Semver.Major, ctx.Semver.Minor, ctx.Semver.Patch),
|
|
License: cfg.License,
|
|
Rel: cfg.Rel,
|
|
Maintainers: cfg.Maintainers,
|
|
Contributors: cfg.Contributors,
|
|
Provides: cfg.Provides,
|
|
Conflicts: cfg.Conflicts,
|
|
Backup: cfg.Backup,
|
|
Depends: cfg.Depends,
|
|
OptDepends: cfg.OptDepends,
|
|
Package: cfg.Package,
|
|
}
|
|
|
|
for _, art := range artifacts {
|
|
sum, err := art.Checksum("sha256")
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
|
|
if cfg.URLTemplate == "" {
|
|
url, err := cl.ReleaseURLTemplate(ctx)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
cfg.URLTemplate = url
|
|
}
|
|
url, err := tmpl.New(ctx).WithArtifact(art).Apply(cfg.URLTemplate)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
|
|
releasePackage := releasePackage{
|
|
DownloadURL: url,
|
|
SHA256: sum,
|
|
Arch: toPkgBuildArch(art.Goarch + art.Goarm),
|
|
Format: artifact.ExtraOr(*art, artifact.ExtraFormat, ""),
|
|
}
|
|
result.ReleasePackages = append(result.ReleasePackages, releasePackage)
|
|
result.Arches = append(result.Arches, releasePackage.Arch)
|
|
}
|
|
|
|
sort.Strings(result.Arches)
|
|
sort.Slice(result.ReleasePackages, func(i, j int) bool {
|
|
return result.ReleasePackages[i].Arch < result.ReleasePackages[j].Arch
|
|
})
|
|
return result, nil
|
|
}
|
|
|
|
// Publish the PKGBUILD and .SRCINFO files to the AUR repository.
|
|
func (Pipe) Publish(ctx *context.Context) error {
|
|
skips := pipe.SkipMemento{}
|
|
for _, pkgs := range ctx.Artifacts.Filter(
|
|
artifact.Or(
|
|
artifact.ByType(artifact.PkgBuild),
|
|
artifact.ByType(artifact.SrcInfo),
|
|
),
|
|
).GroupByID() {
|
|
err := doPublish(ctx, pkgs)
|
|
if err != nil && pipe.IsSkip(err) {
|
|
skips.Remember(err)
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return skips.Evaluate()
|
|
}
|
|
|
|
func doPublish(ctx *context.Context, pkgs []*artifact.Artifact) error {
|
|
cfg, err := artifact.Extra[config.AUR](*pkgs[0], aurExtra)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if strings.TrimSpace(cfg.SkipUpload) == "true" {
|
|
return pipe.Skip("aur.skip_upload is set")
|
|
}
|
|
|
|
if strings.TrimSpace(cfg.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" {
|
|
return pipe.Skip("prerelease detected with 'auto' upload, skipping aur publish")
|
|
}
|
|
|
|
author, err := commitauthor.Get(ctx, cfg.CommitAuthor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := tmpl.New(ctx).Apply(cfg.CommitMessageTemplate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cli := client.NewGitUploadClient("master")
|
|
repo := client.RepoFromRef(config.RepoRef{
|
|
Git: config.GitRepoRef{
|
|
PrivateKey: cfg.PrivateKey,
|
|
URL: cfg.GitURL,
|
|
SSHCommand: cfg.GitSSHCommand,
|
|
},
|
|
Name: fmt.Sprintf("%x", sha256.Sum256([]byte(cfg.GitURL))),
|
|
})
|
|
|
|
files := make([]client.RepoFile, 0, len(pkgs))
|
|
for _, pkg := range pkgs {
|
|
content, err := os.ReadFile(pkg.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files = append(files, client.RepoFile{
|
|
Path: path.Join(cfg.Directory, pkg.Name),
|
|
Content: content,
|
|
})
|
|
}
|
|
return cli.CreateFiles(ctx, author, repo, msg, files)
|
|
}
|