github.com/joselitofilho/goreleaser@v0.155.1-0.20210123221854-e4891856c593/internal/pipe/sign/sign.go (about) 1 package sign 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 "github.com/apex/log" 12 "github.com/goreleaser/goreleaser/internal/artifact" 13 "github.com/goreleaser/goreleaser/internal/ids" 14 "github.com/goreleaser/goreleaser/internal/logext" 15 "github.com/goreleaser/goreleaser/internal/pipe" 16 "github.com/goreleaser/goreleaser/internal/semerrgroup" 17 "github.com/goreleaser/goreleaser/internal/tmpl" 18 "github.com/goreleaser/goreleaser/pkg/config" 19 "github.com/goreleaser/goreleaser/pkg/context" 20 ) 21 22 // Pipe for artifact signing. 23 type Pipe struct{} 24 25 func (Pipe) String() string { 26 return "signing artifacts" 27 } 28 29 // Default sets the Pipes defaults. 30 func (Pipe) Default(ctx *context.Context) error { 31 var ids = ids.New("signs") 32 for i := range ctx.Config.Signs { 33 cfg := &ctx.Config.Signs[i] 34 if cfg.Cmd == "" { 35 cfg.Cmd = "gpg" 36 } 37 if cfg.Signature == "" { 38 cfg.Signature = "${artifact}.sig" 39 } 40 if len(cfg.Args) == 0 { 41 cfg.Args = []string{"--output", "$signature", "--detach-sig", "$artifact"} 42 } 43 if cfg.Artifacts == "" { 44 cfg.Artifacts = "none" 45 } 46 if cfg.ID == "" { 47 cfg.ID = "default" 48 } 49 ids.Inc(cfg.ID) 50 } 51 return ids.Validate() 52 } 53 54 // Run executes the Pipe. 55 func (Pipe) Run(ctx *context.Context) error { 56 if ctx.SkipSign { 57 return pipe.ErrSkipSignEnabled 58 } 59 60 var g = semerrgroup.New(ctx.Parallelism) 61 for i := range ctx.Config.Signs { 62 cfg := ctx.Config.Signs[i] 63 g.Go(func() error { 64 var filters []artifact.Filter 65 switch cfg.Artifacts { 66 case "checksum": 67 filters = append(filters, artifact.ByType(artifact.Checksum)) 68 if len(cfg.IDs) > 0 { 69 log.Warn("when artifacts is `checksum`, `ids` has no effect. ignoring") 70 } 71 case "source": 72 filters = append(filters, artifact.ByType(artifact.UploadableSourceArchive)) 73 if len(cfg.IDs) > 0 { 74 log.Warn("when artifacts is `source`, `ids` has no effect. ignoring") 75 } 76 case "all": 77 filters = append(filters, artifact.Or( 78 artifact.ByType(artifact.UploadableArchive), 79 artifact.ByType(artifact.UploadableBinary), 80 artifact.ByType(artifact.UploadableSourceArchive), 81 artifact.ByType(artifact.Checksum), 82 artifact.ByType(artifact.LinuxPackage), 83 )) 84 if len(cfg.IDs) > 0 { 85 filters = append(filters, artifact.ByIDs(cfg.IDs...)) 86 } 87 case "none": 88 return pipe.ErrSkipSignEnabled 89 default: 90 return fmt.Errorf("invalid list of artifacts to sign: %s", cfg.Artifacts) 91 } 92 return sign(ctx, cfg, ctx.Artifacts.Filter(artifact.And(filters...)).List()) 93 }) 94 } 95 return g.Wait() 96 } 97 98 func sign(ctx *context.Context, cfg config.Sign, artifacts []*artifact.Artifact) error { 99 for _, a := range artifacts { 100 artifact, err := signone(ctx, cfg, a) 101 if err != nil { 102 return err 103 } 104 ctx.Artifacts.Add(artifact) 105 } 106 return nil 107 } 108 109 func signone(ctx *context.Context, cfg config.Sign, a *artifact.Artifact) (*artifact.Artifact, error) { 110 env := ctx.Env.Copy() 111 env["artifact"] = a.Path 112 env["signature"] = expand(cfg.Signature, env) 113 114 // nolint:prealloc 115 var args []string 116 for _, a := range cfg.Args { 117 var arg = expand(a, env) 118 arg, err := tmpl.New(ctx).WithEnv(env).Apply(arg) 119 if err != nil { 120 return nil, fmt.Errorf("sign failed: %s: invalid template: %w", a, err) 121 } 122 args = append(args, arg) 123 } 124 125 var stdin io.Reader 126 if cfg.Stdin != nil { 127 stdin = strings.NewReader(*cfg.Stdin) 128 } else if cfg.StdinFile != "" { 129 f, err := os.Open(cfg.StdinFile) 130 if err != nil { 131 return nil, fmt.Errorf("sign failed: cannot open file %s: %w", cfg.StdinFile, err) 132 } 133 defer f.Close() 134 135 stdin = f 136 } 137 138 // The GoASTScanner flags this as a security risk. 139 // However, this works as intended. The nosec annotation 140 // tells the scanner to ignore this. 141 // #nosec 142 cmd := exec.CommandContext(ctx, cfg.Cmd, args...) 143 cmd.Stderr = logext.NewWriter(log.WithField("cmd", cfg.Cmd)) 144 cmd.Stdout = cmd.Stderr 145 if stdin != nil { 146 cmd.Stdin = stdin 147 } 148 log.WithField("cmd", cmd.Args).Info("signing") 149 if err := cmd.Run(); err != nil { 150 return nil, fmt.Errorf("sign: %s failed", cfg.Cmd) 151 } 152 153 artifactPathBase, _ := filepath.Split(a.Path) 154 155 env["artifact"] = a.Name 156 name := expand(cfg.Signature, env) 157 158 sigFilename := filepath.Base(env["signature"]) 159 return &artifact.Artifact{ 160 Type: artifact.Signature, 161 Name: name, 162 Path: filepath.Join(artifactPathBase, sigFilename), 163 Extra: map[string]interface{}{ 164 "ID": cfg.ID, 165 }, 166 }, nil 167 } 168 169 func expand(s string, env map[string]string) string { 170 return os.Expand(s, func(key string) string { 171 return env[key] 172 }) 173 }