Refactor commands to subpackage.

This commit is contained in:
Andrey Smirnov
2014-02-19 14:59:00 +04:00
parent 35c2178074
commit eb18b04c40
12 changed files with 144 additions and 36 deletions
+62
View File
@@ -0,0 +1,62 @@
// Package cmd implements console commands
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"os"
)
// ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *debian.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database)
err = reflist.ForEach(func(key []byte) error {
p, err := packageCollection.ByKey(key)
if err != nil {
return err
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
// RootCommand creates root command in command tree
func RootCommand() *commander.Command {
cmd := &commander.Command{
UsageLine: os.Args[0],
Short: "Debian repository management tool",
Long: `
}
aptly is a tool to create partial and full mirrors of remote
repositories, filter them, merge, upgrade individual packages,
take snapshots and publish them back as Debian repositories.`,
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
Subcommands: []*commander.Command{
makeCmdDb(),
makeCmdGraph(),
makeCmdMirror(),
makeCmdServe(),
makeCmdSnapshot(),
makeCmdPublish(),
makeCmdVersion(),
},
}
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
cmd.Flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
return cmd
}
+68
View File
@@ -0,0 +1,68 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/console"
"github.com/smira/aptly/database"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/files"
"github.com/smira/aptly/http"
"github.com/smira/aptly/utils"
"path/filepath"
"strings"
)
// Common context shared by all commands
var context struct {
progress aptly.Progress
downloader aptly.Downloader
database database.Storage
packagePool aptly.PackagePool
publishedStorage aptly.PublishedStorage
dependencyOptions int
architecturesList []string
}
func InitContext(cmd *commander.Command) error {
var err error
context.dependencyOptions = 0
if utils.Config.DepFollowSuggests || cmd.Flag.Lookup("dep-follow-suggests").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowSuggests
}
if utils.Config.DepFollowRecommends || cmd.Flag.Lookup("dep-follow-recommends").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowRecommends
}
if utils.Config.DepFollowAllVariants || cmd.Flag.Lookup("dep-follow-all-variants").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowAllVariants
}
if utils.Config.DepFollowSource || cmd.Flag.Lookup("dep-follow-source").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowSource
}
context.architecturesList = utils.Config.Architectures
optionArchitectures := cmd.Flag.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
context.progress = console.NewProgress()
context.progress.Start()
defer context.progress.Shutdown()
context.downloader = http.NewDownloader(utils.Config.DownloadConcurrency, context.progress)
defer context.downloader.Shutdown()
context.database, err = database.OpenDB(filepath.Join(utils.Config.RootDir, "db"))
if err != nil {
return fmt.Errorf("can't open database: %s", err)
}
defer context.database.Close()
context.packagePool = files.NewPackagePool(utils.Config.RootDir)
context.publishedStorage = files.NewPublishedStorage(utils.Config.RootDir)
return nil
}
+35
View File
@@ -0,0 +1,35 @@
package cmd
import (
"github.com/gonuts/commander"
"github.com/gonuts/flag"
)
func makeCmdDbCleanup() *commander.Command {
cmd := &commander.Command{
Run: aptlyDbCleanup,
UsageLine: "cleanup",
Short: "remove unused entries in DB and unreferenced files in the pool",
Long: `
Database cleanup removes information about unreferenced packages and removes
files in the package pool that aren't used by packages anymore
ex:
$ aptly db cleanup
`,
Flag: *flag.NewFlagSet("aptly-db-cleanup", flag.ExitOnError),
}
return cmd
}
func makeCmdDb() *commander.Command {
return &commander.Command{
UsageLine: "db",
Short: "manage aptly's internal database and package pool",
Subcommands: []*commander.Command{
makeCmdDbCleanup(),
},
Flag: *flag.NewFlagSet("aptly-db", flag.ExitOnError),
}
}
+129
View File
@@ -0,0 +1,129 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
)
// aptly db cleanup
func aptlyDbCleanup(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
// collect information about references packages...
existingPackageRefs := debian.NewPackageRefList()
context.progress.Printf("Loading mirrors and snapshots...\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
err := repoCollection.LoadComplete(repo)
if err != nil {
return err
}
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
return nil
})
if err != nil {
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
err := snapshotCollection.LoadComplete(snapshot)
if err != nil {
return err
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
return nil
})
if err != nil {
return err
}
// ... and compare it to the list of all packages
context.progress.Printf("Loading list of all packages...\n")
packageCollection := debian.NewPackageCollection(context.database)
allPackageRefs := packageCollection.AllPackageRefs()
toDelete := allPackageRefs.Substract(existingPackageRefs)
// delete packages that are no longer referenced
context.progress.Printf("Deleting unreferenced packages (%d)...\n", toDelete.Len())
context.database.StartBatch()
err = toDelete.ForEach(func(ref []byte) error {
return packageCollection.DeleteByKey(ref)
})
if err != nil {
return err
}
err = context.database.FinishBatch()
if err != nil {
return fmt.Errorf("unable to write to DB: %s", err)
}
// now, build a list of files that should be present in Repository (package pool)
context.progress.Printf("Building list of files referenced by packages...\n")
referencedFiles := make([]string, 0, existingPackageRefs.Len())
context.progress.InitBar(int64(existingPackageRefs.Len()), false)
err = existingPackageRefs.ForEach(func(key []byte) error {
pkg, err := packageCollection.ByKey(key)
if err != nil {
return err
}
paths, err := pkg.FilepathList(context.packagePool)
if err != nil {
return err
}
referencedFiles = append(referencedFiles, paths...)
context.progress.AddBar(1)
return nil
})
if err != nil {
return err
}
sort.Strings(referencedFiles)
context.progress.ShutdownBar()
// build a list of files in the package pool
context.progress.Printf("Building list of files in package pool...\n")
existingFiles, err := context.packagePool.FilepathList(context.progress)
if err != nil {
return fmt.Errorf("unable to collect file paths: %s", err)
}
// find files which are in the pool but not referenced by packages
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
// delete files that are no longer referenced
context.progress.Printf("Deleting unreferenced files (%d)...\n", len(filesToDelete))
if len(filesToDelete) > 0 {
context.progress.InitBar(int64(len(filesToDelete)), false)
totalSize := int64(0)
for _, file := range filesToDelete {
size, err := context.packagePool.Remove(file)
if err != nil {
return err
}
context.progress.AddBar(1)
totalSize += size
}
context.progress.ShutdownBar()
context.progress.Printf("Disk space freed: %.2f GiB...\n", float64(totalSize)/1024.0/1024.0/1024.0)
}
return err
}
+174
View File
@@ -0,0 +1,174 @@
package cmd
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
graph := gographviz.NewGraph()
graph.SetDir(true)
graph.SetName("aptly")
fmt.Printf("Loading mirrors...\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
err := repoCollection.LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())),
})
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
err := snapshotCollection.LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
})
if snapshot.SourceKind == "repo" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
} else if snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
publishedCollection := debian.NewPublishedRepoCollection(context.database)
publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
})
graph.AddEdge(graphvizEscape(repo.SnapshotUUID), "", graphvizEscape(repo.UUID), "", true, nil)
return nil
})
fmt.Printf("Generating graph...\n")
buf := bytes.NewBufferString(graph.String())
tempfile, err := ioutil.TempFile("", "aptly-graph")
if err != nil {
return err
}
tempfile.Close()
os.Remove(tempfile.Name())
tempfilename := tempfile.Name() + ".png"
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
}
_, err = io.Copy(stdin, buf)
if err != nil {
return err
}
err = stdin.Close()
if err != nil {
return err
}
err = command.Wait()
if err != nil {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
}
return err
}
func makeCmdGraph() *commander.Command {
cmd := &commander.Command{
Run: aptlyGraph,
UsageLine: "graph",
Short: "display graph of dependencies between aptly objects (requires graphviz)",
Long: `
Command graph displays relationship between mirrors, snapshots and published repositories using
graphviz package to render graph as image.
ex:
$ aptly graph
`,
Flag: *flag.NewFlagSet("aptly-graph", flag.ExitOnError),
}
return cmd
}
+370
View File
@@ -0,0 +1,370 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getVerifier(cmd *commander.Command) (utils.Verifier, error) {
if utils.Config.GpgDisableVerify || cmd.Flag.Lookup("ignore-signatures").Value.Get().(bool) {
return nil, nil
}
verifier := &utils.GpgVerifier{}
for _, keyRing := range keyRings.keyRings {
verifier.AddKeyring(keyRing)
}
err := verifier.InitKeyring()
if err != nil {
return nil, err
}
return verifier, nil
}
type keyRingsFlag struct {
keyRings []string
}
func (k *keyRingsFlag) Set(value string) error {
k.keyRings = append(k.keyRings, value)
return nil
}
func (k *keyRingsFlag) Get() interface{} {
return k.keyRings
}
func (k *keyRingsFlag) String() string {
return strings.Join(k.keyRings, ",")
}
var keyRings = keyRingsFlag{}
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
if repoCollection.Len() > 0 {
fmt.Printf("List of mirrors:\n")
repos := make([]string, repoCollection.Len())
i := 0
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
repos[i] = repo.String()
i++
return nil
})
sort.Strings(repos)
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
return err
}
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
}
downloadSources := utils.Config.DownloadSourcePackages || cmd.Flag.Lookup("with-sources").Value.Get().(bool)
repo, err := debian.NewRemoteRepo(args[0], args[1], args[2], args[3:], context.architecturesList, downloadSources)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
downloadSources := "no"
if repo.DownloadSources {
downloadSources = "yes"
}
fmt.Printf("Download Sources: %s\n", downloadSources)
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err
}
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := cmd.Flag.Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = repo.Download(context.progress, context.downloader, packageCollection, context.packagePool, ignoreMismatch)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
fmt.Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshots := snapshotCollection.ByRemoteRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Mirror `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete mirror with snapshots, use -force to override")
}
}
err = repoCollection.Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Mirror `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror of Debian repository",
Long: `
Create records information about new mirror and fetches Release file (it doesn't download packages).
ex:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Bool("with-sources", false, "download source packages")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors of remote repositories",
Long: `
List shows full list of remote repositories.
ex:
$ aptly mirror list
`,
Flag: *flag.NewFlagSet("aptly-mirror-list", flag.ExitOnError),
}
cmd.Flag.Bool("v", false, "enable verbose output")
return cmd
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show <name>",
Short: "show details about remote repository mirror",
Long: `
Show shows full information about mirror.
ex:
$ aptly mirror show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update <name>",
Short: "update packages from remote mirror",
Long: `
Update downloads list of packages and package files.
ex:
$ aptly mirror update wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete remote repository mirror",
Long: `
Drop deletes information about remote repository mirror. Package data is not deleted
(it could be still used by other mirrors or snapshots).
ex:
$ aptly mirror drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force mirror deletion even if used by snapshots")
return cmd
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
},
Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError),
}
}
+265
View File
@@ -0,0 +1,265 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getSigner(cmd *commander.Command) (utils.Signer, error) {
if cmd.Flag.Lookup("skip-signing").Value.Get().(bool) || utils.Config.GpgDisableSign {
return nil, nil
}
signer := &utils.GpgSigner{}
signer.SetKey(cmd.Flag.Lookup("gpg-key").Value.String())
signer.SetKeyRing(cmd.Flag.Lookup("keyring").Value.String(), cmd.Flag.Lookup("secret-keyring").Value.String())
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
} else {
prefix = ""
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
var sourceRepo *debian.RemoteRepo
if snapshot.SourceKind == "repo" && len(snapshot.SourceIDs) == 1 {
repoCollection := debian.NewRemoteRepoCollection(context.database)
sourceRepo, _ = repoCollection.ByUUID(snapshot.SourceIDs[0])
}
component := cmd.Flag.Lookup("component").Value.String()
if component == "" {
if sourceRepo != nil && len(sourceRepo.Components) == 1 {
component = sourceRepo.Components[0]
} else {
component = "main"
}
}
distribution := cmd.Flag.Lookup("distribution").Value.String()
if distribution == "" {
if sourceRepo != nil {
distribution = sourceRepo.Distribution
}
if distribution == "" {
return fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
published, err := debian.NewPublishedRepo(prefix, distribution, component, context.architecturesList, snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
duplicate := publishedCollecton.CheckDuplicate(published)
if duplicate != nil {
publishedCollecton.LoadComplete(duplicate, snapshotCollection)
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = published.Publish(context.packagePool, context.publishedStorage, packageCollection, signer)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = publishedCollecton.Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
fmt.Printf("\nSnapshot %s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
snapshot.Name, context.publishedStorage.PublicPath())
fmt.Printf("Now you can add following line to apt sources:\n")
fmt.Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component)
fmt.Printf("Don't forget to add your GPG key to apt with apt-key.\n")
fmt.Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollecton.Len() == 0 {
fmt.Printf("No snapshots have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
published := make([]string, 0, publishedCollecton.Len())
err = publishedCollecton.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollecton.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
published = append(published, repo.String())
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
sort.Strings(published)
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
return err
}
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
distribution := args[0]
prefix := "."
if len(args) == 2 {
prefix = args[1]
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
err = publishedCollecton.Remove(context.publishedStorage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
fmt.Printf("\nPublished repositroy has been removed successfully.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshot,
UsageLine: "snapshot <name> [<prefix>]",
Short: "makes Debian repository out of snapshot",
Long: `
Command publish oublishes snapshot as Debian repository ready to be used by apt tools.
ex.
$ aptly publish snapshot wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.String("keyring", "", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
return cmd
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
Short: "removes files of published repository",
Long: `
Command removes whatever has been published under specified prefix and distribution name.
ex.
$ aptly publish drop wheezy
`,
Flag: *flag.NewFlagSet("aptly-publish-drop", flag.ExitOnError),
}
return cmd
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "displays list of published repositories",
Long: `
Display command displays list of currently published snapshots with information about published root.
ex.
$ aptly publish list
`,
Flag: *flag.NewFlagSet("aptly-publish-list", flag.ExitOnError),
}
return cmd
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishSnapshot(),
makeCmdPublishList(),
makeCmdPublishDrop(),
},
Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError),
}
}
+112
View File
@@ -0,0 +1,112 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"net"
"net/http"
"os"
"sort"
)
func aptlyServe(cmd *commander.Command, args []string) error {
var err error
publishedCollection := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollection.Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := cmd.Flag.Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen)
if err != nil {
return fmt.Errorf("wrong -listen specification: %s", err)
}
if listenHost == "" {
listenHost, err = os.Hostname()
if err != nil {
listenHost = "localhost"
}
}
fmt.Printf("Serving published repositories, recommended apt sources list:\n\n")
sources := make(sort.StringSlice, 0, publishedCollection.Len())
published := make(map[string]*debian.PublishedRepo, publishedCollection.Len())
err = publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
sources = append(sources, repo.String())
published[repo.String()] = repo
return nil
})
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
sort.Strings(sources)
for _, source := range sources {
repo := published[source]
prefix := repo.Prefix
if prefix == "." {
prefix = ""
} else {
prefix += "/"
}
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component)
if utils.StrSliceHasItem(repo.Architectures, "source") {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, repo.Component)
}
}
context.database.Close()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
err = http.ListenAndServe(listen, http.FileServer(http.Dir(context.publishedStorage.PublicPath())))
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
return nil
}
func makeCmdServe() *commander.Command {
cmd := &commander.Command{
Run: aptlyServe,
UsageLine: "serve",
Short: "start embedded HTTP server to serve published repositories",
Long: `
Command serve starts embedded HTTP server (not suitable for real production usage) to serve
contents of public/ subdirectory of aptly's root that contains published repositories.
ex:
$ aptly serve -listen=:8080
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
}
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
return cmd
}
+732
View File
@@ -0,0 +1,732 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/wsxiaoys/terminal/color"
"sort"
"strings"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var (
err error
snapshot *debian.Snapshot
)
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
// aptly snapshot create snap from mirror mirror
repoName, snapshotName := args[3], args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = debian.NewSnapshotFromRepository(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := debian.NewPackageList()
snapshot = debian.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
if snapshotCollection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
snapshots := make([]string, snapshotCollection.Len())
i := 0
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
snapshots[i] = snapshot.String()
i++
return nil
})
sort.Strings(snapshots)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
return err
}
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
return err
}
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
snapshots := make([]*debian.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = snapshotCollection.ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = snapshotCollection.LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
packageList, err := debian.NewPackageListFromRefList(snapshots[0].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := debian.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
for i := 1; i < len(snapshots); i++ {
pL, err := debian.NewPackageListFromRefList(snapshots[i].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures(true)
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
missing, err := packageList.VerifyDependencies(context.dependencyOptions, architecturesList, sourcePackageList)
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
fmt.Printf("All dependencies are satisfied.\n")
} else {
fmt.Printf("Missing dependencies (%d):\n", len(missing))
deps := make([]string, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
fmt.Printf(" %s\n", dep)
}
}
return err
}
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
}
noDeps := cmd.Flag.Lookup("no-deps").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name> snapshot
snapshot, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
fmt.Printf("Dependencies would be pulled into snapshot:\n %s\nfrom snapshot:\n %s\nand result would be saved as new snapshot %s.\n",
snapshot, source, args[2])
// Convert snapshot to package list
fmt.Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := debian.NewPackageListFromRefList(snapshot.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := debian.NewPackageListFromRefList(source.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
fmt.Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]debian.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = debian.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]debian.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
}
// Go over list of initial dependencies + list of dependencies found
for i := 0; i < len(dependencies); i++ {
dep := dependencies[i]
// Search for package that can satisfy dependencies
pkg := sourcePackageList.Search(dep)
if pkg == nil {
color.Printf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
fmt.Printf("\n")
continue
}
// Remove all packages with the same name and architecture
for p := packageList.Search(debian.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
color.Printf("@r[-]@| %s removed", p)
fmt.Printf("\n")
p = packageList.Search(debian.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name})
}
// Add new discovered package
packageList.Add(pkg)
color.Printf("@g[+]@| %s added", pkg)
fmt.Printf("\n")
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := debian.NewPackageList()
pL.Add(pkg)
missing, err := pL.VerifyDependencies(context.dependencyOptions, []string{arch}, packageList)
if err != nil {
color.Printf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
fmt.Printf("\n")
}
// Append missing dependencies to the list of dependencies to satisfy
for _, misDep := range missing {
found := false
for _, d := range dependencies {
if d == misDep {
found = true
break
}
}
if !found {
dependencies = append(dependencies, misDep)
}
}
}
}
if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
fmt.Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := debian.NewSnapshotFromPackageList(args[2], []*debian.Snapshot{snapshot, source}, packageList,
fmt.Sprintf("Pulled into '%s' with '%s' as source, pull request was: '%s'", snapshot.Name, source.Name, strings.Join(args[3:], " ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
}
return err
}
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
}
onlyMatching := cmd.Flag.Lookup("only-matching").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name-a> snapshot
snapshotA, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotB)
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
fmt.Printf("Snapshots are identical.\n")
} else {
fmt.Printf(" Arch | Package | Version in A | Version in B\n")
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
var verA, verB, pkg, arch, code string
if pdiff.Left == nil {
verA = "-"
verB = pdiff.Right.Version
pkg = pdiff.Right.Name
arch = pdiff.Right.Architecture
} else {
pkg = pdiff.Left.Name
arch = pdiff.Left.Architecture
verA = pdiff.Left.Version
if pdiff.Right == nil {
verB = "-"
} else {
verB = pdiff.Right.Version
}
}
if pdiff.Left == nil {
code = "@g+@|"
} else {
if pdiff.Right == nil {
code = "@r-@|"
} else {
code = "@y!@|"
}
}
color.Printf(code+" %-6s | %-40s | %-40s | %-40s\n", arch, pkg, verA, verB)
}
}
return err
}
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
sources := make([]*debian.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = snapshotCollection.ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList(), true)
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := debian.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
publishedRepoCollection := debian.NewPublishedRepoCollection(context.database)
published := publishedRepoCollection.BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = publishedRepoCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: snapshot is published")
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
fmt.Printf("Snapshot `%s` was used as a source in following snapshots:\n", snapshot.Name)
for _, snap := range snapshots {
fmt.Printf(" * %s\n", snap)
}
return fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use -force to override")
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create <name> from mirror <mirror-name> | create <name> empty",
Short: "creates immutable snapshot of mirror contents",
Long: `
Command create .. from mirror makes persistent immutable snapshot of remote repository mirror. Snapshot could be
published or further modified using merge, pull and other aptly features.
Command create .. empty creates empty snapshot that could be used as a basis for snapshot pull operations, for example.
As snapshots are immutable, creating one empty snapshot should be enough.
ex.
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "lists snapshots",
Long: `
Command list shows full list of snapshots created.
ex:
$ aptly snapshot list
`,
Flag: *flag.NewFlagSet("aptly-snapshot-list", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show <name>",
Short: "shows details about snapshot",
Long: `
Command show displays full information about snapshot.
ex.
$ aptly snapshot show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verifies that dependencies are satisfied in snapshot",
Long: `
Verify does depenency resolution in snapshot, possibly using additional snapshots as dependency sources.
All unsatisfied dependencies are returned.
ex.
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
Flag: *flag.NewFlagSet("aptly-snapshot-verify", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
Short: "performs partial upgrades (pulls new packages) from another snapshot",
Long: `
Command pull pulls new packages along with its dependencies in <name> snapshot
from <source> snapshot. Also can upgrade package version from one snapshot into
another, once again along with dependencies. New snapshot <destination> is created as result of this
process. Packages could be specified simply as 'package-name' or as dependency 'package-name (>= version)'.
ex.
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
return cmd
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "calculates difference in packages between two snapshots",
Long: `
Command diff shows list of missing and new packages, difference in package versions between two snapshots.
ex.
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots into one, replacing matching packages",
Long: `
Merge merges several snapshots into one. Merge happens from left to right. Packages with the same
name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
If run with only one source snapshot, merge copies source into destination.
ex.
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-merge", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about snapshot. If snapshot is published,
it can't be dropped.
ex.
$ aptly snapshot drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "remove snapshot even if it was used as source for other snapshots")
return cmd
}
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
},
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
}
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/aptly"
)
func aptlyVersion(cmd *commander.Command, args []string) error {
fmt.Printf("aptly version: %s\n", aptly.Version)
return nil
}
func makeCmdVersion() *commander.Command {
return &commander.Command{
Run: aptlyVersion,
UsageLine: "version",
Short: "display version",
Long: `
Shows aptly version.
ex:
$ aptly version
`,
Flag: *flag.NewFlagSet("aptly-version", flag.ExitOnError),
}
}