github.phpd.cn/goreleaser/goreleaser@v0.92.0/internal/pipe/sign/sign.go (about)

     1  package sign
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path/filepath"
     8  
     9  	"github.com/goreleaser/goreleaser/internal/artifact"
    10  	"github.com/goreleaser/goreleaser/internal/pipe"
    11  	"github.com/goreleaser/goreleaser/pkg/context"
    12  )
    13  
    14  // Pipe for artifact signing.
    15  type Pipe struct{}
    16  
    17  func (Pipe) String() string {
    18  	return "signing artifacts"
    19  }
    20  
    21  // Default sets the Pipes defaults.
    22  func (Pipe) Default(ctx *context.Context) error {
    23  	cfg := &ctx.Config.Sign
    24  	if cfg.Cmd == "" {
    25  		cfg.Cmd = "gpg"
    26  	}
    27  	if cfg.Signature == "" {
    28  		cfg.Signature = "${artifact}.sig"
    29  	}
    30  	if len(cfg.Args) == 0 {
    31  		cfg.Args = []string{"--output", "$signature", "--detach-sig", "$artifact"}
    32  	}
    33  	if cfg.Artifacts == "" {
    34  		cfg.Artifacts = "none"
    35  	}
    36  	return nil
    37  }
    38  
    39  // Run executes the Pipe.
    40  func (Pipe) Run(ctx *context.Context) error {
    41  	if ctx.SkipSign {
    42  		return pipe.ErrSkipSignEnabled
    43  	}
    44  
    45  	switch ctx.Config.Sign.Artifacts {
    46  	case "checksum":
    47  		return sign(ctx, ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum)).List())
    48  	case "all":
    49  		return sign(ctx, ctx.Artifacts.Filter(
    50  			artifact.Or(
    51  				artifact.ByType(artifact.UploadableArchive),
    52  				artifact.ByType(artifact.UploadableBinary),
    53  				artifact.ByType(artifact.Checksum),
    54  				artifact.ByType(artifact.LinuxPackage),
    55  			)).List())
    56  	case "none":
    57  		return pipe.ErrSkipSignEnabled
    58  	default:
    59  		return fmt.Errorf("invalid list of artifacts to sign: %s", ctx.Config.Sign.Artifacts)
    60  	}
    61  }
    62  
    63  func sign(ctx *context.Context, artifacts []artifact.Artifact) error {
    64  	// nolint:prealloc
    65  	var sigs []string
    66  	for _, a := range artifacts {
    67  		sig, err := signone(ctx, a)
    68  		if err != nil {
    69  			return err
    70  		}
    71  		sigs = append(sigs, sig)
    72  	}
    73  	for _, sig := range sigs {
    74  		ctx.Artifacts.Add(artifact.Artifact{
    75  			Type: artifact.Signature,
    76  			Name: sig,
    77  			Path: filepath.Join(ctx.Config.Dist, sig),
    78  		})
    79  	}
    80  	return nil
    81  }
    82  
    83  func signone(ctx *context.Context, artifact artifact.Artifact) (string, error) {
    84  	cfg := ctx.Config.Sign
    85  
    86  	env := map[string]string{
    87  		"artifact": artifact.Path,
    88  	}
    89  	env["signature"] = expand(cfg.Signature, env)
    90  
    91  	// nolint:prealloc
    92  	var args []string
    93  	for _, a := range cfg.Args {
    94  		args = append(args, expand(a, env))
    95  	}
    96  
    97  	// The GoASTScanner flags this as a security risk.
    98  	// However, this works as intended. The nosec annotation
    99  	// tells the scanner to ignore this.
   100  	// #nosec
   101  	cmd := exec.CommandContext(ctx, cfg.Cmd, args...)
   102  	output, err := cmd.CombinedOutput()
   103  	if err != nil {
   104  		return "", fmt.Errorf("sign: %s failed with %q", cfg.Cmd, string(output))
   105  	}
   106  	return filepath.Base(env["signature"]), nil
   107  }
   108  
   109  func expand(s string, env map[string]string) string {
   110  	return os.Expand(s, func(key string) string {
   111  		return env[key]
   112  	})
   113  }