2021-06-02 20:05:54 +02:00
|
|
|
// Package exec can execute commands on the OS.
|
2020-05-10 18:03:49 +02:00
|
|
|
package exec
|
|
|
|
|
|
|
|
import (
|
2021-07-25 02:32:04 +02:00
|
|
|
"bytes"
|
2020-05-10 18:03:49 +02:00
|
|
|
"fmt"
|
2021-07-25 02:32:04 +02:00
|
|
|
"io"
|
2021-06-02 20:05:54 +02:00
|
|
|
"os"
|
2020-05-10 18:03:49 +02:00
|
|
|
"os/exec"
|
|
|
|
|
2021-03-22 13:45:18 +02:00
|
|
|
"github.com/caarlos0/go-shellwords"
|
2022-06-22 02:11:15 +02:00
|
|
|
"github.com/caarlos0/log"
|
2024-05-26 20:02:57 +02:00
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/artifact"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/extrafiles"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/gio"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/logext"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/pipe"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/semerrgroup"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/pkg/config"
|
|
|
|
"github.com/goreleaser/goreleaser/v2/pkg/context"
|
2020-05-10 18:03:49 +02:00
|
|
|
)
|
|
|
|
|
2021-06-02 20:05:54 +02:00
|
|
|
// Environment variables to pass through to exec
|
|
|
|
var passthroughEnvVars = []string{"HOME", "USER", "USERPROFILE", "TMPDIR", "TMP", "TEMP", "PATH"}
|
|
|
|
|
|
|
|
// Execute the given publisher
|
2020-05-10 18:03:49 +02:00
|
|
|
func Execute(ctx *context.Context, publishers []config.Publisher) error {
|
2023-08-09 03:43:09 +02:00
|
|
|
skips := pipe.SkipMemento{}
|
2020-05-10 18:03:49 +02:00
|
|
|
for _, p := range publishers {
|
|
|
|
log.WithField("name", p.Name).Debug("executing custom publisher")
|
|
|
|
err := executePublisher(ctx, p)
|
2023-08-09 03:43:09 +02:00
|
|
|
if err != nil && pipe.IsSkip(err) {
|
|
|
|
skips.Remember(err)
|
|
|
|
continue
|
|
|
|
}
|
2020-05-10 18:03:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-08-09 03:43:09 +02:00
|
|
|
return skips.Evaluate()
|
2020-05-10 18:03:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func executePublisher(ctx *context.Context, publisher config.Publisher) error {
|
2023-08-09 03:43:09 +02:00
|
|
|
disabled, err := tmpl.New(ctx).Bool(publisher.Disable)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if disabled {
|
|
|
|
return pipe.Skip("publisher is disabled")
|
|
|
|
}
|
|
|
|
|
2020-05-10 18:03:49 +02:00
|
|
|
log.Debugf("filtering %d artifacts", len(ctx.Artifacts.List()))
|
|
|
|
artifacts := filterArtifacts(ctx.Artifacts, publisher)
|
2022-01-07 03:04:43 +02:00
|
|
|
|
|
|
|
extraFiles, err := extrafiles.Find(ctx, publisher.ExtraFiles)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, path := range extraFiles {
|
|
|
|
artifacts = append(artifacts, &artifact.Artifact{
|
|
|
|
Name: name,
|
|
|
|
Path: path,
|
|
|
|
Type: artifact.UploadableFile,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-10 18:03:49 +02:00
|
|
|
log.Debugf("will execute custom publisher with %d artifacts", len(artifacts))
|
|
|
|
|
2021-03-22 13:45:18 +02:00
|
|
|
g := semerrgroup.New(ctx.Parallelism)
|
2020-05-10 18:03:49 +02:00
|
|
|
for _, artifact := range artifacts {
|
|
|
|
g.Go(func() error {
|
|
|
|
c, err := resolveCommand(ctx, publisher, artifact)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-07-26 14:04:50 +02:00
|
|
|
return executeCommand(c, artifact)
|
2020-05-10 18:03:49 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return g.Wait()
|
|
|
|
}
|
|
|
|
|
2021-07-26 14:04:50 +02:00
|
|
|
func executeCommand(c *command, artifact *artifact.Artifact) error {
|
2020-05-10 18:03:49 +02:00
|
|
|
log.WithField("args", c.Args).
|
2021-07-26 14:04:50 +02:00
|
|
|
WithField("artifact", artifact.Name).
|
2020-05-10 18:03:49 +02:00
|
|
|
Debug("executing command")
|
|
|
|
|
2024-05-12 19:11:11 +02:00
|
|
|
//nolint:gosec
|
2021-03-22 13:45:18 +02:00
|
|
|
cmd := exec.CommandContext(c.Ctx, c.Args[0], c.Args[1:]...)
|
2021-06-02 20:05:54 +02:00
|
|
|
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...)
|
|
|
|
|
2020-05-10 18:03:49 +02:00
|
|
|
if c.Dir != "" {
|
|
|
|
cmd.Dir = c.Dir
|
|
|
|
}
|
|
|
|
|
2021-07-25 02:32:04 +02:00
|
|
|
var b bytes.Buffer
|
|
|
|
w := gio.Safe(&b)
|
2023-02-02 04:25:36 +02:00
|
|
|
cmd.Stderr = io.MultiWriter(logext.NewWriter(), w)
|
|
|
|
cmd.Stdout = io.MultiWriter(logext.NewWriter(), w)
|
2020-05-10 18:03:49 +02:00
|
|
|
|
2023-05-02 14:06:35 +02:00
|
|
|
log := log.WithField("cmd", c.Args[0]).
|
|
|
|
WithField("artifact", artifact.Name)
|
|
|
|
|
|
|
|
log.Info("publishing")
|
2020-05-10 18:03:49 +02:00
|
|
|
if err := cmd.Run(); err != nil {
|
2021-07-25 02:32:04 +02:00
|
|
|
return fmt.Errorf("publishing: %s failed: %w: %s", c.Args[0], err, b.String())
|
2020-05-10 18:03:49 +02:00
|
|
|
}
|
|
|
|
|
2023-05-02 14:06:35 +02:00
|
|
|
log.Debug("command finished successfully")
|
2020-05-10 18:03:49 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-01 06:05:30 +02:00
|
|
|
func filterArtifacts(artifacts *artifact.Artifacts, publisher config.Publisher) []*artifact.Artifact {
|
2020-05-10 18:03:49 +02:00
|
|
|
filters := []artifact.Filter{
|
|
|
|
artifact.ByType(artifact.UploadableArchive),
|
|
|
|
artifact.ByType(artifact.UploadableFile),
|
|
|
|
artifact.ByType(artifact.LinuxPackage),
|
|
|
|
artifact.ByType(artifact.UploadableBinary),
|
2021-08-17 03:11:54 +02:00
|
|
|
artifact.ByType(artifact.DockerImage),
|
|
|
|
artifact.ByType(artifact.DockerManifest),
|
2020-05-10 18:03:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if publisher.Checksum {
|
|
|
|
filters = append(filters, artifact.ByType(artifact.Checksum))
|
|
|
|
}
|
|
|
|
|
2024-03-27 04:41:41 +02:00
|
|
|
if publisher.Meta {
|
|
|
|
filters = append(filters, artifact.ByType(artifact.Metadata))
|
|
|
|
}
|
|
|
|
|
2020-05-10 18:03:49 +02:00
|
|
|
if publisher.Signature {
|
2021-11-12 03:56:03 +02:00
|
|
|
filters = append(filters, artifact.ByType(artifact.Signature), artifact.ByType(artifact.Certificate))
|
2020-05-10 18:03:49 +02:00
|
|
|
}
|
|
|
|
|
2021-03-22 13:45:18 +02:00
|
|
|
filter := artifact.Or(filters...)
|
2020-05-10 18:03:49 +02:00
|
|
|
|
|
|
|
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
|
2020-05-26 05:48:10 +02:00
|
|
|
// Those variables can be replaced by the given context, goos, goarch, goarm and more.
|
2020-05-10 18:03:49 +02:00
|
|
|
func resolveCommand(ctx *context.Context, publisher config.Publisher, artifact *artifact.Artifact) (*command, error) {
|
|
|
|
var err error
|
|
|
|
dir := publisher.Dir
|
2022-11-25 20:26:14 +02:00
|
|
|
|
2023-06-06 16:46:02 +02:00
|
|
|
tpl := tmpl.New(ctx).WithArtifact(artifact)
|
2020-05-10 18:03:49 +02:00
|
|
|
if dir != "" {
|
2022-11-25 20:26:14 +02:00
|
|
|
dir, err = tpl.Apply(dir)
|
2020-05-10 18:03:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := publisher.Cmd
|
|
|
|
if cmd != "" {
|
2022-11-25 20:26:14 +02:00
|
|
|
cmd, err = tpl.Apply(cmd)
|
2020-05-10 18:03:49 +02:00
|
|
|
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 {
|
2022-11-25 20:26:14 +02:00
|
|
|
e, err = tpl.Apply(e)
|
2020-05-10 18:03:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
env[i] = e
|
|
|
|
}
|
|
|
|
|
|
|
|
return &command{
|
|
|
|
Ctx: ctx,
|
|
|
|
Dir: dir,
|
|
|
|
Env: env,
|
|
|
|
Args: args,
|
|
|
|
}, nil
|
|
|
|
}
|