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.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_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/cmd/repo_include.go b/cmd/repo_include.go new file mode 100644 index 00000000..37bddb8b --- /dev/null +++ b/cmd/repo_include.go @@ -0,0 +1,182 @@ +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) + } + + 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) + 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 +} diff --git a/deb/changes.go b/deb/changes.go new file mode 100644 index 00000000..c9888bb6 --- /dev/null +++ b/deb/changes.go @@ -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 +} diff --git a/deb/changes_test.go b/deb/changes_test.go new file mode 100644 index 00000000..058e7fb8 --- /dev/null +++ b/deb/changes_test.go @@ -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 +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` 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/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 { diff --git a/deb/package.go b/deb/package.go index f032a222..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[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) 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 +} 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/man/aptly.1 b/man/aptly.1 index 866b839d..d2fcb8ba 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 . 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 00000000..560b9811 Binary files /dev/null and b/system/changes/hardlink_0.2.1.tar.gz differ 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 00000000..739d2849 Binary files /dev/null and b/system/changes/hardlink_0.2.1_amd64.deb differ 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/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/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/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/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/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/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/__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..33281fbd --- /dev/null +++ b/system/t09_repo/include.py @@ -0,0 +1,314 @@ +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"), "") +tempDirRemove = lambda self, s: s.replace(self.tempSrcDir, "") + + +class IncludeRepo1Test(BaseTest): + """ + include 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): + """ + include 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): + """ + include 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): + """ + 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", path) + 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() + + 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) + + +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) + + +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')) + f.truncate() + + self.runCmd += self.tempSrcDir + + def check(self): + try: + 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) + + +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) 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() +} 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()