github.com/joselitofilho/goreleaser@v0.155.1-0.20210123221854-e4891856c593/internal/exec/exec.go (about) 1 package exec 2 3 import ( 4 "fmt" 5 "os/exec" 6 7 "github.com/apex/log" 8 "github.com/goreleaser/goreleaser/internal/artifact" 9 "github.com/goreleaser/goreleaser/internal/logext" 10 "github.com/goreleaser/goreleaser/internal/pipe" 11 "github.com/goreleaser/goreleaser/internal/semerrgroup" 12 "github.com/goreleaser/goreleaser/internal/tmpl" 13 "github.com/goreleaser/goreleaser/pkg/config" 14 "github.com/goreleaser/goreleaser/pkg/context" 15 "github.com/mattn/go-shellwords" 16 ) 17 18 func Execute(ctx *context.Context, publishers []config.Publisher) error { 19 if ctx.SkipPublish { 20 return pipe.ErrSkipPublishEnabled 21 } 22 23 for _, p := range publishers { 24 log.WithField("name", p.Name).Debug("executing custom publisher") 25 err := executePublisher(ctx, p) 26 if err != nil { 27 return err 28 } 29 } 30 31 return nil 32 } 33 34 func executePublisher(ctx *context.Context, publisher config.Publisher) error { 35 log.Debugf("filtering %d artifacts", len(ctx.Artifacts.List())) 36 artifacts := filterArtifacts(ctx.Artifacts, publisher) 37 log.Debugf("will execute custom publisher with %d artifacts", len(artifacts)) 38 39 var g = semerrgroup.New(ctx.Parallelism) 40 for _, artifact := range artifacts { 41 artifact := artifact 42 g.Go(func() error { 43 c, err := resolveCommand(ctx, publisher, artifact) 44 if err != nil { 45 return err 46 } 47 48 return executeCommand(c) 49 }) 50 } 51 52 return g.Wait() 53 } 54 55 func executeCommand(c *command) error { 56 log.WithField("args", c.Args). 57 WithField("env", c.Env). 58 Debug("executing command") 59 60 // nolint: gosec 61 var cmd = exec.CommandContext(c.Ctx, c.Args[0], c.Args[1:]...) 62 cmd.Env = c.Env 63 if c.Dir != "" { 64 cmd.Dir = c.Dir 65 } 66 67 entry := log.WithField("cmd", c.Args[0]) 68 cmd.Stderr = logext.NewErrWriter(entry) 69 cmd.Stdout = logext.NewWriter(entry) 70 71 log.WithField("cmd", cmd.Args).Info("publishing") 72 if err := cmd.Run(); err != nil { 73 return fmt.Errorf("publishing: %s failed: %w", 74 c.Args[0], err) 75 } 76 77 log.Debugf("command %s finished successfully", c.Args[0]) 78 return nil 79 } 80 81 func filterArtifacts(artifacts artifact.Artifacts, publisher config.Publisher) []*artifact.Artifact { 82 filters := []artifact.Filter{ 83 artifact.ByType(artifact.UploadableArchive), 84 artifact.ByType(artifact.UploadableFile), 85 artifact.ByType(artifact.LinuxPackage), 86 artifact.ByType(artifact.UploadableBinary), 87 } 88 89 if publisher.Checksum { 90 filters = append(filters, artifact.ByType(artifact.Checksum)) 91 } 92 93 if publisher.Signature { 94 filters = append(filters, artifact.ByType(artifact.Signature)) 95 } 96 97 var filter = artifact.Or(filters...) 98 99 if len(publisher.IDs) > 0 { 100 filter = artifact.And(filter, artifact.ByIDs(publisher.IDs...)) 101 } 102 103 return artifacts.Filter(filter).List() 104 } 105 106 type command struct { 107 Ctx *context.Context 108 Dir string 109 Env []string 110 Args []string 111 } 112 113 // resolveCommand returns the a command based on publisher template with replaced variables 114 // Those variables can be replaced by the given context, goos, goarch, goarm and more. 115 func resolveCommand(ctx *context.Context, publisher config.Publisher, artifact *artifact.Artifact) (*command, error) { 116 var err error 117 118 replacements := make(map[string]string) 119 // TODO: Replacements should be associated only with relevant artifacts/archives 120 archives := ctx.Config.Archives 121 if len(archives) > 0 { 122 replacements = archives[0].Replacements 123 } 124 125 dir := publisher.Dir 126 if dir != "" { 127 dir, err = tmpl.New(ctx). 128 WithArtifact(artifact, replacements). 129 Apply(dir) 130 if err != nil { 131 return nil, err 132 } 133 } 134 135 cmd := publisher.Cmd 136 if cmd != "" { 137 cmd, err = tmpl.New(ctx). 138 WithArtifact(artifact, replacements). 139 Apply(cmd) 140 if err != nil { 141 return nil, err 142 } 143 } 144 145 args, err := shellwords.Parse(cmd) 146 if err != nil { 147 return nil, err 148 } 149 150 env := make([]string, len(publisher.Env)) 151 for i, e := range publisher.Env { 152 e, err = tmpl.New(ctx). 153 WithArtifact(artifact, replacements). 154 Apply(e) 155 if err != nil { 156 return nil, err 157 } 158 env[i] = e 159 } 160 161 return &command{ 162 Ctx: ctx, 163 Dir: dir, 164 Env: env, 165 Args: args, 166 }, nil 167 }