Merge branch '71-changes-support'

This commit is contained in:
Andrey Smirnov
2015-03-17 00:19:28 +03:00
35 changed files with 1074 additions and 69 deletions
+188
View File
@@ -0,0 +1,188 @@
package deb
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)
// Changes is a result of .changes file parsing
type Changes struct {
Changes string
Distribution string
Files PackageFiles
BasePath, ChangesName string
TempDir string
Stanza Stanza
}
func NewChanges(path string) (*Changes, error) {
var err error
c := &Changes{
BasePath: filepath.Dir(path),
ChangesName: filepath.Base(path),
}
c.TempDir, err = ioutil.TempDir(os.TempDir(), "aptly")
if err != nil {
return nil, err
}
// copy .changes file into temporary directory
err = utils.CopyFile(filepath.Join(c.BasePath, c.ChangesName), filepath.Join(c.TempDir, c.ChangesName))
if err != nil {
return nil, err
}
return c, nil
}
// VerifyAndParse does optional signature verification and parses changes files
func (c *Changes) VerifyAndParse(acceptUnsigned, ignoreSignature bool, verifier utils.Verifier) error {
input, err := os.Open(filepath.Join(c.TempDir, c.ChangesName))
if err != nil {
return err
}
defer input.Close()
isClearSigned, err := verifier.IsClearSigned(input)
if err != nil {
return err
}
input.Seek(0, 0)
if !isClearSigned && !acceptUnsigned {
return fmt.Errorf(".changes file is not signed and unsigned processing hasn't been enabled")
}
if isClearSigned && !ignoreSignature {
err = verifier.VerifyClearsigned(input)
if err != nil {
return err
}
input.Seek(0, 0)
}
var text *os.File
if isClearSigned {
text, err = verifier.ExtractClearsigned(input)
if err != nil {
return err
}
defer text.Close()
} else {
text = input
}
reader := NewControlFileReader(text)
c.Stanza, err = reader.ReadStanza()
if err != nil {
return err
}
c.Distribution = c.Stanza["Distribution"]
c.Changes = c.Stanza["Changes"]
c.Files, err = c.Files.ParseSumFields(c.Stanza)
if err != nil {
return err
}
return nil
}
// Prepare creates temporary directory, copies file there and verifies checksums
func (c *Changes) Prepare() error {
var err error
for _, file := range c.Files {
if filepath.Dir(file.Filename) != "." {
return fmt.Errorf("file is not in the same folder as .changes file: %s", file.Filename)
}
file.Filename = filepath.Base(file.Filename)
err = utils.CopyFile(filepath.Join(c.BasePath, file.Filename), filepath.Join(c.TempDir, file.Filename))
if err != nil {
return err
}
}
for _, file := range c.Files {
var info utils.ChecksumInfo
info, err = utils.ChecksumsForFile(filepath.Join(c.TempDir, file.Filename))
if err != nil {
return err
}
if info.Size != file.Checksums.Size {
return fmt.Errorf("size mismatch: expected %v != obtained %v", file.Checksums.Size, info.Size)
}
if info.MD5 != file.Checksums.MD5 {
return fmt.Errorf("checksum mismatch MD5: expected %v != obtained %v", file.Checksums.MD5, info.MD5)
}
if info.SHA1 != file.Checksums.SHA1 {
return fmt.Errorf("checksum mismatch SHA1: expected %v != obtained %v", file.Checksums.SHA1, info.SHA1)
}
if info.SHA256 != file.Checksums.SHA256 {
return fmt.Errorf("checksum mismatch SHA256 expected %v != obtained %v", file.Checksums.SHA256, info.SHA256)
}
}
return nil
}
// Cleanup removes all temporary files
func (c *Changes) Cleanup() error {
if c.TempDir == "" {
return nil
}
return os.RemoveAll(c.TempDir)
}
// CollectChangesFiles walks filesystem collecting all .changes files
func CollectChangesFiles(locations []string, reporter aptly.ResultReporter) (changesFiles, failedFiles []string) {
for _, location := range locations {
info, err2 := os.Stat(location)
if err2 != nil {
reporter.Warning("Unable to process %s: %s", location, err2)
failedFiles = append(failedFiles, location)
continue
}
if info.IsDir() {
err2 = filepath.Walk(location, func(path string, info os.FileInfo, err3 error) error {
if err3 != nil {
return err3
}
if info.IsDir() {
return nil
}
if strings.HasSuffix(info.Name(), ".changes") {
changesFiles = append(changesFiles, path)
}
return nil
})
} else if strings.HasSuffix(info.Name(), ".changes") {
changesFiles = append(changesFiles, location)
}
}
sort.Strings(changesFiles)
return
}
+71
View File
@@ -0,0 +1,71 @@
package deb
import (
. "gopkg.in/check.v1"
"os"
"path/filepath"
)
type ChangesSuite struct {
}
var _ = Suite(&ChangesSuite{})
func (s *ChangesSuite) TestParseAndVerify(c *C) {
dir := c.MkDir()
path := filepath.Join(dir, "calamares.changes")
f, err := os.Create(path)
c.Assert(err, IsNil)
f.WriteString(changesFile)
f.Close()
changes, err := NewChanges(path)
c.Check(err, IsNil)
err = changes.VerifyAndParse(true, true, &NullVerifier{})
c.Check(err, IsNil)
c.Check(changes.Distribution, Equals, "sid")
c.Check(changes.Files, HasLen, 4)
c.Check(changes.Files[0].Filename, Equals, "calamares_0+git20141127.99.dsc")
c.Check(changes.Files[0].Checksums.Size, Equals, int64(1106))
c.Check(changes.Files[0].Checksums.MD5, Equals, "05fd8f3ffe8f362c5ef9bad2f936a56e")
c.Check(changes.Files[0].Checksums.SHA1, Equals, "79f10e955dab6eb25b7f7bae18213f367a3a0396")
c.Check(changes.Files[0].Checksums.SHA256, Equals, "35b3280a7b1ffe159a276128cb5c408d687318f60ecbb8ab6dedb2e49c4e82dc")
c.Check(changes.BasePath, Equals, dir)
}
var changesFile = `Format: 1.8
Date: Thu, 27 Nov 2014 13:24:53 +0000
Source: calamares
Binary: calamares calamares-dbg
Architecture: source amd64
Version: 0+git20141127.99
Distribution: sid
Urgency: medium
Maintainer: Rohan Garg <rohan@kde.org>
Changed-By: Rohan <rohan@kde.org>
Description:
calamares - distribution-independent installer framework
calamares-dbg - distribution-independent installer framework -- debug symbols
Changes:
calamares (0+git20141127.99) sid; urgency=medium
.
* Update from git
Checksums-Sha1:
79f10e955dab6eb25b7f7bae18213f367a3a0396 1106 calamares_0+git20141127.99.dsc
294c28e2c8e34e72ca9ee0d9da5c14f3bf4188db 2694800 calamares_0+git20141127.99.tar.xz
d6c26c04b5407c7511f61cb3e3de60c4a1d6c4ff 1698924 calamares_0+git20141127.99_amd64.deb
a3da632d193007b0d4a1aff73159fde1b532d7a8 12835902 calamares-dbg_0+git20141127.99_amd64.deb
Checksums-Sha256:
35b3280a7b1ffe159a276128cb5c408d687318f60ecbb8ab6dedb2e49c4e82dc 1106 calamares_0+git20141127.99.dsc
5576b9caaf814564830f95561227e4f04ee87b31da22c1371aab155cbf7ce395 2694800 calamares_0+git20141127.99.tar.xz
2e6e2f232ed7ffe52369928ebdf5436d90feb37840286ffba79e87d57a43a2e9 1698924 calamares_0+git20141127.99_amd64.deb
8dd926080ed7bad2e2439e37e49ce12d5f1357c5041b7da4d860a1041f878a8a 12835902 calamares-dbg_0+git20141127.99_amd64.deb
Files:
05fd8f3ffe8f362c5ef9bad2f936a56e 1106 devel optional calamares_0+git20141127.99.dsc
097e55c81abd8e5f30bb2eed90c2c1e9 2694800 devel optional calamares_0+git20141127.99.tar.xz
827fb3b12534241e119815d331e8197b 1698924 devel optional calamares_0+git20141127.99_amd64.deb
e6f8ce70f564d1f68cb57758b15b13e3 12835902 debug optional calamares-dbg_0+git20141127.99_amd64.deb`
+4 -6
View File
@@ -2,14 +2,12 @@ package deb
import (
"archive/tar"
"bufio"
"compress/gzip"
"fmt"
"github.com/mkrautz/goar"
"github.com/smira/aptly/utils"
"io"
"os"
"strings"
)
// GetControlFileFromDeb reads control file from deb package
@@ -69,16 +67,16 @@ func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, err
}
defer file.Close()
line, err := bufio.NewReader(file).ReadString('\n')
isClearSigned, err := verifier.IsClearSigned(file)
file.Seek(0, 0)
if err != nil {
return nil, err
}
file.Seek(0, 0)
var text *os.File
if strings.Index(line, "BEGIN PGP SIGN") != -1 {
if isClearSigned {
text, err = verifier.ExtractClearsigned(file)
if err != nil {
return nil, err
+1 -1
View File
@@ -10,7 +10,7 @@ import (
)
// CollectPackageFiles walks filesystem collecting all candidates for package files
func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, failedFiles []string, err error) {
func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, failedFiles []string) {
for _, location := range locations {
info, err2 := os.Stat(location)
if err2 != nil {
+10 -52
View File
@@ -114,62 +114,20 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
delete(input, "Version")
delete(input, "Architecture")
var err error
files := make(PackageFiles, 0, 3)
parseSums := func(field string, setter func(sum *utils.ChecksumInfo, data string)) error {
for _, line := range strings.Split(input[field], "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) != 3 {
return fmt.Errorf("unparseable hash sum line: %#v", line)
}
size, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return fmt.Errorf("unable to parse size: %s", err)
}
filename := filepath.Base(parts[2])
found := false
pos := 0
for i, file := range files {
if file.Filename == filename {
found = true
pos = i
break
}
}
if !found {
files = append(files, PackageFile{Filename: filename, downloadPath: input["Directory"]})
pos = len(files) - 1
}
files[pos].Checksums.Size = size
setter(&files[pos].Checksums, parts[0])
}
delete(input, field)
return nil
}
err := parseSums("Files", func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data })
files, err = files.ParseSumFields(input)
if err != nil {
return nil, err
}
err = parseSums("Checksums-Sha1", func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data })
if err != nil {
return nil, err
}
err = parseSums("Checksums-Sha256", func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data })
if err != nil {
return nil, err
delete(input, "Files")
delete(input, "Checksums-Sha1")
delete(input, "Checksums-Sha256")
for i := range files {
files[i].downloadPath = input["Directory"]
}
result.UpdateFiles(files)
+65
View File
@@ -2,12 +2,15 @@ package deb
import (
"encoding/binary"
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"hash/fnv"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)
// PackageFile is a single file entry in package
@@ -76,3 +79,65 @@ func (files PackageFiles) Swap(i, j int) {
func (files PackageFiles) Less(i, j int) bool {
return files[i].Filename < files[j].Filename
}
func (files PackageFiles) parseSumField(input string, setter func(sum *utils.ChecksumInfo, data string)) (PackageFiles, error) {
for _, line := range strings.Split(input, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) < 3 {
return nil, fmt.Errorf("unparseable hash sum line: %#v", line)
}
size, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("unable to parse size: %s", err)
}
filename := filepath.Base(parts[len(parts)-1])
found := false
pos := 0
for i, file := range files {
if file.Filename == filename {
found = true
pos = i
break
}
}
if !found {
files = append(files, PackageFile{Filename: filename})
pos = len(files) - 1
}
files[pos].Checksums.Size = size
setter(&files[pos].Checksums, parts[0])
}
return files, nil
}
// ParseSumFields populates PackageFiles by parsing stanza checksums fields
func (files PackageFiles) ParseSumFields(stanza Stanza) (PackageFiles, error) {
var err error
files, err = files.parseSumField(stanza["Files"], func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data })
if err != nil {
return nil, err
}
files, err = files.parseSumField(stanza["Checksums-Sha1"], func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data })
if err != nil {
return nil, err
}
files, err = files.parseSumField(stanza["Checksums-Sha256"], func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data })
if err != nil {
return nil, err
}
return files, nil
}
+4
View File
@@ -43,6 +43,10 @@ func (n *NullVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File,
return
}
func (n *NullVerifier) IsClearSigned(clearsign io.Reader) (bool, error) {
return false, nil
}
type PackageListMixinSuite struct {
p1, p2, p3 *Package
list *PackageList