Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/deb package changes #612

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
8 changes: 6 additions & 2 deletions deb/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,11 @@ type controlData struct {
}

func writeControl(w io.Writer, data controlData) error {
tmpl := template.New("control")
return writeTemplate("control", controlTemplate, w, data)
}

func writeTemplate(name, t string, w io.Writer, data interface{}) error {
tmpl := template.New(name)
tmpl.Funcs(template.FuncMap{
"join": func(strs []string) string {
return strings.Trim(strings.Join(strs, ", "), " ")
Expand Down Expand Up @@ -806,5 +810,5 @@ func writeControl(w io.Writer, data controlData) error {
return result
},
})
return template.Must(tmpl.Parse(controlTemplate)).Execute(w, data)
return template.Must(tmpl.Parse(t)).Execute(w, data)
}
161 changes: 161 additions & 0 deletions deb/deb_meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package deb

import (
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"fmt"
"io"
"path/filepath"
"strings"
"time"

"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/internal/sign"
)

const debChangesTemplate = `
{{- /* Mandatory fields */ -}}
Format: 1.8
Date: {{.Date}}
Source: {{.Info.Name}}
Binary: {{.Info.Deb.Metadata.Binary}}
Architecture: {{ if ne .Info.Platform "linux"}}{{ .Info.Platform }}-{{ end }}{{.Info.Arch}}
Version: {{ if .Info.Epoch}}{{ .Info.Epoch }}:{{ end }}{{.Info.Version}}
{{- if .Info.Prerelease}}~{{ .Info.Prerelease }}{{- end }}
{{- if .Info.VersionMetadata}}+{{ .Info.VersionMetadata }}{{- end }}
{{- if .Info.Release}}-{{ .Info.Release }}{{- end }}
Distribution: {{.Info.Deb.Metadata.Distribution}}
{{- if .Info.Deb.Metadata.Urgency }}
Urgency: {{.Info.Deb.Metadata.Urgency}}
{{- end }}
Maintainer: {{.Info.Maintainer}}
{{- if .Info.Deb.Metadata.ChangedBy }}
Changed-By: {{.Info.Deb.Metadata.ChangedBy}}
{{- end }}
Description: {{ multiline .Info.Description }}
{{- range $key, $value := .Info.Deb.Metadata.Fields }}
{{- if $value }}
{{$key}}: {{$value}}
{{- end }}
{{- end }}
Changes:
{{range .Changes}} {{.}}{{end}}
Checksums-Sha256:
{{range .Files}} {{ .Sha256Sum }} {{.Size}} {{.Name}}{{end}}
Checksums-Sha1:
{{range .Files}} {{ .Sha1Sum }} {{.Size}} {{.Name}}{{end}}
Files:
{{range .Files}} {{ .Md5Sum }} {{.Size}} {{.Section}} {{.Priority}} {{.Name}}{{end}}

`

type changesData struct {
Date string
Info *nfpm.Info
Changes []string
Files []changesFileData
}

type changesFileData struct {
Name string
Size int
Section string
Priority string
Md5Sum string
Sha1Sum string
Sha256Sum string
}

var now = time.Now

func (d *Deb) ConventionalMetadataFileName(info *nfpm.Info) string {
target := info.Target

if target == "" {
target = d.ConventionalFileName(info)
}

return strings.Replace(target, ".deb", ".changes", 1)
}

func (d *Deb) PackageMetadata(metaInfo *nfpm.MetaInfo, w io.Writer) error {
data, err := createChanges(metaInfo)
if err != nil {
return err

Check warning on line 86 in deb/deb_meta.go

View check run for this annotation

Codecov / codecov/patch

deb/deb_meta.go#L86

Added line #L86 was not covered by tests
}

if metaInfo.Info.Deb.Signature.KeyFile == "" {
_, err = w.Write(data.Bytes())
return err
}

signedData, err := signChanges(data, metaInfo.Info)
if err != nil {
return err
}

_, err = w.Write(signedData)
return err
}

func createChanges(info *nfpm.MetaInfo) (*bytes.Buffer, error) {
data, err := prepareChangesData(info)
if err != nil {
return nil, err

Check warning on line 106 in deb/deb_meta.go

View check run for this annotation

Codecov / codecov/patch

deb/deb_meta.go#L106

Added line #L106 was not covered by tests
}

buf := new(bytes.Buffer)

if err := writeTemplate("changes", debChangesTemplate, buf, data); err != nil {
return nil, err

Check warning on line 112 in deb/deb_meta.go

View check run for this annotation

Codecov / codecov/patch

deb/deb_meta.go#L112

Added line #L112 was not covered by tests
}

return buf, nil
}

func prepareChangesData(meta *nfpm.MetaInfo) (*changesData, error) {
info := meta.Info

_, err := meta.Package.Seek(0, io.SeekStart)
if err != nil {
return nil, err

Check warning on line 123 in deb/deb_meta.go

View check run for this annotation

Codecov / codecov/patch

deb/deb_meta.go#L123

Added line #L123 was not covered by tests
}

buf := new(bytes.Buffer)

_, err = buf.ReadFrom(meta.Package)
if err != nil {
return nil, err

Check warning on line 130 in deb/deb_meta.go

View check run for this annotation

Codecov / codecov/patch

deb/deb_meta.go#L130

Added line #L130 was not covered by tests
}

return &changesData{
Date: now().Format(time.RFC1123Z),
Info: info,
Changes: []string{
fmt.Sprintf("%s (%s) %s; urgency=%s\n * Package created with nFPM",
info.Name, info.Version, info.Deb.Metadata.Distribution, info.Deb.Metadata.Urgency),
},
Files: []changesFileData{
{
Name: filepath.Base(info.Target),
Size: buf.Len(),
Section: info.Section,
Priority: info.Priority,
Md5Sum: fmt.Sprintf("%x", md5.Sum(buf.Bytes())),
Sha1Sum: fmt.Sprintf("%x", sha1.Sum(buf.Bytes())),
Sha256Sum: fmt.Sprintf("%x", sha256.Sum256(buf.Bytes())),
},
},
}, nil
}

func signChanges(data *bytes.Buffer, info *nfpm.Info) ([]byte, error) {
signConfig := info.Deb.Signature
signature, err := sign.PGPClearSignWithKeyID(data, signConfig.KeyFile, signConfig.KeyPassphrase, signConfig.KeyID)
if err != nil {
return nil, &nfpm.ErrSigningFailure{Err: err}
}
return signature, nil
}
110 changes: 110 additions & 0 deletions deb/deb_meta_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package deb

import (
"bytes"
"os"
"testing"
"time"

"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/internal/sign"
"github.com/stretchr/testify/require"
)

func fakeTime() time.Time {
t, _ := time.Parse(time.RFC1123Z, "Mon, 30 Jan 2023 08:36:31 +0300")
return t
}

func TestMetaFilename(t *testing.T) {
info := exampleInfo()

require.Equal(t, "foo_1.0.0_amd64.changes", Default.ConventionalMetadataFileName(info))
}

func TestMetaFilenameTarget(t *testing.T) {
info := exampleInfo()
info.Target = "bar_1.0.0_amd64.deb"

require.Equal(t, "bar_1.0.0_amd64.changes", Default.ConventionalMetadataFileName(info))
}

func TestMetaFields(t *testing.T) {
now = fakeTime

var w bytes.Buffer
pkg := bytes.NewReader([]byte{1, 2, 3})
info := nfpm.WithDefaults(&nfpm.Info{
Name: "foo",
Description: "Foo does things",
Priority: "extra",
Maintainer: "Carlos A Becker <[email protected]>",
Version: "v1.0.0",
Section: "default",
Homepage: "http://carlosbecker.com",
EnableMetadata: true,
Overridables: nfpm.Overridables{
Deb: nfpm.Deb{
Metadata: nfpm.DebMetadata{
Binary: "foo",
Distribution: "unstable",
Urgency: "medium",
ChangedBy: "Carlos A Becker <[email protected]>",
Fields: map[string]string{
"Bugs": "https://github.com/goreleaser/nfpm/issues",
"Empty": "",
},
},
},
},
})
info.Target = Default.ConventionalFileName(info)

require.NoError(t, Default.PackageMetadata(&nfpm.MetaInfo{
Info: info,
Package: pkg,
}, &w))
golden := "testdata/changes.golden"
if *update {
require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600))
}
bts, err := os.ReadFile(golden) //nolint:gosec
require.NoError(t, err)
require.Equal(t, string(bts), w.String())
}

func TestMetaSignature(t *testing.T) {
info := exampleInfo()
info.Target = Default.ConventionalFileName(info)
info.Deb.Signature.KeyFile = "../internal/sign/testdata/privkey.asc"
info.Deb.Signature.KeyPassphrase = "hunter2"

var w bytes.Buffer
pkg := bytes.NewReader([]byte{1, 2, 3})

require.NoError(t, Default.PackageMetadata(&nfpm.MetaInfo{
Info: info,
Package: pkg,
}, &w))

_, err := sign.PGPReadMessage(w.Bytes(), "../internal/sign/testdata/pubkey.asc")
require.NoError(t, err)
}

func TestMetaSignatureError(t *testing.T) {
now = fakeTime
info := exampleInfo()
info.Target = Default.ConventionalFileName(info)
info.Deb.Signature.KeyFile = "/does/not/exist"

var w bytes.Buffer
pkg := bytes.NewReader([]byte{1, 2, 3})
err := Default.PackageMetadata(&nfpm.MetaInfo{
Info: info,
Package: pkg,
}, &w)
require.Error(t, err)

var expectedError *nfpm.ErrSigningFailure
require.ErrorAs(t, err, &expectedError)
}
22 changes: 22 additions & 0 deletions deb/testdata/changes.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Format: 1.8
Date: Mon, 30 Jan 2023 08:36:31 +0300
Source: foo
Binary: foo
Architecture: amd64
Version: 1.0.0
Distribution: unstable
Urgency: medium
Maintainer: Carlos A Becker <[email protected]>
Changed-By: Carlos A Becker <[email protected]>
Description: Foo does things
Bugs: https://github.com/goreleaser/nfpm/issues
Changes:
foo (1.0.0) unstable; urgency=medium
* Package created with nFPM
Checksums-Sha256:
039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81 3 foo_1.0.0_amd64.deb
Checksums-Sha1:
7037807198c22a7d2b0807371d763779a84fdfcf 3 foo_1.0.0_amd64.deb
Files:
5289df737df57326fcdd22597afb1fac 3 default extra foo_1.0.0_amd64.deb

30 changes: 30 additions & 0 deletions internal/cmd/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -113,5 +114,34 @@
}

fmt.Printf("created package: %s\n", target)

meta, supports := pkg.(nfpm.PackagerWithMetadata)
if !supports || !info.EnableMetadata {
return nil

Check warning on line 120 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L118-L120

Added lines #L118 - L120 were not covered by tests
}

return doPackageMeta(meta, f, info)

Check warning on line 123 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L123

Added line #L123 was not covered by tests
}

func doPackageMeta(pkgMeta nfpm.PackagerWithMetadata, p io.ReadSeeker, info *nfpm.Info) error {
target := pkgMeta.ConventionalMetadataFileName(info)

Check warning on line 127 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L126-L127

Added lines #L126 - L127 were not covered by tests

f, err := os.Create(target)
if err != nil {
return err

Check warning on line 131 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L129-L131

Added lines #L129 - L131 were not covered by tests
}
defer f.Close()

Check warning on line 133 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L133

Added line #L133 was not covered by tests

metaInfo := &nfpm.MetaInfo{
Info: info,
Package: p,

Check warning on line 137 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L135-L137

Added lines #L135 - L137 were not covered by tests
}

if err := pkgMeta.PackageMetadata(metaInfo, f); err != nil {
_ = os.Remove(target)
return err

Check warning on line 142 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L140-L142

Added lines #L140 - L142 were not covered by tests
}

fmt.Printf("created package metadata: %s\n", target)

Check warning on line 145 in internal/cmd/package.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/package.go#L145

Added line #L145 was not covered by tests
return f.Close()
}
Loading
Loading