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 +}