Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[submodule "vendor/github.com/blakesmith/ar"]
path = vendor/github.com/blakesmith/ar
url = https://github.com/blakesmith/ar
url = https://github.com/xor-gate/ar
[submodule "vendor/golang.org/x/crypto"]
path = vendor/golang.org/x/crypto
url = https://github.com/golang/crypto
[submodule "vendor/gopkg.in/yaml.v2"]
path = vendor/gopkg.in/yaml.v2
url = http://gopkg.in/yaml.v2
url = https://gopkg.in/yaml.v2
2 changes: 0 additions & 2 deletions AUTHORS

This file was deleted.

4 changes: 4 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# debpkg Authors

* Jerry Jacobs ([@xor-gate](https://github.com/xor-gate))
* Rik van der Heijden ([@rikvdh](https://github.com/rikvdh))
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ fmt:

clean:
rm -Rf *.deb
rm -Rf *.tar.gz

.PHONY: clean
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ The feature list below describes the usability state of the project:
- [ ] [dpkg](http://manpages.ubuntu.com/manpages/precise/man1/dpkg.1.html) like tool with a subset of commands (--info, --extract, --vextract, --control)
- [ ] GPG sign package (implemented but doesnt work yet with `dpkg -i`)

**Known issues**

- Unable to specifiy file or folder destination when adding

## Why this package was created

This package was created due to the lack to debianize from other platforms (windows/mac/*bsd). Because
the whole debian package management system is glued together with Perl scripts and uses a bunch of Perl
modules. And I saw in the Golang world people crafting debian packages in a [very ugly way](https://github.com/syncthing/syncthing/blob/b8c5cf11428e42873c54847eb1968ac5ac04f7d3/build.go#L388-L444).
modules.

And converting a directory and files into a debian package is a pain without knowing the `deb`-file internals.

Expand Down
45 changes: 36 additions & 9 deletions ar.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ package debpkg

import (
"fmt"
"io"
"os"
"time"

"github.com/blakesmith/ar"
)

func addArFile(now time.Time, w *ar.Writer, name string, body []byte) error {
func addArFileFromBuffer(now time.Time, w *ar.Writer, name string, body []byte) error {
hdr := ar.Header{
Name: name,
Size: int64(len(body)),
Expand All @@ -29,6 +30,36 @@ func addArFile(now time.Time, w *ar.Writer, name string, body []byte) error {
return err
}

func addArFile(now time.Time, w *ar.Writer, dstname, filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}

stat, err := f.Stat()
if err != nil {
f.Close()
return err
}

hdr := ar.Header{
Name: dstname,
Size: stat.Size(),
Mode: 0644,
ModTime: now,
}

if err := w.WriteHeader(&hdr); err != nil {
f.Close()
return fmt.Errorf("cannot write file header: %v", err)
}

_, err = io.Copy(w, f)
f.Close()

return err
}

func (deb *DebPkg) createDebAr(filename string) error {
removeDeb := true
fd, err := os.Create(filename)
Expand All @@ -43,27 +74,23 @@ func (deb *DebPkg) createDebAr(filename string) error {
}
}()

if err := deb.data.tgz.Close(); err != nil {
return fmt.Errorf("cannot close tgz writer: %v", err)
}

now := time.Now()
w := ar.NewWriter(fd)

if err := w.WriteGlobalHeader(); err != nil {
return fmt.Errorf("cannot write ar header to deb file: %v", err)
}
if err := addArFile(now, w, "debian-binary", []byte(deb.debianBinary)); err != nil {
if err := addArFileFromBuffer(now, w, "debian-binary", []byte(deb.debianBinary)); err != nil {
return fmt.Errorf("cannot pack debian-binary: %v", err)
}
if err := addArFile(now, w, "control.tar.gz", deb.control.buf.Bytes()); err != nil {
if err := addArFile(now, w, "control.tar.gz", deb.control.tgz.Name()); err != nil {
return fmt.Errorf("cannot add control.tar.gz to deb: %v", err)
}
if err := addArFile(now, w, "data.tar.gz", deb.data.buf.Bytes()); err != nil {
if err := addArFile(now, w, "data.tar.gz", deb.data.tgz.Name()); err != nil {
return fmt.Errorf("cannot add data.tar.gz to deb: %v", err)
}
if deb.digest.clearsign != "" {
if err := addArFile(now, w, "digests.asc", []byte(deb.digest.clearsign)); err != nil {
if err := addArFileFromBuffer(now, w, "digests.asc", []byte(deb.digest.clearsign)); err != nil {
return fmt.Errorf("cannot add digests.asc to deb: %v", err)
}
}
Expand Down
4 changes: 4 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// TestExampleConfig verifies if the config example in the root is correctly loaded
func TestExampleConfig(t *testing.T) {
deb := New()
defer deb.Close()

err := deb.Config("debpkg.yml")
if err != nil {
Expand Down Expand Up @@ -45,6 +46,8 @@ func TestDefaultConfig(t *testing.T) {
}
f.Close()
deb := New()
defer deb.Close()

if err := deb.Config(f.Name()); err != nil {
t.Errorf("Unexpected error during load of empty config: %v", err)
}
Expand Down Expand Up @@ -74,6 +77,7 @@ func TestDefaultConfig(t *testing.T) {

func TestNonExistingConfig(t *testing.T) {
deb := New()
defer deb.Close()

err := deb.Config("/non/existant/config/file")
assert.Error(t, err, "error expected")
Expand Down
4 changes: 2 additions & 2 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ const (
VcsTypeSubversion VcsType = "Svn" // Subversion
)

const debPkgDebianBinary = "2.0\n"
const debPkgDebianFileExtension = "deb"
const debianBinaryVersion = "2.0\n"
const debianFileExtension = "deb"
56 changes: 33 additions & 23 deletions control.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,30 @@
package debpkg

import (
"bytes"
"fmt"
"math"
"strings"

"github.com/xor-gate/debpkg/lib/targzip"
)

type debPkgControl struct {
buf *bytes.Buffer
type control struct {
tgz *targzip.TarGzip
info debPkgControlInfo
info controlInfo
extra []string // Extra files added to the control.tar.gz. Typical usage is for conffiles, postinst, postrm, prerm.
conffiles []string // Conffiles which must be treated as configuration files
}

type debPkgControlInfoVersion struct {
type controlInfoVersion struct {
full string // Full version string. E.g "0.1.2"
major uint // Major version number
minor uint // Minor version number
patch uint // Patch version number
}

type debPkgControlInfo struct {
type controlInfo struct {
name string
version debPkgControlInfoVersion
version controlInfoVersion
architecture string
maintainer string
maintainerEmail string
Expand All @@ -54,10 +53,10 @@ func (deb *DebPkg) SetName(name string) {
deb.control.info.name = name
}

// SetVersion sets the full version string (mandatory), or user SetVersion* functions for "major.minor.patch"
// SetVersion sets the full version string (mandatory), or use SetVersion* functions for "major.minor.patch"
// The upstream_version may contain only alphanumerics ( A-Za-z0-9 ) and the characters . + - : ~
// (full stop, plus, hyphen, colon, tilde) and should start with a digit.
// NOTE: When the full string is set the SetVersion* function calls are ignored
// NOTE: When the full string is set the other SetVersion* function calls are ignored
// See: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
func (deb *DebPkg) SetVersion(version string) {
deb.control.info.version.full = version
Expand Down Expand Up @@ -203,39 +202,50 @@ func (deb *DebPkg) AddConffile(filename string) {
deb.control.conffiles = append(deb.control.conffiles, filename)
}

// verify the control file for validity
func (c *control) verify() error {
if c.info.name == "" {
return fmt.Errorf("empty package name")
}
if c.info.architecture == "" {
return fmt.Errorf("empty architecture")
}
return nil
}

func createControlTarGz(deb *DebPkg) error {
controlFile := []byte(deb.control.String(deb.data.size))
controlFile := []byte(deb.control.String(deb.data.tgz.Written()))
if err := deb.control.tgz.AddFileFromBuffer("control", controlFile); err != nil {
return err
}
if err := deb.control.tgz.AddFileFromBuffer("md5sums", []byte(deb.data.md5sums)); err != nil {
return err
}
if err := deb.control.tgz.Close(); err != nil {
return err
}
return nil
}

// Create control file for control.tar.gz
func (c *debPkgControl) String(installedSize int64) string {
var o string

// Autogenerate version string (e.g "1.2.3") when unset
if c.info.version.full == "" {
c.info.version.full = fmt.Sprintf("%d.%d.%d",
// Generate version string (e.g "1.2.3") from major,minor patch or from full version
func (c *control) version() string {
if c.info.version.full != "" {
return c.info.version.full
}
return fmt.Sprintf("%d.%d.%d",
c.info.version.major,
c.info.version.minor,
c.info.version.patch)
}
}

// Create control file for control.tar.gz
func (c *control) String(installedSize uint64) string {
var o string

o += fmt.Sprintf("Package: %s\n", c.info.name)
o += fmt.Sprintf("Version: %s\n", c.info.version.full)
o += fmt.Sprintf("Version: %s\n", c.version())
o += fmt.Sprintf("Architecture: %s\n", c.info.architecture)
o += fmt.Sprintf("Maintainer: %s <%s>\n",
c.info.maintainer,
c.info.maintainerEmail)
o += fmt.Sprintf("Installed-Size: %d\n", installedSize)
o += fmt.Sprintf("Installed-Size: %d\n", uint64(math.Ceil(float64(installedSize)/1024)))

if c.info.section != "" {
o += fmt.Sprintf("Section: %s\n", c.info.section)
Expand Down
46 changes: 46 additions & 0 deletions control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Description:
`
// Empty
deb := New()
defer deb.Close()

// architecture is auto-set when empty, this makes sure it is always set to amd64
deb.SetArchitecture("amd64")
Expand All @@ -46,6 +47,7 @@ Description:
`
// Empty
deb := New()
defer deb.Close()

// architecture is auto-set when empty, this makes sure it is always set to amd64
deb.SetArchitecture("amd64")
Expand All @@ -65,6 +67,7 @@ Description:
func TestControlFileSetVersionMajorMinorPatch(t *testing.T) {
// Empty
deb := New()
defer deb.Close()

deb.SetName("foobar")
deb.SetArchitecture("amd64")
Expand Down Expand Up @@ -137,6 +140,7 @@ Description: Golang package for creating (gpg signed) debian packages

// Empty
deb := New()
defer deb.Close()

deb.SetName("debpkg")
deb.SetVersion("0.0.0")
Expand All @@ -154,3 +158,45 @@ Description: Golang package for creating (gpg signed) debian packages
fmt.Printf("--- expected (len %d):\n'%s'\n--- got (len %d):\n'%s'---\n", len(controlExpect), controlExpect, len(control), control)
}
}

// Test correct output of a control file Installed-Size property
func TestControlInstalledSize(t *testing.T) {
controlExpect1K := `Package:
Version: 0.0.0
Architecture: amd64
Maintainer: <>
Installed-Size: 1
Description:
`
// Empty
deb := New()
defer deb.Close()

// architecture is auto-set when empty, this makes sure it is always set to amd64
deb.SetArchitecture("amd64")
control := deb.control.String(1024)

if control != controlExpect1K {
t.Error("Unexpected control file")
}

controlExpect2K := `Package:
Version: 0.0.0
Architecture: amd64
Maintainer: <>
Installed-Size: 2
Description:
`

// 1KByte + 1 byte
control = deb.control.String(1025)
if control != controlExpect2K {
t.Error("Unexpected control file")
}

// 2KByte
control = deb.control.String(2048)
if control != controlExpect2K {
t.Error("Unexpected control file")
}
}
Loading