Skip to content

Commit

Permalink
feat(sign): id, concurrent map (#1321)
Browse files Browse the repository at this point in the history
* feat(sign): id, concurrent map

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: more tests

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>

* fix: comments

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
  • Loading branch information
caarlos0 committed Jan 30, 2020
1 parent 1a4e242 commit 181799c
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 10 deletions.
14 changes: 11 additions & 3 deletions internal/pipe/sign/sign.go
Expand Up @@ -8,9 +8,9 @@ import (
"reflect"

"github.com/apex/log"

"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/deprecate"
"github.com/goreleaser/goreleaser/internal/ids"
"github.com/goreleaser/goreleaser/internal/logext"
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/internal/semerrgroup"
Expand All @@ -33,6 +33,7 @@ func (Pipe) Default(ctx *context.Context) error {
deprecate.Notice("sign")
}
}
var ids = ids.New("signs")
for i := range ctx.Config.Signs {
cfg := &ctx.Config.Signs[i]
if cfg.Cmd == "" {
Expand All @@ -47,8 +48,12 @@ func (Pipe) Default(ctx *context.Context) error {
if cfg.Artifacts == "" {
cfg.Artifacts = "none"
}
if cfg.ID == "" {
cfg.ID = "default"
}
ids.Inc(cfg.ID)
}
return nil
return ids.Validate()
}

// Run executes the Pipe.
Expand Down Expand Up @@ -101,7 +106,7 @@ func sign(ctx *context.Context, cfg config.Sign, artifacts []*artifact.Artifact)
}

func signone(ctx *context.Context, cfg config.Sign, a *artifact.Artifact) (*artifact.Artifact, error) {
env := ctx.Env
env := ctx.Env.Copy()
env["artifact"] = a.Path
env["signature"] = expand(cfg.Signature, env)

Expand Down Expand Up @@ -133,6 +138,9 @@ func signone(ctx *context.Context, cfg config.Sign, a *artifact.Artifact) (*arti
Type: artifact.Signature,
Name: name,
Path: filepath.Join(artifactPathBase, sigFilename),
Extra: map[string]interface{}{
"ID": cfg.ID,
},
}, nil
}

Expand Down
91 changes: 84 additions & 7 deletions internal/pipe/sign/sign_test.go
Expand Up @@ -9,13 +9,15 @@ import (
"os/exec"
"path/filepath"
"sort"
"strings"
"testing"
"time"

"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var originKeyring = "testdata/gnupg"
Expand Down Expand Up @@ -78,7 +80,21 @@ func TestSignArtifacts(t *testing.T) {
ctx *context.Context
signaturePaths []string
signatureNames []string
expectedErrMsg string
}{
{
desc: "sign errors",
expectedErrMsg: "sign: exit failed",
ctx: context.New(
config.Project{
Sign: config.Sign{
Artifacts: "all",
Cmd: "exit",
Args: []string{"1"},
},
},
),
},
{
desc: "sign single",
ctx: context.New(
Expand All @@ -105,6 +121,36 @@ func TestSignArtifacts(t *testing.T) {
signaturePaths: []string{"artifact1.sig", "artifact2.sig", "artifact3.sig", "checksum.sig", "checksum2.sig", "linux_amd64/artifact4.sig"},
signatureNames: []string{"artifact1.sig", "artifact2.sig", "artifact3_1.0.0_linux_amd64.sig", "checksum.sig", "checksum2.sig", "artifact4_1.0.0_linux_amd64.sig"},
},
{
desc: "multiple sign configs",
ctx: context.New(
config.Project{
Signs: []config.Sign{
{
ID: "s1",
Artifacts: "checksum",
},
{
ID: "s2",
Artifacts: "checksum",
Signature: "${artifact}.sog",
},
},
},
),
signaturePaths: []string{
"checksum.sig",
"checksum2.sig",
"checksum.sog",
"checksum2.sog",
},
signatureNames: []string{
"checksum.sig",
"checksum2.sig",
"checksum.sog",
"checksum2.sog",
},
},
{
desc: "sign filtered artifacts",
ctx: context.New(
Expand Down Expand Up @@ -178,14 +224,14 @@ func TestSignArtifacts(t *testing.T) {

for _, test := range tests {
t.Run(test.desc, func(tt *testing.T) {
testSign(tt, test.ctx, test.signaturePaths, test.signatureNames)
testSign(tt, test.ctx, test.signaturePaths, test.signatureNames, test.expectedErrMsg)
})
}
}

const user = "nopass"

func testSign(t *testing.T, ctx *context.Context, signaturePaths []string, signatureNames []string) {
func testSign(t *testing.T, ctx *context.Context, signaturePaths []string, signatureNames []string, expectedErrMsg string) {
// create temp dir for file and signature
tmpdir, err := ioutil.TempDir("", "goreleaser")
assert.NoError(t, err)
Expand Down Expand Up @@ -249,11 +295,26 @@ func testSign(t *testing.T, ctx *context.Context, signaturePaths []string, signa
// configure the pipeline
// make sure we are using the test keyring
assert.NoError(t, Pipe{}.Default(ctx))
ctx.Config.Signs[0].Args = append([]string{"--homedir", keyring}, ctx.Config.Signs[0].Args...)
for i := range ctx.Config.Signs {
ctx.Config.Signs[i].Args = append(
[]string{"--homedir", keyring},
ctx.Config.Signs[i].Args...,
)
}

// run the pipeline
if expectedErrMsg != "" {
assert.EqualError(t, Pipe{}.Run(ctx), expectedErrMsg)
return
}

assert.NoError(t, Pipe{}.Run(ctx))

// ensure all artifacts have an ID
for _, arti := range ctx.Artifacts.Filter(artifact.ByType(artifact.Signature)).List() {
assert.NotEmptyf(t, arti.ExtraOr("ID", ""), ".Extra.ID on %s", arti.Path)
}

// verify that only the artifacts and the signatures are in the dist dir
gotFiles := []string{}

Expand All @@ -276,7 +337,7 @@ func testSign(t *testing.T, ctx *context.Context, signaturePaths []string, signa

wantFiles := append(artifacts, signaturePaths...)
sort.Strings(wantFiles)
assert.Equal(t, wantFiles, gotFiles)
assert.ElementsMatch(t, wantFiles, gotFiles)

// verify the signatures
for _, sig := range signaturePaths {
Expand All @@ -288,11 +349,11 @@ func testSign(t *testing.T, ctx *context.Context, signaturePaths []string, signa
signArtifacts = append(signArtifacts, sig.Name)
}
// check signature is an artifact
assert.Equal(t, signArtifacts, signatureNames)
assert.ElementsMatch(t, signArtifacts, signatureNames)
}

func verifySignature(t *testing.T, ctx *context.Context, sig string) {
artifact := sig[:len(sig)-len(".sig")]
artifact := strings.Replace(sig, filepath.Ext(sig), "", 1)

// verify signature was made with key for usesr 'nopass'
cmd := exec.Command("gpg", "--homedir", keyring, "--verify", filepath.Join(ctx.Config.Dist, sig), filepath.Join(ctx.Config.Dist, artifact))
Expand All @@ -304,6 +365,22 @@ func verifySignature(t *testing.T, ctx *context.Context, sig string) {
// keyring before we do the verification. For now we punt and look in the
// output.
if !bytes.Contains(out, []byte(user)) {
t.Fatalf("signature is not from %s", user)
t.Fatalf("%s: signature is not from %s: %s", sig, user, string(out))
}
}

func TestSeveralSignsWithTheSameID(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Signs: []config.Sign{
{
ID: "a",
},
{
ID: "a",
},
},
},
}
require.EqualError(t, Pipe{}.Default(ctx), "found 2 signs with the ID 'a', please fix your config")
}
1 change: 1 addition & 0 deletions pkg/config/config.go
Expand Up @@ -230,6 +230,7 @@ type NFPMOverridables struct {

// Sign config
type Sign struct {
ID string `yaml:"id,omitempty"`
Cmd string `yaml:"cmd,omitempty"`
Args []string `yaml:"args,omitempty"`
Signature string `yaml:"signature,omitempty"`
Expand Down
9 changes: 9 additions & 0 deletions pkg/context/context.go
Expand Up @@ -28,6 +28,15 @@ type GitInfo struct {
// Env is the environment variables
type Env map[string]string

// Copy returns a copy of the environment.
func (e Env) Copy() Env {
var out = Env{}
for k, v := range e {
out[k] = v
}
return out
}

// Strings returns the current environment as a list of strings, suitable for
// os executions.
func (e Env) Strings() []string {
Expand Down
4 changes: 4 additions & 0 deletions www/content/sign.md
Expand Up @@ -28,6 +28,10 @@ To customize the signing pipeline you can use the following options:
# .goreleaser.yml
signs:
-
# ID of the sign config, must be unique.
# Defaults to "default".
id: foo

# name of the signature file.
# '${artifact}' is the path to the artifact that should be signed.
#
Expand Down

0 comments on commit 181799c

Please sign in to comment.