github.skymusic.top/goreleaser/goreleaser@v0.92.0/internal/pipe/sign/sign_test.go (about)

     1  package sign
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"sort"
    10  	"testing"
    11  
    12  	"github.com/goreleaser/goreleaser/internal/artifact"
    13  	"github.com/goreleaser/goreleaser/pkg/config"
    14  	"github.com/goreleaser/goreleaser/pkg/context"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func TestDescription(t *testing.T) {
    19  	assert.NotEmpty(t, Pipe{}.String())
    20  }
    21  
    22  func TestSignDefault(t *testing.T) {
    23  	ctx := &context.Context{}
    24  	Pipe{}.Default(ctx)
    25  	assert.Equal(t, ctx.Config.Sign.Cmd, "gpg")
    26  	assert.Equal(t, ctx.Config.Sign.Signature, "${artifact}.sig")
    27  	assert.Equal(t, ctx.Config.Sign.Args, []string{"--output", "$signature", "--detach-sig", "$artifact"})
    28  	assert.Equal(t, ctx.Config.Sign.Artifacts, "none")
    29  }
    30  
    31  func TestSignDisabled(t *testing.T) {
    32  	ctx := &context.Context{}
    33  	ctx.Config.Sign.Artifacts = "none"
    34  	err := Pipe{}.Run(ctx)
    35  	assert.EqualError(t, err, "artifact signing is disabled")
    36  }
    37  
    38  func TestSignSkipped(t *testing.T) {
    39  	ctx := &context.Context{}
    40  	ctx.SkipSign = true
    41  	err := Pipe{}.Run(ctx)
    42  	assert.EqualError(t, err, "artifact signing is disabled")
    43  }
    44  
    45  func TestSignInvalidArtifacts(t *testing.T) {
    46  	ctx := &context.Context{}
    47  	ctx.Config.Sign.Artifacts = "foo"
    48  	err := Pipe{}.Run(ctx)
    49  	assert.EqualError(t, err, "invalid list of artifacts to sign: foo")
    50  }
    51  
    52  func TestSignArtifacts(t *testing.T) {
    53  	// fix permission on keyring dir to suppress warning about insecure permissions
    54  	assert.NoError(t, os.Chmod(keyring, 0700))
    55  
    56  	tests := []struct {
    57  		desc       string
    58  		ctx        *context.Context
    59  		signatures []string
    60  	}{
    61  		{
    62  			desc: "sign all artifacts",
    63  			ctx: context.New(
    64  				config.Project{
    65  					Sign: config.Sign{Artifacts: "all"},
    66  				},
    67  			),
    68  			signatures: []string{"artifact1.sig", "artifact2.sig", "checksum.sig"},
    69  		},
    70  		{
    71  			desc: "sign only checksums",
    72  			ctx: context.New(
    73  				config.Project{
    74  					Sign: config.Sign{Artifacts: "checksum"},
    75  				},
    76  			),
    77  			signatures: []string{"checksum.sig"},
    78  		},
    79  	}
    80  
    81  	for _, tt := range tests {
    82  		t.Run(tt.desc, func(t *testing.T) {
    83  			testSign(t, tt.ctx, tt.signatures)
    84  		})
    85  	}
    86  }
    87  
    88  const keyring = "testdata/gnupg"
    89  const user = "nopass"
    90  
    91  func testSign(t *testing.T, ctx *context.Context, signatures []string) {
    92  	// create temp dir for file and signature
    93  	tmpdir, err := ioutil.TempDir("", "goreleaser")
    94  	assert.NoError(t, err)
    95  	defer os.RemoveAll(tmpdir)
    96  
    97  	ctx.Config.Dist = tmpdir
    98  
    99  	// create some fake artifacts
   100  	var artifacts = []string{"artifact1", "artifact2", "checksum"}
   101  	for _, f := range artifacts {
   102  		file := filepath.Join(tmpdir, f)
   103  		assert.NoError(t, ioutil.WriteFile(file, []byte("foo"), 0644))
   104  	}
   105  	ctx.Artifacts.Add(artifact.Artifact{
   106  		Name: "artifact1",
   107  		Path: filepath.Join(tmpdir, "artifact1"),
   108  		Type: artifact.UploadableArchive,
   109  	})
   110  	ctx.Artifacts.Add(artifact.Artifact{
   111  		Name: "artifact2",
   112  		Path: filepath.Join(tmpdir, "artifact2"),
   113  		Type: artifact.UploadableArchive,
   114  	})
   115  	ctx.Artifacts.Add(artifact.Artifact{
   116  		Name: "checksum",
   117  		Path: filepath.Join(tmpdir, "checksum"),
   118  		Type: artifact.Checksum,
   119  	})
   120  
   121  	// configure the pipeline
   122  	// make sure we are using the test keyring
   123  	assert.NoError(t, Pipe{}.Default(ctx))
   124  	ctx.Config.Sign.Args = append([]string{"--homedir", keyring}, ctx.Config.Sign.Args...)
   125  
   126  	// run the pipeline
   127  	assert.NoError(t, Pipe{}.Run(ctx))
   128  
   129  	// verify that only the artifacts and the signatures are in the dist dir
   130  	files, err := ioutil.ReadDir(tmpdir)
   131  	assert.NoError(t, err)
   132  	gotFiles := []string{}
   133  	for _, f := range files {
   134  		gotFiles = append(gotFiles, f.Name())
   135  	}
   136  	wantFiles := append(artifacts, signatures...)
   137  	sort.Strings(wantFiles)
   138  	assert.Equal(t, wantFiles, gotFiles)
   139  
   140  	// verify the signatures
   141  	for _, sig := range signatures {
   142  		verifySignature(t, ctx, sig)
   143  	}
   144  
   145  	var signArtifacts []string
   146  	for _, sig := range ctx.Artifacts.Filter(artifact.ByType(artifact.Signature)).List() {
   147  		signArtifacts = append(signArtifacts, sig.Name)
   148  	}
   149  	// check signature is an artifact
   150  	assert.Equal(t, signArtifacts, signatures)
   151  }
   152  
   153  func verifySignature(t *testing.T, ctx *context.Context, sig string) {
   154  	artifact := sig[:len(sig)-len(".sig")]
   155  
   156  	// verify signature was made with key for usesr 'nopass'
   157  	cmd := exec.Command("gpg", "--homedir", keyring, "--verify", filepath.Join(ctx.Config.Dist, sig), filepath.Join(ctx.Config.Dist, artifact))
   158  	out, err := cmd.CombinedOutput()
   159  	assert.NoError(t, err)
   160  
   161  	// check if the signature matches the user we expect to do this properly we
   162  	// might need to have either separate keyrings or export the key from the
   163  	// keyring before we do the verification. For now we punt and look in the
   164  	// output.
   165  	if !bytes.Contains(out, []byte(user)) {
   166  		t.Fatalf("signature is not from %s", user)
   167  	}
   168  }