From 8e20daa9276f058cc93ab744b086825247a8c45e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 13 Mar 2015 18:42:34 +0300 Subject: [PATCH 01/19] Refactor out IsClearSigned to separate method. #71 --- deb/deb.go | 10 ++++------ deb/remote_test.go | 4 ++++ utils/gpg.go | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/deb/deb.go b/deb/deb.go index c4985558..2a31125c 100644 --- a/deb/deb.go +++ b/deb/deb.go @@ -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 diff --git a/deb/remote_test.go b/deb/remote_test.go index 4bdfbbaf..09fe20b9 100644 --- a/deb/remote_test.go +++ b/deb/remote_test.go @@ -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 diff --git a/utils/gpg.go b/utils/gpg.go index 080aa74a..b66ad1af 100644 --- a/utils/gpg.go +++ b/utils/gpg.go @@ -1,6 +1,7 @@ package utils import ( + "bufio" "bytes" "fmt" "io" @@ -28,6 +29,7 @@ type Verifier interface { InitKeyring() error AddKeyring(keyring string) VerifyDetachedSignature(signature, cleartext io.Reader) error + IsClearSigned(clearsigned io.Reader) (bool, error) VerifyClearsigned(clearsigned io.Reader) error ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error) } @@ -257,6 +259,22 @@ func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader) er return g.runGpgv(args, "detached signature") } +// IsClearSigned returns true if file contains signature +func (g *GpgVerifier) IsClearSigned(clearsigned io.Reader) (bool, error) { + scanner := bufio.NewScanner(clearsigned) + for scanner.Scan() { + if strings.Index(scanner.Text(), "BEGIN PGP SIGN") != -1 { + return true, nil + } + } + + if err := scanner.Err(); err != nil { + return false, err + } + + return false, nil +} + // VerifyClearsigned verifies clearsigned file using gpgv func (g *GpgVerifier) VerifyClearsigned(clearsigned io.Reader) error { args := g.argsKeyrings() From 69eff97b343b7c7bc0ceea764d45ba5fb022338e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 13 Mar 2015 20:53:53 +0300 Subject: [PATCH 02/19] Relax .dsc checkshums parsing. #71 --- deb/package.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deb/package.go b/deb/package.go index f032a222..8dddda0c 100644 --- a/deb/package.go +++ b/deb/package.go @@ -124,7 +124,7 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) { } parts := strings.Fields(line) - if len(parts) != 3 { + if len(parts) < 3 { return fmt.Errorf("unparseable hash sum line: %#v", line) } @@ -133,7 +133,7 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) { return fmt.Errorf("unable to parse size: %s", err) } - filename := filepath.Base(parts[2]) + filename := filepath.Base(parts[len(parts)-1]) found := false pos := 0 From 903e999cdcb55894fdfeb874dcc63f4f1012020b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 13 Mar 2015 21:23:22 +0300 Subject: [PATCH 03/19] Refactor checksum parsing out of package parsing code. #71 --- deb/package.go | 62 +++++++----------------------------------- deb/package_files.go | 65 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/deb/package.go b/deb/package.go index 8dddda0c..ba0d6956 100644 --- a/deb/package.go +++ b/deb/package.go @@ -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[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, 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) diff --git a/deb/package_files.go b/deb/package_files.go index 31a37cbe..489b67b1 100644 --- a/deb/package_files.go +++ b/deb/package_files.go @@ -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 +} From a7103623af8ffe746633bb4b7538ee499e08d331 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 13 Mar 2015 21:46:32 +0300 Subject: [PATCH 04/19] .changes files parsing. #71 --- deb/changes.go | 72 +++++++++++++++++++++++++++++++++++++++++++++ deb/changes_test.go | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 deb/changes.go create mode 100644 deb/changes_test.go diff --git a/deb/changes.go b/deb/changes.go new file mode 100644 index 00000000..913dee9c --- /dev/null +++ b/deb/changes.go @@ -0,0 +1,72 @@ +package deb + +import ( + "fmt" + "github.com/smira/aptly/utils" + "os" +) + +// Changes is a result of .changes file parsing +type Changes struct { + Changes string + Distribution string + Files PackageFiles +} + +// ParseChangesFile does optional signature verification and parses changes files +func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifier utils.Verifier) (*Changes, error) { + input, err := os.Open(path) + if err != nil { + return nil, err + } + defer input.Close() + + isClearSigned, err := verifier.IsClearSigned(input) + if err != nil { + return nil, err + } + + input.Seek(0, 0) + + if !isClearSigned && !acceptUnsigned { + return nil, 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 nil, err + } + input.Seek(0, 0) + } + + var text *os.File + + if isClearSigned { + text, err = verifier.ExtractClearsigned(input) + if err != nil { + return nil, err + } + defer text.Close() + } else { + text = input + } + + reader := NewControlFileReader(text) + stanza, err := reader.ReadStanza() + if err != nil { + return nil, err + } + + result := &Changes{ + Distribution: stanza["Distribution"], + Changes: stanza["Changes"], + } + + result.Files, err = result.Files.ParseSumFields(stanza) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/deb/changes_test.go b/deb/changes_test.go new file mode 100644 index 00000000..eae5ee18 --- /dev/null +++ b/deb/changes_test.go @@ -0,0 +1,67 @@ +package deb + +import ( + . "gopkg.in/check.v1" + "os" + "path/filepath" +) + +type ChangesSuite struct { +} + +var _ = Suite(&ChangesSuite{}) + +func (s *ChangesSuite) TestParseChanges(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 := ParseChangesFile(path, 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") +} + +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 +Changed-By: Rohan +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` From 5b4563f250e62d91e91087f0c0c6290ff3441a0b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 15 Mar 2015 18:15:46 +0300 Subject: [PATCH 05/19] Simple CopyFile utility function. #71 --- utils/copyfile.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 utils/copyfile.go diff --git a/utils/copyfile.go b/utils/copyfile.go new file mode 100644 index 00000000..c9689a58 --- /dev/null +++ b/utils/copyfile.go @@ -0,0 +1,28 @@ +package utils + +import ( + "io" + "os" +) + +// CopyFile copeis file from src to dst, not preserving attributes +func CopyFile(src, dst string) error { + sf, err := os.Open(src) + if err != nil { + return err + } + defer sf.Close() + + df, err := os.Create(dst) + if err != nil { + return err + } + + _, err = io.Copy(df, sf) + if err != nil { + df.Close() + return err + } + + return df.Close() +} From 2f3b5f5a514d51cd5086bb3b61ab559158bd674b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 15 Mar 2015 18:16:11 +0300 Subject: [PATCH 06/19] Refactor Changes structure, new method prepare to verify checksums and copy files. #71 --- deb/changes.go | 113 ++++++++++++++++++++++++++++++++++++-------- deb/changes_test.go | 8 +++- 2 files changed, 100 insertions(+), 21 deletions(-) diff --git a/deb/changes.go b/deb/changes.go index 913dee9c..1df9bdda 100644 --- a/deb/changes.go +++ b/deb/changes.go @@ -3,39 +3,66 @@ package deb import ( "fmt" "github.com/smira/aptly/utils" + "io/ioutil" "os" + "path/filepath" ) // Changes is a result of .changes file parsing type Changes struct { - Changes string - Distribution string - Files PackageFiles + Changes string + Distribution string + Files PackageFiles + BasePath, ChangesName string + TempDir string + Stanza Stanza } -// ParseChangesFile does optional signature verification and parses changes files -func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifier utils.Verifier) (*Changes, error) { - input, err := os.Open(path) +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 nil, err + return err } input.Seek(0, 0) if !isClearSigned && !acceptUnsigned { - return nil, fmt.Errorf(".changes file is not signed and unsigned processing hasn't been enabled") + 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 nil, err + return err } input.Seek(0, 0) } @@ -45,7 +72,7 @@ func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifie if isClearSigned { text, err = verifier.ExtractClearsigned(input) if err != nil { - return nil, err + return err } defer text.Close() } else { @@ -53,20 +80,68 @@ func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifie } reader := NewControlFileReader(text) - stanza, err := reader.ReadStanza() + c.Stanza, err = reader.ReadStanza() if err != nil { - return nil, err + return err } - result := &Changes{ - Distribution: stanza["Distribution"], - Changes: stanza["Changes"], - } + c.Distribution = c.Stanza["Distribution"] + c.Changes = c.Stanza["Changes"] - result.Files, err = result.Files.ParseSumFields(stanza) + c.Files, err = c.Files.ParseSumFields(c.Stanza) if err != nil { - return nil, err + return err } - return result, nil + 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.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) } diff --git a/deb/changes_test.go b/deb/changes_test.go index eae5ee18..058e7fb8 100644 --- a/deb/changes_test.go +++ b/deb/changes_test.go @@ -11,7 +11,7 @@ type ChangesSuite struct { var _ = Suite(&ChangesSuite{}) -func (s *ChangesSuite) TestParseChanges(c *C) { +func (s *ChangesSuite) TestParseAndVerify(c *C) { dir := c.MkDir() path := filepath.Join(dir, "calamares.changes") @@ -21,7 +21,10 @@ func (s *ChangesSuite) TestParseChanges(c *C) { f.WriteString(changesFile) f.Close() - changes, err := ParseChangesFile(path, true, true, &NullVerifier{}) + changes, err := NewChanges(path) + c.Check(err, IsNil) + + err = changes.VerifyAndParse(true, true, &NullVerifier{}) c.Check(err, IsNil) c.Check(changes.Distribution, Equals, "sid") @@ -31,6 +34,7 @@ func (s *ChangesSuite) TestParseChanges(c *C) { 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 From 4a6d6a85f796d435dc5147654bf7a87f986f708a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 15 Mar 2015 20:06:59 +0300 Subject: [PATCH 07/19] Remove unused error argument. --- api/repos.go | 7 +------ cmd/repo_add.go | 5 +---- deb/import.go | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/api/repos.go b/api/repos.go index 17e5b550..915d62f8 100644 --- a/api/repos.go +++ b/api/repos.go @@ -315,12 +315,7 @@ func apiReposPackageFromDir(c *gin.Context) { sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))} } - packageFiles, failedFiles, err = deb.CollectPackageFiles(sources, reporter) - - if err != nil { - c.Fail(500, fmt.Errorf("unable to collect package files: %s", err)) - return - } + packageFiles, failedFiles = deb.CollectPackageFiles(sources, reporter) list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil) if err != nil { diff --git a/cmd/repo_add.go b/cmd/repo_add.go index 7529a105..38322367 100644 --- a/cmd/repo_add.go +++ b/cmd/repo_add.go @@ -42,10 +42,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error { var packageFiles, failedFiles []string - packageFiles, failedFiles, err = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()}) - if err != nil { - return fmt.Errorf("unable to collect package files: %s", err) - } + packageFiles, failedFiles = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()}) var processedFiles, failedFiles2 []string diff --git a/deb/import.go b/deb/import.go index 2aa9a037..dfc02577 100644 --- a/deb/import.go +++ b/deb/import.go @@ -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 { From 615a5ee3f9d043698d05767fbd77e3bbada955fe Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 15 Mar 2015 21:21:23 +0300 Subject: [PATCH 08/19] Example of package upload with .changes file. #71 --- system/changes/hardlink_0.2.1.dsc | 30 +++++++++++++++ system/changes/hardlink_0.2.1.tar.gz | Bin 0 -> 12516 bytes system/changes/hardlink_0.2.1_amd64.changes | 39 ++++++++++++++++++++ system/changes/hardlink_0.2.1_amd64.deb | Bin 0 -> 12468 bytes 4 files changed, 69 insertions(+) create mode 100644 system/changes/hardlink_0.2.1.dsc create mode 100644 system/changes/hardlink_0.2.1.tar.gz create mode 100644 system/changes/hardlink_0.2.1_amd64.changes create mode 100644 system/changes/hardlink_0.2.1_amd64.deb diff --git a/system/changes/hardlink_0.2.1.dsc b/system/changes/hardlink_0.2.1.dsc new file mode 100644 index 00000000..8937287c --- /dev/null +++ b/system/changes/hardlink_0.2.1.dsc @@ -0,0 +1,30 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +Format: 1.0 +Source: hardlink +Binary: hardlink +Architecture: any +Version: 0.2.1 +Maintainer: Julian Andres Klode +Homepage: http://jak-linux.org/projects/hardlink/ +Standards-Version: 3.9.3 +Vcs-Browser: http://git.debian.org/?p=users/jak/hardlink.git;a=summary +Vcs-Git: git://git.debian.org/git/users/jak/hardlink.git +Build-Depends: debhelper (>= 9), pkg-config, libpcre3-dev +Package-List: + hardlink deb utils optional +Checksums-Sha1: + 6e95b8cba450343ab4dc01902e521f29fbd87ac2 12516 hardlink_0.2.1.tar.gz +Checksums-Sha256: + 4df0adce005526a1f0e1b38171ddb1f017faae9205f5b1c6dfb0fb4207767271 12516 hardlink_0.2.1.tar.gz +Files: + 8e2caa4d82f228bac08dc9a38bc6edb3 12516 hardlink_0.2.1.tar.gz + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.12 (GNU/Linux) + +iEYEARECAAYFAlUFwywACgkQIdu4nBbbPm2M5wCg0pHD8adE1rY1/DpZ4efRuMXY +MPMAni4xUtyAnwIvkk3MCE2rFrGP3L78 +=5CU1 +-----END PGP SIGNATURE----- diff --git a/system/changes/hardlink_0.2.1.tar.gz b/system/changes/hardlink_0.2.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..560b9811864a65be05740ea5e6ea709d23eeb9c1 GIT binary patch literal 12516 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 literal 0 HcmV?d00001 diff --git a/system/changes/hardlink_0.2.1_amd64.changes b/system/changes/hardlink_0.2.1_amd64.changes new file mode 100644 index 00000000..c48899ea --- /dev/null +++ b/system/changes/hardlink_0.2.1_amd64.changes @@ -0,0 +1,39 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +Format: 1.8 +Date: Sat, 12 May 2014 12:57:02 +0200 +Source: hardlink +Binary: hardlink +Architecture: source amd64 +Version: 0.2.1 +Distribution: unstable +Urgency: low +Maintainer: Julian Andres Klode +Changed-By: Aptly Tester (don't use it) +Description: + hardlink - Hardlinks multiple copies of the same file +Changes: + hardlink (0.2.1) unstable; urgency=low + . + * Update just to try it out :) +Checksums-Sha1: + ff306b8f923653b78e00c45ebbc6c1c734859cdf 949 hardlink_0.2.1.dsc + 6e95b8cba450343ab4dc01902e521f29fbd87ac2 12516 hardlink_0.2.1.tar.gz + 1ac0e962854dff46f14fa7943746660d3cad1679 12468 hardlink_0.2.1_amd64.deb +Checksums-Sha256: + c0d7458aa2ca3886cd6885f395a289efbc9a396e6765cbbca45f51fde859ea70 949 hardlink_0.2.1.dsc + 4df0adce005526a1f0e1b38171ddb1f017faae9205f5b1c6dfb0fb4207767271 12516 hardlink_0.2.1.tar.gz + 668399580590bf1ffcd9eb161b6e574751e15f71820c6e08245dac7c5111a0ee 12468 hardlink_0.2.1_amd64.deb +Files: + 4efce26825af5842f43961096dd890b3 949 utils optional hardlink_0.2.1.dsc + 8e2caa4d82f228bac08dc9a38bc6edb3 12516 utils optional hardlink_0.2.1.tar.gz + 2081e20b36c47f82811c25841cc0e41b 12468 utils optional hardlink_0.2.1_amd64.deb + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.12 (GNU/Linux) + +iEYEARECAAYFAlUFwywACgkQIdu4nBbbPm1DLACgwW4V8qLQC/QHC/7+t3Iq47Ez +eesAn3ZYLQvLYRw3wPTKVAPI+AW6Fjxi +=hRBo +-----END PGP SIGNATURE----- diff --git a/system/changes/hardlink_0.2.1_amd64.deb b/system/changes/hardlink_0.2.1_amd64.deb new file mode 100644 index 0000000000000000000000000000000000000000..739d28492e18261120b7bdcde635f5a8639d6b00 GIT binary patch literal 12468 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 Date: Sun, 15 Mar 2015 21:26:58 +0300 Subject: [PATCH 09/19] Collect .changes file in directory hiearchy. #71 --- deb/changes.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/deb/changes.go b/deb/changes.go index 1df9bdda..cb6a6e3a 100644 --- a/deb/changes.go +++ b/deb/changes.go @@ -2,10 +2,13 @@ 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 @@ -145,3 +148,37 @@ func (c *Changes) Cleanup() error { 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 +} From a937ebc7447c66459cde2df73c651b3c311e44aa Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 15 Mar 2015 21:30:54 +0300 Subject: [PATCH 10/19] First version aptly repo include command processing .changes files. #71 --- cmd/repo.go | 1 + cmd/repo_include.go | 178 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 cmd/repo_include.go diff --git a/cmd/repo.go b/cmd/repo.go index 2823a149..2f54eb55 100644 --- a/cmd/repo.go +++ b/cmd/repo.go @@ -21,6 +21,7 @@ func makeCmdRepo() *commander.Command { makeCmdRepoShow(), makeCmdRepoRename(), makeCmdRepoSearch(), + makeCmdRepoInclude(), }, } } diff --git a/cmd/repo_include.go b/cmd/repo_include.go new file mode 100644 index 00000000..414e3de8 --- /dev/null +++ b/cmd/repo_include.go @@ -0,0 +1,178 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/smira/aptly/aptly" + "github.com/smira/aptly/deb" + "github.com/smira/aptly/utils" + "github.com/smira/commander" + "github.com/smira/flag" + "os" + "path/filepath" + "text/template" +) + +func aptlyRepoInclude(cmd *commander.Command, args []string) error { + var err error + if len(args) < 1 { + cmd.Usage() + return commander.ErrCommandError + } + + verifier, err := getVerifier(context.Flags()) + if err != nil { + return fmt.Errorf("unable to initialize GPG verifier: %s", err) + } + + forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool) + acceptUnsigned := context.Flags().Lookup("accept-unsigned").Value.Get().(bool) + ignoreSignatures := context.Flags().Lookup("ignore-signatures").Value.Get().(bool) + noRemoveFiles := context.Flags().Lookup("no-remove-files").Value.Get().(bool) + + repoTemplate, err := template.New("repo").Parse(context.Flags().Lookup("repo").Value.Get().(string)) + if err != nil { + return fmt.Errorf("error parsing -repo template: %s", err) + } + + reporter := &aptly.ConsoleResultReporter{Progress: context.Progress()} + + var changesFiles, failedFiles, processedFiles []string + + changesFiles, failedFiles = deb.CollectChangesFiles(args, reporter) + + for _, path := range changesFiles { + var changes *deb.Changes + + changes, err = deb.NewChanges(path) + if err != nil { + failedFiles = append(failedFiles, path) + reporter.Warning("unable to process file %s: %s", path, err) + continue + } + + err = changes.VerifyAndParse(acceptUnsigned, ignoreSignatures, verifier) + if err != nil { + failedFiles = append(failedFiles, path) + reporter.Warning("unable to process file %s: %s", changes.ChangesName, err) + changes.Cleanup() + continue + } + + err = changes.Prepare() + if err != nil { + failedFiles = append(failedFiles, path) + reporter.Warning("unable to process file %s: %s", changes.ChangesName, err) + changes.Cleanup() + continue + } + + repoName := &bytes.Buffer{} + err = repoTemplate.Execute(repoName, changes.Stanza) + if err != nil { + return fmt.Errorf("error applying template to repo: %s", err) + } + + context.Progress().Printf("Loading repository %s for changes file %s...\n", repoName.String(), changes.ChangesName) + + repo, err := context.CollectionFactory().LocalRepoCollection().ByName(repoName.String()) + if err != nil { + failedFiles = append(failedFiles, path) + reporter.Warning("unable to process file %s: %s", changes.ChangesName, err) + changes.Cleanup() + continue + } + + err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo) + if err != nil { + return fmt.Errorf("unable to load repo: %s", err) + } + + list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress()) + if err != nil { + return fmt.Errorf("unable to load packages: %s", err) + } + + packageFiles, _ := deb.CollectPackageFiles([]string{changes.TempDir}, reporter) + + var processedFiles2, failedFiles2 []string + + processedFiles2, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(), + context.CollectionFactory().PackageCollection(), reporter) + + if err != nil { + return fmt.Errorf("unable to import package files: %s", err) + } + + repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list)) + + err = context.CollectionFactory().LocalRepoCollection().Update(repo) + if err != nil { + return fmt.Errorf("unable to save: %s", err) + } + + err = changes.Cleanup() + if err != nil { + return err + } + + for _, file := range failedFiles2 { + failedFiles = append(failedFiles, filepath.Join(changes.BasePath, filepath.Base(file))) + } + + for _, file := range processedFiles2 { + processedFiles = append(processedFiles, filepath.Join(changes.BasePath, filepath.Base(file))) + } + + processedFiles = append(processedFiles, path) + } + + if !noRemoveFiles { + processedFiles = utils.StrSliceDeduplicate(processedFiles) + + for _, file := range processedFiles { + err := os.Remove(file) + if err != nil { + return fmt.Errorf("unable to remove file: %s", err) + } + } + } + + if len(failedFiles) > 0 { + context.Progress().ColoredPrintf("@y[!]@| @!Some files were skipped due to errors:@|") + for _, file := range failedFiles { + context.Progress().ColoredPrintf(" %s", file) + } + + return fmt.Errorf("some files failed to be added") + } + + return err +} + +func makeCmdRepoInclude() *commander.Command { + cmd := &commander.Command{ + Run: aptlyRepoInclude, + UsageLine: "include | ...", + Short: "add packages to local repositories based on .changes files", + Long: ` +Command include looks for .changes files in list of arguments or specified directories. Each +.changes file is verified, parsed, referenced files are put into separate temporary directory +and added into local repository. Successfully imported files are removed by default. + +Example: + + $ aptly repo include -repo=foo-release incoming/ +`, + Flag: *flag.NewFlagSet("aptly-repo-include", flag.ExitOnError), + } + + cmd.Flag.Bool("no-remove-files", false, "don't remove files that have been imported successfully into repository") + cmd.Flag.Bool("force-replace", false, "when adding package that conflicts with existing package, remove existing package") + cmd.Flag.String("repo", "{{.Distribution}}", "which repo should files go to, defaults to Distribution field of .changes file") + cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)") + cmd.Flag.Bool("ignore-signatures", false, "disable verification of .changes file signature") + cmd.Flag.Bool("accept-unsigned", false, "accept unsigned .changes files") + + return cmd +} From 71b7de7a630291d84f7686c44de36a5390d3669a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 16 Mar 2015 22:49:41 +0300 Subject: [PATCH 11/19] Initialize empty verifier if -ignore-signatures is given to check for signature. #71 --- cmd/repo_include.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/repo_include.go b/cmd/repo_include.go index 414e3de8..37bddb8b 100644 --- a/cmd/repo_include.go +++ b/cmd/repo_include.go @@ -25,6 +25,10 @@ func aptlyRepoInclude(cmd *commander.Command, args []string) error { return fmt.Errorf("unable to initialize GPG verifier: %s", err) } + if verifier == nil { + verifier = &utils.GpgVerifier{} + } + forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool) acceptUnsigned := context.Flags().Lookup("accept-unsigned").Value.Get().(bool) ignoreSignatures := context.Flags().Lookup("ignore-signatures").Value.Get().(bool) From 103fa5310f28e1ae6e34df25867a41541b11f6de Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 16 Mar 2015 22:50:58 +0300 Subject: [PATCH 12/19] First pack of system tests for aptly repo include. #71 --- system/lib.py | 1 + system/t09_repo/IncludeRepo1Test_gold | 5 ++ system/t09_repo/IncludeRepo1Test_repo_show | 8 +++ system/t09_repo/IncludeRepo2Test_gold | 5 ++ system/t09_repo/IncludeRepo2Test_repo_show | 11 ++++ system/t09_repo/IncludeRepo3Test_gold | 1 + system/t09_repo/IncludeRepo4Test_gold | 5 ++ system/t09_repo/__init__.py | 1 + system/t09_repo/include.py | 71 ++++++++++++++++++++++ 9 files changed, 108 insertions(+) create mode 100644 system/t09_repo/IncludeRepo1Test_gold create mode 100644 system/t09_repo/IncludeRepo1Test_repo_show create mode 100644 system/t09_repo/IncludeRepo2Test_gold create mode 100644 system/t09_repo/IncludeRepo2Test_repo_show create mode 100644 system/t09_repo/IncludeRepo3Test_gold create mode 100644 system/t09_repo/IncludeRepo4Test_gold create mode 100644 system/t09_repo/include.py diff --git a/system/lib.py b/system/lib.py index b7fccd36..a1374b61 100644 --- a/system/lib.py +++ b/system/lib.py @@ -154,6 +154,7 @@ class BaseTest(object): if not hasattr(command, "__iter__"): params = { 'files': os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "files"), + 'changes': os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), 'udebs': os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "udebs"), 'testfiles': os.path.join(os.path.dirname(inspect.getsourcefile(self.__class__)), self.__class__.__name__), 'aptlyroot': os.path.join(os.environ["HOME"], ".aptly"), diff --git a/system/t09_repo/IncludeRepo1Test_gold b/system/t09_repo/IncludeRepo1Test_gold new file mode 100644 index 00000000..456eb0b5 --- /dev/null +++ b/system/t09_repo/IncludeRepo1Test_gold @@ -0,0 +1,5 @@ +gpgv: Signature made Sun Mar 15 20:36:44 2015 MSK using DSA key ID 16DB3E6D +gpgv: Good signature from "Aptly Tester (don't use it) " +Loading repository unstable for changes file hardlink_0.2.1_amd64.changes... +[+] hardlink_0.2.1_source added +[+] hardlink_0.2.1_amd64 added diff --git a/system/t09_repo/IncludeRepo1Test_repo_show b/system/t09_repo/IncludeRepo1Test_repo_show new file mode 100644 index 00000000..6543b523 --- /dev/null +++ b/system/t09_repo/IncludeRepo1Test_repo_show @@ -0,0 +1,8 @@ +Name: unstable +Comment: +Default Distribution: +Default Component: main +Number of packages: 2 +Packages: + hardlink_0.2.1_amd64 + hardlink_0.2.1_source diff --git a/system/t09_repo/IncludeRepo2Test_gold b/system/t09_repo/IncludeRepo2Test_gold new file mode 100644 index 00000000..a9f191f2 --- /dev/null +++ b/system/t09_repo/IncludeRepo2Test_gold @@ -0,0 +1,5 @@ +gpgv: DSA key ID 16DB3E6D +gpgv: Good signature from "Aptly Tester (don't use it) " +Loading repository my-unstable for changes file hardlink_0.2.1_amd64.changes... +[+] hardlink_0.2.1_source added +[+] hardlink_0.2.1_amd64 added diff --git a/system/t09_repo/IncludeRepo2Test_repo_show b/system/t09_repo/IncludeRepo2Test_repo_show new file mode 100644 index 00000000..ab2503c0 --- /dev/null +++ b/system/t09_repo/IncludeRepo2Test_repo_show @@ -0,0 +1,11 @@ +Name: my-unstable +Comment: +Default Distribution: +Default Component: main +Number of packages: 5 +Packages: + hardlink_0.2.1_amd64 + libboost-program-options-dev_1.49.0.1_i386 + hardlink_0.2.1_source + pyspi_0.6.1-1.3_source + pyspi_0.6.1-1.4_source diff --git a/system/t09_repo/IncludeRepo3Test_gold b/system/t09_repo/IncludeRepo3Test_gold new file mode 100644 index 00000000..96cda590 --- /dev/null +++ b/system/t09_repo/IncludeRepo3Test_gold @@ -0,0 +1 @@ +ERROR: error parsing -repo template: template: repo:1: unexpected "}" in operand; missing space? diff --git a/system/t09_repo/IncludeRepo4Test_gold b/system/t09_repo/IncludeRepo4Test_gold new file mode 100644 index 00000000..2fcf3fd4 --- /dev/null +++ b/system/t09_repo/IncludeRepo4Test_gold @@ -0,0 +1,5 @@ +Loading repository unstable for changes file hardlink_0.2.1_amd64.changes... +[!] unable to process file hardlink_0.2.1_amd64.changes: local repo with name unstable not found +[!] Some files were skipped due to errors: + /hardlink_0.2.1_amd64.changes +ERROR: some files failed to be added diff --git a/system/t09_repo/__init__.py b/system/t09_repo/__init__.py index 54b70ef5..302f65c1 100644 --- a/system/t09_repo/__init__.py +++ b/system/t09_repo/__init__.py @@ -14,3 +14,4 @@ from .remove import * from .show import * from .rename import * from .search import * +from .include import * diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py new file mode 100644 index 00000000..b942ba73 --- /dev/null +++ b/system/t09_repo/include.py @@ -0,0 +1,71 @@ +import tempfile +import shutil +import os +import inspect +import re +from lib import BaseTest + +gpgRemove = lambda _, s: re.sub(r'Signature made .* using|gpgv: keyblock resource .*$|gpgv: Can\'t check signature: .*$', '', s, flags=re.MULTILINE) +changesRemove = lambda _, s: s.replace(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), "") + + +class IncludeRepo1Test(BaseTest): + """ + incldue packages to local repo: .changes file from directory + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -no-remove-files -keyring=${files}/aptly.pub ${changes}" + outputMatchPrepare = gpgRemove + + def check(self): + self.check_output() + self.check_cmd_output("aptly repo show -with-packages unstable", "repo_show") + + # check pool + self.check_exists('pool//20/81/hardlink_0.2.1_amd64.deb') + self.check_exists('pool/4e/fc/hardlink_0.2.1.dsc') + self.check_exists('pool/8e/2c/hardlink_0.2.1.tar.gz') + + +class IncludeRepo2Test(BaseTest): + """ + incldue packages to local repo: .changes file from file + custom repo + """ + fixtureCmds = [ + "aptly repo create my-unstable", + "aptly repo add my-unstable ${files}", + ] + runCmd = "aptly repo include -no-remove-files -keyring=${files}/aptly.pub -repo=my-{{.Distribution}} ${changes}/hardlink_0.2.1_amd64.changes" + outputMatchPrepare = gpgRemove + + def check(self): + self.check_output() + self.check_cmd_output("aptly repo show -with-packages my-unstable", "repo_show") + + # check pool + self.check_exists('pool//20/81/hardlink_0.2.1_amd64.deb') + self.check_exists('pool/4e/fc/hardlink_0.2.1.dsc') + self.check_exists('pool/8e/2c/hardlink_0.2.1.tar.gz') + + +class IncludeRepo3Test(BaseTest): + """ + incldue packages to local repo: broken repo flag + """ + fixtureCmds = [ + ] + runCmd = "aptly repo include -no-remove-files -keyring=${files}/aptly.pub -repo=my-{{.Distribution} ${changes}" + expectedCode = 1 + + +class IncludeRepo4Test(BaseTest): + """ + incldue packages to local repo: missing repo + """ + fixtureCmds = [ + ] + runCmd = "aptly repo include -no-remove-files -ignore-signatures -keyring=${files}/aptly.pub ${changes}" + outputMatchPrepare = changesRemove + expectedCode = 1 From a59c2ac8593a4b70807eb5b4e491e5485e52795b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 16 Mar 2015 23:55:47 +0300 Subject: [PATCH 13/19] Tests for file removal + missing files. #71 --- system/t09_repo/IncludeRepo5Test_gold | 5 ++ system/t09_repo/IncludeRepo5Test_repo_show | 8 +++ system/t09_repo/IncludeRepo6Test_gold | 6 ++ system/t09_repo/include.py | 84 ++++++++++++++++++++-- 4 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 system/t09_repo/IncludeRepo5Test_gold create mode 100644 system/t09_repo/IncludeRepo5Test_repo_show create mode 100644 system/t09_repo/IncludeRepo6Test_gold diff --git a/system/t09_repo/IncludeRepo5Test_gold b/system/t09_repo/IncludeRepo5Test_gold new file mode 100644 index 00000000..58285bb6 --- /dev/null +++ b/system/t09_repo/IncludeRepo5Test_gold @@ -0,0 +1,5 @@ +gpgv: DSA key ID 16DB3E6D +gpgv: Good signature from "Aptly Tester (don't use it) " +Loading repository unstable for changes file hardlink_0.2.1_amd64.changes... +[+] hardlink_0.2.1_source added +[+] hardlink_0.2.1_amd64 added diff --git a/system/t09_repo/IncludeRepo5Test_repo_show b/system/t09_repo/IncludeRepo5Test_repo_show new file mode 100644 index 00000000..6543b523 --- /dev/null +++ b/system/t09_repo/IncludeRepo5Test_repo_show @@ -0,0 +1,8 @@ +Name: unstable +Comment: +Default Distribution: +Default Component: main +Number of packages: 2 +Packages: + hardlink_0.2.1_amd64 + hardlink_0.2.1_source diff --git a/system/t09_repo/IncludeRepo6Test_gold b/system/t09_repo/IncludeRepo6Test_gold new file mode 100644 index 00000000..9c02bfa6 --- /dev/null +++ b/system/t09_repo/IncludeRepo6Test_gold @@ -0,0 +1,6 @@ +gpgv: DSA key ID 16DB3E6D +gpgv: Good signature from "Aptly Tester (don't use it) " +[!] unable to process file hardlink_0.2.1_amd64.changes: open /01/hardlink_0.2.1.tar.gz: no such file or directory +[!] Some files were skipped due to errors: + /01/hardlink_0.2.1_amd64.changes +ERROR: some files failed to be added diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py index b942ba73..ac907504 100644 --- a/system/t09_repo/include.py +++ b/system/t09_repo/include.py @@ -7,11 +7,12 @@ from lib import BaseTest gpgRemove = lambda _, s: re.sub(r'Signature made .* using|gpgv: keyblock resource .*$|gpgv: Can\'t check signature: .*$', '', s, flags=re.MULTILINE) changesRemove = lambda _, s: s.replace(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), "") +tempDirRemove = lambda self, s: s.replace(self.tempSrcDir, "") class IncludeRepo1Test(BaseTest): """ - incldue packages to local repo: .changes file from directory + include packages to local repo: .changes file from directory """ fixtureCmds = [ "aptly repo create unstable", @@ -31,7 +32,7 @@ class IncludeRepo1Test(BaseTest): class IncludeRepo2Test(BaseTest): """ - incldue packages to local repo: .changes file from file + custom repo + include packages to local repo: .changes file from file + custom repo """ fixtureCmds = [ "aptly repo create my-unstable", @@ -52,7 +53,7 @@ class IncludeRepo2Test(BaseTest): class IncludeRepo3Test(BaseTest): """ - incldue packages to local repo: broken repo flag + include packages to local repo: broken repo flag """ fixtureCmds = [ ] @@ -62,10 +63,85 @@ class IncludeRepo3Test(BaseTest): class IncludeRepo4Test(BaseTest): """ - incldue packages to local repo: missing repo + include packages to local repo: missing repo """ fixtureCmds = [ ] runCmd = "aptly repo include -no-remove-files -ignore-signatures -keyring=${files}/aptly.pub ${changes}" outputMatchPrepare = changesRemove expectedCode = 1 + + +class IncludeRepo5Test(BaseTest): + """ + include packages to local repo: remove files being added + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -keyring=${files}/aptly.pub " + outputMatchPrepare = gpgRemove + + def prepare(self): + super(IncludeRepo5Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + shutil.copy(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "files", "pyspi_0.6.1-1.3.diff.gz"), + os.path.join(self.tempSrcDir, "01", "pyspi_0.6.1-1.3.diff.gz")) + + self.runCmd += self.tempSrcDir + + def check(self): + try: + self.check_output() + self.check_cmd_output("aptly repo show -with-packages unstable", "repo_show") + + # check pool + self.check_exists('pool//20/81/hardlink_0.2.1_amd64.deb') + self.check_exists('pool/4e/fc/hardlink_0.2.1.dsc') + self.check_exists('pool/8e/2c/hardlink_0.2.1.tar.gz') + + for path in ["hardlink_0.2.1.dsc", "hardlink_0.2.1.tar.gz", "hardlink_0.2.1_amd64.changes", "hardlink_0.2.1_amd64.deb"]: + path = os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1.dsc") + if os.path.exists(path): + raise Exception("path %s shouldn't exist" % (path, )) + + path = os.path.join(self.tempSrcDir, "01", "pyspi_0.6.1-1.3.diff.gz") + if not os.path.exists(path): + raise Exception("path %s doesn't exist" % (path, )) + + finally: + shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo6Test(BaseTest): + """ + include packages to local repo: missing files + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -keyring=${files}/aptly.pub " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + expectedCode = 1 + + def prepare(self): + super(IncludeRepo6Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + os.makedirs(os.path.join(self.tempSrcDir, "01"), 0755) + + for path in ["hardlink_0.2.1.dsc", "hardlink_0.2.1_amd64.changes", "hardlink_0.2.1_amd64.deb"]: + shutil.copy(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes", path), + os.path.join(self.tempSrcDir, "01", path)) + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo6Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) From 90d3b623b4454a404828c3e5a7eabb47324b33d8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:01:58 +0300 Subject: [PATCH 14/19] Check file size as well as checksums. #71 --- deb/changes.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deb/changes.go b/deb/changes.go index cb6a6e3a..c9888bb6 100644 --- a/deb/changes.go +++ b/deb/changes.go @@ -124,6 +124,10 @@ func (c *Changes) Prepare() error { 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) } From c6eeac11a44a08d264222f7e8dd7b15d0512d1fa Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:02:39 +0300 Subject: [PATCH 15/19] System test for wrong checkums. #71 --- system/t09_repo/IncludeRepo7Test_gold | 6 ++++++ system/t09_repo/include.py | 30 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 system/t09_repo/IncludeRepo7Test_gold diff --git a/system/t09_repo/IncludeRepo7Test_gold b/system/t09_repo/IncludeRepo7Test_gold new file mode 100644 index 00000000..2215c9dd --- /dev/null +++ b/system/t09_repo/IncludeRepo7Test_gold @@ -0,0 +1,6 @@ +gpgv: DSA key ID 16DB3E6D +gpgv: Good signature from "Aptly Tester (don't use it) " +[!] unable to process file hardlink_0.2.1_amd64.changes: checksum mismatch MD5: expected 4efce26825af5842f43961096dd890b3 != obtained 7515e9279fc32d0c89db965f4dfdec8c +[!] Some files were skipped due to errors: + /01/hardlink_0.2.1_amd64.changes +ERROR: some files failed to be added diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py index ac907504..240900fe 100644 --- a/system/t09_repo/include.py +++ b/system/t09_repo/include.py @@ -145,3 +145,33 @@ class IncludeRepo6Test(BaseTest): super(IncludeRepo6Test, self).check() finally: shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo7Test(BaseTest): + """ + include packages to local repo: wrong checksum + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -keyring=${files}/aptly.pub " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + expectedCode = 1 + + def prepare(self): + super(IncludeRepo7Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + with open(os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1.dsc"), "w") as f: + f.write("A" * 949) # file size + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo7Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) From 197e230ef11976ee822f7cc58d935587de02598c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:08:47 +0300 Subject: [PATCH 16/19] System tests: wrong signature. #71 --- system/t09_repo/IncludeRepo8Test_gold | 6 +++++ system/t09_repo/include.py | 39 ++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 system/t09_repo/IncludeRepo8Test_gold diff --git a/system/t09_repo/IncludeRepo8Test_gold b/system/t09_repo/IncludeRepo8Test_gold new file mode 100644 index 00000000..1f1e0949 --- /dev/null +++ b/system/t09_repo/IncludeRepo8Test_gold @@ -0,0 +1,6 @@ +gpgv: DSA key ID 16DB3E6D +gpgv: BAD signature from "Aptly Tester (don't use it) " +[!] unable to process file hardlink_0.2.1_amd64.changes: verification of clearsigned file failed: exit status 1 +[!] Some files were skipped due to errors: + /01/hardlink_0.2.1_amd64.changes +ERROR: some files failed to be added diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py index 240900fe..18fff3ac 100644 --- a/system/t09_repo/include.py +++ b/system/t09_repo/include.py @@ -105,7 +105,7 @@ class IncludeRepo5Test(BaseTest): self.check_exists('pool/8e/2c/hardlink_0.2.1.tar.gz') for path in ["hardlink_0.2.1.dsc", "hardlink_0.2.1.tar.gz", "hardlink_0.2.1_amd64.changes", "hardlink_0.2.1_amd64.deb"]: - path = os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1.dsc") + path = os.path.join(self.tempSrcDir, "01", path) if os.path.exists(path): raise Exception("path %s shouldn't exist" % (path, )) @@ -143,6 +143,11 @@ class IncludeRepo6Test(BaseTest): def check(self): try: super(IncludeRepo6Test, self).check() + + for path in ["hardlink_0.2.1.dsc", "hardlink_0.2.1_amd64.changes", "hardlink_0.2.1_amd64.deb"]: + path = os.path.join(self.tempSrcDir, "01", path) + if not os.path.exists(path): + raise Exception("path %s doesn't exist" % (path, )) finally: shutil.rmtree(self.tempSrcDir) @@ -175,3 +180,35 @@ class IncludeRepo7Test(BaseTest): super(IncludeRepo7Test, self).check() finally: shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo8Test(BaseTest): + """ + include packages to local repo: wrong signature + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -keyring=${files}/aptly.pub " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + expectedCode = 1 + + def prepare(self): + super(IncludeRepo8Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + with open(os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1_amd64.changes"), "r+") as f: + contents = f.read() + f.seek(0, 0) + f.write(contents.replace('Julian', 'Andrey')) + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo8Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) From 2a3bd5546a2e6798fa5a8e9af1c1ee728a88b92f Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:15:45 +0300 Subject: [PATCH 17/19] Unsigned files shouldn't be accepted. #71 --- system/t09_repo/IncludeRepo9Test_gold | 4 +++ system/t09_repo/include.py | 35 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 system/t09_repo/IncludeRepo9Test_gold diff --git a/system/t09_repo/IncludeRepo9Test_gold b/system/t09_repo/IncludeRepo9Test_gold new file mode 100644 index 00000000..13fb221e --- /dev/null +++ b/system/t09_repo/IncludeRepo9Test_gold @@ -0,0 +1,4 @@ +[!] unable to process file hardlink_0.2.1_amd64.changes: .changes file is not signed and unsigned processing hasn't been enabled +[!] Some files were skipped due to errors: + /01/hardlink_0.2.1_amd64.changes +ERROR: some files failed to be added diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py index 18fff3ac..b978f215 100644 --- a/system/t09_repo/include.py +++ b/system/t09_repo/include.py @@ -204,6 +204,7 @@ class IncludeRepo8Test(BaseTest): contents = f.read() f.seek(0, 0) f.write(contents.replace('Julian', 'Andrey')) + f.truncate() self.runCmd += self.tempSrcDir @@ -212,3 +213,37 @@ class IncludeRepo8Test(BaseTest): super(IncludeRepo8Test, self).check() finally: shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo9Test(BaseTest): + """ + include packages to local repo: unsigned + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -keyring=${files}/aptly.pub " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + expectedCode = 1 + + def prepare(self): + super(IncludeRepo9Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + with open(os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1_amd64.changes"), "r+") as f: + contents = f.readlines() + contents = contents[3:31] + f.seek(0, 0) + f.write("".join(contents)) + f.truncate() + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo9Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) From 775670181c9266e7e86fe0c274c0ef2fcf3d604b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:17:43 +0300 Subject: [PATCH 18/19] System tests for -ignore-signatures + -accept-unsigned. #71 --- system/t09_repo/IncludeRepo10Test_gold | 3 ++ system/t09_repo/IncludeRepo11Test_gold | 3 ++ system/t09_repo/include.py | 65 ++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 system/t09_repo/IncludeRepo10Test_gold create mode 100644 system/t09_repo/IncludeRepo11Test_gold diff --git a/system/t09_repo/IncludeRepo10Test_gold b/system/t09_repo/IncludeRepo10Test_gold new file mode 100644 index 00000000..aad7ca65 --- /dev/null +++ b/system/t09_repo/IncludeRepo10Test_gold @@ -0,0 +1,3 @@ +Loading repository unstable for changes file hardlink_0.2.1_amd64.changes... +[+] hardlink_0.2.1_source added +[+] hardlink_0.2.1_amd64 added diff --git a/system/t09_repo/IncludeRepo11Test_gold b/system/t09_repo/IncludeRepo11Test_gold new file mode 100644 index 00000000..aad7ca65 --- /dev/null +++ b/system/t09_repo/IncludeRepo11Test_gold @@ -0,0 +1,3 @@ +Loading repository unstable for changes file hardlink_0.2.1_amd64.changes... +[+] hardlink_0.2.1_source added +[+] hardlink_0.2.1_amd64 added diff --git a/system/t09_repo/include.py b/system/t09_repo/include.py index b978f215..33281fbd 100644 --- a/system/t09_repo/include.py +++ b/system/t09_repo/include.py @@ -247,3 +247,68 @@ class IncludeRepo9Test(BaseTest): super(IncludeRepo9Test, self).check() finally: shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo10Test(BaseTest): + """ + include packages to local repo: wrong signature + -ignore-signatures + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -ignore-signatures " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + + def prepare(self): + super(IncludeRepo10Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + with open(os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1_amd64.changes"), "r+") as f: + contents = f.read() + f.seek(0, 0) + f.write(contents.replace('Julian', 'Andrey')) + f.truncate() + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo10Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) + + +class IncludeRepo11Test(BaseTest): + """ + include packages to local repo: unsigned + -accept-unsigned + """ + fixtureCmds = [ + "aptly repo create unstable", + ] + runCmd = "aptly repo include -accept-unsigned " + outputMatchPrepare = lambda self, s: gpgRemove(self, tempDirRemove(self, s)) + + def prepare(self): + super(IncludeRepo11Test, self).prepare() + + self.tempSrcDir = tempfile.mkdtemp() + + shutil.copytree(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "changes"), os.path.join(self.tempSrcDir, "01")) + + with open(os.path.join(self.tempSrcDir, "01", "hardlink_0.2.1_amd64.changes"), "r+") as f: + contents = f.readlines() + contents = contents[3:31] + f.seek(0, 0) + f.write("".join(contents)) + f.truncate() + + self.runCmd += self.tempSrcDir + + def check(self): + try: + super(IncludeRepo11Test, self).check() + finally: + shutil.rmtree(self.tempSrcDir) From 5b9d287b62899a4dc9a54a401a188c553958af94 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 17 Mar 2015 00:19:06 +0300 Subject: [PATCH 19/19] Add `aptly repo include` to man. #71 --- man/aptly.1 | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/man/aptly.1 b/man/aptly.1 index f4ed4b38..baf434a0 100644 --- a/man/aptly.1 +++ b/man/aptly.1 @@ -797,6 +797,45 @@ Options: \-\fBwith\-deps\fR=false include dependencies into search results . +.SH "ADD PACKAGES TO LOCAL REPOSITORIES BASED ON \.CHANGES FILES" +\fBaptly\fR \fBrepo\fR \fBinclude\fR |\fIdirectory\fR \fB\|\.\|\.\|\.\fR +. +.P +Command include looks for \.changes files in list of arguments or specified directories\. Each \.changes file is verified, parsed, referenced files are put into separate temporary directory and added into local repository\. Successfully imported files are removed by default\. +. +.P +Example: +. +.P +$ aptly repo include \-repo=foo\-release incoming/ +. +.P +Options: +. +.TP +\-\fBaccept\-unsigned\fR=false +accept unsigned \.changes files +. +.TP +\-\fBforce\-replace\fR=false +when adding package that conflicts with existing package, remove existing package +. +.TP +\-\fBignore\-signatures\fR=false +disable verification of \.changes file signature +. +.TP +\-\fBkeyring\fR= +gpg keyring to use when verifying Release file (could be specified multiple times) +. +.TP +\-\fBno\-remove\-files\fR=false +don\(cqt remove files that have been imported successfully into repository +. +.TP +\-\fBrepo\fR={{\.Distribution}} +which repo should files go to, defaults to Distribution field of \.changes file +. .SH "CREATES SNAPSHOT OF MIRROR (LOCAL REPOSITORY) CONTENTS" \fBaptly\fR \fBsnapshot\fR \fBcreate\fR \fIname\fR \fBfrom\fR \fBmirror\fR \fImirror\-name\fR \fB|\fR \fBfrom\fR \fBrepo\fR \fIrepo\-name\fR \fB|\fR \fBempty\fR .