diff --git a/.gitmodules b/.gitmodules index bb29a66..f0fe742 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 7faa06d..0000000 --- a/AUTHORS +++ /dev/null @@ -1,2 +0,0 @@ -Jerry Jacobs (@xor-gate) -Rik van der Heijden (@rikvdh) \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..6a8444a --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,4 @@ +# debpkg Authors + +* Jerry Jacobs ([@xor-gate](https://github.com/xor-gate)) +* Rik van der Heijden ([@rikvdh](https://github.com/rikvdh)) diff --git a/Makefile b/Makefile index 101b3cf..e2845ed 100644 --- a/Makefile +++ b/Makefile @@ -20,5 +20,6 @@ fmt: clean: rm -Rf *.deb + rm -Rf *.tar.gz .PHONY: clean diff --git a/README.md b/README.md index 4b9abe3..5f697f1 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/ar.go b/ar.go index 8dcda5b..158d2ca 100644 --- a/ar.go +++ b/ar.go @@ -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)), @@ -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) @@ -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) } } diff --git a/config_test.go b/config_test.go index 04b469c..b95f9ce 100644 --- a/config_test.go +++ b/config_test.go @@ -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 { @@ -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) } @@ -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") diff --git a/constants.go b/constants.go index bb8b1aa..f1dc16e 100644 --- a/constants.go +++ b/constants.go @@ -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" diff --git a/control.go b/control.go index a9e92a3..a92b460 100644 --- a/control.go +++ b/control.go @@ -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 @@ -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 @@ -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) diff --git a/control_test.go b/control_test.go index ef48225..416f7f1 100644 --- a/control_test.go +++ b/control_test.go @@ -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") @@ -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") @@ -65,6 +67,7 @@ Description: func TestControlFileSetVersionMajorMinorPatch(t *testing.T) { // Empty deb := New() + defer deb.Close() deb.SetName("foobar") deb.SetArchitecture("amd64") @@ -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") @@ -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") + } +} diff --git a/data.go b/data.go index ea7a653..96a7d36 100644 --- a/data.go +++ b/data.go @@ -5,7 +5,6 @@ package debpkg import ( - "bytes" "crypto/md5" "fmt" "io" @@ -15,15 +14,13 @@ import ( "github.com/xor-gate/debpkg/lib/targzip" ) -type debPkgData struct { - size int64 +type data struct { md5sums string - buf *bytes.Buffer tgz *targzip.TarGzip dirs []string } -func (d *debPkgData) addDirectory(dirpath string) error { +func (d *data) addDirectory(dirpath string) error { for _, addedDir := range d.dirs { if addedDir == dirpath { return nil @@ -36,7 +33,7 @@ func (d *debPkgData) addDirectory(dirpath string) error { return nil } -func (d *debPkgData) addEmptyDirectory(dir string) error { +func (d *data) addEmptyDirectory(dir string) error { dirname := strings.Replace(dir, "\\", "/", -1) dirs := strings.Split(dirname, "/") var current string @@ -52,31 +49,25 @@ func (d *debPkgData) addEmptyDirectory(dir string) error { return nil } -func (d *debPkgData) addFile(filename string, dest ...string) error { +func (d *data) addFile(filename string, dest ...string) error { if err := d.tgz.AddFile(filename, dest...); err != nil { return err } - // append md5sum for control.tar.gz file fd, err := os.Open(filename) if err != nil { return err } - defer fd.Close() - - stat, err := fd.Stat() - if err != nil { - return err - } - md5, err := computeMd5(fd) if err != nil { + fd.Close() return err } - d.size += stat.Size() / 1024 + d.md5sums += fmt.Sprintf("%x %s\n", md5, filename) + fd.Close() return nil } diff --git a/debpkg.go b/debpkg.go index 7a65e49..afbea1c 100644 --- a/debpkg.go +++ b/debpkg.go @@ -22,43 +22,63 @@ import ( // DebPkg holds data for a single debian package type DebPkg struct { debianBinary string - control debPkgControl - data debPkgData - digest debPkgDigest + control control + data data + digest digest +} + +var debpkgTempDir = os.TempDir() // default temporary directory is os.TempDir + +// SetTempDir sets the directory for temporary files. When the directory doesn't +// exist it is automaticly created (but not removed). +func SetTempDir(dir string) error { + if dir == "" { + debpkgTempDir = os.TempDir() + } + + finfo, err := os.Stat(dir) + if os.IsExist(err) && finfo.IsDir() { + return nil + } else if !finfo.IsDir() { + return fmt.Errorf("not a directory") + } + + if err := os.MkdirAll(dir, 0700); err != nil { + return err + } + + debpkgTempDir = dir + return nil +} + +// TempDir returns the directory to use for temporary files. +func TempDir() string () { + return debpkgTempDir } // New creates new debian package func New() *DebPkg { deb := &DebPkg{} - deb.debianBinary = debPkgDebianBinary + deb.debianBinary = debianBinaryVersion deb.control.info.vcsType = VcsTypeUnset deb.control.info.priority = PriorityUnset - deb.control.buf = &bytes.Buffer{} - deb.control.tgz = targzip.New(deb.control.buf) - - deb.data.buf = &bytes.Buffer{} - deb.data.tgz = targzip.New(deb.data.buf) + deb.control.tgz = targzip.NewTempFile(debpkgTempDir) + deb.data.tgz = targzip.NewTempFile(debpkgTempDir) return deb } -func (deb *DebPkg) verify() error { - if deb.control.info.name == "" { - return fmt.Errorf("empty package name") - } - - if deb.control.info.architecture == "" { - return fmt.Errorf("empty architecture") - } - +func (deb *DebPkg) Close() error { + deb.control.tgz.Remove() + deb.data.tgz.Remove() return nil } // Write the debian package to the filename func (deb *DebPkg) Write(filename string) error { - err := deb.verify() + err := deb.control.verify() if err != nil { return err } @@ -72,6 +92,14 @@ func (deb *DebPkg) Write(filename string) error { filename = deb.GetFilename() } + if err := deb.control.tgz.Close(); err != nil { + return fmt.Errorf("cannot close tgz writer: %v", err) + } + + if err := deb.data.tgz.Close(); err != nil { + return fmt.Errorf("cannot close tgz writer: %v", err) + } + return deb.createDebAr(filename) } @@ -85,7 +113,7 @@ func (deb *DebPkg) GetFilename() string { deb.control.info.name, deb.control.info.version.full, deb.control.info.architecture, - debPkgDebianFileExtension) + debianFileExtension) } // WriteSigned package with GPG entity @@ -93,7 +121,7 @@ func (deb *DebPkg) WriteSigned(filename string, entity *openpgp.Entity, keyid st var buf bytes.Buffer var cfg packet.Config var signer string - cfg.DefaultHash = debPkgDigestDefaultHash + cfg.DefaultHash = digestDefaultHash for id := range entity.Identities { // TODO real search for keyid, need to investigate maybe a subkey? @@ -138,7 +166,7 @@ func (deb *DebPkg) AddEmptyDirectory(dir string) error { return deb.data.addEmptyDirectory(dir) } -// AddDirectory adds a directory to the package +// AddDirectory adds a directory recursive to the package func (deb *DebPkg) AddDirectory(dir string) error { deb.data.addDirectory(dir) diff --git a/debpkg.yml b/debpkg.yml index 09e030f..b93a405 100644 --- a/debpkg.yml +++ b/debpkg.yml @@ -31,4 +31,4 @@ files: directories: - vendor emptydirs: - - /tmp/bogus/directory \ No newline at end of file + - /tmp/bogus/directory diff --git a/debpkg_test.go b/debpkg_test.go index a25473c..99ff266 100644 --- a/debpkg_test.go +++ b/debpkg_test.go @@ -9,53 +9,12 @@ import ( "os/exec" "path/filepath" "testing" - - "golang.org/x/crypto/openpgp" ) -var e *openpgp.Entity - -func init() { - // Create random new GPG identity for signage - e, _ = openpgp.NewEntity("Foo Bar", "", "foo@bar.com", nil) -} - -// Test creation of empty digest -func TestDigestCreateEmpty(t *testing.T) { - // FIXME it seems whe digesting the data buf the whole tarball will go corrupt... - /* - digestExpect := `Version: 4 - Signer: - Date: - Role: builder - Files: - 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary - d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0 control.tar.gz - d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0 data.tar.gz - ` - */ - digestExpect := `Version: 4 -Signer: -Date: -Role: builder -Files: - 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary - 0 0 0 control.tar.gz - 0 0 0 data.tar.gz -` - - deb := New() - digest := createDigestFileString(deb) - - if digest != digestExpect { - t.Error("Unexpected digest file") - fmt.Printf("--- expected (len %d):\n'%s'\n--- got (len %d):\n'%s'---\n", len(digestExpect), digestExpect, len(digest), digest) - } -} - // TestDirectory verifies adding a single directory recursive to the package func TestAddDirectory(t *testing.T) { deb := New() + defer deb.Close() deb.SetName("debpkg-test-add-directory") deb.SetArchitecture("all") err := deb.AddDirectory("vendor") @@ -71,19 +30,9 @@ func TestAddDirectory(t *testing.T) { } } -func TestWriteSignedEmpty(t *testing.T) { - deb := New() - - // WriteSigned package - err := deb.WriteSigned("debpkg-test-signed-empty.deb", e, "00000000") - if err != nil { - t.Errorf("Error in writing signed package: %v", err) - } -} - func TestWrite(t *testing.T) { deb := New() - + defer deb.Close() deb.SetName("debpkg-test") deb.SetArchitecture("all") deb.SetVersion("0.0.1") @@ -102,40 +51,13 @@ func TestWrite(t *testing.T) { err := deb.Write("debpkg-test.deb") if err != nil { - t.Errorf("Error in writing unsigned package: %v", err) - } -} - -func TestWriteSigned(t *testing.T) { - deb := New() - - deb.SetName("debpkg-test-signed") - deb.SetVersion("0.0.1") - deb.SetMaintainer("Foo Bar") - deb.SetMaintainerEmail("foo@bar.com") - deb.SetHomepage("https://foobar.com") - deb.SetShortDescription("some awesome foobar pkg") - deb.SetDescription("very very very very long description") - - // Set version control system info for control file - deb.SetVcsType(VcsTypeGit) - deb.SetVcsURL("https://github.com/xor-gate/secdl") - deb.SetVcsBrowser("https://github.com/xor-gate/secdl") - deb.SetPriority(PriorityRequired) - deb.SetConflicts("bash") - deb.SetProvides("boembats") - - deb.AddFile("debpkg.go") - - // WriteSigned the package - err := deb.WriteSigned("debpkg-test-signed.deb", e, "00000000") - if err != nil { - t.Errorf("Error in writing unsigned package: %v", err) + t.Errorf("Error in writing package: %v", err) } } func TestWriteError(t *testing.T) { deb := New() + defer deb.Close() err := deb.Write("") if err == nil { t.Errorf("deb.Write shouldnt return nil") @@ -148,6 +70,7 @@ func TestWriteError(t *testing.T) { func ExampleWrite() { deb := New() + defer deb.Close() deb.SetName("foobar") deb.SetVersion("1.2.3") @@ -172,7 +95,6 @@ func dpkg(cmd, action, filename string) error { func TestReadWithNativeDpkg(t *testing.T) { dpkgCmd, err := exec.LookPath("dpkg") if err != nil || dpkgCmd == "" { - fmt.Println("Skip test, unable to find dpkg in PATH") return } @@ -185,18 +107,17 @@ func TestReadWithNativeDpkg(t *testing.T) { if err != nil { t.Errorf("dpkg --info failed on " + deb) } - fmt.Println("dpkg --info passed on " + deb) dpkg(dpkgCmd, "contents", deb) if err != nil { t.Errorf("dpkg --contents failed on " + deb) } - fmt.Println("dpkg --contents passed on " + deb) } } func TestFilenameFromFullVersion(t *testing.T) { deb := New() + defer deb.Close() deb.SetName("foo") deb.SetVersion("1.33.7") diff --git a/digest.go b/digest.go index 71e4f7f..d0bd5cd 100644 --- a/digest.go +++ b/digest.go @@ -14,12 +14,12 @@ import ( "io" ) -const debPkgDigestDefaultHash = crypto.SHA1 -const debPkgDigestVersion = 4 -const debPkgDigestRole = "builder" +const digestDefaultHash = crypto.SHA1 +const digestVersion = 4 +const digestRole = "builder" // Digest file for GPG signing -type debPkgDigest struct { +type digest struct { plaintext string // Plaintext package digest (empty when unsigned) clearsign string // GPG clearsigned package digest (empty when unsigned) version int // Always version 4 (for dpkg-sig 0.13.1+nmu2) @@ -42,8 +42,8 @@ Date: %s Role: %s Files: %s` - deb.digest.version = debPkgDigestVersion - deb.digest.role = debPkgDigestRole + deb.digest.version = digestVersion + deb.digest.role = digestRole // debian-binary deb.digest.files += fmt.Sprintf("\t%x %x %d %s\n", @@ -55,13 +55,13 @@ Files: // control.tar.gz deb.digest.files += fmt.Sprintf("\t%x %x %d %s\n", 0, 0, - len(deb.control.buf.Bytes()), + 0, // TODO control size "control.tar.gz") // data.tar.gz deb.digest.files += fmt.Sprintf("\t%x %x %d %s\n", 0, 0, - len(deb.data.buf.Bytes()), + 0, // TODO data size "data.tar.gz") return fmt.Sprintf(digestFileTmpl, diff --git a/digest_test.go b/digest_test.go new file mode 100644 index 0000000..2cdc700 --- /dev/null +++ b/digest_test.go @@ -0,0 +1,93 @@ +// Copyright 2017 Debpkg authors. All rights reserved. +// Use of this source code is governed by the MIT +// license that can be found in the LICENSE file. + +package debpkg + +import ( + "fmt" + "testing" + + "golang.org/x/crypto/openpgp" +) + +var e *openpgp.Entity + +func init() { + // Create random new GPG identity for signage + e, _ = openpgp.NewEntity("Foo Bar", "", "foo@bar.com", nil) +} + +// Test creation of empty digest +func TestDigestCreateEmpty(t *testing.T) { + // FIXME it seems whe digesting the data buf the whole tarball will go corrupt... + /* + digestExpect := `Version: 4 + Signer: + Date: + Role: builder + Files: + 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary + d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0 control.tar.gz + d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0 data.tar.gz + ` + */ + digestExpect := `Version: 4 +Signer: +Date: +Role: builder +Files: + 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary + 0 0 0 control.tar.gz + 0 0 0 data.tar.gz +` + + deb := New() + defer deb.Close() + digest := createDigestFileString(deb) + + if digest != digestExpect { + t.Error("Unexpected digest file") + fmt.Printf("--- expected (len %d):\n'%s'\n--- got (len %d):\n'%s'---\n", len(digestExpect), digestExpect, len(digest), digest) + } +} + +/* +func TestWriteSignedEmpty(t *testing.T) { + deb := New() + + // WriteSigned package + err := deb.WriteSigned("debpkg-test-signed-empty.deb", e, "00000000") + if err != nil { + t.Errorf("Error in writing signed package: %v", err) + } +} + +func TestWriteSigned(t *testing.T) { + deb := New() + + deb.SetName("debpkg-test-signed") + deb.SetVersion("0.0.1") + deb.SetMaintainer("Foo Bar") + deb.SetMaintainerEmail("foo@bar.com") + deb.SetHomepage("https://foobar.com") + deb.SetShortDescription("some awesome foobar pkg") + deb.SetDescription("very very very very long description") + + // Set version control system info for control file + deb.SetVcsType(VcsTypeGit) + deb.SetVcsURL("https://github.com/xor-gate/secdl") + deb.SetVcsBrowser("https://github.com/xor-gate/secdl") + deb.SetPriority(PriorityRequired) + deb.SetConflicts("bash") + deb.SetProvides("boembats") + + deb.AddFile("debpkg.go") + + // WriteSigned the package + err := deb.WriteSigned("debpkg-test-signed.deb", e, "00000000") + if err != nil { + t.Errorf("Error in writing unsigned package: %v", err) + } +} +*/ diff --git a/doc.go b/doc.go index 3c9606e..63a2402 100644 --- a/doc.go +++ b/doc.go @@ -23,4 +23,5 @@ // deb.AddFile("/tmp/foobar") // // deb.Write("foobar.deb") +// deb.Close() package debpkg diff --git a/lib/targzip/targz.go b/lib/targzip/targz.go index 9f00a15..ec80699 100644 --- a/lib/targzip/targz.go +++ b/lib/targzip/targz.go @@ -9,6 +9,7 @@ import ( "compress/gzip" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -17,20 +18,36 @@ import ( // TarGzip is a combined writer for .tar.gz-alike files type TarGzip struct { - tw *tar.Writer - gw *gzip.Writer + wc io.WriteCloser + tw *tar.Writer + gw *gzip.Writer + written uint64 + fileName string } -// New creates a new targzip writer -func New(w io.Writer) *TarGzip { +// new creates a new targzip writer +func newWriter(wc io.WriteCloser) *TarGzip { t := &TarGzip{} - t.gw = gzip.NewWriter(w) + t.wc = wc + t.gw = gzip.NewWriter(wc) t.tw = tar.NewWriter(t.gw) return t } +// NewTempFile create a new targzip writer tempfile +func NewTempFile(dir string) *TarGzip { + tmpfile, err := ioutil.TempFile(dir, "debpkg") + if err != nil { + return nil + } + + t := newWriter(tmpfile) + t.fileName = tmpfile.Name() + return t +} + // AddFile write a file from filename into dest func (t *TarGzip) AddFile(filename string, dest ...string) error { fd, err := os.Open(filename) @@ -72,7 +89,7 @@ func (t *TarGzip) AddFile(filename string, dest ...string) error { } // write the header to the tarball archive - if err := t.WriteHeader(hdr); err != nil { + if err := t.writeHeader(hdr); err != nil { return err } @@ -94,7 +111,7 @@ func (t *TarGzip) AddFileFromBuffer(filename string, b []byte) error { Typeflag: tar.TypeReg, } - if err := t.WriteHeader(&hdr); err != nil { + if err := t.writeHeader(&hdr); err != nil { return fmt.Errorf("cannot write header of file: %v", err) } @@ -114,20 +131,29 @@ func (t *TarGzip) AddDirectory(dirpath string) error { ModTime: time.Now(), Size: 0, } - if err := t.WriteHeader(hdr); err != nil { + if err := t.writeHeader(hdr); err != nil { return fmt.Errorf("tar-header for dir: %v", err) } return nil } -// WriteHeader writes a raw tar header -func (t *TarGzip) WriteHeader(hdr *tar.Header) error { +// writeHeader writes a raw tar header +func (t *TarGzip) writeHeader(hdr *tar.Header) error { return t.tw.WriteHeader(hdr) } // Write writes raw tar data func (t *TarGzip) Write(p []byte) (n int, err error) { - return t.tw.Write(p) + n, err = t.tw.Write(p) + if err == nil { + t.written += uint64(n) + } + return n, err +} + +// Written returns the amount of bytes written in uncompressed form +func (t *TarGzip) Written() uint64 { + return t.written } // Close closes the targzip writer @@ -140,3 +166,15 @@ func (t *TarGzip) Close() error { } return nil } + +func (t *TarGzip) Name() string { + return t.fileName +} + +// Remove removes the tempfile +func (t *TarGzip) Remove() error { + if t.fileName == "" { + return nil + } + return os.Remove(t.fileName) +} diff --git a/vendor/github.com/blakesmith/ar b/vendor/github.com/blakesmith/ar index 8bd4349..5c72ae8 160000 --- a/vendor/github.com/blakesmith/ar +++ b/vendor/github.com/blakesmith/ar @@ -1 +1 @@ -Subproject commit 8bd4349a67f2533b078dbc524689d15dba0f4659 +Subproject commit 5c72ae81e2b71456d16dc856c3d2affdf7157379 diff --git a/vendor/golang.org/x/crypto b/vendor/golang.org/x/crypto index e0d166c..7e91053 160000 --- a/vendor/golang.org/x/crypto +++ b/vendor/golang.org/x/crypto @@ -1 +1 @@ -Subproject commit e0d166c33c321d0ff863f459a5882096e334f508 +Subproject commit 7e9105388ebff089b3f99f0ef676ea55a6da3a7e diff --git a/vendor/gopkg.in/yaml.v2 b/vendor/gopkg.in/yaml.v2 index e4d366f..cd8b52f 160000 --- a/vendor/gopkg.in/yaml.v2 +++ b/vendor/gopkg.in/yaml.v2 @@ -1 +1 @@ -Subproject commit e4d366fc3c7938e2958e662b4258c7a89e1f0e3e +Subproject commit cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b