1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-04 03:11:55 +02:00
goreleaser/internal/exec/exec.go
2021-06-02 15:05:54 -03:00

181 lines
4.3 KiB
Go

// Package exec can execute commands on the OS.
package exec
import (
"fmt"
"os"
"os/exec"
"github.com/apex/log"
"github.com/caarlos0/go-shellwords"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/logext"
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/internal/semerrgroup"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
)
// Environment variables to pass through to exec
var passthroughEnvVars = []string{"HOME", "USER", "USERPROFILE", "TMPDIR", "TMP", "TEMP", "PATH"}
// Execute the given publisher
func Execute(ctx *context.Context, publishers []config.Publisher) error {
if ctx.SkipPublish {
return pipe.ErrSkipPublishEnabled
}
for _, p := range publishers {
log.WithField("name", p.Name).Debug("executing custom publisher")
err := executePublisher(ctx, p)
if err != nil {
return err
}
}
return nil
}
func executePublisher(ctx *context.Context, publisher config.Publisher) error {
log.Debugf("filtering %d artifacts", len(ctx.Artifacts.List()))
artifacts := filterArtifacts(ctx.Artifacts, publisher)
log.Debugf("will execute custom publisher with %d artifacts", len(artifacts))
g := semerrgroup.New(ctx.Parallelism)
for _, artifact := range artifacts {
artifact := artifact
g.Go(func() error {
c, err := resolveCommand(ctx, publisher, artifact)
if err != nil {
return err
}
return executeCommand(c)
})
}
return g.Wait()
}
func executeCommand(c *command) error {
log.WithField("args", c.Args).
WithField("env", c.Env).
Debug("executing command")
// nolint: gosec
cmd := exec.CommandContext(c.Ctx, c.Args[0], c.Args[1:]...)
cmd.Env = []string{}
for _, key := range passthroughEnvVars {
if value := os.Getenv(key); value != "" {
cmd.Env = append(cmd.Env, key+"="+value)
}
}
cmd.Env = append(cmd.Env, c.Env...)
if c.Dir != "" {
cmd.Dir = c.Dir
}
entry := log.WithField("cmd", c.Args[0])
cmd.Stderr = logext.NewErrWriter(entry)
cmd.Stdout = logext.NewWriter(entry)
entry.Info("publishing")
if err := cmd.Run(); err != nil {
return fmt.Errorf("publishing: %s failed: %w",
c.Args[0], err)
}
log.Debugf("command %s finished successfully", c.Args[0])
return nil
}
func filterArtifacts(artifacts artifact.Artifacts, publisher config.Publisher) []*artifact.Artifact {
filters := []artifact.Filter{
artifact.ByType(artifact.UploadableArchive),
artifact.ByType(artifact.UploadableFile),
artifact.ByType(artifact.LinuxPackage),
artifact.ByType(artifact.UploadableBinary),
}
if publisher.Checksum {
filters = append(filters, artifact.ByType(artifact.Checksum))
}
if publisher.Signature {
filters = append(filters, artifact.ByType(artifact.Signature))
}
filter := artifact.Or(filters...)
if len(publisher.IDs) > 0 {
filter = artifact.And(filter, artifact.ByIDs(publisher.IDs...))
}
return artifacts.Filter(filter).List()
}
type command struct {
Ctx *context.Context
Dir string
Env []string
Args []string
}
// resolveCommand returns the a command based on publisher template with replaced variables
// Those variables can be replaced by the given context, goos, goarch, goarm and more.
func resolveCommand(ctx *context.Context, publisher config.Publisher, artifact *artifact.Artifact) (*command, error) {
var err error
replacements := make(map[string]string)
// TODO: Replacements should be associated only with relevant artifacts/archives
archives := ctx.Config.Archives
if len(archives) > 0 {
replacements = archives[0].Replacements
}
dir := publisher.Dir
if dir != "" {
dir, err = tmpl.New(ctx).
WithArtifact(artifact, replacements).
Apply(dir)
if err != nil {
return nil, err
}
}
cmd := publisher.Cmd
if cmd != "" {
cmd, err = tmpl.New(ctx).
WithArtifact(artifact, replacements).
Apply(cmd)
if err != nil {
return nil, err
}
}
args, err := shellwords.Parse(cmd)
if err != nil {
return nil, err
}
env := make([]string, len(publisher.Env))
for i, e := range publisher.Env {
e, err = tmpl.New(ctx).
WithArtifact(artifact, replacements).
Apply(e)
if err != nil {
return nil, err
}
env[i] = e
}
return &command{
Ctx: ctx,
Dir: dir,
Env: env,
Args: args,
}, nil
}