Compare commits

..

53 Commits

Author SHA1 Message Date
Andrey Smirnov 28e050c14e Fix style warnings. 2014-06-07 18:00:36 +04:00
Andrey Smirnov 8fb399026d Version bump for 0.6. 2014-06-07 17:58:33 +04:00
Andrey Smirnov e15f23962a Add missing files. #61 2014-06-07 17:27:24 +04:00
Andrey Smirnov 81af5882b9 Test on per-component/arch Release contents. #61 2014-06-07 17:19:02 +04:00
Andrey Smirnov e554d8befa Update tests with introduction of Release files under each arch/component. #61 2014-06-07 17:01:37 +04:00
Andrey Smirnov 17c564358a Refactoring, support for atomic updates and checksumming. #61 2014-06-07 16:44:54 +04:00
Andrey Smirnov 2e4c1c491e Merge branch 'debinst' of https://github.com/ryanuber/aptly into ryanuber-debinst
Conflicts:
	deb/publish_test.go
2014-06-07 16:34:39 +04:00
Andrey Smirnov e7230d9ee6 One more test fix. #36 2014-06-07 16:28:15 +04:00
Andrey Smirnov 0f1074a721 Fix tests. #36 2014-06-07 16:17:59 +04:00
Andrey Smirnov 17ed34fdaa System tests for aptly publish switch with multiple repositories. #36 2014-06-07 15:18:58 +04:00
Andrey Smirnov 17b320eac4 Sort components when doing string representation. #36 2014-06-07 15:18:36 +04:00
Andrey Smirnov e3a71c81e1 Both components should contain same architectures. #36 2014-06-06 03:05:22 +04:00
Andrey Smirnov bf900deb4b Remove whitespace. 2014-06-06 03:03:58 +04:00
Andrey Smirnov 142387311b Tests for aptly publish update and multi-component repository. #36 2014-06-06 03:00:44 +04:00
Andrey Smirnov 68fbb0cbb9 Fix tests, don't break pool contents. #36 2014-06-06 03:00:29 +04:00
Andrey Smirnov 835da9cb3c One more test fix: duplicate archs. #36 2014-06-06 02:37:09 +04:00
Andrey Smirnov 7cd0d394d4 Test fix: source shouldn't be part of Architectures: field. #36 2014-06-06 02:25:11 +04:00
Andrey Smirnov 2040be2f8a Deduplicate architectures when guessed. #36 2014-06-06 02:14:15 +04:00
Andrey Smirnov 1957c811e8 Tests for multi-component and aptly publish list/snapshot/repo, aptly serve. #36 2014-06-06 02:09:13 +04:00
Andrey Smirnov 2dae9b01a1 Grammar fix. #36 2014-06-06 02:09:00 +04:00
Andrey Smirnov 9a34b4ff1f Update commands to handle multiple components repositories. #36 2014-06-04 17:43:16 +04:00
Andrey Smirnov d218159455 Multiple connections for published repo. #36 2014-06-04 17:06:34 +04:00
Andrey Smirnov 8be6911238 Fix multiple component in aptly graph. #36 2014-06-04 17:04:05 +04:00
Andrey Smirnov 20a7c5ae2d A bit more unit-tests with multi-component repositories. #36 2014-06-04 01:07:32 +04:00
Andrey Smirnov e161313efa Fix aptly serve: correct components list. #36 2014-06-03 17:25:15 +04:00
Andrey Smirnov 7192049c16 Update to new PublishedRepo with multiple components. #36
Multiple component publishing doesn't work yet, but old features are working.
2014-06-03 17:09:00 +04:00
Andrey Smirnov ee71b93669 Major change: published repo now supports multiple components <> snapshots (local repos). #36 2014-06-03 14:34:26 +04:00
Andrey Smirnov 43ee735aa4 Fix error capitalization. 2014-06-03 14:33:19 +04:00
Andrey Smirnov da5b0c9a66 Test fixes. #55 2014-05-31 21:27:36 +04:00
Andrey Smirnov e1dbab6988 Allow publishing of empty snapshots and local repos. #55 2014-05-31 21:13:30 +04:00
Andrey Smirnov bcdfb7d99a Fix system tests: update test data. 2014-05-31 19:37:31 +04:00
Andrey Smirnov ac85a0897a Update gographviz to fixed version with XML escaping. #58 2014-05-30 22:08:11 +04:00
Ryan Uber 9a4543500c Handle source repos while creating dist release file 2014-05-29 22:24:19 -07:00
Ryan Uber b0f9a4a419 Added tests for Release file in distribution directory 2014-05-29 22:07:59 -07:00
Ryan Uber 71ea2be6c1 deb: added Release file to each individual arch dir for d-i. 2014-05-29 14:04:06 -07:00
Andrey Smirnov b717caeda4 Make 'Package:' line field first when serializing stanza. #49 2014-05-30 00:43:19 +04:00
Andrey Smirnov fcc283bdb1 Regenerate man page (-no-remove for aptly snapshot merge). #57 2014-05-30 00:05:15 +04:00
Andrey Smirnov d3d41dd1c9 Add missing files. #57 2014-05-29 20:49:35 +04:00
Andrey Smirnov c72ef05a2a Support for -no-remove while merging snapshots. #57 2014-05-29 18:05:52 +04:00
Andrey Smirnov a1e360b07b Conflict detection for packages in one list. #60 2014-05-29 18:01:07 +04:00
Andrey Smirnov 90bba977d7 Introduce Package.ShortKey(): packages should have no conflict on that key in one list. #60 2014-05-29 18:00:14 +04:00
Andrey Smirnov dc248c5603 Fix diff algorithm for reflists with duplicate entries. #57 2014-05-29 17:58:59 +04:00
Andrey Smirnov f007465d18 Change the way package key works: now it includes FilesHash. #60
Now duplicate packages (the same name/version/arch) but with different set of files
would be handled as separate entities.
2014-05-29 16:49:05 +04:00
Andrey Smirnov 869e83713d Remove debugging output. #51 2014-05-29 00:49:26 +04:00
Andrey Smirnov 7b9e3429fd Use gographviz escaping instead of homegrown escape function. #51 #58
It doesn't fix the bug #58, waiting for maintainer of gographviz to
accept the patch: https://code.google.com/p/gographviz/issues/detail?id=2
2014-05-29 00:47:37 +04:00
Andrey Smirnov 5b75dbc481 Fix system tests. #54 2014-05-18 00:16:00 +04:00
Andrey Smirnov d96839f99d Fix unit tests. #54 2014-05-17 22:47:47 +04:00
Andrey Smirnov 8b2920d5dd aptly repo add now exists with non-zero exit code if some files fail to add. #54 2014-05-17 22:23:20 +04:00
Andrey Smirnov 4240b134e6 Fix spelling. 2014-05-17 22:08:31 +04:00
Andrey Smirnov 1d31a5c25f Don't use fixed cap, as it might be more than length. #53 2014-05-16 00:29:43 +04:00
Andrey Smirnov 05a42f4cba aptly exits with 2 on command/flag parse error. #52 2014-05-16 00:22:51 +04:00
Andrey Smirnov 2cbb486f6b Update tests with new Varnish repo. 2014-05-13 13:39:14 +04:00
Andrey Smirnov d0ff11390b Revert "Version bump for 0.5.1."
This reverts commit b5d025f141.
2014-05-13 12:29:13 +04:00
125 changed files with 64084 additions and 559 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5' gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
gom 'code.google.com/p/go.crypto/ssh/terminal', :commit => '7aa593ce8cea' gom 'code.google.com/p/go.crypto/ssh/terminal', :commit => '7aa593ce8cea'
gom 'code.google.com/p/gographviz', :commit => '212766062629' gom 'code.google.com/p/gographviz', :commit => '454bc64fdfa2'
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793' gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
gom 'github.com/cheggaaa/pb', :commit => '74be7a1388046f374ac36e93d46f5d56e856f827' gom 'github.com/cheggaaa/pb', :commit => '74be7a1388046f374ac36e93d46f5d56e856f827'
gom 'github.com/smira/commander', :commit => '082a3ce267a8225a8ccf94deaf18901223d38fed' gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
gom 'github.com/smira/flag', :commit => '0d0aac2addb39050f45e92c5a6252926096dc841' gom 'github.com/smira/flag', :commit => '0d0aac2addb39050f45e92c5a6252926096dc841'
gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b' gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
gom 'github.com/syndtr/goleveldb/leveldb', :commit => 'ff3719c6816e2cd194f05058452d660608e178ac' gom 'github.com/syndtr/goleveldb/leveldb', :commit => 'ff3719c6816e2cd194f05058452d660608e178ac'
+1 -1
View File
@@ -1,7 +1,7 @@
package aptly package aptly
// Version of aptly // Version of aptly
const Version = "0.5.1" const Version = "0.6"
// Enable debugging features? // Enable debugging features?
const EnableDebug = false const EnableDebug = false
+6 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/smira/aptly/files" "github.com/smira/aptly/files"
"github.com/smira/aptly/http" "github.com/smira/aptly/http"
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag" "github.com/smira/flag"
"os" "os"
"path/filepath" "path/filepath"
@@ -48,7 +49,11 @@ type FatalError struct {
// Fatal panics and aborts execution with exit code 1 // Fatal panics and aborts execution with exit code 1
func Fatal(err error) { func Fatal(err error) {
panic(&FatalError{ReturnCode: 1, Message: err.Error()}) returnCode := 1
if err == commander.ErrFlagError || err == commander.ErrCommandError {
returnCode = 2
}
panic(&FatalError{ReturnCode: returnCode, Message: err.Error()})
} }
// Config loads and returns current configuration // Config loads and returns current configuration
+1 -1
View File
@@ -14,7 +14,7 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
// collect information about references packages... // collect information about references packages...
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyDbRecover(cmd *commander.Command, args []string) error {
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
context.Progress().Printf("Recovering database...\n") context.Progress().Printf("Recovering database...\n")
+22 -18
View File
@@ -13,14 +13,15 @@ import (
"strings" "strings"
) )
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
func aptlyGraph(cmd *commander.Command, args []string) error { func aptlyGraph(cmd *commander.Command, args []string) error {
var err error var err error
graph := gographviz.NewGraph() if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
graph := gographviz.NewEscape()
graph.SetDir(true) graph.SetDir(true)
graph.SetName("aptly") graph.SetName("aptly")
@@ -34,13 +35,13 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
return err return err
} }
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{ graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord", "shape": "Mrecord",
"style": "filled", "style": "filled",
"fillcolor": "darkgoldenrod1", "fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}", "label": fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "), repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())), strings.Join(repo.Architectures, ", "), repo.NumPackages()),
}) })
existingNodes[repo.UUID] = true existingNodes[repo.UUID] = true
return nil return nil
@@ -58,12 +59,12 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
return err return err
} }
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{ graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord", "shape": "Mrecord",
"style": "filled", "style": "filled",
"fillcolor": "mediumseagreen", "fillcolor": "mediumseagreen",
"label": graphvizEscape(fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}", "label": fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
repo.Name, repo.Comment, repo.NumPackages())), repo.Name, repo.Comment, repo.NumPackages()),
}) })
existingNodes[repo.UUID] = true existingNodes[repo.UUID] = true
return nil return nil
@@ -91,18 +92,18 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
description = "Snapshot from repo" description = "Snapshot from repo"
} }
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{ graph.AddNode("aptly", snapshot.UUID, map[string]string{
"shape": "Mrecord", "shape": "Mrecord",
"style": "filled", "style": "filled",
"fillcolor": "cadetblue1", "fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())), "label": fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages()),
}) })
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" { if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs { for _, uuid := range snapshot.SourceIDs {
_, exists := existingNodes[uuid] _, exists := existingNodes[uuid]
if exists { if exists {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil) graph.AddEdge(uuid, snapshot.UUID, true, nil)
} }
} }
} }
@@ -116,16 +117,19 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
fmt.Printf("Loading published repos...\n") fmt.Printf("Loading published repos...\n")
context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error { context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{ graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord", "shape": "Mrecord",
"style": "filled", "style": "filled",
"fillcolor": "darkolivegreen1", "fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))), "label": fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution,
strings.Join(repo.Components(), " "), strings.Join(repo.Architectures, ", ")),
}) })
_, exists := existingNodes[repo.SourceUUID] for _, uuid := range repo.Sources {
_, exists := existingNodes[uuid]
if exists { if exists {
graph.AddEdge(graphvizEscape(repo.SourceUUID), "", graphvizEscape(repo.UUID), "", true, nil) graph.AddEdge(uuid, repo.UUID, true, nil)
}
} }
return nil return nil
+1 -1
View File
@@ -12,7 +12,7 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error var err error
if !(len(args) == 2 && strings.HasPrefix(args[1], "ppa:") || len(args) >= 3) { if !(len(args) == 2 && strings.HasPrefix(args[1], "ppa:") || len(args) >= 3) {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool) downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool)
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
raw := cmd.Flag.Lookup("raw").Value.Get().(bool) raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
+1 -1
View File
@@ -12,7 +12,7 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -9,7 +9,7 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 1 || len(args) > 2 { if len(args) < 1 || len(args) > 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
distribution := args[0] distribution := args[0]
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
raw := cmd.Flag.Lookup("raw").Value.Get().(bool) raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
+7 -1
View File
@@ -15,6 +15,12 @@ Command publishes current state of local repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory. by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing. Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple local
repositories as the arguments:
aptly publish repo -component=main,contrib repo-main repo-contrib
It is not recommended to publish local repositories directly unless the It is not recommended to publish local repositories directly unless the
repository is for testing purposes and changes happen frequently. For repository is for testing purposes and changes happen frequently. For
production usage please take snapshot of repository and publish it production usage please take snapshot of repository and publish it
@@ -27,7 +33,7 @@ Example:
Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError),
} }
cmd.Flag.String("distribution", "", "distribution name to publish") cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish") cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
+72 -20
View File
@@ -11,27 +11,35 @@ import (
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error { func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 1 || len(args) > 2 {
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
if len(args) < len(components) || len(args) > len(components)+1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0]
var prefix string var prefix string
if len(args) == 2 { if len(args) == len(components)+1 {
prefix = args[1] prefix = args[len(components)]
args = args[0 : len(args)-1]
} else { } else {
prefix = "" prefix = ""
} }
var ( var (
source interface{} sources = []interface{}{}
message string message string
) )
if cmd.Name() == "snapshot" { if cmd.Name() == "snapshot" {
var snapshot *deb.Snapshot var (
snapshot *deb.Snapshot
emptyWarning = false
parts = []string{}
)
for _, name := range args {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name) snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil { if err != nil {
return fmt.Errorf("unable to publish: %s", err) return fmt.Errorf("unable to publish: %s", err)
@@ -42,10 +50,32 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to publish: %s", err) return fmt.Errorf("unable to publish: %s", err)
} }
source = snapshot sources = append(sources, snapshot)
message = fmt.Sprintf("Snapshot %s", snapshot.Name) parts = append(parts, snapshot.Name)
if snapshot.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Snapshot %s has", parts[0])
} else {
message = fmt.Sprintf("Snapshots %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else if cmd.Name() == "repo" { } else if cmd.Name() == "repo" {
var localRepo *deb.LocalRepo var (
localRepo *deb.LocalRepo
emptyWarning = false
parts = []string{}
)
for _, name := range args {
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name) localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil { if err != nil {
return fmt.Errorf("unable to publish: %s", err) return fmt.Errorf("unable to publish: %s", err)
@@ -56,16 +86,31 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to publish: %s", err) return fmt.Errorf("unable to publish: %s", err)
} }
source = localRepo sources = append(sources, localRepo)
message = fmt.Sprintf("Local repo %s", localRepo.Name) parts = append(parts, localRepo.Name)
if localRepo.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Local repo %s has", parts[0])
} else {
message = fmt.Sprintf("Local repos %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else { } else {
panic("unknown command") panic("unknown command")
} }
component := context.flags.Lookup("component").Value.String()
distribution := context.flags.Lookup("distribution").Value.String() distribution := context.flags.Lookup("distribution").Value.String()
published, err := deb.NewPublishedRepo(prefix, distribution, component, context.ArchitecturesList(), source, context.CollectionFactory()) published, err := deb.NewPublishedRepo(prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
if err != nil { if err != nil {
return fmt.Errorf("unable to publish: %s", err) return fmt.Errorf("unable to publish: %s", err)
} }
@@ -93,19 +138,20 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err) return fmt.Errorf("unable to save to DB: %s", err)
} }
prefix, component, distribution = published.Prefix, published.Component, published.Distribution var repoComponents string
prefix, repoComponents, distribution = published.Prefix, strings.Join(published.Components(), " "), published.Distribution
if prefix == "." { if prefix == "." {
prefix = "" prefix = ""
} else if !strings.HasSuffix(prefix, "/") { } else if !strings.HasSuffix(prefix, "/") {
prefix += "/" prefix += "/"
} }
context.Progress().Printf("\n%s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n", context.Progress().Printf("\n%s been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
message, context.PublishedStorage().PublicPath()) message, context.PublishedStorage().PublicPath())
context.Progress().Printf("Now you can add following line to apt sources:\n") context.Progress().Printf("Now you can add following line to apt sources:\n")
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component) context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
if utils.StrSliceHasItem(published.Architectures, "source") { if utils.StrSliceHasItem(published.Architectures, "source") {
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, component) context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
} }
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n") context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n") context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
@@ -123,6 +169,12 @@ Command publishes snapshot as Debian repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory. by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing. Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple snapshots
as the arguments:
aptly publish snapshot -component=main,contrib snap-main snap-contrib
Example: Example:
$ aptly publish snapshot wheezy-main $ aptly publish snapshot wheezy-main
@@ -130,7 +182,7 @@ Example:
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
} }
cmd.Flag.String("distribution", "", "distribution name to publish") cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish") cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
+42 -19
View File
@@ -5,38 +5,32 @@ import (
"github.com/smira/aptly/deb" "github.com/smira/aptly/deb"
"github.com/smira/commander" "github.com/smira/commander"
"github.com/smira/flag" "github.com/smira/flag"
"strings"
) )
func aptlyPublishSwitch(cmd *commander.Command, args []string) error { func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 2 || len(args) > 3 {
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
if len(args) < len(components)+1 || len(args) > len(components)+2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
distribution := args[0] distribution := args[0]
prefix := "." prefix := "."
var ( var (
name string names []string
snapshot *deb.Snapshot snapshot *deb.Snapshot
) )
if len(args) == 3 { if len(args) == len(components)+2 {
prefix = args[1] prefix = args[1]
name = args[2] names = args[2:]
} else { } else {
name = args[1] names = args[1:]
}
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
} }
var published *deb.PublishedRepo var published *deb.PublishedRepo
@@ -55,7 +49,28 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
} }
published.UpdateSnapshot(snapshot) publishedComponents := published.Components()
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
components = publishedComponents
}
if len(names) != len(components) {
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
}
for i, component := range components {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
published.UpdateSnapshot(component, snapshot)
}
signer, err := getSigner(context.flags) signer, err := getSigner(context.flags)
if err != nil { if err != nil {
@@ -72,7 +87,7 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err) return fmt.Errorf("unable to save to DB: %s", err)
} }
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component, err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.PublishedStorage(), context.CollectionFactory(), context.Progress()) context.PublishedStorage(), context.CollectionFactory(), context.Progress())
if err != nil { if err != nil {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
@@ -90,7 +105,14 @@ func makeCmdPublishSwitch() *commander.Command {
Short: "update published repository by switching to new snapshot", Short: "update published repository by switching to new snapshot",
Long: ` Long: `
Command switches in-place published repository with new snapshot contents. All Command switches in-place published repository with new snapshot contents. All
publishing parameters are preserved (architecture list, distribution, component). publishing parameters are preserved (architecture list, distribution,
component).
For multiple component repositories, flag -component should be given with
list of components to update. Corresponding snapshots should be given in the
same order, e.g.:
aptly publish update -component=main,contrib wheezy wh-main wh-contrib
Example: Example:
@@ -102,6 +124,7 @@ Example:
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret 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") cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.String("component", "", "component names to update (for multi-component publishing, separate components with commas)")
return cmd return cmd
} }
+9 -3
View File
@@ -11,7 +11,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 1 || len(args) > 2 { if len(args) < 1 || len(args) > 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
distribution := args[0] distribution := args[0]
@@ -37,7 +37,10 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
} }
published.UpdateLocalRepo() components := published.Components()
for _, component := range components {
published.UpdateLocalRepo(component)
}
signer, err := getSigner(context.flags) signer, err := getSigner(context.flags)
if err != nil { if err != nil {
@@ -54,7 +57,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err) return fmt.Errorf("unable to save to DB: %s", err)
} }
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component, err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.PublishedStorage(), context.CollectionFactory(), context.Progress()) context.PublishedStorage(), context.CollectionFactory(), context.Progress())
if err != nil { if err != nil {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
@@ -76,6 +79,9 @@ and <prefix> should be occupied with local repository published
using command aptly publish repo. Update happens in-place with using command aptly publish repo. Update happens in-place with
minimum possible downtime for published repository. minimum possible downtime for published repository.
For multiple component published repositories, all local repositories
are updated.
Example: Example:
$ aptly publish update wheezy ppa $ aptly publish update wheezy ppa
+18 -1
View File
@@ -16,7 +16,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 2 { if len(args) < 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
@@ -41,11 +41,13 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
} }
packageFiles := []string{} packageFiles := []string{}
failedFiles := []string{}
for _, location := range args[1:] { for _, location := range args[1:] {
info, err2 := os.Stat(location) info, err2 := os.Stat(location)
if err2 != nil { if err2 != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2) context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2)
failedFiles = append(failedFiles, location)
continue continue
} }
if info.IsDir() { if info.IsDir() {
@@ -68,6 +70,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
packageFiles = append(packageFiles, location) packageFiles = append(packageFiles, location)
} else { } else {
context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location) context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
failedFiles = append(failedFiles, location)
continue continue
} }
} }
@@ -100,6 +103,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
} }
if err != nil { if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err) context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
failedFiles = append(failedFiles, file)
continue continue
} }
@@ -118,6 +122,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
err = context.PackagePool().Import(file, checksums.MD5) err = context.PackagePool().Import(file, checksums.MD5)
if err != nil { if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err) context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err)
failedFiles = append(failedFiles, file)
continue continue
} }
@@ -132,6 +137,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
err = context.PackagePool().Import(sourceFile, f.Checksums.MD5) err = context.PackagePool().Import(sourceFile, f.Checksums.MD5)
if err != nil { if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err) context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err)
failedFiles = append(failedFiles, file)
break break
} }
@@ -145,12 +151,14 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
err = context.CollectionFactory().PackageCollection().Update(p) err = context.CollectionFactory().PackageCollection().Update(p)
if err != nil { if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err) context.Progress().ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err)
failedFiles = append(failedFiles, file)
continue continue
} }
err = list.Add(p) err = list.Add(p)
if err != nil { if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err) context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
failedFiles = append(failedFiles, file)
continue continue
} }
@@ -176,6 +184,15 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
} }
} }
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 return err
} }
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
repo := deb.NewLocalRepo(args[0], context.flags.Lookup("comment").Value.String()) repo := deb.NewLocalRepo(args[0], context.flags.Lookup("comment").Value.String())
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0]) repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0])
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
raw := cmd.Flag.Lookup("raw").Value.Get().(bool) raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
+1 -1
View File
@@ -12,7 +12,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 3 { if len(args) < 3 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
command := cmd.Name() command := cmd.Name()
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 2 { if len(args) < 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+8 -2
View File
@@ -10,11 +10,17 @@ import (
"net/http" "net/http"
"os" "os"
"sort" "sort"
"strings"
) )
func aptlyServe(cmd *commander.Command, args []string) error { func aptlyServe(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 { if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n") fmt.Printf("No published repositories, unable to serve.\n")
return nil return nil
@@ -69,11 +75,11 @@ func aptlyServe(cmd *commander.Command, args []string) error {
} }
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n", fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component) repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
if utils.StrSliceHasItem(repo.Architectures, "source") { if utils.StrSliceHasItem(repo.Architectures, "source") {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n", fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, repo.Component) listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
} }
} }
+1 -1
View File
@@ -61,7 +61,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty") snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else { } else {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
err = context.CollectionFactory().SnapshotCollection().Add(snapshot) err = context.CollectionFactory().SnapshotCollection().Add(snapshot)
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 2 { if len(args) != 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool) onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool)
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
raw := cmd.Flag.Lookup("raw").Value.Get().(bool) raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
+9 -3
View File
@@ -11,7 +11,7 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 2 { if len(args) < 2 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
sources := make([]*deb.Snapshot, len(args)-1) sources := make([]*deb.Snapshot, len(args)-1)
@@ -29,10 +29,15 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
} }
latest := context.flags.Lookup("latest").Value.Get().(bool) latest := context.flags.Lookup("latest").Value.Get().(bool)
overrideMatching := !latest noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
if noRemove && latest {
return fmt.Errorf("-no-remove and -latest can't be specified together")
}
overrideMatching := !latest && !noRemove
result := sources[0].RefList() result := sources[0].RefList()
for i := 1; i < len(sources); i++ { for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList(), overrideMatching) result = result.Merge(sources[i].RefList(), overrideMatching)
} }
@@ -79,6 +84,7 @@ Example:
} }
cmd.Flag.Bool("latest", false, "use only the latest version of each package") cmd.Flag.Bool("latest", false, "use only the latest version of each package")
cmd.Flag.Bool("no-remove", false, "don't remove duplicate arch/name packages")
return cmd return cmd
} }
+2 -2
View File
@@ -13,7 +13,7 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 4 { if len(args) < 4 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
noDeps := context.flags.Lookup("no-deps").Value.Get().(bool) noDeps := context.flags.Lookup("no-deps").Value.Get().(bool)
@@ -86,7 +86,7 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
// Perform pull // Perform pull
for _, arch := range architecturesList { for _, arch := range architecturesList {
dependencies := make([]deb.Dependency, len(initialDependencies), 128) dependencies := make([]deb.Dependency, len(initialDependencies), 2*len(initialDependencies))
for i := range dependencies { for i := range dependencies {
dependencies[i] = initialDependencies[i] dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch dependencies[i].Architecture = arch
+1 -1
View File
@@ -10,7 +10,7 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 1 { if len(args) != 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
name := args[0] name := args[0]
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 1 { if len(args) < 1 {
cmd.Usage() cmd.Usage()
return err return commander.ErrCommandError
} }
snapshots := make([]*deb.Snapshot, len(args)) snapshots := make([]*deb.Snapshot, len(args))
+5
View File
@@ -7,6 +7,11 @@ import (
) )
func aptlyVersion(cmd *commander.Command, args []string) error { func aptlyVersion(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("aptly version: %s\n", aptly.Version) fmt.Printf("aptly version: %s\n", aptly.Version)
return nil return nil
} }
+1 -1
View File
@@ -11,7 +11,7 @@ import (
type Stanza map[string]string type Stanza map[string]string
// Canonical order of fields in stanza // Canonical order of fields in stanza
var canocialOrder = []string{"Origin", "Label", "Suite", "Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer", var canocialOrder = []string{"Package", "Origin", "Label", "Suite", "Version", "Installed-Size", "Priority", "Section", "Maintainer",
"Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256"} "Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256"}
// Copy returns copy of Stanza // Copy returns copy of Stanza
+4 -4
View File
@@ -86,11 +86,11 @@ func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageColle
// Add appends package to package list, additionally checking for uniqueness // Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error { func (l *PackageList) Add(p *Package) error {
key := string(p.Key("")) key := string(p.ShortKey(""))
existing, ok := l.packages[key] existing, ok := l.packages[key]
if ok { if ok {
if !existing.Equals(p) { if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p) return fmt.Errorf("conflict in package %s", p)
} }
return nil return nil
} }
@@ -137,7 +137,7 @@ func (l *PackageList) Append(pl *PackageList) error {
existing, ok := l.packages[k] existing, ok := l.packages[k]
if ok { if ok {
if !existing.Equals(p) { if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p) return fmt.Errorf("conflict in package %s", p)
} }
} else { } else {
l.packages[k] = p l.packages[k] = p
@@ -149,7 +149,7 @@ func (l *PackageList) Append(pl *PackageList) error {
// Remove removes package from the list, and updates index when required // Remove removes package from the list, and updates index when required
func (l *PackageList) Remove(p *Package) { func (l *PackageList) Remove(p *Package) {
delete(l.packages, string(p.Key(""))) delete(l.packages, string(p.ShortKey("")))
if l.indexed { if l.indexed {
for _, provides := range p.Provides { for _, provides := range p.Provides {
for i, pkg := range l.providesIndex[provides] { for i, pkg := range l.providesIndex[provides] {
+1 -1
View File
@@ -29,7 +29,7 @@ func (s *PackageListSuite) SetUpTest(c *C) {
stanza["Package"] = "mars-invaders" stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza) s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy() stanza = packageStanza.Copy()
stanza["Size"] = "42" stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza) s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy() stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers" stanza["Package"] = "lonely-strangers"
+14 -1
View File
@@ -26,6 +26,8 @@ type Package struct {
IsSource bool IsSource bool
// Hash of files section // Hash of files section
FilesHash uint64 FilesHash uint64
// Is this >= 0.6 package?
V06Plus bool
// Offload fields // Offload fields
deps *PackageDependencies deps *PackageDependencies
extra *Stanza extra *Stanza
@@ -41,6 +43,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
Version: input["Version"], Version: input["Version"],
Architecture: input["Architecture"], Architecture: input["Architecture"],
Source: input["Source"], Source: input["Source"],
V06Plus: true,
} }
delete(input, "Package") delete(input, "Package")
@@ -89,6 +92,7 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
Version: input["Version"], Version: input["Version"],
Architecture: "source", Architecture: "source",
SourceArchitecture: input["Architecture"], SourceArchitecture: input["Architecture"],
V06Plus: true,
} }
delete(input, "Package") delete(input, "Package")
@@ -167,7 +171,16 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
// Key returns unique key identifying package // Key returns unique key identifying package
func (p *Package) Key(prefix string) []byte { func (p *Package) Key(prefix string) []byte {
return []byte(prefix + "P" + p.Architecture + " " + p.Name + " " + p.Version) if p.V06Plus {
return []byte(fmt.Sprintf("%sP%s %s %s %08x", prefix, p.Architecture, p.Name, p.Version, p.FilesHash))
}
return p.ShortKey(prefix)
}
// ShortKey returns key for the package that should be unique in one list
func (p *Package) ShortKey(prefix string) []byte {
return []byte(fmt.Sprintf("%sP%s %s %s", prefix, p.Architecture, p.Name, p.Version))
} }
// String creates readable representation // String creates readable representation
+1 -20
View File
@@ -83,7 +83,7 @@ func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
p.UpdateFiles(PackageFiles(oldp.Files)) p.UpdateFiles(PackageFiles(oldp.Files))
// Save in new format // Save in new format
err = collection.internalUpdate(p) err = collection.Update(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -156,25 +156,6 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
// Update adds or updates information about package in DB checking for conficts first // Update adds or updates information about package in DB checking for conficts first
func (collection *PackageCollection) Update(p *Package) error { func (collection *PackageCollection) Update(p *Package) error {
existing, err := collection.ByKey(p.Key(""))
if err == nil {
// if .Files is different, consider to be conflict
if p.FilesHash != existing.FilesHash {
return fmt.Errorf("unable to save: %s, conflict with existing packge", p)
}
// ok, .Files are the same, but maybe some meta-data is different, proceed to saving
} else {
if err != database.ErrNotFound {
return err
}
// ok, package doesn't exist yet
}
return collection.internalUpdate(p)
}
// internalUpdate updates information in DB about package and offloaded fields
func (collection *PackageCollection) internalUpdate(p *Package) error {
encoder := codec.NewEncoder(&collection.encodeBuffer, &codec.MsgpackHandle{}) encoder := codec.NewEncoder(&collection.encodeBuffer, &codec.MsgpackHandle{})
collection.encodeBuffer.Reset() collection.encodeBuffer.Reset()
-14
View File
@@ -48,20 +48,6 @@ func (s *PackageCollectionSuite) TestUpdate(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(res.Equals(s.p), Equals, false) c.Assert(res.Equals(s.p), Equals, false)
c.Assert(res.Equals(p2), Equals, true) c.Assert(res.Equals(p2), Equals, true)
// change file info
p2 = NewPackageFromControlFile(packageStanza.Copy())
p2.UpdateFiles(nil)
res, err = s.collection.ByKey(p2.Key(""))
err = s.collection.Update(p2)
c.Assert(err, ErrorMatches, ".*conflict with existing packge")
p2 = NewPackageFromControlFile(packageStanza.Copy())
files := p2.Files()
files[0].Checksums.MD5 = "abcdef"
p2.UpdateFiles(files)
res, err = s.collection.ByKey(p2.Key(""))
err = s.collection.Update(p2)
c.Assert(err, ErrorMatches, ".*conflict with existing packge")
} }
func (s *PackageCollectionSuite) TestByKey(c *C) { func (s *PackageCollectionSuite) TestByKey(c *C) {
+11
View File
@@ -87,10 +87,21 @@ func (s *PackageSuite) TestWithProvides(c *C) {
func (s *PackageSuite) TestKey(c *C) { func (s *PackageSuite) TestKey(c *C) {
p := NewPackageFromControlFile(s.stanza) p := NewPackageFromControlFile(s.stanza)
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
p.V06Plus = false
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2")) c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2")) c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
} }
func (s *PackageSuite) TestShortKey(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.ShortKey(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
c.Check(p.ShortKey("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
}
func (s *PackageSuite) TestStanza(c *C) { func (s *PackageSuite) TestStanza(c *C) {
p := NewPackageFromControlFile(s.stanza.Copy()) p := NewPackageFromControlFile(s.stanza.Copy())
stanza := p.Stanza() stanza := p.Stanza()
+388 -139
View File
@@ -17,6 +17,15 @@ import (
"time" "time"
) )
type repoSourceItem struct {
// Pointer to snapshot if SourceKind == "snapshot"
snapshot *Snapshot
// Pointer to local repo if SourceKind == "local"
localRepo *LocalRepo
// Package references is SourceKind == "local"
packageRefs *PackageRefList
}
// PublishedRepo is a published for http/ftp representation of snapshot as Debian repository // PublishedRepo is a published for http/ftp representation of snapshot as Debian repository
type PublishedRepo struct { type PublishedRepo struct {
// Internal unique ID // Internal unique ID
@@ -24,81 +33,38 @@ type PublishedRepo struct {
// Prefix & distribution should be unique across all published repositories // Prefix & distribution should be unique across all published repositories
Prefix string Prefix string
Distribution string Distribution string
Component string
Origin string Origin string
Label string Label string
// Architectures is a list of all architectures published // Architectures is a list of all architectures published
Architectures []string Architectures []string
// SourceKind is "local"/"repo" // SourceKind is "local"/"repo"
SourceKind string SourceKind string
// Map of sources by each component: component name -> source UUID
Sources map[string]string
// Legacy fields for compatibily with old published repositories (< 0.6)
Component string
// SourceUUID is UUID of either snapshot or local repo // SourceUUID is UUID of either snapshot or local repo
SourceUUID string `codec:"SnapshotUUID"` SourceUUID string `codec:"SnapshotUUID"`
// Pointer to snapshot if SourceKind == "snapshot" // Map of component to source items
snapshot *Snapshot sourceItems map[string]repoSourceItem
// Pointer to local repo if SourceKind == "local"
localRepo *LocalRepo
// Package references is SourceKind == "local"
packageRefs *PackageRefList
// True if repo is being re-published // True if repo is being re-published
rePublishing bool rePublishing bool
} }
// NewPublishedRepo creates new published repository // walkUpTree goes from source in the tree of source snapshots/mirrors/local repos
// // gathering information about declared components and distributions
// prefix specifies publishing prefix func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootDistributions []string, rootComponents []string) {
// distribution, component and architectures are user-defined properties
// source could either be *Snapshot or *LocalRepo
func NewPublishedRepo(prefix string, distribution string, component string, architectures []string, source interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
var ok bool
result := &PublishedRepo{
UUID: uuid.New(),
Architectures: architectures,
}
// figure out source
result.snapshot, ok = source.(*Snapshot)
if ok {
result.SourceKind = "snapshot"
result.SourceUUID = result.snapshot.UUID
} else {
result.localRepo, ok = source.(*LocalRepo)
if ok {
result.SourceKind = "local"
result.SourceUUID = result.localRepo.UUID
result.packageRefs = result.localRepo.RefList()
} else {
panic("unknown source kind")
}
}
// clean & verify prefix
prefix = filepath.Clean(prefix)
if strings.HasPrefix(prefix, "/") {
prefix = prefix[1:]
}
if strings.HasSuffix(prefix, "/") {
prefix = prefix[:len(prefix)-1]
}
prefix = filepath.Clean(prefix)
for _, part := range strings.Split(prefix, "/") {
if part == ".." || part == "dists" || part == "pool" {
return nil, fmt.Errorf("invalid prefix %s", prefix)
}
}
result.Prefix = prefix
// guessing distribution & component
if component == "" || distribution == "" {
var ( var (
head interface{} head interface{}
current = []interface{}{source} current = []interface{}{source}
)
rootComponents = []string{} rootComponents = []string{}
rootDistributions = []string{} rootDistributions = []string{}
)
// walk up the tree from current source up to roots (local or remote repos) // walk up the tree from current source up to roots (local or remote repos)
// and collect information about distribution and components // and collect information about distribution and components
@@ -144,53 +110,163 @@ func NewPublishedRepo(prefix string, distribution string, component string, arch
} }
} }
return
}
// NewPublishedRepo creates new published repository
//
// prefix specifies publishing prefix
// distribution and architectures are user-defined properties
// components & sources are lists of component to source mapping (*Snapshot or *LocalRepo)
func NewPublishedRepo(prefix string, distribution string, architectures []string,
components []string, sources []interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
result := &PublishedRepo{
UUID: uuid.New(),
Architectures: architectures,
Sources: make(map[string]string),
sourceItems: make(map[string]repoSourceItem),
}
if len(sources) == 0 {
panic("publish with empty sources")
}
if len(sources) != len(components) {
panic("sources and components should be equal in size")
}
var (
discoveredDistributions = []string{}
source interface{}
component string
snapshot *Snapshot
localRepo *LocalRepo
ok bool
)
// get first source
source = sources[0]
// figure out source kind
snapshot, ok = source.(*Snapshot)
if ok {
result.SourceKind = "snapshot"
} else {
localRepo, ok = source.(*LocalRepo)
if ok {
result.SourceKind = "local"
} else {
panic("unknown source kind")
}
}
for i := range sources {
component, source = components[i], sources[i]
if distribution == "" || component == "" {
rootDistributions, rootComponents := walkUpTree(source, collectionFactory)
if distribution == "" { if distribution == "" {
sort.Strings(rootDistributions) discoveredDistributions = append(discoveredDistributions, rootDistributions...)
if len(rootDistributions) > 0 && rootDistributions[0] == rootDistributions[len(rootDistributions)-1] { }
distribution = rootDistributions[0] if component == "" {
sort.Strings(rootComponents)
if len(rootComponents) > 0 && rootComponents[0] == rootComponents[len(rootComponents)-1] {
component = rootComponents[0]
} else if len(sources) == 1 {
// only if going from one source, assume default component "main"
component = "main"
} else {
return nil, fmt.Errorf("unable to figure out component name for %s", source)
}
}
}
_, exists := result.Sources[component]
if exists {
return nil, fmt.Errorf("duplicate component name: %s", component)
}
if result.SourceKind == "snapshot" {
snapshot = source.(*Snapshot)
result.Sources[component] = snapshot.UUID
result.sourceItems[component] = repoSourceItem{snapshot: snapshot}
} else if result.SourceKind == "local" {
localRepo = source.(*LocalRepo)
result.Sources[component] = localRepo.UUID
result.sourceItems[component] = repoSourceItem{localRepo: localRepo, packageRefs: localRepo.RefList()}
}
}
// clean & verify prefix
prefix = filepath.Clean(prefix)
if strings.HasPrefix(prefix, "/") {
prefix = prefix[1:]
}
if strings.HasSuffix(prefix, "/") {
prefix = prefix[:len(prefix)-1]
}
prefix = filepath.Clean(prefix)
for _, part := range strings.Split(prefix, "/") {
if part == ".." || part == "dists" || part == "pool" {
return nil, fmt.Errorf("invalid prefix %s", prefix)
}
}
result.Prefix = prefix
// guessing distribution
if distribution == "" {
sort.Strings(discoveredDistributions)
if len(discoveredDistributions) > 0 && discoveredDistributions[0] == discoveredDistributions[len(discoveredDistributions)-1] {
distribution = discoveredDistributions[0]
} else { } else {
return nil, fmt.Errorf("unable to guess distribution name, please specify explicitly") return nil, fmt.Errorf("unable to guess distribution name, please specify explicitly")
} }
} }
if component == "" { result.Distribution = distribution
sort.Strings(rootComponents)
if len(rootComponents) > 0 && rootComponents[0] == rootComponents[len(rootComponents)-1] {
component = rootComponents[0]
} else {
component = "main"
}
}
}
result.Distribution, result.Component = distribution, component
return result, nil return result, nil
} }
// String returns human-readable represenation of PublishedRepo // String returns human-readable represenation of PublishedRepo
func (p *PublishedRepo) String() string { func (p *PublishedRepo) String() string {
var sources = []string{}
for _, component := range p.Components() {
var source string var source string
if p.snapshot != nil { item := p.sourceItems[component]
source = p.snapshot.String() if item.snapshot != nil {
} else if p.localRepo != nil { source = item.snapshot.String()
source = p.localRepo.String() } else if item.localRepo != nil {
source = item.localRepo.String()
} else { } else {
panic("no snapshot/localRepo") panic("no snapshot/localRepo")
} }
sources = append(sources, fmt.Sprintf("{%s: %s}", component, source))
}
var extra string var extra string
if p.Origin != "" { if p.Origin != "" {
extra += fmt.Sprintf(", origin: %s", p.Origin) extra += fmt.Sprintf("origin: %s", p.Origin)
} }
if p.Label != "" { if p.Label != "" {
extra += fmt.Sprintf(", label: %s", p.Label) if extra != "" {
extra += ", "
}
extra += fmt.Sprintf("label: %s", p.Label)
} }
return fmt.Sprintf("%s/%s (%s%s) [%s] publishes %s", p.Prefix, p.Distribution, p.Component, extra, strings.Join(p.Architectures, ", "), source) if extra != "" {
extra = " (" + extra + ")"
}
return fmt.Sprintf("%s/%s%s [%s] publishes %s", p.Prefix, p.Distribution, extra, strings.Join(p.Architectures, ", "),
strings.Join(sources, ", "))
} }
// Key returns unique key identifying PublishedRepo // Key returns unique key identifying PublishedRepo
@@ -199,37 +275,57 @@ func (p *PublishedRepo) Key() []byte {
} }
// RefKey is a unique id for package reference list // RefKey is a unique id for package reference list
func (p *PublishedRepo) RefKey() []byte { func (p *PublishedRepo) RefKey(component string) []byte {
return []byte("E" + p.UUID) return []byte("E" + p.UUID + component)
} }
// RefList returns list of package refs in local repo // RefList returns list of package refs in local repo
func (p *PublishedRepo) RefList() *PackageRefList { func (p *PublishedRepo) RefList(component string) *PackageRefList {
item := p.sourceItems[component]
if p.SourceKind == "local" { if p.SourceKind == "local" {
return p.packageRefs return item.packageRefs
} }
if p.SourceKind == "snapshot" { if p.SourceKind == "snapshot" {
return p.snapshot.RefList() return item.snapshot.RefList()
} }
panic("unknown source") panic("unknown source")
} }
func (p *PublishedRepo) UpdateLocalRepo() { // Components returns sorted list of published repo components
func (p *PublishedRepo) Components() []string {
result := make([]string, 0, len(p.Sources))
for component := range p.Sources {
result = append(result, component)
}
sort.Strings(result)
return result
}
// UpdateLocalRepo updates content from local repo in component
func (p *PublishedRepo) UpdateLocalRepo(component string) {
if p.SourceKind != "local" { if p.SourceKind != "local" {
panic("not local repo publish") panic("not local repo publish")
} }
p.packageRefs = p.localRepo.RefList() item := p.sourceItems[component]
item.packageRefs = item.localRepo.RefList()
p.sourceItems[component] = item
p.rePublishing = true p.rePublishing = true
} }
func (p *PublishedRepo) UpdateSnapshot(snapshot *Snapshot) { // UpdateSnapshot switches snapshot for component
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
if p.SourceKind != "snapshot" { if p.SourceKind != "snapshot" {
panic("not snapshot publish") panic("not snapshot publish")
} }
p.snapshot = snapshot item := p.sourceItems[component]
p.SourceUUID = snapshot.UUID item.snapshot = snapshot
p.sourceItems[component] = item
p.Sources[component] = snapshot.UUID
p.rePublishing = true p.rePublishing = true
} }
@@ -256,9 +352,32 @@ func (p *PublishedRepo) Decode(input []byte) error {
p.SourceKind = "snapshot" p.SourceKind = "snapshot"
} }
// <0.6 aptly used single SourceUUID + Component instead of Sources
if p.Component != "" && p.SourceUUID != "" && len(p.Sources) == 0 {
p.Sources = map[string]string{p.Component: p.SourceUUID}
p.Component = ""
p.SourceUUID = ""
}
return nil return nil
} }
// GetOrigin returns default or manual Origin:
func (p *PublishedRepo) GetOrigin() string {
if p.Origin == "" {
return p.Prefix + " " + p.Distribution
}
return p.Origin
}
// GetLabel returns default or manual Label:
func (p *PublishedRepo) GetLabel() string {
if p.Label == "" {
return p.Prefix + " " + p.Distribution
}
return p.Label
}
// Publish publishes snapshot (repository) contents, links package files, generates Packages & Release files, signs them // Publish publishes snapshot (repository) contents, links package files, generates Packages & Release files, signs them
func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage aptly.PublishedStorage, func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage aptly.PublishedStorage,
collectionFactory *CollectionFactory, signer utils.Signer, progress aptly.Progress) error { collectionFactory *CollectionFactory, signer utils.Signer, progress aptly.Progress) error {
@@ -276,19 +395,21 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
progress.Printf("Loading packages...\n") progress.Printf("Loading packages...\n")
} }
lists := map[string]*PackageList{}
for component := range p.sourceItems {
// Load all packages // Load all packages
list, err := NewPackageListFromRefList(p.RefList(), collectionFactory.PackageCollection(), progress) lists[component], err = NewPackageListFromRefList(p.RefList(component), collectionFactory.PackageCollection(), progress)
if err != nil { if err != nil {
return fmt.Errorf("unable to load packages: %s", err) return fmt.Errorf("unable to load packages: %s", err)
} }
if list.Len() == 0 {
return fmt.Errorf("source is empty")
} }
if !p.rePublishing { if !p.rePublishing {
if len(p.Architectures) == 0 { if len(p.Architectures) == 0 {
p.Architectures = list.Architectures(true) for _, list := range lists {
p.Architectures = append(p.Architectures, list.Architectures(true)...)
}
} }
if len(p.Architectures) == 0 { if len(p.Architectures) == 0 {
@@ -296,6 +417,7 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
} }
sort.Strings(p.Architectures) sort.Strings(p.Architectures)
p.Architectures = utils.StrSliceDeduplicate(p.Architectures)
} }
var suffix string var suffix string
@@ -310,17 +432,19 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
progress.Printf("Generating metadata files and linking package files...\n") progress.Printf("Generating metadata files and linking package files...\n")
} }
// For all architectures, generate release file for component, list := range lists {
var relativePath string
// For all architectures, generate packages/sources files
for _, arch := range p.Architectures { for _, arch := range p.Architectures {
if progress != nil { if progress != nil {
progress.InitBar(int64(list.Len()), false) progress.InitBar(int64(list.Len()), false)
} }
var relativePath string
if arch == "source" { if arch == "source" {
relativePath = filepath.Join(p.Component, "source", "Sources") relativePath = filepath.Join(component, "source", "Sources")
} else { } else {
relativePath = filepath.Join(p.Component, fmt.Sprintf("binary-%s", arch), "Packages") relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Packages")
} }
err = publishedStorage.MkDir(filepath.Dir(filepath.Join(basePath, relativePath))) err = publishedStorage.MkDir(filepath.Dir(filepath.Join(basePath, relativePath)))
if err != nil { if err != nil {
@@ -344,7 +468,7 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
progress.AddBar(1) progress.AddBar(1)
} }
if pkg.MatchesArchitecture(arch) { if pkg.MatchesArchitecture(arch) {
err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, p.Component) err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, component)
if err != nil { if err != nil {
return err return err
} }
@@ -412,26 +536,67 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
} }
} }
// For all architectures, generate Release files
for _, arch := range p.Architectures {
release := make(Stanza) release := make(Stanza)
if p.Origin == "" { release["Archive"] = p.Distribution
release["Origin"] = p.Prefix + " " + p.Distribution release["Architecture"] = arch
release["Component"] = component
release["Origin"] = p.GetOrigin()
release["Label"] = p.GetLabel()
if arch == "source" {
relativePath = filepath.Join(component, "source", "Release")
} else { } else {
release["Origin"] = p.Origin relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Release")
} }
if p.Label == "" {
release["Label"] = p.Prefix + " " + p.Distribution var file *os.File
} else { file, err = publishedStorage.CreateFile(filepath.Join(basePath, relativePath+suffix))
release["Label"] = p.Label if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
} }
if suffix != "" {
renameMap[filepath.Join(basePath, relativePath+suffix)] = filepath.Join(basePath, relativePath)
}
bufWriter := bufio.NewWriter(file)
err = release.WriteTo(bufWriter)
if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
}
err = bufWriter.Flush()
if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
}
file.Close()
var checksumInfo utils.ChecksumInfo
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath] = checksumInfo
}
}
release := make(Stanza)
release["Origin"] = p.GetOrigin()
release["Label"] = p.GetLabel()
release["Codename"] = p.Distribution release["Codename"] = p.Distribution
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST") release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
release["Components"] = p.Component
release["Architectures"] = strings.Join(utils.StrSlicesSubstract(p.Architectures, []string{"source"}), " ") release["Architectures"] = strings.Join(utils.StrSlicesSubstract(p.Architectures, []string{"source"}), " ")
release["Description"] = " Generated by aptly\n" release["Description"] = " Generated by aptly\n"
release["MD5Sum"] = "\n" release["MD5Sum"] = "\n"
release["SHA1"] = "\n" release["SHA1"] = "\n"
release["SHA256"] = "\n" release["SHA256"] = "\n"
release["Components"] = strings.Join(p.Components(), " ")
for path, info := range generatedFiles { for path, info := range generatedFiles {
release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path) release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path)
release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path) release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path)
@@ -498,7 +663,8 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
// RemoveFiles removes files that were created by Publish // RemoveFiles removes files that were created by Publish
// //
// It can remove prefix fully, and part of pool (for specific component) // It can remove prefix fully, and part of pool (for specific component)
func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, removePrefix, removePoolComponent bool, progress aptly.Progress) error { func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, removePrefix bool,
removePoolComponents []string, progress aptly.Progress) error {
// I. Easy: remove whole prefix (meta+packages) // I. Easy: remove whole prefix (meta+packages)
if removePrefix { if removePrefix {
err := publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "dists"), progress) err := publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "dists"), progress)
@@ -516,15 +682,13 @@ func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, rem
} }
// III. Complex: there are no other publishes with the same prefix + component // III. Complex: there are no other publishes with the same prefix + component
if removePoolComponent { for _, component := range removePoolComponents {
err = publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "pool", p.Component), progress) err = publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "pool", component), progress)
if err != nil { if err != nil {
return err return err
} }
} else {
/// IV: Hard: should have removed published files from the pool + component
/// that are unique to this published repo
} }
return nil return nil
} }
@@ -589,42 +753,74 @@ func (collection *PublishedRepoCollection) Update(repo *PublishedRepo) (err erro
} }
if repo.SourceKind == "local" { if repo.SourceKind == "local" {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode()) for component, item := range repo.sourceItems {
err = collection.db.Put(repo.RefKey(component), item.packageRefs.Encode())
if err != nil {
return
}
}
} }
return return
} }
// LoadComplete loads additional information for remote repo // LoadComplete loads additional information for remote repo
func (collection *PublishedRepoCollection) LoadComplete(repo *PublishedRepo, collectionFactory *CollectionFactory) (err error) { func (collection *PublishedRepoCollection) LoadComplete(repo *PublishedRepo, collectionFactory *CollectionFactory) (err error) {
repo.sourceItems = make(map[string]repoSourceItem)
if repo.SourceKind == "snapshot" { if repo.SourceKind == "snapshot" {
repo.snapshot, err = collectionFactory.SnapshotCollection().ByUUID(repo.SourceUUID) for component, sourceUUID := range repo.Sources {
item := repoSourceItem{}
item.snapshot, err = collectionFactory.SnapshotCollection().ByUUID(sourceUUID)
if err != nil { if err != nil {
return return
} }
err = collectionFactory.SnapshotCollection().LoadComplete(repo.snapshot) err = collectionFactory.SnapshotCollection().LoadComplete(item.snapshot)
if err != nil {
return
}
repo.sourceItems[component] = item
}
} else if repo.SourceKind == "local" { } else if repo.SourceKind == "local" {
repo.localRepo, err = collectionFactory.LocalRepoCollection().ByUUID(repo.SourceUUID) for component, sourceUUID := range repo.Sources {
item := repoSourceItem{}
item.localRepo, err = collectionFactory.LocalRepoCollection().ByUUID(sourceUUID)
if err != nil { if err != nil {
return return
} }
err = collectionFactory.LocalRepoCollection().LoadComplete(repo.localRepo) err = collectionFactory.LocalRepoCollection().LoadComplete(item.localRepo)
if err != nil { if err != nil {
return return
} }
var encoded []byte var encoded []byte
encoded, err = collection.db.Get(repo.RefKey()) encoded, err = collection.db.Get(repo.RefKey(component))
if err != nil { if err != nil {
return err // < 0.6 saving w/o component name
if err == database.ErrNotFound && len(repo.Sources) == 1 {
encoded, err = collection.db.Get(repo.RefKey(""))
} }
repo.packageRefs = &PackageRefList{} if err != nil {
err = repo.packageRefs.Decode(encoded) return
}
}
item.packageRefs = &PackageRefList{}
err = item.packageRefs.Decode(encoded)
if err != nil {
return
}
repo.sourceItems[component] = item
}
} else { } else {
panic("unknown SourceKind") panic("unknown SourceKind")
} }
return err return
} }
// ByPrefixDistribution looks up repository by prefix & distribution // ByPrefixDistribution looks up repository by prefix & distribution
@@ -651,9 +847,18 @@ func (collection *PublishedRepoCollection) ByUUID(uuid string) (*PublishedRepo,
func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*PublishedRepo { func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*PublishedRepo {
result := make([]*PublishedRepo, 0) result := make([]*PublishedRepo, 0)
for _, r := range collection.list { for _, r := range collection.list {
if r.SourceKind == "snapshot" && r.SourceUUID == snapshot.UUID { if r.SourceKind == "snapshot" {
if r.SourceUUID == snapshot.UUID {
result = append(result, r) result = append(result, r)
} }
for _, sourceUUID := range r.Sources {
if sourceUUID == snapshot.UUID {
result = append(result, r)
break
}
}
}
} }
return result return result
} }
@@ -662,9 +867,18 @@ func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*Pub
func (collection *PublishedRepoCollection) ByLocalRepo(repo *LocalRepo) []*PublishedRepo { func (collection *PublishedRepoCollection) ByLocalRepo(repo *LocalRepo) []*PublishedRepo {
result := make([]*PublishedRepo, 0) result := make([]*PublishedRepo, 0)
for _, r := range collection.list { for _, r := range collection.list {
if r.SourceKind == "local" && r.SourceUUID == repo.UUID { if r.SourceKind == "local" {
if r.SourceUUID == repo.UUID {
result = append(result, r) result = append(result, r)
} }
for _, sourceUUID := range r.Sources {
if sourceUUID == repo.UUID {
result = append(result, r)
break
}
}
}
} }
return result return result
} }
@@ -687,24 +901,41 @@ func (collection *PublishedRepoCollection) Len() int {
} }
// CleanupPrefixComponentFiles removes all unreferenced files in published storage under prefix/component pair // CleanupPrefixComponentFiles removes all unreferenced files in published storage under prefix/component pair
func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix, component string, func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix string, components []string,
publishedStorage aptly.PublishedStorage, collectionFactory *CollectionFactory, progress aptly.Progress) error { publishedStorage aptly.PublishedStorage, collectionFactory *CollectionFactory, progress aptly.Progress) error {
var err error var err error
referencedFiles := []string{} referencedFiles := map[string][]string{}
if progress != nil { if progress != nil {
progress.Printf("Cleaning up prefix %#v component %#v...\n", prefix, component) progress.Printf("Cleaning up prefix %#v components %s...\n", prefix, strings.Join(components, ", "))
} }
for _, r := range collection.list { for _, r := range collection.list {
if r.Prefix == prefix && r.Component == component { if r.Prefix == prefix {
matches := false
repoComponents := r.Components()
for _, component := range components {
if utils.StrSliceHasItem(repoComponents, component) {
matches = true
break
}
}
if !matches {
continue
}
err = collection.LoadComplete(r, collectionFactory) err = collection.LoadComplete(r, collectionFactory)
if err != nil { if err != nil {
return err return err
} }
packageList, err := NewPackageListFromRefList(r.RefList(), collectionFactory.PackageCollection(), progress) for _, component := range components {
if utils.StrSliceHasItem(repoComponents, component) {
packageList, err := NewPackageListFromRefList(r.RefList(component), collectionFactory.PackageCollection(), progress)
if err != nil { if err != nil {
return err return err
} }
@@ -716,15 +947,18 @@ func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix, c
} }
for _, f := range p.Files() { for _, f := range p.Files() {
referencedFiles = append(referencedFiles, filepath.Join(poolDir, f.Filename)) referencedFiles[component] = append(referencedFiles[component], filepath.Join(poolDir, f.Filename))
} }
return nil return nil
}) })
} }
} }
}
}
sort.Strings(referencedFiles) for _, component := range components {
sort.Strings(referencedFiles[component])
rootPath := filepath.Join(prefix, "pool", component) rootPath := filepath.Join(prefix, "pool", component)
existingFiles, err := publishedStorage.Filelist(rootPath) existingFiles, err := publishedStorage.Filelist(rootPath)
@@ -734,7 +968,7 @@ func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix, c
sort.Strings(existingFiles) sort.Strings(existingFiles)
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles) filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles[component])
for _, file := range filesToDelete { for _, file := range filesToDelete {
err = publishedStorage.Remove(filepath.Join(rootPath, file)) err = publishedStorage.Remove(filepath.Join(rootPath, file))
@@ -742,6 +976,7 @@ func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix, c
return err return err
} }
} }
}
return nil return nil
} }
@@ -755,7 +990,8 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
} }
removePrefix := true removePrefix := true
removePoolComponent := true removePoolComponents := repo.Components()
cleanComponents := []string{}
repoPosition := -1 repoPosition := -1
for i, r := range collection.list { for i, r := range collection.list {
@@ -765,13 +1001,18 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
} }
if r.Prefix == repo.Prefix { if r.Prefix == repo.Prefix {
removePrefix = false removePrefix = false
if r.Component == repo.Component {
removePoolComponent = false rComponents := r.Components()
for _, component := range rComponents {
if utils.StrSliceHasItem(removePoolComponents, component) {
removePoolComponents = utils.StrSlicesSubstract(removePoolComponents, []string{component})
cleanComponents = append(cleanComponents, component)
}
} }
} }
} }
err = repo.RemoveFiles(publishedStorage, removePrefix, removePoolComponent, progress) err = repo.RemoveFiles(publishedStorage, removePrefix, removePoolComponents, progress)
if err != nil { if err != nil {
return err return err
} }
@@ -779,8 +1020,8 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list = collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1] nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
if !removePrefix && !removePoolComponent { if len(cleanComponents) > 0 {
err = collection.CleanupPrefixComponentFiles(repo.Prefix, repo.Component, publishedStorage, collectionFactory, progress) err = collection.CleanupPrefixComponentFiles(repo.Prefix, cleanComponents, publishedStorage, collectionFactory, progress)
if err != nil { if err != nil {
return err return err
} }
@@ -790,5 +1031,13 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
if err != nil { if err != nil {
return err return err
} }
return collection.db.Delete(repo.RefKey())
for _, component := range repo.Components() {
err = collection.db.Delete(repo.RefKey(component))
if err != nil {
return err
}
}
return nil
} }
+171 -55
View File
@@ -1,10 +1,12 @@
package deb package deb
import ( import (
"bytes"
"errors" "errors"
"github.com/smira/aptly/aptly" "github.com/smira/aptly/aptly"
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
"github.com/smira/aptly/files" "github.com/smira/aptly/files"
"github.com/ugorji/go/codec"
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"os" "os"
"path/filepath" "path/filepath"
@@ -45,12 +47,12 @@ func (n *NullSigner) ClearSign(source string, destination string) error {
type PublishedRepoSuite struct { type PublishedRepoSuite struct {
PackageListMixinSuite PackageListMixinSuite
repo, repo2 *PublishedRepo repo, repo2, repo3, repo4 *PublishedRepo
root string root string
publishedStorage aptly.PublishedStorage publishedStorage aptly.PublishedStorage
packagePool aptly.PackagePool packagePool aptly.PackagePool
localRepo *LocalRepo localRepo *LocalRepo
snapshot *Snapshot snapshot, snapshot2 *Snapshot
db database.Storage db database.Storage
factory *CollectionFactory factory *CollectionFactory
packageCollection *PackageCollection packageCollection *PackageCollection
@@ -79,14 +81,21 @@ func (s *PublishedRepoSuite) SetUpTest(c *C) {
s.snapshot, _ = NewSnapshotFromRepository("snap", repo) s.snapshot, _ = NewSnapshotFromRepository("snap", repo)
s.factory.SnapshotCollection().Add(s.snapshot) s.factory.SnapshotCollection().Add(s.snapshot)
s.snapshot2, _ = NewSnapshotFromRepository("snap", repo)
s.factory.SnapshotCollection().Add(s.snapshot2)
s.packageCollection = s.factory.PackageCollection() s.packageCollection = s.factory.PackageCollection()
s.packageCollection.Update(s.p1) s.packageCollection.Update(s.p1)
s.packageCollection.Update(s.p2) s.packageCollection.Update(s.p2)
s.packageCollection.Update(s.p3) s.packageCollection.Update(s.p3)
s.repo, _ = NewPublishedRepo("ppa", "squeeze", "main", nil, s.snapshot, s.factory) s.repo, _ = NewPublishedRepo("ppa", "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
s.repo2, _ = NewPublishedRepo("ppa", "maverick", "main", nil, s.localRepo, s.factory) s.repo2, _ = NewPublishedRepo("ppa", "maverick", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.repo3, _ = NewPublishedRepo("linux", "natty", nil, []string{"main", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
s.repo4, _ = NewPublishedRepo("ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
poolPath, _ := s.packagePool.Path(s.p1.Files()[0].Filename, s.p1.Files()[0].Checksums.MD5) poolPath, _ := s.packagePool.Path(s.p1.Files()[0].Filename, s.p1.Files()[0].Checksums.MD5)
err := os.MkdirAll(filepath.Dir(poolPath), 0755) err := os.MkdirAll(filepath.Dir(poolPath), 0755)
@@ -100,17 +109,39 @@ func (s *PublishedRepoSuite) TearDownTest(c *C) {
} }
func (s *PublishedRepoSuite) TestNewPublishedRepo(c *C) { func (s *PublishedRepoSuite) TestNewPublishedRepo(c *C) {
c.Check(s.repo.snapshot, Equals, s.snapshot) c.Check(s.repo.sourceItems["main"].snapshot, Equals, s.snapshot)
c.Check(s.repo.SourceKind, Equals, "snapshot") c.Check(s.repo.SourceKind, Equals, "snapshot")
c.Check(s.repo.SourceUUID, Equals, s.snapshot.UUID) c.Check(s.repo.Sources["main"], Equals, s.snapshot.UUID)
c.Check(s.repo.Components(), DeepEquals, []string{"main"})
c.Check(s.repo2.localRepo, Equals, s.localRepo) c.Check(s.repo2.sourceItems["main"].localRepo, Equals, s.localRepo)
c.Check(s.repo2.SourceKind, Equals, "local") c.Check(s.repo2.SourceKind, Equals, "local")
c.Check(s.repo2.SourceUUID, Equals, s.localRepo.UUID) c.Check(s.repo2.Sources["main"], Equals, s.localRepo.UUID)
c.Check(s.repo2.packageRefs.Len(), Equals, 3) c.Check(s.repo2.sourceItems["main"].packageRefs.Len(), Equals, 3)
c.Check(s.repo2.Components(), DeepEquals, []string{"main"})
c.Check(s.repo.RefList().Len(), Equals, 3) c.Check(s.repo.RefList("main").Len(), Equals, 3)
c.Check(s.repo2.RefList().Len(), Equals, 3) c.Check(s.repo2.RefList("main").Len(), Equals, 3)
c.Check(s.repo3.Sources, DeepEquals, map[string]string{"main": s.snapshot.UUID, "contrib": s.snapshot2.UUID})
c.Check(s.repo3.SourceKind, Equals, "snapshot")
c.Check(s.repo3.sourceItems["main"].snapshot, Equals, s.snapshot)
c.Check(s.repo3.sourceItems["contrib"].snapshot, Equals, s.snapshot2)
c.Check(s.repo3.Components(), DeepEquals, []string{"contrib", "main"})
c.Check(s.repo3.RefList("main").Len(), Equals, 3)
c.Check(s.repo3.RefList("contrib").Len(), Equals, 3)
c.Check(func() { NewPublishedRepo(".", "a", nil, nil, nil, s.factory) }, PanicMatches, "publish with empty sources")
c.Check(func() {
NewPublishedRepo(".", "a", nil, []string{"main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
}, PanicMatches, "sources and components should be equal in size")
c.Check(func() {
NewPublishedRepo(".", "a", nil, []string{"main", "contrib"}, []interface{}{s.localRepo, s.snapshot2}, s.factory)
}, PanicMatches, "interface conversion:.*")
_, err := NewPublishedRepo(".", "a", nil, []string{"main", "main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, ErrorMatches, "duplicate component name: main")
} }
func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) { func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
@@ -169,7 +200,7 @@ func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
errorExpected: "invalid prefix .*", errorExpected: "invalid prefix .*",
}, },
} { } {
repo, err := NewPublishedRepo(t.prefix, "squeeze", "main", nil, s.snapshot, s.factory) repo, err := NewPublishedRepo(t.prefix, "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
if t.errorExpected != "" { if t.errorExpected != "" {
c.Check(err, ErrorMatches, t.errorExpected) c.Check(err, ErrorMatches, t.errorExpected)
} else { } else {
@@ -179,37 +210,45 @@ func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
} }
func (s *PublishedRepoSuite) TestDistributionComponentGuessing(c *C) { func (s *PublishedRepoSuite) TestDistributionComponentGuessing(c *C) {
repo, err := NewPublishedRepo("ppa", "", "", nil, s.snapshot, s.factory) repo, err := NewPublishedRepo("ppa", "", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil) c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze") c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Component, Equals, "main") c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("ppa", "wheezy", "", nil, s.snapshot, s.factory) repo, err = NewPublishedRepo("ppa", "wheezy", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil) c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "wheezy") c.Check(repo.Distribution, Equals, "wheezy")
c.Check(repo.Component, Equals, "main") c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("ppa", "", "non-free", nil, s.snapshot, s.factory) repo, err = NewPublishedRepo("ppa", "", nil, []string{"non-free"}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil) c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze") c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Component, Equals, "non-free") c.Check(repo.Components(), DeepEquals, []string{"non-free"})
repo, err = NewPublishedRepo("ppa", "squeeze", "", nil, s.localRepo, s.factory) repo, err = NewPublishedRepo("ppa", "squeeze", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
c.Check(err, IsNil) c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze") c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Component, Equals, "main") c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("ppa", "", "main", nil, s.localRepo, s.factory) repo, err = NewPublishedRepo("ppa", "", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
c.Check(err, ErrorMatches, "unable to guess distribution name, please specify explicitly") c.Check(err, ErrorMatches, "unable to guess distribution name, please specify explicitly")
s.localRepo.DefaultDistribution = "precise" s.localRepo.DefaultDistribution = "precise"
s.localRepo.DefaultComponent = "contrib" s.localRepo.DefaultComponent = "contrib"
s.factory.LocalRepoCollection().Update(s.localRepo) s.factory.LocalRepoCollection().Update(s.localRepo)
repo, err = NewPublishedRepo("ppa", "", "", nil, s.localRepo, s.factory) repo, err = NewPublishedRepo("ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
c.Check(err, IsNil) c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "precise") c.Check(repo.Distribution, Equals, "precise")
c.Check(repo.Component, Equals, "contrib") c.Check(repo.Components(), DeepEquals, []string{"contrib"})
repo, err = NewPublishedRepo("ppa", "", nil, []string{"", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Components(), DeepEquals, []string{"contrib", "main"})
repo, err = NewPublishedRepo("ppa", "", nil, []string{"", ""}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, ErrorMatches, "duplicate component name: main")
} }
func (s *PublishedRepoSuite) TestPublish(c *C) { func (s *PublishedRepoSuite) TestPublish(c *C) {
@@ -245,6 +284,16 @@ func (s *PublishedRepoSuite) TestPublish(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(st, IsNil) c.Assert(st, IsNil)
drf, err := os.Open(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/main/binary-i386/Release"))
c.Assert(err, IsNil)
cfr = NewControlFileReader(drf)
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Archive"], Equals, "squeeze")
c.Check(st["Architecture"], Equals, "i386")
_, err = os.Stat(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb")) _, err = os.Stat(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb"))
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
@@ -254,6 +303,7 @@ func (s *PublishedRepoSuite) TestPublishNoSigner(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/main/binary-i386/Release"), PathExists)
} }
func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) { func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
@@ -261,25 +311,36 @@ func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/binary-i386/Release"), PathExists)
}
func (s *PublishedRepoSuite) TestPublishLocalSourceRepo(c *C) {
err := s.repo4.Publish(s.packagePool, s.publishedStorage, s.factory, nil, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/source/Release"), PathExists)
} }
func (s *PublishedRepoSuite) TestString(c *C) { func (s *PublishedRepoSuite) TestString(c *C) {
c.Check(s.repo.String(), Equals, c.Check(s.repo.String(), Equals,
"ppa/squeeze (main) [] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze") "ppa/squeeze [] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
c.Check(s.repo2.String(), Equals, c.Check(s.repo2.String(), Equals,
"ppa/maverick (main) [] publishes [local1]: comment1") "ppa/maverick [] publishes {main: [local1]: comment1}")
repo, _ := NewPublishedRepo("", "squeeze", "main", []string{"s390"}, s.snapshot, s.factory) repo, _ := NewPublishedRepo("", "squeeze", []string{"s390"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
c.Check(repo.String(), Equals, c.Check(repo.String(), Equals,
"./squeeze (main) [s390] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze") "./squeeze [s390] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo, _ = NewPublishedRepo("", "squeeze", "main", []string{"i386", "amd64"}, s.snapshot, s.factory) repo, _ = NewPublishedRepo("", "squeeze", []string{"i386", "amd64"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
c.Check(repo.String(), Equals, c.Check(repo.String(), Equals,
"./squeeze (main) [i386, amd64] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze") "./squeeze [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo.Origin = "myorigin" repo.Origin = "myorigin"
c.Check(repo.String(), Equals, c.Check(repo.String(), Equals,
"./squeeze (main, origin: myorigin) [i386, amd64] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze") "./squeeze (origin: myorigin) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo.Label = "mylabel" repo.Label = "mylabel"
c.Check(repo.String(), Equals, c.Check(repo.String(), Equals,
"./squeeze (main, origin: myorigin, label: mylabel) [i386, amd64] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze") "./squeeze (origin: myorigin, label: mylabel) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
c.Check(s.repo3.String(), Equals,
"linux/natty [] publishes {contrib: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}, {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
} }
func (s *PublishedRepoSuite) TestKey(c *C) { func (s *PublishedRepoSuite) TestKey(c *C) {
@@ -287,7 +348,8 @@ func (s *PublishedRepoSuite) TestKey(c *C) {
} }
func (s *PublishedRepoSuite) TestRefKey(c *C) { func (s *PublishedRepoSuite) TestRefKey(c *C) {
c.Check(s.repo.RefKey(), DeepEquals, []byte("E"+s.repo.UUID)) c.Check(s.repo.RefKey(""), DeepEquals, []byte("E"+s.repo.UUID))
c.Check(s.repo.RefKey("main"), DeepEquals, []byte("E"+s.repo.UUID+"main"))
} }
func (s *PublishedRepoSuite) TestEncodeDecode(c *C) { func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
@@ -295,7 +357,7 @@ func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
repo := &PublishedRepo{} repo := &PublishedRepo{}
err := repo.Decode(encoded) err := repo.Decode(encoded)
s.repo.snapshot = nil s.repo.sourceItems = nil
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(repo, DeepEquals, s.repo) c.Assert(repo, DeepEquals, s.repo)
@@ -303,8 +365,7 @@ func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
repo2 := &PublishedRepo{} repo2 := &PublishedRepo{}
err = repo2.Decode(encoded2) err = repo2.Decode(encoded2)
s.repo2.localRepo = nil s.repo2.sourceItems = nil
s.repo2.packageRefs = nil
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(repo2, DeepEquals, s.repo2) c.Assert(repo2, DeepEquals, s.repo2)
} }
@@ -337,10 +398,10 @@ func (s *PublishedRepoCollectionSuite) SetUpTest(c *C) {
s.localRepo = NewLocalRepo("local1", "comment1") s.localRepo = NewLocalRepo("local1", "comment1")
s.factory.LocalRepoCollection().Add(s.localRepo) s.factory.LocalRepoCollection().Add(s.localRepo)
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap1, s.factory) s.repo1, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo2, _ = NewPublishedRepo("", "anaconda", "main", []string{}, s.snap2, s.factory) s.repo2, _ = NewPublishedRepo("", "anaconda", []string{}, []string{"main", "contrib"}, []interface{}{s.snap2, s.snap1}, s.factory)
s.repo3, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap2, s.factory) s.repo3, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap2}, s.factory)
s.repo4, _ = NewPublishedRepo("ppa", "precise", "main", []string{}, s.localRepo, s.factory) s.repo4, _ = NewPublishedRepo("ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.collection = s.factory.PublishedRepoCollection() s.collection = s.factory.PublishedRepoCollection()
} }
@@ -398,18 +459,61 @@ func (s *PublishedRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
collection := NewPublishedRepoCollection(s.db) collection := NewPublishedRepoCollection(s.db)
r, err := collection.ByPrefixDistribution("ppa", "anaconda") r, err := collection.ByPrefixDistribution("ppa", "anaconda")
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(r.snapshot, IsNil) c.Assert(r.sourceItems["main"].snapshot, IsNil)
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil) c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
c.Assert(r.snapshot.UUID, Equals, s.repo1.snapshot.UUID) c.Assert(r.Sources["main"], Equals, s.repo1.sourceItems["main"].snapshot.UUID)
c.Assert(r.RefList().Len(), Equals, 0) c.Assert(r.RefList("main").Len(), Equals, 0)
r, err = collection.ByPrefixDistribution("ppa", "precise") r, err = collection.ByPrefixDistribution("ppa", "precise")
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(r.localRepo, IsNil) c.Assert(r.sourceItems["main"].localRepo, IsNil)
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil) c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
c.Assert(r.localRepo.UUID, Equals, s.repo4.localRepo.UUID) c.Assert(r.sourceItems["main"].localRepo.UUID, Equals, s.repo4.sourceItems["main"].localRepo.UUID)
c.Assert(r.packageRefs.Len(), Equals, 0) c.Assert(r.sourceItems["main"].packageRefs.Len(), Equals, 0)
c.Assert(r.RefList().Len(), Equals, 0) c.Assert(r.RefList("main").Len(), Equals, 0)
}
func (s *PublishedRepoCollectionSuite) TestLoadPre0_6(c *C) {
type oldPublishedRepo struct {
UUID string
Prefix string
Distribution string
Origin string
Label string
Architectures []string
SourceKind string
Component string
SourceUUID string `codec:"SnapshotUUID"`
}
old := oldPublishedRepo{
UUID: s.repo1.UUID,
Prefix: "ppa",
Distribution: "anaconda",
Architectures: []string{"i386"},
SourceKind: "local",
Component: "contrib",
SourceUUID: s.localRepo.UUID,
}
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(&old)
c.Assert(s.db.Put(s.repo1.Key(), buf.Bytes()), IsNil)
c.Assert(s.db.Put(s.repo1.RefKey(""), s.localRepo.RefList().Encode()), IsNil)
collection := NewPublishedRepoCollection(s.db)
repo, err := collection.ByPrefixDistribution("ppa", "anaconda")
c.Check(err, IsNil)
c.Check(repo.Component, Equals, "")
c.Check(repo.SourceUUID, Equals, "")
c.Check(repo.Sources, DeepEquals, map[string]string{"contrib": s.localRepo.UUID})
c.Check(collection.LoadComplete(repo, s.factory), IsNil)
c.Check(repo.sourceItems["contrib"].localRepo.UUID, Equals, s.localRepo.UUID)
c.Check(repo.RefList("contrib").Len(), Equals, 0)
} }
func (s *PublishedRepoCollectionSuite) TestForEachAndLen(c *C) { func (s *PublishedRepoCollectionSuite) TestForEachAndLen(c *C) {
@@ -437,7 +541,7 @@ func (s *PublishedRepoCollectionSuite) TestBySnapshot(c *C) {
c.Check(s.collection.Add(s.repo1), IsNil) c.Check(s.collection.Add(s.repo1), IsNil)
c.Check(s.collection.Add(s.repo2), IsNil) c.Check(s.collection.Add(s.repo2), IsNil)
c.Check(s.collection.BySnapshot(s.snap1), DeepEquals, []*PublishedRepo{s.repo1}) c.Check(s.collection.BySnapshot(s.snap1), DeepEquals, []*PublishedRepo{s.repo1, s.repo2})
c.Check(s.collection.BySnapshot(s.snap2), DeepEquals, []*PublishedRepo{s.repo2}) c.Check(s.collection.BySnapshot(s.snap2), DeepEquals, []*PublishedRepo{s.repo2})
} }
@@ -472,10 +576,10 @@ func (s *PublishedRepoRemoveSuite) SetUpTest(c *C) {
s.snapshotCollection.Add(s.snap1) s.snapshotCollection.Add(s.snap1)
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap1, s.factory) s.repo1, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo2, _ = NewPublishedRepo("", "anaconda", "main", []string{}, s.snap1, s.factory) s.repo2, _ = NewPublishedRepo("", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo3, _ = NewPublishedRepo("ppa", "meduza", "main", []string{}, s.snap1, s.factory) s.repo3, _ = NewPublishedRepo("ppa", "meduza", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo4, _ = NewPublishedRepo("ppa", "osminog", "contrib", []string{}, s.snap1, s.factory) s.repo4, _ = NewPublishedRepo("ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
s.collection = s.factory.PublishedRepoCollection() s.collection = s.factory.PublishedRepoCollection()
s.collection.Add(s.repo1) s.collection.Add(s.repo1)
@@ -499,7 +603,7 @@ func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
} }
func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) { func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
s.repo1.RemoveFiles(s.publishedStorage, false, false, nil) s.repo1.RemoveFiles(s.publishedStorage, false, []string{}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists)) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
@@ -511,7 +615,7 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
} }
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) { func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
s.repo1.RemoveFiles(s.publishedStorage, false, true, nil) s.repo1.RemoveFiles(s.publishedStorage, false, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists)) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
@@ -522,8 +626,20 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
} }
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithTwoPools(c *C) {
s.repo1.RemoveFiles(s.publishedStorage, false, []string{"main", "contrib"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) { func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
s.repo1.RemoveFiles(s.publishedStorage, true, true, nil) s.repo1.RemoveFiles(s.publishedStorage, true, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists)) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists)) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
@@ -535,7 +651,7 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
} }
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) { func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
s.repo2.RemoveFiles(s.publishedStorage, true, true, nil) s.repo2.RemoveFiles(s.publishedStorage, true, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists) c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
+12 -9
View File
@@ -194,26 +194,29 @@ func (l *PackageRefList) Diff(r *PackageRefList, packageCollection *PackageColle
} }
} }
// is pl & pr the same package, but different version?
if pl.Name == pr.Name && pl.Architecture == pr.Architecture {
result = append(result, PackageDiff{Left: pl, Right: pr})
il++
ir++
pl, pr = nil, nil
} else {
// otherwise pl or pr is missing on one of the sides // otherwise pl or pr is missing on one of the sides
if rel < 0 { if rel < 0 {
// compaction: +(,A) -(B,) --> !(A,B)
if len(result) > 0 && result[len(result)-1].Left == nil && result[len(result)-1].Right.Name == pl.Name &&
result[len(result)-1].Right.Architecture == pl.Architecture {
result[len(result)-1] = PackageDiff{Left: pl, Right: result[len(result)-1].Right}
} else {
result = append(result, PackageDiff{Left: pl, Right: nil}) result = append(result, PackageDiff{Left: pl, Right: nil})
}
il++ il++
pl = nil pl = nil
} else {
// compaction: -(A,) +(,B) --> !(A,B)
if len(result) > 0 && result[len(result)-1].Right == nil && result[len(result)-1].Left.Name == pr.Name &&
result[len(result)-1].Left.Architecture == pr.Architecture {
result[len(result)-1] = PackageDiff{Left: result[len(result)-1].Left, Right: pr}
} else { } else {
result = append(result, PackageDiff{Left: nil, Right: pr}) result = append(result, PackageDiff{Left: nil, Right: pr})
}
ir++ ir++
pr = nil pr = nil
} }
} }
}
} }
return return
+1 -1
View File
@@ -31,7 +31,7 @@ func (s *PackageRefListSuite) SetUpTest(c *C) {
stanza["Package"] = "mars-invaders" stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza) s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy() stanza = packageStanza.Copy()
stanza["Size"] = "42" stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza) s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy() stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers" stanza["Package"] = "lonely-strangers"
+66 -4
View File
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3 .\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3
. .
.TH "APTLY" "1" "May 2014" "" "" .TH "APTLY" "1" "June 2014" "" ""
. .
.SH "NAME" .SH "NAME"
\fBaptly\fR \- Debian repository management tool \fBaptly\fR \- Debian repository management tool
@@ -662,6 +662,10 @@ Options:
\-\fBlatest\fR=false \-\fBlatest\fR=false
use only the latest version of each package use only the latest version of each package
. .
.TP
\-\fBno\-remove\fR=false
don\(cqt remove duplicate arch/name packages
.
.SH "DELETE SNAPSHOT" .SH "DELETE SNAPSHOT"
\fBaptly\fR \fBsnapshot\fR \fBdrop\fR \fIname\fR \fBaptly\fR \fBsnapshot\fR \fBdrop\fR \fIname\fR
. .
@@ -740,6 +744,19 @@ display list in machine\-readable format
Command publishes current state of local repository ready to be consumed by apt tools\. Published repostiories appear under rootDir/public directory\. Valid GPG key is required for publishing\. Command publishes current state of local repository ready to be consumed by apt tools\. Published repostiories appear under rootDir/public directory\. Valid GPG key is required for publishing\.
. .
.P .P
Multiple component repository could be published by specifying several components split by commas via \-component flag and multiple local repositories as the arguments:
.
.IP "" 4
.
.nf
aptly publish repo \-component=main,contrib repo\-main repo\-contrib
.
.fi
.
.IP "" 0
.
.P
It is not recommended to publish local repositories directly unless the repository is for testing purposes and changes happen frequently\. For production usage please take snapshot of repository and publish it using publish snapshot command\. It is not recommended to publish local repositories directly unless the repository is for testing purposes and changes happen frequently\. For production usage please take snapshot of repository and publish it using publish snapshot command\.
. .
.P .P
@@ -760,7 +777,7 @@ Options:
. .
.TP .TP
\-\fBcomponent\fR= \-\fBcomponent\fR=
component name to publish component name to publish (for multi\-component publishing, separate components with commas)
. .
.TP .TP
\-\fBdistribution\fR= \-\fBdistribution\fR=
@@ -797,6 +814,19 @@ don\(cqt sign Release files with GPG
Command publishes snapshot as Debian repository ready to be consumed by apt tools\. Published repostiories appear under rootDir/public directory\. Valid GPG key is required for publishing\. Command publishes snapshot as Debian repository ready to be consumed by apt tools\. Published repostiories appear under rootDir/public directory\. Valid GPG key is required for publishing\.
. .
.P .P
Multiple component repository could be published by specifying several components split by commas via \-component flag and multiple snapshots as the arguments:
.
.IP "" 4
.
.nf
aptly publish snapshot \-component=main,contrib snap\-main snap\-contrib
.
.fi
.
.IP "" 0
.
.P
Example: Example:
. .
.IP "" 4 .IP "" 4
@@ -814,7 +844,7 @@ Options:
. .
.TP .TP
\-\fBcomponent\fR= \-\fBcomponent\fR=
component name to publish component name to publish (for multi\-component publishing, separate components with commas)
. .
.TP .TP
\-\fBdistribution\fR= \-\fBdistribution\fR=
@@ -851,6 +881,19 @@ don\(cqt sign Release files with GPG
Command switches in\-place published repository with new snapshot contents\. All publishing parameters are preserved (architecture list, distribution, component)\. Command switches in\-place published repository with new snapshot contents\. All publishing parameters are preserved (architecture list, distribution, component)\.
. .
.P .P
For multiple component repositories, flag \-component should be given with list of components to update\. Corresponding snapshots should be given in the same order, e\.g\.:
.
.IP "" 4
.
.nf
aptly publish update \-component=main,contrib wheezy wh\-main wh\-contrib
.
.fi
.
.IP "" 0
.
.P
Example: Example:
. .
.IP "" 4 .IP "" 4
@@ -867,6 +910,10 @@ $ aptly publish update wheezy ppa wheezy\-7\.5
Options: Options:
. .
.TP .TP
\-\fBcomponent\fR=
component names to update (for multi\-component publishing, separate components with commas)
.
.TP
\-\fBgpg\-key\fR= \-\fBgpg\-key\fR=
GPG key ID to use when signing the release GPG key ID to use when signing the release
. .
@@ -889,6 +936,9 @@ don\(cqt sign Release files with GPG
Command re\-publishes (updates) published local repository\. \fIdistribution\fR and \fIprefix\fR should be occupied with local repository published using command aptly publish repo\. Update happens in\-place with minimum possible downtime for published repository\. Command re\-publishes (updates) published local repository\. \fIdistribution\fR and \fIprefix\fR should be occupied with local repository published using command aptly publish repo\. Update happens in\-place with minimum possible downtime for published repository\.
. .
.P .P
For multiple component published repositories, all local repositories are updated\.
.
.P
Example: Example:
. .
.IP "" 4 .IP "" 4
@@ -979,7 +1029,19 @@ $ aptly graph
If environment variable \fBHTTP_PROXY\fR is set \fBaptly\fR would use its value to proxy all HTTP requests\. If environment variable \fBHTTP_PROXY\fR is set \fBaptly\fR would use its value to proxy all HTTP requests\.
. .
.SH "RETURN VALUES" .SH "RETURN VALUES"
\fBaptly\fR exists with 0 on success and with 1 on failure\. \fBaptly\fR exists with:
.
.TP
0
success
.
.TP
1
general failure
.
.TP
2
command parse failure
. .
.SH "AUTHORS" .SH "AUTHORS"
Andrey Smirnov (me@smira\.ru) Andrey Smirnov (me@smira\.ru)
+10 -1
View File
@@ -139,7 +139,16 @@ to proxy all HTTP requests.
## RETURN VALUES ## RETURN VALUES
`aptly` exists with 0 on success and with 1 on failure. `aptly` exists with:
* 0:
success
* 1:
general failure
* 2:
command parse failure
## AUTHORS ## AUTHORS
+1 -1
View File
@@ -1 +1 @@
aptly version: 0.5.1 aptly version: 0.6
+1
View File
@@ -21,3 +21,4 @@ Options:
-dep-follow-recommends=false: when processing dependencies, follow Recommends -dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-source=false: when processing dependencies, follow from binary to Source packages -dep-follow-source=false: when processing dependencies, follow from binary to Source packages
-dep-follow-suggests=false: when processing dependencies, follow Suggests -dep-follow-suggests=false: when processing dependencies, follow Suggests
ERROR: unable to parse command
+1
View File
@@ -13,3 +13,4 @@ Options:
-ignore-signatures=false: disable verification of Release file signatures -ignore-signatures=false: disable verification of Release file signatures
-keyring=: gpg keyring to use when verifying Release file (could be specified multiple times) -keyring=: gpg keyring to use when verifying Release file (could be specified multiple times)
-with-sources=false: download source packages in addition to binary packages -with-sources=false: download source packages in addition to binary packages
ERROR: unable to parse command
+1
View File
@@ -18,3 +18,4 @@ Options:
-dep-follow-recommends=false: when processing dependencies, follow Recommends -dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-source=false: when processing dependencies, follow from binary to Source packages -dep-follow-source=false: when processing dependencies, follow from binary to Source packages
-dep-follow-suggests=false: when processing dependencies, follow Suggests -dep-follow-suggests=false: when processing dependencies, follow Suggests
ERROR: unable to parse command
+17
View File
@@ -0,0 +1,17 @@
flag provided but not defined: -fxz
Usage: aptly mirror create <name> <archive url> <distribution> [<component1> ...]
aptly mirror create - create new mirror
Options:
-architectures="": list of architectures to consider during (comma-separated), default to all available
-config="": location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)
-dep-follow-all-variants=false: when processing dependencies, follow a & b if depdency is 'a|b'
-dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-source=false: when processing dependencies, follow from binary to Source packages
-dep-follow-suggests=false: when processing dependencies, follow Suggests
-ignore-signatures=false: disable verification of Release file signatures
-keyring=: gpg keyring to use when verifying Release file (could be specified multiple times)
-with-sources=false: download source packages in addition to binary packages
ERROR: unable to parse flags
+11
View File
@@ -10,6 +10,7 @@ class MainTest(BaseTest):
""" """
main main
""" """
expectedCode = 2
runCmd = "aptly" runCmd = "aptly"
outputMatchPrepare = lambda _, s: re.sub(r' -(cpuprofile|memprofile|memstats|meminterval)=.*\n', '', s, flags=re.MULTILINE) outputMatchPrepare = lambda _, s: re.sub(r' -(cpuprofile|memprofile|memstats|meminterval)=.*\n', '', s, flags=re.MULTILINE)
@@ -19,6 +20,7 @@ class MirrorTest(BaseTest):
""" """
main main
""" """
expectedCode = 2
runCmd = "aptly mirror" runCmd = "aptly mirror"
@@ -26,6 +28,7 @@ class MirrorCreateTest(BaseTest):
""" """
main main
""" """
expectedCode = 2
runCmd = "aptly mirror create" runCmd = "aptly mirror create"
@@ -50,3 +53,11 @@ class MirrorCreateHelpTest(BaseTest):
main main
""" """
runCmd = "aptly help mirror create" runCmd = "aptly help mirror create"
class WrongFlagTest(BaseTest):
"""
main
"""
expectedCode = 2
runCmd = "aptly mirror create -fxz=sss"
@@ -8,6 +8,6 @@ Last update: never
Information from release file: Information from release file:
Architectures: all Architectures: all
Date: Thu, 01 May 2014 05:51:11 UTC Date: Fri, 30 May 2014 07:36:11 UTC
Origin: jenkins-ci.org Origin: jenkins-ci.org
Suite: binary Suite: binary
+3 -1
View File
@@ -1,13 +1,15 @@
Building download queue... Building download queue...
Download queue: 28 items (11.04 MiB) Download queue: 30 items (11.70 MiB)
Downloading & parsing package files... Downloading & parsing package files...
Downloading http://repo.varnish-cache.org/debian/dists/wheezy/Release... Downloading http://repo.varnish-cache.org/debian/dists/wheezy/Release...
Downloading http://repo.varnish-cache.org/debian/dists/wheezy/varnish-3.0/binary-amd64/Packages.bz2... Downloading http://repo.varnish-cache.org/debian/dists/wheezy/varnish-3.0/binary-amd64/Packages.bz2...
Downloading http://repo.varnish-cache.org/debian/dists/wheezy/varnish-3.0/binary-i386/Packages.bz2... Downloading http://repo.varnish-cache.org/debian/dists/wheezy/varnish-3.0/binary-i386/Packages.bz2...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.0~wheezy_amd64.deb... Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.0~wheezy_amd64.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.0~wheezy_i386.deb... Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.0~wheezy_i386.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.1~wheezy_amd64.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish-agent/varnish-agent_2.2.1~wheezy_i386.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.3-1~wheezy_amd64.deb... Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.3-1~wheezy_amd64.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.3-1~wheezy_i386.deb... Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.3-1~wheezy_i386.deb...
Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.4-1~wheezy_amd64.deb... Downloading http://repo.varnish-cache.org/debian/pool/varnish-3.0/v/varnish/libvarnishapi-dev_3.0.4-1~wheezy_amd64.deb...
+1 -1
View File
@@ -1,3 +1,3 @@
Snapshot `snap1` is published currently: Snapshot `snap1` is published currently:
* ./maverick (main) [amd64, i386] publishes [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick * ./maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
ERROR: unable to drop: snapshot is published ERROR: unable to drop: snapshot is published
+1 -1
View File
@@ -1,3 +1,3 @@
Snapshot `snap1` is published currently: Snapshot `snap1` is published currently:
* ./maverick (main) [amd64, i386] publishes [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick * ./maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
ERROR: unable to drop: snapshot is published ERROR: unable to drop: snapshot is published
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
ERROR: -no-remove and -latest can't be specified together
@@ -0,0 +1,3 @@
Snapshot snap4 successfully created.
You can run 'aptly publish snapshot snap4' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
+48
View File
@@ -136,3 +136,51 @@ class MergeSnapshot8Test(BaseTest):
] ]
runCmd = "aptly snapshot diff snap4 snap5" runCmd = "aptly snapshot diff snap4 snap5"
expectedCode = 0 expectedCode = 0
class MergeSnapshot9Test(BaseTest):
"""
merge snapshots: -no-remove
"""
fixtureDB = True
fixtureCmds = [
"aptly snapshot create snap1 from mirror wheezy-main",
"aptly snapshot create snap2 from mirror wheezy-non-free",
"aptly snapshot create snap3 from mirror wheezy-backports",
]
runCmd = "aptly snapshot merge -no-remove snap4 snap1 snap2 snap3"
expectedCode = 0
def check(self):
def remove_created_at(s):
return re.sub(r"Created At: [0-9:A-Za-z -]+\n", "", s)
self.check_output()
self.check_cmd_output("aptly snapshot show -with-packages snap4", "snapshot_show", match_prepare=remove_created_at)
class MergeSnapshot10Test(BaseTest):
"""
merge snapshots: compare -no-remove and regular
"""
fixtureDB = True
fixtureCmds = [
"aptly snapshot create snap1 from mirror wheezy-main",
"aptly snapshot create snap2 from mirror wheezy-non-free",
"aptly snapshot create snap3 from mirror wheezy-backports",
"aptly snapshot merge snap4 snap3 snap2 snap1",
"aptly snapshot merge -no-remove snap5 snap3 snap2 snap1",
]
runCmd = "aptly snapshot diff snap4 snap5"
expectedCode = 0
class MergeSnapshot11Test(BaseTest):
"""
merge snapshots: -no-remove & -latest conflict
"""
fixtureCmds = [
"aptly snapshot create snap1 empty"
]
runCmd = "aptly snapshot merge -no-remove -latest snap2 snap1"
expectedCode = 1
+1 -1
View File
@@ -1,4 +1,4 @@
Removing ${HOME}/.aptly/public/dists/sq1... Removing ${HOME}/.aptly/public/dists/sq1...
Cleaning up prefix "." component "main"... Cleaning up prefix "." components main...
Published repository has been removed successfully. Published repository has been removed successfully.
+1 -1
View File
@@ -1,4 +1,4 @@
Removing ${HOME}/.aptly/public/dists/sq2... Removing ${HOME}/.aptly/public/dists/sq2...
Cleaning up prefix "." component "main"... Cleaning up prefix "." components main...
Published repository has been removed successfully. Published repository has been removed successfully.
+5 -4
View File
@@ -1,5 +1,6 @@
Published repositories: Published repositories:
* ./maverick (main) [amd64, i386] publishes [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick * ./maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
* ppa/smira/wheezy (contrib) [amd64] publishes [snap2]: Merged from sources: 'snap1' * ppa/maverick [amd64, i386] publishes {contrib: [snap2]: Merged from sources: 'snap1'}, {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
* ppa/tr1/maverick (main, origin: origin1) [amd64, i386] publishes [snap2]: Merged from sources: 'snap1' * ppa/smira/wheezy [amd64] publishes {contrib: [snap2]: Merged from sources: 'snap1'}
* ppa/tr2/maverick (main, label: label1) [amd64, i386] publishes [snap2]: Merged from sources: 'snap1' * ppa/tr1/maverick (origin: origin1) [amd64, i386] publishes {main: [snap2]: Merged from sources: 'snap1'}
* ppa/tr2/maverick (label: label1) [amd64, i386] publishes {main: [snap2]: Merged from sources: 'snap1'}
+2 -1
View File
@@ -1,2 +1,3 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
Loading packages... Loading packages...
ERROR: unable to publish: source is empty ERROR: unable to publish: unable to figure out list of architectures, please supply explicit list
+14
View File
@@ -0,0 +1,14 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Local repo local-repo has been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ maverick main
deb-src http://your-server/ maverick main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
+13
View File
@@ -0,0 +1,13 @@
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Local repos repo1, repo2 have been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ maverick contrib main
deb-src http://your-server/ maverick contrib main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
@@ -0,0 +1,9 @@
Origin: . maverick
Label: . maverick
Codename: maverick
Architectures: i386
Components: contrib main
Description: Generated by aptly
MD5Sum:
SHA1:
SHA256:
+13
View File
@@ -0,0 +1,13 @@
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/squeeze/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/squeeze/Release' with gpg, please enter your passphrase when prompted:
Local repos repo1, repo2 have been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ squeeze contrib main
deb-src http://your-server/ squeeze contrib main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
@@ -0,0 +1 @@
ERROR: unable to publish: duplicate component name: contrib
@@ -0,0 +1,2 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
ERROR: unable to publish: duplicate component name: b
@@ -0,0 +1,2 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
ERROR: unable to publish: unable to guess distribution name, please specify explicitly
@@ -0,0 +1 @@
ERROR: unable to publish: local repo with name repo2 not found
@@ -0,0 +1 @@
ERROR: unable to parse command
+1 -1
View File
@@ -1 +1 @@
ERROR: prefix/distribution already used by another published repo: ./maverick (main) [i386, source] publishes [local-repo] ERROR: prefix/distribution already used by another published repo: ./maverick [i386, source] publishes {main: [local-repo]}
+1 -1
View File
@@ -1 +1 @@
ERROR: prefix/distribution already used by another published repo: ppa/maverick (main) [i386, source] publishes [local-repo] ERROR: prefix/distribution already used by another published repo: ppa/maverick [i386, source] publishes {main: [local-repo]}
@@ -1,2 +1,3 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
Loading packages... Loading packages...
ERROR: unable to publish: source is empty ERROR: unable to publish: unable to figure out list of architectures, please supply explicit list
@@ -0,0 +1,5 @@
Origin: . maverick
Label: . maverick
Architecture: amd64
Archive: maverick
Component: main
@@ -0,0 +1,5 @@
Origin: . maverick
Label: . maverick
Architecture: i386
Archive: maverick
Component: main
@@ -0,0 +1,13 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Snapshot snap25 has been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ maverick main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
@@ -0,0 +1,13 @@
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Snapshots snap26.1, snap26.2 have been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ maverick contrib main
deb-src http://your-server/ maverick contrib main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
@@ -0,0 +1,9 @@
Origin: . maverick
Label: . maverick
Codename: maverick
Architectures: amd64 i386
Components: contrib main
Description: Generated by aptly
MD5Sum:
SHA1:
SHA256:
@@ -0,0 +1,13 @@
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release' with gpg, please enter your passphrase when prompted:
Snapshots snap27.1, snap27.2 have been successfully published.
Please setup your webserver to serve directory '${HOME}/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ maverick contrib main
deb-src http://your-server/ maverick contrib main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
@@ -0,0 +1 @@
ERROR: unable to publish: duplicate component name: main
@@ -0,0 +1,2 @@
Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)
ERROR: unable to publish: duplicate component name: b
@@ -0,0 +1 @@
ERROR: unable to publish: unable to guess distribution name, please specify explicitly
@@ -0,0 +1 @@
ERROR: unable to publish: snapshot with name snap31.2 not found
@@ -0,0 +1 @@
ERROR: unable to parse command
+1 -1
View File
@@ -1 +1 @@
ERROR: prefix/distribution already used by another published repo: ./maverick (main) [amd64, i386] publishes [snap7]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick ERROR: prefix/distribution already used by another published repo: ./maverick [amd64, i386] publishes {main: [snap7]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
+1 -1
View File
@@ -1 +1 @@
ERROR: prefix/distribution already used by another published repo: ppa/maverick (main) [amd64, i386] publishes [snap8]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick ERROR: prefix/distribution already used by another published repo: ppa/maverick [amd64, i386] publishes {main: [snap8]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}
+2 -2
View File
@@ -2,6 +2,6 @@ Loading packages...
Generating metadata files and linking package files... Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Signing file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Cleaning up prefix "." component "main"... Cleaning up prefix "." components main...
Publish for snapshot ./maverick (main) [amd64, i386] publishes [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11' has been successfully switched to new snapshot. Publish for snapshot ./maverick [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
+2 -2
View File
@@ -2,6 +2,6 @@ Loading packages...
Generating metadata files and linking package files... Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Signing file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Clearsigning file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Cleaning up prefix "ppa" component "main"... Cleaning up prefix "ppa" components main...
Publish for snapshot ppa/maverick (main) [amd64, i386] publishes [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick has been successfully switched to new snapshot. Publish for snapshot ppa/maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new snapshot.
+2 -2
View File
@@ -2,6 +2,6 @@ Loading packages...
Generating metadata files and linking package files... Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Signing file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Cleaning up prefix "." component "main"... Cleaning up prefix "." components main...
Publish for snapshot ./maverick (main) [amd64, i386] publishes [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11' has been successfully switched to new snapshot. Publish for snapshot ./maverick [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
+2 -2
View File
@@ -2,6 +2,6 @@ Loading packages...
Generating metadata files and linking package files... Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Signing file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted: Clearsigning file '${HOME}/.aptly/public/ppa/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Cleaning up prefix "ppa" component "main"... Cleaning up prefix "ppa" components main...
Publish for snapshot ppa/maverick (main) [i386] publishes [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick has been successfully switched to new snapshot. Publish for snapshot ppa/maverick [i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new snapshot.
@@ -0,0 +1,109 @@
.
.
.
.
.
.
.
.
C-like language. Can perform smoothing, spline-fitting, or nonlinear fits,
C-like language. Can perform smoothing, spline-fitting, or nonlinear fits,
C-like language. Can perform smoothing, spline-fitting, or nonlinear fits,
C-like language. Can perform smoothing, spline-fitting, or nonlinear fits,
Data files and self-defined functions can be manipulated by the internal
Data files and self-defined functions can be manipulated by the internal
Data files and self-defined functions can be manipulated by the internal
Data files and self-defined functions can be manipulated by the internal
Gnuplot is a portable command-line driven interactive data and function
Gnuplot is a portable command-line driven interactive data and function
Gnuplot is a portable command-line driven interactive data and function
Gnuplot is a portable command-line driven interactive data and function
This package contains the additional documentation.
This package contains the terminal driver that enables gnuplot to plot
This package is for transition and to install a full-featured gnuplot
This package is for working without an X server.
and can work with complex numbers.
and can work with complex numbers.
and can work with complex numbers.
and can work with complex numbers.
for many printers, (La)TeX, (x)fig, Postscript, and so on. The X11-output
for many printers, (La)TeX, (x)fig, Postscript, and so on. The X11-output
for many printers, (La)TeX, (x)fig, Postscript, and so on. The X11-output
for many printers, (La)TeX, (x)fig, Postscript, and so on. The X11-output
gnuplot.
images interactively under X11. Most users will want this, it is however
is packaged in gnuplot-x11.
is packaged in gnuplot-x11.
is packaged in gnuplot-x11.
is packaged in gnuplot-x11.
packaged separately so that low-end systems don't need X installed to use
plotting utility that supports lots of output formats, including drivers
plotting utility that supports lots of output formats, including drivers
plotting utility that supports lots of output formats, including drivers
plotting utility that supports lots of output formats, including drivers
supporting the X11-output.
Architecture: all
Architecture: all
Architecture: i386
Architecture: i386
Depends: dpkg (>= 1.15.4) | install-info
Depends: gnuplot-nox (>= 4.6.1-1~maverick2), gnuplot-x11 (>= 4.6.1-1~maverick2)
Depends: gnuplot-nox (>= 4.6.1-1~maverick2), libc6 (>= 2.11), libcairo2 (>= 1.6.0), libedit2 (>= 2.5.cvs.20010821-1), libgcc1 (>= 1:4.1.1), libgd2-noxpm (>= 2.0.36~rc1~dfsg) | libgd2-xpm (>= 2.0.36~rc1~dfsg), libglib2.0-0 (>= 2.12.0), liblua5.1-0, libpango1.0-0 (>= 1.14.0), libstdc++6 (>= 4.1.1), libwxbase2.8-0 (>= 2.8.11.0), libwxgtk2.8-0 (>= 2.8.11.0), libx11-6
Depends: libc6 (>= 2.11), libcairo2 (>= 1.6.0), libedit2 (>= 2.5.cvs.20010821-1), libgd2-noxpm (>= 2.0.36~rc1~dfsg) | libgd2-xpm (>= 2.0.36~rc1~dfsg), libglib2.0-0 (>= 2.12.0), liblua5.1-0, libpango1.0-0 (>= 1.14.0)
Description: Command-line driven interactive plotting program
Description: Command-line driven interactive plotting program
Description: Command-line driven interactive plotting program
Description: Command-line driven interactive plotting program
Filename: pool/a/g/gnuplot/gnuplot-doc_4.6.1-1~maverick2_all.deb
Filename: pool/a/g/gnuplot/gnuplot-nox_4.6.1-1~maverick2_i386.deb
Filename: pool/a/g/gnuplot/gnuplot-x11_4.6.1-1~maverick2_i386.deb
Filename: pool/a/g/gnuplot/gnuplot_4.6.1-1~maverick2_all.deb
Installed-Size: 1604
Installed-Size: 20
Installed-Size: 2536
Installed-Size: 5572
MD5sum: 25a5028811171f2f1fa157a2f6953e82
MD5sum: 4912a4464d5588f685c4aa6cfc6be46c
MD5sum: a7ef16004b62fd78acb77edb058ea1c1
MD5sum: fcad938905d0ace50a6ce0c73b2c6583
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Package: gnuplot
Package: gnuplot-doc
Package: gnuplot-nox
Package: gnuplot-x11
Priority: optional
Priority: optional
Priority: optional
Priority: optional
Recommends: groff, ttf-liberation
Replaces: gnuplot (<< 4.0.0)
Replaces: gnuplot (<< 4.0.0)
SHA1: 02f9a93097a8f798a054e26154dbe5789088c069
SHA1: 4a50deb413e05f77b31687405465b1229b3be328
SHA1: 629c3e62f787b0af47b184beb0460dd261c9ca4d
SHA1: 837dd002143054ca01d3b01cae410cc4b4fe10c4
Section: doc
Section: math
Section: math
Section: math
Size: 1046
Size: 1046496
Size: 2675242
Size: 724388
Source:
Source: gnuplot
Source: gnuplot
Source: gnuplot
Suggests: gnuplot-doc (>= 4.6.1-1~maverick2)
Suggests: gnuplot-x11 (>= 4.6.1-1~maverick2), gnuplot-doc (>= 4.6.1-1~maverick2)
Version: 4.6.1-1~maverick2
Version: 4.6.1-1~maverick2
Version: 4.6.1-1~maverick2
Version: 4.6.1-1~maverick2
@@ -0,0 +1,30 @@
.
.
C-like language. Can perform smoothing, spline-fitting, or nonlinear fits,
Data files and self-defined functions can be manipulated by the internal
Gnuplot is a portable command-line driven interactive data and function
This package contains the terminal driver that enables gnuplot to plot
and can work with complex numbers.
for many printers, (La)TeX, (x)fig, Postscript, and so on. The X11-output
gnuplot.
images interactively under X11. Most users will want this, it is however
is packaged in gnuplot-x11.
packaged separately so that low-end systems don't need X installed to use
plotting utility that supports lots of output formats, including drivers
Architecture: i386
Depends: gnuplot-nox (>= 4.6.1-1~maverick2), libc6 (>= 2.11), libcairo2 (>= 1.6.0), libedit2 (>= 2.5.cvs.20010821-1), libgcc1 (>= 1:4.1.1), libgd2-noxpm (>= 2.0.36~rc1~dfsg) | libgd2-xpm (>= 2.0.36~rc1~dfsg), libglib2.0-0 (>= 2.12.0), liblua5.1-0, libpango1.0-0 (>= 1.14.0), libstdc++6 (>= 4.1.1), libwxbase2.8-0 (>= 2.8.11.0), libwxgtk2.8-0 (>= 2.8.11.0), libx11-6
Description: Command-line driven interactive plotting program
Filename: pool/b/g/gnuplot/gnuplot-x11_4.6.1-1~maverick2_i386.deb
Installed-Size: 1604
MD5sum: fcad938905d0ace50a6ce0c73b2c6583
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Package: gnuplot-x11
Priority: optional
Replaces: gnuplot (<< 4.0.0)
SHA1: 02f9a93097a8f798a054e26154dbe5789088c069
Section: math
Size: 724388
Source: gnuplot
Version: 4.6.1-1~maverick2
@@ -0,0 +1,26 @@
(name, value) pairs from the user, via conventional methods such as
.
.
Boost version (currently 1.49).
Library to let program developers obtain program options, that is
This package forms part of the Boost C++ Libraries collection.
This package is a dependency package, which depends on Debian's default
command line and config file.
Architecture: i386
Depends: libboost-program-options1.49-dev
Description: program options library for C++ (default version)
Filename: pool/c/b/boost-defaults/libboost-program-options-dev_1.49.0.1_i386.deb
Homepage: http://www.boost.org/libs/program_options/
Installed-Size: 26
MD5sum: 0035d7822b2f8f0ec4013f270fd650c2
Maintainer: Debian Boost Team <pkg-boost-devel@lists.alioth.debian.org>
Package: libboost-program-options-dev
Priority: optional
SHA1: 36895eb64cfe89c33c0a2f7ac2f0c6e0e889e04b
SHA256: c76b4bd12fd92e4dfe1b55b18a67a669d92f62985d6a96c8a21d96120982cf12
Section: libdevel
Size: 2738
Source: boost-defaults
Version: 1.49.0.1
@@ -0,0 +1,7 @@
Loading packages...
Generating metadata files and linking package files...
Signing file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Clearsigning file '${HOME}/.aptly/public/dists/maverick/Release.tmp' with gpg, please enter your passphrase when prompted:
Cleaning up prefix "." components b, c...
Publish for snapshot ./maverick [amd64, i386, source] publishes {a: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}, {b: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'}, {c: [local2]: Snapshot from local repo [local-repo]} has been successfully switched to new snapshot.

Some files were not shown because too many files have changed in this diff Show More