Compare commits

...

82 Commits

Author SHA1 Message Date
Andrey Smirnov 898f726659 Update and fix integrated help in aptly. 2014-01-24 21:49:51 +04:00
Andrey Smirnov 5a9e13265f Snapshot merge tests. 2014-01-24 19:24:20 +04:00
Andrey Smirnov 17f5afb494 Tests for aptly snapshot diff. 2014-01-24 18:56:59 +04:00
Andrey Smirnov d87fc1be21 System tests for snapshot create, show, lit, pull and verify. 2014-01-24 18:33:19 +04:00
Andrey Smirnov c3b3e580bd Leave color reset on the same line. 2014-01-24 18:33:03 +04:00
Andrey Smirnov abc117531d Correctly spli command line as shell. 2014-01-24 18:32:43 +04:00
Andrey Smirnov 03b800882c Deduplicate variants slice before processing it. 2014-01-24 17:45:33 +04:00
Andrey Smirnov 6ffc6056c4 StrSlice deduplication. 2014-01-24 17:45:16 +04:00
Andrey Smirnov 2fe8f5cc1a Script to re-create fixture. 2014-01-24 14:14:20 +04:00
Andrey Smirnov a738d4843d Add 'mirror already exists' test. 2014-01-24 14:13:40 +04:00
Andrey Smirnov 86f3a0b463 Add support for pool/db fixtures, outuput match prepare. 2014-01-24 14:12:52 +04:00
Andrey Smirnov 121f93957d Fix multiple subtle bugs in dependency verification. 2014-01-24 14:12:03 +04:00
Andrey Smirnov be3cd88a31 Sort dependencies when displaying result of verify. 2014-01-24 14:11:42 +04:00
Andrey Smirnov 81d75ccba4 Sort snapshots when listing. 2014-01-24 13:21:41 +04:00
Andrey Smirnov a3df28ec4b aptly mirror update tests. 2014-01-21 18:07:06 +04:00
Andrey Smirnov 06ed37225a Fix config test dependency on $HOME. 2014-01-21 17:21:01 +04:00
Andrey Smirnov a9cb70dc08 Build aptly before system-testing it. 2014-01-21 17:13:55 +04:00
Andrey Smirnov 845ef28a79 Add go's binpath when running system test. 2014-01-21 17:10:49 +04:00
Andrey Smirnov c9e8d98e37 Inlcude system tests in Travis CI and regular make all. 2014-01-21 16:37:00 +04:00
Andrey Smirnov 1728691462 Don't hard require colors, exit with 1 on errors. 2014-01-21 16:36:40 +04:00
Andrey Smirnov 4197af902e Tests for mirror list/show. 2014-01-21 15:33:22 +04:00
Andrey Smirnov 656dddda53 Sort mirrors when listing. 2014-01-21 15:33:05 +04:00
Andrey Smirnov e1ca459329 aptly mirror create system tests. 2014-01-21 14:21:22 +04:00
Andrey Smirnov 7ec27ad88c Print mirror Release file in sorted way. 2014-01-21 13:36:24 +04:00
Andrey Smirnov e21506d373 Method to sort keys in map. 2014-01-21 13:36:09 +04:00
Andrey Smirnov e183ddb981 Stop using log when printing lines. 2014-01-21 13:35:56 +04:00
Andrey Smirnov c61a6b6dd8 Make build instructions simpler. [ci skip] 2014-01-21 13:01:17 +04:00
Andrey Smirnov 211cce1a8e Simple tests for inline help. 2014-01-21 12:45:48 +04:00
Andrey Smirnov 362cdbcd57 Refactor system tests to live together in one package. 2014-01-21 12:15:28 +04:00
Andrey Smirnov 0f902ee74b System test, first sketch. 2014-01-20 23:39:25 +04:00
Andrey Smirnov f1d892c759 Fix bug: bad config shouldn't result in overwriting with default. Stop using log. 2014-01-20 23:38:22 +04:00
Andrey Smirnov ce5f277f36 Change URL to bintray. [ci skip] 2014-01-20 18:30:18 +04:00
Andrey Smirnov 9b4ea531d2 Fix website link. 2014-01-20 11:25:38 +04:00
Andrey Smirnov 69b48de0af Command aptly snapshot merge. 2014-01-19 12:57:15 +04:00
Andrey Smirnov 99621119e8 NewSnapshotFromRefList method. 2014-01-19 12:57:04 +04:00
Andrey Smirnov aa803efd28 Merging two snapshots into one. 2014-01-19 12:32:46 +04:00
Andrey Smirnov b48ebbae81 Publishing persistence: creating, listing, dropping. 2014-01-17 20:59:25 +04:00
Andrey Smirnov 4fea570f5e Removing published repos & matching files. 2014-01-17 20:49:38 +04:00
Andrey Smirnov cf12c0b751 Removing directories under public root. 2014-01-17 13:46:59 +04:00
Andrey Smirnov bbec7ef948 Implement key deletion. 2014-01-17 00:28:10 +04:00
Andrey Smirnov 311c8ca6ad aptly publish list 2014-01-16 23:45:06 +04:00
Andrey Smirnov 33a8dcdacd Published persistence: save persisted when publishing. 2014-01-16 23:04:22 +04:00
Andrey Smirnov a8e6251a80 Add SnapshotCollection.ByUUID. 2014-01-16 20:57:45 +04:00
Andrey Smirnov 326d589f56 No need for hack with gonuts/commander anymore. 2014-01-16 18:57:51 +04:00
Andrey Smirnov 7adc065bb8 Fix words. [ci skip] 2014-01-16 18:52:20 +04:00
Andrey Smirnov 640c9fe8bd Trim documentation moved to web. 2014-01-16 18:51:22 +04:00
Andrey Smirnov 81d6325730 Rework downloading: first build queue, then use it to download. 2014-01-16 17:46:57 +04:00
Andrey Smirnov 8ece5368b1 Report back estimated size of downloads. 2014-01-14 14:17:41 +04:00
Andrey Smirnov 7afcc93507 Method Pause/Resume for Downloader. 2014-01-14 14:11:28 +04:00
Andrey Smirnov e63bbe839b aptly snapshot diff command and documentation. 2014-01-14 01:59:48 +04:00
Andrey Smirnov 49c8c8bdc0 Snapshot difference algorithm. 2014-01-13 20:27:14 +04:00
Andrey Smirnov e70517b9ca Change package key to be more groupable: arch goes first. 2014-01-13 20:27:01 +04:00
Andrey Smirnov d66fe47def Docs for dependency & architectures options. 2014-01-13 11:20:07 +04:00
Andrey Smirnov 667703a9ac Correctly process global options. 2014-01-13 11:19:56 +04:00
Andrey Smirnov 1268f744ab Don't download the same files twice in one cycle. 2014-01-13 00:19:45 +04:00
Andrey Smirnov d684c87fd1 Add support for dependency & architectures as common options. 2014-01-12 23:55:58 +04:00
Andrey Smirnov f9853de144 Documentation for aptly snapshot pull. 2014-01-12 13:21:50 +04:00
Andrey Smirnov b144227fdf Support for dry-run & no-deps. 2014-01-12 13:21:40 +04:00
Andrey Smirnov 7e11e5c652 Snapshot pull command. 2014-01-12 13:05:36 +04:00
Andrey Smirnov 9ca005147c Snapshot creation from package list. 2014-01-12 13:05:27 +04:00
Andrey Smirnov c0b41a7e96 Merge PackageList & PackageIndexedList, support for package removal. 2014-01-11 20:05:04 +04:00
Andrey Smirnov 335298d074 Export ParseDependency & friends. 2014-01-11 18:57:50 +04:00
Andrey Smirnov c903633363 Fix UsageLines in command help. 2014-01-11 18:57:25 +04:00
Andrey Smirnov 5bf015b2b7 Documentation for aptly snapshot verify. 2014-01-10 13:27:19 +04:00
Andrey Smirnov 01338d6037 A bit of niceness: more hints for first-time user. 2014-01-10 13:21:37 +04:00
Andrey Smirnov 356187f3ae PrepareIndex before trying to resolve dependencies. 2014-01-09 20:07:13 +04:00
Andrey Smirnov 645bba1924 Provides is a _list_. 2014-01-09 20:07:00 +04:00
Andrey Smirnov 10da8330b0 Fix to support dependency variants. 2014-01-09 14:38:36 +04:00
Andrey Smirnov ba30a42d6d Command snapshot verify. 2014-01-09 14:21:41 +04:00
Andrey Smirnov 6412bee952 Use method to load package list from reflist. 2014-01-09 14:21:24 +04:00
Andrey Smirnov f168feb9b8 Architecture parsing, package list from reflist. 2014-01-09 14:18:37 +04:00
Andrey Smirnov 4e2fce7251 Dependency to string. 2014-01-09 14:18:28 +04:00
Andrey Smirnov e37fe33203 Dependency resolution engine with tests. 2014-01-09 13:12:32 +04:00
Andrey Smirnov 5dbb771ba8 Refactor to use single struct Dependency. 2014-01-08 18:38:02 +04:00
Andrey Smirnov 4969c31e38 Push not finished file to fix build. 2014-01-08 18:28:48 +04:00
Andrey Smirnov b1e9b82ce3 Dependency parsing. 2014-01-08 18:16:01 +04:00
Andrey Smirnov 275db14b9f Package.getDependencies method. 2014-01-08 17:40:04 +04:00
Andrey Smirnov 5ccbd232f4 Package MatchesArchitecture, Provides. 2014-01-08 17:32:52 +04:00
Andrey Smirnov 82c06f78f7 Debian version comparison. 2014-01-08 17:14:23 +04:00
Andrey Smirnov 4aa24048d5 Major refactoring of files in package: hide them in Package type. 2014-01-05 13:02:33 +04:00
Andrey Smirnov 06f1c62ef0 Download section. 2014-01-04 14:20:47 +04:00
Andrey Smirnov 2c5dcde29d Heading for 0.2 2014-01-04 13:25:47 +04:00
119 changed files with 467580 additions and 546 deletions
+3 -1
View File
@@ -23,4 +23,6 @@ _testmain.go
*.test *.test
coverage.html coverage.html
coverage*.out coverage*.out
*.pyc
+8 -4
View File
@@ -3,9 +3,11 @@ GOVERSION=$(shell go version | awk '{print $$3;}')
ifeq ($(TRAVIS), true) ifeq ($(TRAVIS), true)
GOVERALLS?=$(HOME)/gopath/bin/goveralls GOVERALLS?=$(HOME)/gopath/bin/goveralls
SRCPATH?=$(HOME)/gopath/src SRCPATH?=$(HOME)/gopath/src
BINPATH=$(HOME)/gopath/bin
else else
GOVERALLS?=goveralls GOVERALLS?=goveralls
SRCPATH?=$(GOPATH)/src SRCPATH?=$(GOPATH)/src
BINPATH?=$(GOPATH)/bin
endif endif
ifeq ($(GOVERSION), go1.2) ifeq ($(GOVERSION), go1.2)
@@ -16,13 +18,11 @@ TRAVIS_TARGET=test
PREPARE_LIST= PREPARE_LIST=
endif endif
all: test check all: test check system-test
prepare: $(PREPARE_LIST) prepare: $(PREPARE_LIST)
go get -d -v ./... go get -d -v ./...
go get launchpad.net/gocheck go get launchpad.net/gocheck
# temporary fix: use commander develop version for now (https://github.com/smira/aptly/pull/1)
cd $(SRCPATH)/github.com/gonuts/commander && git fetch && git checkout develop
cover-prepare: cover-prepare:
go get github.com/golang/lint/golint go get github.com/golang/lint/golint
@@ -45,7 +45,11 @@ check:
go tool vet -all=true . go tool vet -all=true .
golint . golint .
travis: $(TRAVIS_TARGET) system-test:
go install
PATH=$(BINPATH):$(PATH) python system/run.py
travis: $(TRAVIS_TARGET) system-test
test: test:
go test -v ./... -gocheck.v=true go test -v ./... -gocheck.v=true
+8 -341
View File
@@ -10,14 +10,17 @@ aptly
Aptly is a swiss army knife for Debian repository management. Aptly is a swiss army knife for Debian repository management.
It allows to: ("+" means planned features) Documentation is available at `http://www.aptly.info/ <http://www.aptly.info/>`_. For support use
mailing list `aptly-discuss <https://groups.google.com/forum/#!forum/aptly-discuss>`_.
Aptly features: ("+" means planned features)
* make mirrors of remote Debian/Ubuntu repositories, limiting by components/architectures * make mirrors of remote Debian/Ubuntu repositories, limiting by components/architectures
* take snapshots of mirrors at any point in time, fixing state of repository at some moment of time * take snapshots of mirrors at any point in time, fixing state of repository at some moment of time
* publish snapshot as Debian repository, ready to be consumed by apt * publish snapshot as Debian repository, ready to be consumed by apt
* controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies
* merge two or more snapshots into one (+) * merge two or more snapshots into one (+)
* filter repository by search query, pulling dependencies when required (+) * filter repository by search query, pulling dependencies when required (+)
* controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies (+)
* publish self-made packages as Debian repositories (+) * publish self-made packages as Debian repositories (+)
Current limitations: Current limitations:
@@ -29,347 +32,11 @@ Current limitations:
Currently aptly is under heavy development, so please use it with care. Currently aptly is under heavy development, so please use it with care.
.. contents::
Download Download
-------- --------
TBD Binary executables (depends almost only on libc) are available for download from `Bintray <http://dl.bintray.com/smira/aptly/>`_.
Configuration If you have Go environment set up, you can build aptly from source by running::
-------------
aptly looks for configuration file in ``/etc/aptly.conf`` and ``~/.aptly.conf``, if no config file found, go get github.com/smira/aptly
new one is created. Also aptly needs root directory for database, package and published repository storage.
If not specified, directory defaults to ``~/.aptly``, it will be created if missing.
Configuration file is stored in JSON format::
{
"rootDir": "/var/aptly",
"downloadConcurrency": 4
}
Options:
* ``rootDir`` is root of directory storage to store datbase (``rootDir/db``), downloaded packages (``rootDir/pool``) and
published repositories (``rootDir/public``)
* ``downloadConcurrency`` is a number of parallel download threads to use when downloading packages
Example
-------
Create mirror::
$ aptly mirror create --architecture="amd64" debian-main http://ftp.ru.debian.org/debian/ squeeze main
2013/12/28 19:44:45 Downloading http://ftp.ru.debian.org/debian/dists/squeeze/Release...
...
Mirror [debian-main]: http://ftp.ru.debian.org/debian/ squeeze successfully added.
You can run 'aptly mirror update debian-main' to download repository contents.
Take snapshot::
$ aptly snapshot create debian-3112 from mirror debian-main
Snapshot debian-3112 successfully created.
You can run 'aptly publish snapshot debian-3112' to publish snapshot as Debian repository.
Publish snapshot (requires generated GPG key)::
$ aptly publish snapshot debian-3112
...
Snapshot back has been successfully published.
Please setup your webserver to serve directory '/var/aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ squeeze main
Don't forget to add your GPG key to apt with apt-key.
Set up webserver (e.g. nginx)::
server {
root /home/example/.aptly/public;
server_name mirror.local;
location / {
autoindex on;
}
Add new repository to apt's sources::
deb http://mirror.local/ squeeze main
Run apt-get to fetch repository metadata::
apt-get update
Enjoy!
Usage
-----
Aptly supports commands in three basic categories:
* ``mirror``
* ``snapshot``
* ``publish``
Command ``mirror``
~~~~~~~~~~~~~~~~~~
Mirror subcommands manage mirrors of remote Debian repositories.
``aptly mirror create``
^^^^^^^^^^^^^^^^^^^^^^^
Creates mirror of remote repository. It supports only HTTP repositories.
Usage::
$ aptly mirror create <name> <archive url> <distribution> [<component1> ...]
Params are:
* ``name`` is a name that would be used in aptly to reference this mirror
* ``archive url`` is a root of archive, e.g. http://ftp.ru.debian.org/debian/
* ``distribution`` is a distribution name, e.g. ``squeeze``
* ``component1`` is an optional list of components to download, if not
specified aptly would fetch all components, e.g. ``main``
Options:
* ``--architecture="i386,amd64"`` list of architectures to fetch, if not specified,
aptly would fetch packages for all architectures
Example::
$ aptly mirror create --architecture="amd64" debian-main http://ftp.ru.debian.org/debian/ squeeze main
2013/12/28 19:44:45 Downloading http://ftp.ru.debian.org/debian/dists/squeeze/Release...
...
Mirror [debian-main]: http://ftp.ru.debian.org/debian/ squeeze successfully added.
You can run 'aptly mirror update debian-main' to download repository contents.
``aptly mirror update``
^^^^^^^^^^^^^^^^^^^^^^^
Updates (fetches packages and meta) remote mirror. When mirror is created, it should be run for the
first time to fetch mirror contents. This command could be run many times. If interrupted, it could
be restarted in a safe way.
Usage::
$ aptly mirror update <name>
Params are:
* ``name`` is a mirror name (given when mirror was created)
All packages would be stored under aptly's root dir (see section on Configuration).
Example::
$ aptly mirror update debian-main
2013/12/29 18:32:34 Downloading http://ftp.ru.debian.org/debian/dists/squeeze/Release...
2013/12/29 18:32:37 Downloading http://ftp.ru.debian.org/debian/dists/squeeze/main/binary-amd64/Packages.bz2...
2013/12/29 18:37:19 Downloading http://ftp.ru.debian.org/debian/pool/main/libg/libgwenhywfar/libgwenhywfar47-dev_3.11.3-1_amd64.deb...
....
``aptly mirror list``
^^^^^^^^^^^^^^^^^^^^^
Shows list of registered mirrors of repositories.
Usage::
$ aptly mirror list
Example::
$ aptly mirror list
List of mirrors:
* [backports]: http://mirror.yandex.ru/backports.org/ squeeze-backports
* [debian-main]: http://ftp.ru.debian.org/debian/ squeeze
To get more information about repository, run `aptly mirror show <name>`.
``aptly mirror show``
^^^^^^^^^^^^^^^^^^^^^
Shows detailed information about mirror.
Usage::
$ aptly mirror show <name>
Params are:
* ``name`` is a mirror name (given when mirror was created)
Example::
$ aptly mirror show backports2
Name: backports2
Archive Root URL: http://mirror.yandex.ru/backports.org/
Distribution: squeeze-backports
Components: main, contrib, non-free
Architectures: i386, amd64
Last update: 2013-12-27 19:30:19 MSK
Number of packages: 3898
Information from release file:
...
In detailed information, one can see basiс parameters of the mirror, filters by component & architecture, timestamp
of last successful repository fetch and number of packages.
Command ``snapshot``
~~~~~~~~~~~~~~~~~~~~
Snapshot is a fixed state of remote repository. Internally snapshot is list of packages with explicit version.
Snapshot is immutable, i.e. it can't change since it has been created.
``aptly snapshot create .. from mirror``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Creates snapshot from current state of remote mirror. Mirros should be updated at least once before using this command.
Usage::
$ aptly snapshot create <name> from mirror <mirror-name>
Params are:
* ``name`` is a name for the snapshot to be created
* ``mirror-name`` is a mirror name (given when mirror was created)
Example::
$ aptly snapshot create monday-updates from mirror backports2
Snapshot monday-updates successfully created.
You can run 'aptly publish snapshot monday-updates' to publish snapshot as Debian repository.
``aptly snapshot list``
^^^^^^^^^^^^^^^^^^^^^^^
Displays list of all created snapshots.
Usage::
$ aptly snapshot list
Example::
$ aptly snapshot list
List of snapshots:
* [monday-updates]: Snapshot from mirror [backports2]: http://mirror.yandex.ru/backports.org/ squeeze-backports
* [back]: Snapshot from mirror [backports2]: http://mirror.yandex.ru/backports.org/ squeeze-backports
To get more information about snapshot, run `aptly snapshot show <name>`.
With snapshot information, basic information about snapshot origin is displayed: which mirror it has been created from.
``aptly snapshot show``
^^^^^^^^^^^^^^^^^^^^^^^
Shows detailed information about snapshot. Full list of packages in the snapshot is displayed as well.
Usage::
$ aptly snapshot show <name>
Params:
* ``name`` is snapshot name which has been given during snapshot creation
Example::
Name: back
Created At: 2013-12-24 15:39:29 MSK
Description: Snapshot from mirror [backports2]: http://mirror.yandex.ru/backports.org/ squeeze-backports
Number of packages: 3898
Packages:
altos-1.0.3~bpo60+1_i386
amanda-client-1:3.3.1-3~bpo60+1_amd64
...
Command ``publish``
~~~~~~~~~~~~~~~~~~~
Publishing snapshot as Debian repository which could be served by HTTP/FTP/rsync server. Repository is signed by
user's key with GnuPG. Key should be created beforehand (see section GPG Keys). Published repository could
be consumed directly by apt.
``aptly publish snapshot``
^^^^^^^^^^^^^^^^^^^^^^^^^^
Published repositories appear under ``rootDir/public`` directory.
Usage::
$ aptly publish snapshot <name> [<prefix>]
Params:
* ``name`` is a snapshot name that snould be published
* ``prefix`` is an optional prefix for publishing, if not specified, repository would be published to the root of
publiс directory
Options:
* ``-architectures=""``: list of architectures to publish (comma-separated); derived automatically from
snapshot contents
* ``-component=""``: component name to publish; guessed from original repository (if any), or defaults to
main
* ``-distribution=""``: distribution name to publish; guessed from original repository distribution
* ``-gpg-key=""``: GPG key ID to use when signing the release, if not specified default key is used
Example::
$ aptly publish snapshot back
Signing file '/var/aptly/public/dists/squeeze-backports/Release' with gpg, please enter your passphrase when prompted:
<<gpg asks for passphrase>>
Clearsigning file '/var/aptly/public/dists/squeeze-backports/Release' with gpg, please enter your passphrase when prompted:
<<gpg asks for passphrase>>
Snapshot back has been successfully published.
Please setup your webserver to serve directory '/var/aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ squeeze-backports main
Don't forget to add your GPG key to apt with apt-key.
Directory structure for published repositories::
public/ - root of published tree (root for webserver)
dists/
squeeze/ - distribution name
Release - raw file
InRelease - clearsigned file
Release.gpg - signature for Release file
binary-i386/
Packages - list of metadata for packages
Packages.gz
Packages.bz2
pool/
main/ - component name
m/
mars-invaders/
mars-invaders_1.0.3_i386.deb - package (hard link to package from main pool)
GPG Keys
--------
GPG key is required to sign any published repository. Key should be generated before publishing first repository.
Key generation, storage, backup and revocation is out of scope of this document, there are many tutorials available,
e.g. `this one <http://fedoraproject.org/wiki/Creating_GPG_Keys>`_.
Publiс part of the key should be exported (``gpg --export --armor``) and imported into apt keyring on all machines that would be using
published repositories using ``apt-key``.
+35 -28
View File
@@ -5,6 +5,8 @@ import (
"github.com/gonuts/commander" "github.com/gonuts/commander"
"github.com/gonuts/flag" "github.com/gonuts/flag"
"github.com/smira/aptly/debian" "github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings" "strings"
) )
@@ -15,15 +17,27 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
return err return err
} }
fmt.Printf("List of mirrors:\n")
repoCollection := debian.NewRemoteRepoCollection(context.database) repoCollection := debian.NewRemoteRepoCollection(context.database)
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
fmt.Printf(" * %s\n", repo)
return nil
})
fmt.Printf("\nTo get more information about repository, run `aptly mirror show <name>`.\n") if repoCollection.Len() > 0 {
fmt.Printf("List of mirrors:\n")
repos := make(sort.StringSlice, repoCollection.Len())
i := 0
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
repos[i] = repo.String()
i++
return nil
})
sort.Strings(repos)
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
return err return err
} }
@@ -34,13 +48,7 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
return err return err
} }
var architectures []string repo, err := debian.NewRemoteRepo(args[0], args[1], args[2], args[3:], context.architecturesList)
archs := cmd.Flag.Lookup("architecture").Value.String()
if len(archs) > 0 {
architectures = strings.Split(archs, ",")
}
repo, err := debian.NewRemoteRepo(args[0], args[1], args[2], args[3:], architectures)
if err != nil { if err != nil {
return fmt.Errorf("unable to create mirror: %s", err) return fmt.Errorf("unable to create mirror: %s", err)
} }
@@ -94,9 +102,10 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
} }
fmt.Printf("\nInformation from release file:\n") fmt.Printf("\nInformation from release file:\n")
for name, value := range repo.Meta { for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", name, value) fmt.Printf("%s: %s\n", k, repo.Meta[k])
} }
return err return err
} }
@@ -144,20 +153,18 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
func makeCmdMirrorCreate() *commander.Command { func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlyMirrorCreate, Run: aptlyMirrorCreate,
UsageLine: "create", UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror of Debian repository", Short: "create new mirror of Debian repository",
Long: ` Long: `
create only stores metadata about new mirror, and fetches Release files (it doesn't download packages) Create records information about new mirror and fetches Release file (it doesn't download packages).
ex: ex:
$ aptly mirror create <name> <archive url> <distribution> [<component1> ...] $ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`, `,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
} }
cmd.Flag.String("architecture", "", "limit architectures to download, comma-delimited list")
return cmd return cmd
} }
func makeCmdMirrorList() *commander.Command { func makeCmdMirrorList() *commander.Command {
@@ -166,7 +173,7 @@ func makeCmdMirrorList() *commander.Command {
UsageLine: "list", UsageLine: "list",
Short: "list mirrors of remote repositories", Short: "list mirrors of remote repositories",
Long: ` Long: `
list shows full list of remote repositories. List shows full list of remote repositories.
ex: ex:
$ aptly mirror list $ aptly mirror list
@@ -181,13 +188,13 @@ ex:
func makeCmdMirrorShow() *commander.Command { func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlyMirrorShow, Run: aptlyMirrorShow,
UsageLine: "show", UsageLine: "show <name>",
Short: "show details about remote repository mirror", Short: "show details about remote repository mirror",
Long: ` Long: `
show shows full information about mirror. Show shows full information about mirror.
ex: ex:
$ aptly mirror show <name> $ aptly mirror show wheezy-main
`, `,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
} }
@@ -198,13 +205,13 @@ ex:
func makeCmdMirrorUpdate() *commander.Command { func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlyMirrorUpdate, Run: aptlyMirrorUpdate,
UsageLine: "update", UsageLine: "update <name>",
Short: "update packages from remote mirror", Short: "update packages from remote mirror",
Long: ` Long: `
Update downloads list of packages and packages themselves. Update downloads list of packages and package files.
ex: ex:
$ aptly mirror update <name> $ aptly mirror update wheezy-main
`, `,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
} }
+125 -13
View File
@@ -6,6 +6,7 @@ import (
"github.com/gonuts/flag" "github.com/gonuts/flag"
"github.com/smira/aptly/debian" "github.com/smira/aptly/debian"
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"sort"
"strings" "strings"
) )
@@ -25,6 +26,8 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
prefix = "" prefix = ""
} }
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database) snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name) snapshot, err := snapshotCollection.ByName(name)
if err != nil { if err != nil {
@@ -62,21 +65,29 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
} }
} }
var architecturesList []string
architectures := cmd.Flag.Lookup("architectures").Value.String()
if architectures != "" {
architecturesList = strings.Split(architectures, ",")
}
signer := &utils.GpgSigner{} signer := &utils.GpgSigner{}
signer.SetKey(cmd.Flag.Lookup("gpg-key").Value.String()) signer.SetKey(cmd.Flag.Lookup("gpg-key").Value.String())
published := debian.NewPublishedRepo(prefix, distribution, component, architecturesList, snapshot) published, err := debian.NewPublishedRepo(prefix, distribution, component, context.architecturesList, snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
duplicate := publishedCollecton.CheckDuplicate(published)
if duplicate != nil {
publishedCollecton.LoadComplete(duplicate, snapshotCollection)
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
packageCollection := debian.NewPackageCollection(context.database) packageCollection := debian.NewPackageCollection(context.database)
err = published.Publish(context.packageRepository, packageCollection, signer) err = published.Publish(context.packageRepository, packageCollection, signer)
if err != nil { if err != nil {
return err return fmt.Errorf("unable to publish: %s", err)
}
err = publishedCollecton.Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
} }
if prefix != "" && !strings.HasSuffix(prefix, "/") { if prefix != "" && !strings.HasSuffix(prefix, "/") {
@@ -92,33 +103,134 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
return err return err
} }
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollecton.Len() == 0 {
fmt.Printf("No snapshots have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
published := make(sort.StringSlice, 0, publishedCollecton.Len())
err = publishedCollecton.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollecton.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
published = append(published, repo.String())
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
sort.Strings(published)
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
return err
}
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
distribution := args[0]
prefix := "."
if len(args) == 2 {
prefix = args[1]
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
err = publishedCollecton.Remove(context.packageRepository, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
return err
}
func makeCmdPublishSnapshot() *commander.Command { func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlyPublishSnapshot, Run: aptlyPublishSnapshot,
UsageLine: "snapshot", UsageLine: "snapshot <name> [<prefix>]",
Short: "makes Debian repository out of snapshot", Short: "makes Debian repository out of snapshot",
Long: ` Long: `
Publishes snapshot as Debian repository ready to be used by apt tools. Command publish oublishes snapshot as Debian repository ready to be used by apt tools.
ex: ex.
$ aptly publish snapshot <name> [<prefix>] $ aptly publish snapshot wheezy-main
`, `,
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")
cmd.Flag.String("architectures", "", "list of architectures to publish (comma-separated)")
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")
return cmd return cmd
} }
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
Short: "removes files of published repository",
Long: `
Command removes whatever has been published under specified prefix and distribution name.
ex.
$ aptly publish drop wheezy
`,
Flag: *flag.NewFlagSet("aptly-publish-drop", flag.ExitOnError),
}
return cmd
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "displays list of published repositories",
Long: `
Display command displays list of currently published snapshots with information about published root.
ex.
$ aptly publish list
`,
Flag: *flag.NewFlagSet("aptly-publish-list", flag.ExitOnError),
}
return cmd
}
func makeCmdPublish() *commander.Command { func makeCmdPublish() *commander.Command {
return &commander.Command{ return &commander.Command{
UsageLine: "publish", UsageLine: "publish",
Short: "manage published repositories", Short: "manage published repositories",
Subcommands: []*commander.Command{ Subcommands: []*commander.Command{
makeCmdPublishSnapshot(), makeCmdPublishSnapshot(),
makeCmdPublishList(),
makeCmdPublishDrop(),
}, },
Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError),
} }
+492 -16
View File
@@ -5,6 +5,9 @@ import (
"github.com/gonuts/commander" "github.com/gonuts/commander"
"github.com/gonuts/flag" "github.com/gonuts/flag"
"github.com/smira/aptly/debian" "github.com/smira/aptly/debian"
"github.com/wsxiaoys/terminal/color"
"sort"
"strings"
) )
func aptlySnapshotCreate(cmd *commander.Command, args []string) error { func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
@@ -52,16 +55,31 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
return err return err
} }
fmt.Printf("List of snapshots:\n")
snapshotCollection := debian.NewSnapshotCollection(context.database) snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
fmt.Printf(" * %s\n", snapshot)
return nil
})
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n") if snapshotCollection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
snapshots := make(sort.StringSlice, snapshotCollection.Len())
i := 0
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
snapshots[i] = snapshot.String()
i++
return nil
})
sort.Strings(snapshots)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
return err return err
} }
func aptlySnapshotShow(cmd *commander.Command, args []string) error { func aptlySnapshotShow(cmd *commander.Command, args []string) error {
@@ -107,16 +125,391 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
return err return err
} }
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
snapshots := make([]*debian.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = snapshotCollection.ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = snapshotCollection.LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
packageList, err := debian.NewPackageListFromRefList(snapshots[0].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := debian.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
for i := 1; i < len(snapshots); i++ {
pL, err := debian.NewPackageListFromRefList(snapshots[i].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
missing, err := packageList.VerifyDependencies(context.dependencyOptions, architecturesList, sourcePackageList)
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
fmt.Printf("All dependencies are satisfied.\n")
} else {
fmt.Printf("Missing dependencies (%d):\n", len(missing))
deps := make(sort.StringSlice, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
fmt.Printf(" %s\n", dep)
}
}
return err
}
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
}
noDeps := cmd.Flag.Lookup("no-deps").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name> snapshot
snapshot, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
fmt.Printf("Dependencies would be pulled into snapshot:\n %s\nfrom snapshot:\n %s\nand result would be saved as new snapshot %s.\n",
snapshot, source, args[2])
// Convert snapshot to package list
fmt.Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := debian.NewPackageListFromRefList(snapshot.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := debian.NewPackageListFromRefList(source.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
fmt.Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]debian.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = debian.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]debian.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
}
// Go over list of initial dependencies + list of dependencies found
for i := 0; i < len(dependencies); i++ {
dep := dependencies[i]
// Search for package that can satisfy dependencies
pkg := sourcePackageList.Search(dep)
if pkg == nil {
color.Printf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
fmt.Printf("\n")
continue
}
// Remove all packages with the same name and architecture
for p := packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
color.Printf("@r[-]@| %s removed", p)
fmt.Printf("\n")
p = packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name})
}
// Add new discovered package
packageList.Add(pkg)
color.Printf("@g[+]@| %s added", pkg)
fmt.Printf("\n")
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := debian.NewPackageList()
pL.Add(pkg)
missing, err := pL.VerifyDependencies(context.dependencyOptions, []string{arch}, packageList)
if err != nil {
color.Printf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
fmt.Printf("\n")
}
// Append missing dependencies to the list of dependencies to satisfy
for _, misDep := range missing {
found := false
for _, d := range dependencies {
if d == misDep {
found = true
break
}
}
if !found {
dependencies = append(dependencies, misDep)
}
}
}
}
if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
fmt.Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := debian.NewSnapshotFromPackageList(args[2], []*debian.Snapshot{snapshot, source}, packageList,
fmt.Sprintf("Pulled into '%s' with '%s' as source, pull request was: '%s'", snapshot.Name, source.Name, strings.Join(args[3:], " ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
}
return err
}
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
}
onlyMatching := cmd.Flag.Lookup("only-matching").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name-a> snapshot
snapshotA, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotB)
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
fmt.Printf("Snapshots are identical.\n")
} else {
fmt.Printf(" Arch | Package | Version in A | Version in B\n")
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
var verA, verB, pkg, arch, code string
if pdiff.Left == nil {
verA = "-"
verB = pdiff.Right.Version
pkg = pdiff.Right.Name
arch = pdiff.Right.Architecture
} else {
pkg = pdiff.Left.Name
arch = pdiff.Left.Architecture
verA = pdiff.Left.Version
if pdiff.Right == nil {
verB = "-"
} else {
verB = pdiff.Right.Version
}
}
if pdiff.Left == nil {
code = "@g+@|"
} else {
if pdiff.Right == nil {
code = "@r-@|"
} else {
code = "@y!@|"
}
}
color.Printf(code+" %-6s | %-40s | %-40s | %-40s\n", arch, pkg, verA, verB)
}
}
return err
}
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
sources := make([]*debian.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = snapshotCollection.ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList())
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := debian.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command { func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlySnapshotCreate, Run: aptlySnapshotCreate,
UsageLine: "create", UsageLine: "create <name> from mirror <mirror-name>",
Short: "creates snapshot out of any mirror", Short: "creates snapshot out of any mirror",
Long: ` Long: `
Create makes persistent immutable snapshot of repository mirror state in givent moment of time. Command create makes persistent immutable snapshot of remote repository mirror. Snapshot could be
published or further modified using merge, pull and other aptly features.
ex: ex.
$ aptly snapshot create <name> from mirror <mirror-name> $ aptly snapshot create wheezy-main-today from mirror wheezy-main
`, `,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
} }
@@ -131,7 +524,7 @@ func makeCmdSnapshotList() *commander.Command {
UsageLine: "list", UsageLine: "list",
Short: "lists snapshots", Short: "lists snapshots",
Long: ` Long: `
list shows full list of snapshots created. Command list shows full list of snapshots created.
ex: ex:
$ aptly snapshot list $ aptly snapshot list
@@ -145,13 +538,13 @@ ex:
func makeCmdSnapshotShow() *commander.Command { func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlySnapshotShow, Run: aptlySnapshotShow,
UsageLine: "show", UsageLine: "show <name>",
Short: "shows details about snapshot", Short: "shows details about snapshot",
Long: ` Long: `
shows shows full information about snapshot. Command show displays full information about snapshot.
ex: ex.
$ aptly snapshot show <name> $ aptly snapshot show wheezy-main
`, `,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
} }
@@ -159,6 +552,85 @@ ex:
return cmd return cmd
} }
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verifies that dependencies are satisfied in snapshot",
Long: `
Verify does depenency resolution in snapshot, possibly using additional snapshots as dependency sources.
All unsatisfied dependencies are returned.
ex.
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
Flag: *flag.NewFlagSet("aptly-snapshot-verify", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
Short: "performs partial upgrades (pulls new packages) from another snapshot",
Long: `
Command pull pulls new packages along with its dependencies in <name> snapshot
from <source> snapshot. Also can upgrade package version from one snapshot into
another, once again along with dependencies. New snapshot <destination> is created as result of this
process. Packages could be specified simply as 'package-name' or as dependency 'package-name (>= version)'.
ex.
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
return cmd
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "calculates difference in packages between two snapshots",
Long: `
Command diff shows list of missing and new packages, difference in package versions between two snapshots.
ex.
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots into one, replacing matching packages",
Long: `
Merge merges several snapshots into one. Merge happens from left to right. Packages with the same
name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
If run with only one source snapshot, merge copies source into destination.
ex.
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-merge", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshot() *commander.Command { func makeCmdSnapshot() *commander.Command {
return &commander.Command{ return &commander.Command{
UsageLine: "snapshot", UsageLine: "snapshot",
@@ -167,6 +639,10 @@ func makeCmdSnapshot() *commander.Command {
makeCmdSnapshotCreate(), makeCmdSnapshotCreate(),
makeCmdSnapshotList(), makeCmdSnapshotList(),
makeCmdSnapshotShow(), makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
//makeCmdSnapshotDestroy(), //makeCmdSnapshotDestroy(),
}, },
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
+5
View File
@@ -18,6 +18,7 @@ var (
type Storage interface { type Storage interface {
Get(key []byte) ([]byte, error) Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error Put(key []byte, value []byte) error
Delete(key []byte) error
FetchByPrefix(prefix []byte) [][]byte FetchByPrefix(prefix []byte) [][]byte
Close() error Close() error
} }
@@ -60,6 +61,10 @@ func (l *levelDB) Put(key []byte, value []byte) error {
return l.db.Put(key, value, nil) return l.db.Put(key, value, nil)
} }
func (l *levelDB) Delete(key []byte) error {
return l.db.Delete(key, nil)
}
func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte { func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0, 20) result := make([][]byte, 0, 20)
+16
View File
@@ -45,6 +45,22 @@ func (s *LevelDBSuite) TestGetPut(c *C) {
c.Assert(result, DeepEquals, value) c.Assert(result, DeepEquals, value)
} }
func (s *LevelDBSuite) TestDelete(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
err = s.db.Delete(key)
c.Assert(err, IsNil)
_, err = s.db.Get(key)
c.Assert(err, ErrorMatches, "key not found")
}
func (s *LevelDBSuite) TestFetchByPrefix(c *C) { func (s *LevelDBSuite) TestFetchByPrefix(c *C) {
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{}) c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{})
+458 -1
View File
@@ -3,23 +3,69 @@ package debian
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"sort" "sort"
) )
// Dependency options
const (
// DepFollowSource pulls source packages when required
DepFollowSource = 1 << iota
// DepFollowSuggests pulls from suggests
DepFollowSuggests
// DepFollowRecommends pulls from recommends
DepFollowRecommends
// DepFollowAllVariants follows all variants if depends on "a | b"
DepFollowAllVariants
)
// PackageList is list of unique (by key) packages // PackageList is list of unique (by key) packages
// //
// It could be seen as repo snapshot, repo contents, result of filtering, // It could be seen as repo snapshot, repo contents, result of filtering,
// merge, etc. // merge, etc.
//
// If indexed, PackageList starts supporting searching
type PackageList struct { type PackageList struct {
// Straight list of packages as map
packages map[string]*Package packages map[string]*Package
// Has index been prepared?
indexed bool
// Indexed list of packages, sorted by name internally
packagesIndex []*Package
// Map of packages for each virtual package (provides)
providesIndex map[string][]*Package
} }
// Verify interface
var (
_ sort.Interface = &PackageList{}
)
// NewPackageList creates empty package list // NewPackageList creates empty package list
func NewPackageList() *PackageList { func NewPackageList() *PackageList {
return &PackageList{packages: make(map[string]*Package, 1000)} return &PackageList{packages: make(map[string]*Package, 1000)}
} }
// NewPackageListFromRefList loads packages list from PackageRefList
func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageCollection) (*PackageList, error) {
result := &PackageList{packages: make(map[string]*Package, reflist.Len())}
err := reflist.ForEach(func(key []byte) error {
p, err := collection.ByKey(key)
if err != nil {
return fmt.Errorf("unable to load package with key %s: %s", key, err)
}
return result.Add(p)
})
if err != nil {
return nil, err
}
return result, nil
}
// 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.Key())
@@ -31,6 +77,19 @@ func (l *PackageList) Add(p *Package) error {
return nil return nil
} }
l.packages[key] = p l.packages[key] = p
if l.indexed {
for _, provides := range p.Provides {
l.providesIndex[provides] = append(l.providesIndex[provides], p)
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= p.Name })
// insert p into l.packagesIndex in position i
l.packagesIndex = append(l.packagesIndex, nil)
copy(l.packagesIndex[i+1:], l.packagesIndex[i:])
l.packagesIndex[i] = p
}
return nil return nil
} }
@@ -51,15 +110,253 @@ func (l *PackageList) Len() int {
return len(l.packages) return len(l.packages)
} }
// Append adds content from one package list to another
func (l *PackageList) Append(pl *PackageList) error {
if l.indexed {
panic("Append not supported when indexed")
}
for k, p := range pl.packages {
existing, ok := l.packages[k]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
}
} else {
l.packages[k] = p
}
}
return nil
}
// Remove removes package from the list, and updates index when required
func (l *PackageList) Remove(p *Package) {
delete(l.packages, string(p.Key()))
if l.indexed {
for _, provides := range p.Provides {
for i, pkg := range l.providesIndex[provides] {
if pkg.Equals(p) {
// remove l.ProvidesIndex[provides][i] w/o preserving order
l.providesIndex[provides][len(l.providesIndex[provides])-1], l.providesIndex[provides][i], l.providesIndex[provides] =
nil, l.providesIndex[provides][len(l.providesIndex[provides])-1], l.providesIndex[provides][:len(l.providesIndex[provides])-1]
break
}
}
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= p.Name })
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == p.Name {
if l.packagesIndex[i].Equals(p) {
// remove l.packagesIndex[i] preserving order
copy(l.packagesIndex[i:], l.packagesIndex[i+1:])
l.packagesIndex[len(l.packagesIndex)-1] = nil
l.packagesIndex = l.packagesIndex[:len(l.packagesIndex)-1]
break
}
i++
}
}
}
// Architectures returns list of architectures present in packages
func (l *PackageList) Architectures() (result []string) {
result = make([]string, 0, 10)
for _, pkg := range l.packages {
if pkg.Architecture != "all" && !utils.StrSliceHasItem(result, pkg.Architecture) {
result = append(result, pkg.Architecture)
}
}
return
}
// depSliceDeduplicate removes dups in slice of Dependencies
func depSliceDeduplicate(s []Dependency) []Dependency {
l := len(s)
if l < 2 {
return s
}
if l == 2 {
if s[0] == s[1] {
return s[0:1]
}
return s
}
found := make(map[string]bool, l)
j := 0
for i, x := range s {
h := x.Hash()
if !found[h] {
found[h] = true
s[j] = s[i]
j++
}
}
return s[:j]
}
// VerifyDependencies looks for missing dependencies in package list.
//
// Analysis would be peformed for each architecture, in specified sources
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageList) ([]Dependency, error) {
missing := make([]Dependency, 0, 128)
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packages {
if !p.MatchesArchitecture(arch) {
continue
}
for _, dep := range p.GetDependencies(options) {
variants, err := ParseDependencyVariants(dep)
if err != nil {
return nil, fmt.Errorf("unable to process package %s: %s", p, err)
}
variants = depSliceDeduplicate(variants)
variantsMissing := make([]Dependency, 0, len(variants))
missingCount := 0
for _, dep := range variants {
dep.Architecture = arch
hash := dep.Hash()
r, ok := cache[hash]
if ok {
if !r {
missingCount++
}
continue
}
if sources.Search(dep) == nil {
variantsMissing = append(variantsMissing, dep)
missingCount++
} else {
cache[hash] = true
}
}
if options&DepFollowAllVariants == DepFollowAllVariants {
missing = append(missing, variantsMissing...)
for _, dep := range variantsMissing {
cache[dep.Hash()] = false
}
} else {
if missingCount == len(variants) {
missing = append(missing, variantsMissing...)
for _, dep := range variantsMissing {
cache[dep.Hash()] = false
}
}
}
}
}
}
return missing, nil
}
// Swap swaps two packages in index
func (l *PackageList) Swap(i, j int) {
l.packagesIndex[i], l.packagesIndex[j] = l.packagesIndex[j], l.packagesIndex[i]
}
// Compare compares two names in lexographical order
func (l *PackageList) Less(i, j int) bool {
return l.packagesIndex[i].Name < l.packagesIndex[j].Name
}
// PrepareIndex prepares list for indexing
func (l *PackageList) PrepareIndex() {
l.packagesIndex = make([]*Package, l.Len())
l.providesIndex = make(map[string][]*Package, 128)
i := 0
for _, p := range l.packages {
l.packagesIndex[i] = p
i++
for _, provides := range p.Provides {
l.providesIndex[provides] = append(l.providesIndex[provides], p)
}
}
sort.Sort(l)
l.indexed = true
}
// Search searches package index for specified package
func (l *PackageList) Search(dep Dependency) *Package {
if !l.indexed {
panic("list not indexed, can't search")
}
if dep.Relation == VersionDontCare {
for _, p := range l.providesIndex[dep.Pkg] {
if p.MatchesArchitecture(dep.Architecture) {
return p
}
}
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= dep.Pkg })
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
p := l.packagesIndex[i]
if p.MatchesArchitecture(dep.Architecture) {
if dep.Relation == VersionDontCare {
return p
}
r := CompareVersions(p.Version, dep.Version)
switch dep.Relation {
case VersionEqual:
if r == 0 {
return p
}
case VersionLess:
if r < 0 {
return p
}
case VersionGreater:
if r > 0 {
return p
}
case VersionLessOrEqual:
if r <= 0 {
return p
}
case VersionGreaterOrEqual:
if r >= 0 {
return p
}
}
}
i++
}
return nil
}
// PackageRefList is a list of keys of packages, this is basis for snapshot // PackageRefList is a list of keys of packages, this is basis for snapshot
// and similar stuff // and similar stuff
// //
// Refs are sorted in lexographical order // Refs are sorted in lexicographical order
type PackageRefList struct { type PackageRefList struct {
// List of package keys // List of package keys
Refs [][]byte Refs [][]byte
} }
// Verify interface
var (
_ sort.Interface = &PackageRefList{}
)
// NewPackageRefListFromPackageList creates PackageRefList from PackageList // NewPackageRefListFromPackageList creates PackageRefList from PackageList
func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList { func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList {
reflist := &PackageRefList{} reflist := &PackageRefList{}
@@ -118,3 +415,163 @@ func (l *PackageRefList) ForEach(handler func([]byte) error) error {
} }
return err return err
} }
// PackageDiff is a difference between two packages in a list.
//
// If left & right are present, difference is in package version
// If left is nil, package is present only in right
// If right is nil, package is present only in left
type PackageDiff struct {
Left, Right *Package
}
// PackageDiffs is a list of PackageDiff records
type PackageDiffs []PackageDiff
// Diff calculates difference between two reflists
func (l *PackageRefList) Diff(r *PackageRefList, packageCollection *PackageCollection) (result PackageDiffs, err error) {
result = make(PackageDiffs, 0, 128)
// pointer to left and right reflists
il, ir := 0, 0
// length of reflists
ll, lr := l.Len(), r.Len()
// cached loaded packages on the left & right
pl, pr := (*Package)(nil), (*Package)(nil)
// until we reached end of both lists
for il < ll || ir < lr {
// if we've exhausted left list, pull the rest from the right
if il == ll {
pr, err = packageCollection.ByKey(r.Refs[ir])
if err != nil {
return nil, err
}
result = append(result, PackageDiff{Left: nil, Right: pr})
ir++
continue
}
// if we've exhausted right list, pull the rest from the left
if ir == lr {
pl, err = packageCollection.ByKey(l.Refs[il])
if err != nil {
return nil, err
}
result = append(result, PackageDiff{Left: pl, Right: nil})
il++
continue
}
// refs on both sides are present, load them
rl, rr := l.Refs[il], r.Refs[ir]
// compare refs
rel := bytes.Compare(rl, rr)
if rel == 0 {
// refs are identical, so are packages, advance pointer
il++
ir++
pl, pr = nil, nil
} else {
// load pl & pr if they haven't been loaded before
if pl == nil {
pl, err = packageCollection.ByKey(rl)
if err != nil {
return nil, err
}
}
if pr == nil {
pr, err = packageCollection.ByKey(rr)
if err != nil {
return nil, err
}
}
// 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
if rel < 0 {
result = append(result, PackageDiff{Left: pl, Right: nil})
il++
pl = nil
} else {
result = append(result, PackageDiff{Left: nil, Right: pr})
ir++
pr = nil
}
}
}
}
return
}
// Merge merges reflist r into current reflist. Merge replaces matching packages (by architecture/name)
// with reference from r.
func (l *PackageRefList) Merge(r *PackageRefList) (result *PackageRefList) {
// pointer to left and right reflists
il, ir := 0, 0
// length of reflists
ll, lr := l.Len(), r.Len()
result = &PackageRefList{}
result.Refs = make([][]byte, 0, ll+lr)
// until we reached end of both lists
for il < ll || ir < lr {
// if we've exhausted left list, pull the rest from the right
if il == ll {
result.Refs = append(result.Refs, r.Refs[ir:]...)
break
}
// if we've exhausted right list, pull the rest from the left
if ir == lr {
result.Refs = append(result.Refs, l.Refs[il:]...)
break
}
// refs on both sides are present, load them
rl, rr := l.Refs[il], r.Refs[ir]
// compare refs
rel := bytes.Compare(rl, rr)
if rel == 0 {
// refs are identical, so are packages, advance pointer
result.Refs = append(result.Refs, l.Refs[il])
il++
ir++
} else {
partsL := bytes.Split(rl, []byte(" "))
archL, nameL := partsL[0][1:], partsL[1]
partsR := bytes.Split(rr, []byte(" "))
archR, nameR := partsR[0][1:], partsR[1]
if bytes.Compare(archL, archR) == 0 && bytes.Compare(nameL, nameR) == 0 {
// override with package from the right
result.Refs = append(result.Refs, r.Refs[ir])
il++
ir++
} else {
// otherwise append smallest of two
if rel < 0 {
result.Refs = append(result.Refs, l.Refs[il])
il++
} else {
result.Refs = append(result.Refs, r.Refs[ir])
ir++
}
}
}
}
return
}
+328
View File
@@ -2,12 +2,19 @@ package debian
import ( import (
"errors" "errors"
"github.com/smira/aptly/database"
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"sort"
) )
type PackageListSuite struct { type PackageListSuite struct {
// Simple list with "real" packages from stanzas
list *PackageList list *PackageList
p1, p2, p3, p4, p5, p6 *Package p1, p2, p3, p4, p5, p6 *Package
// Mocked packages in list
packages []*Package
il *PackageList
} }
var _ = Suite(&PackageListSuite{}) var _ = Suite(&PackageListSuite{})
@@ -29,6 +36,27 @@ func (s *PackageListSuite) SetUpTest(c *C) {
stanza = packageStanza.Copy() stanza = packageStanza.Copy()
stanza["Version"] = "99.1" stanza["Version"] = "99.1"
s.p6 = NewPackageFromControlFile(stanza) s.p6 = NewPackageFromControlFile(stanza)
s.il = NewPackageList()
s.packages = []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}},
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386", Provides: []string{"package-installer"}},
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all", PreDepends: []string{"dpkg (>= 1.6)"}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "mailer", Version: "3.5.8", Architecture: "i386", Provides: []string{"mail-agent"}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "amd64", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "arm", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9) | libx (>= 1.5)", "data (>= 1.0) | mail-agent"}},
&Package{Name: "app", Version: "1.0", Architecture: "s390", PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "aa", Version: "2.0-1", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64", Provides: []string{"package-installer"}},
&Package{Name: "libx", Version: "1.5", Architecture: "arm", PreDepends: []string{"dpkg (>= 1.6)"}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "arm", Provides: []string{"package-installer"}},
}
for _, p := range s.packages {
s.il.Add(p)
}
s.il.PrepareIndex()
} }
func (s *PackageListSuite) TestAddLen(c *C) { func (s *PackageListSuite) TestAddLen(c *C) {
@@ -42,6 +70,73 @@ func (s *PackageListSuite) TestAddLen(c *C) {
c.Check(s.list.Add(s.p4), ErrorMatches, "conflict in package.*") c.Check(s.list.Add(s.p4), ErrorMatches, "conflict in package.*")
} }
func (s *PackageListSuite) TestRemove(c *C) {
c.Check(s.list.Add(s.p1), IsNil)
c.Check(s.list.Add(s.p3), IsNil)
c.Check(s.list.Len(), Equals, 2)
s.list.Remove(s.p1)
c.Check(s.list.Len(), Equals, 1)
}
func (s *PackageListSuite) TestAddWhenIndexed(c *C) {
c.Check(s.list.Len(), Equals, 0)
s.list.PrepareIndex()
c.Check(s.list.Add(&Package{Name: "a1st", Version: "1.0", Architecture: "i386", Provides: []string{"fa", "fb"}}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
c.Check(s.list.Add(&Package{Name: "c3rd", Version: "1.0", Architecture: "i386", Provides: []string{"fa"}}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.packagesIndex[1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
c.Check(s.list.Add(&Package{Name: "b2nd", Version: "1.0", Architecture: "i386"}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.packagesIndex[1].Name, Equals, "b2nd")
c.Check(s.list.packagesIndex[2].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
}
func (s *PackageListSuite) TestRemoveWhenIndexed(c *C) {
s.il.Remove(s.packages[0])
names := make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "libx", "mailer"})
s.il.Remove(s.packages[4])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["mail-agent"], DeepEquals, []*Package{})
s.il.Remove(s.packages[9])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["package-installer"], HasLen, 2)
s.il.Remove(s.packages[1])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "libx"})
c.Check(s.il.providesIndex["package-installer"], DeepEquals, []*Package{s.packages[11]})
}
func (s *PackageListSuite) TestForeach(c *C) { func (s *PackageListSuite) TestForeach(c *C) {
s.list.Add(s.p1) s.list.Add(s.p1)
s.list.Add(s.p3) s.list.Add(s.p3)
@@ -65,6 +160,114 @@ func (s *PackageListSuite) TestForeach(c *C) {
} }
func (s *PackageListSuite) TestIndex(c *C) {
c.Check(len(s.il.providesIndex), Equals, 2)
c.Check(len(s.il.providesIndex["mail-agent"]), Equals, 1)
c.Check(len(s.il.providesIndex["package-installer"]), Equals, 3)
c.Check(s.il.packagesIndex[0], Equals, s.packages[8])
}
func (s *PackageListSuite) TestAppend(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
err := s.list.Append(s.il)
c.Check(err, IsNil)
c.Check(s.list.Len(), Equals, 14)
list := NewPackageList()
list.Add(s.p4)
err = s.list.Append(list)
c.Check(err, ErrorMatches, "conflict.*")
s.list.PrepareIndex()
c.Check(func() { s.list.Append(s.il) }, Panics, "Append not supported when indexed")
}
func (s *PackageListSuite) TestSearch(c *C) {
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}) }, Panics, "list not indexed, can't search")
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}), Equals, s.packages[4])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}), IsNil)
}
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
missing, err := s.il.VerifyDependencies(0, []string{"i386"}, s.il)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(0, []string{"i386", "amd64"}, s.il)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
missing, err = s.il.VerifyDependencies(0, []string{"arm"}, s.il)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(DepFollowAllVariants, []string{"arm"}, s.il)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "arm"},
Dependency{Pkg: "mail-agent", Relation: VersionDontCare, Version: "", Architecture: "arm"}})
_, err = s.il.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.il)
c.Check(err, ErrorMatches, "unable to process package app-1.0_s390:.*")
}
func (s *PackageListSuite) TestArchitectures(c *C) {
archs := s.il.Architectures()
sort.Strings(archs)
c.Check(archs, DeepEquals, []string{"amd64", "arm", "i386", "s390"})
}
func (s *PackageListSuite) TestNewPackageListFromRefList(c *C) {
db, _ := database.OpenDB(c.MkDir())
coll := NewPackageCollection(db)
coll.Update(s.p1)
coll.Update(s.p3)
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
_, err := NewPackageListFromRefList(reflist, coll)
c.Assert(err, ErrorMatches, "unable to load package with key.*")
coll.Update(s.p5)
coll.Update(s.p6)
list, err := NewPackageListFromRefList(reflist, coll)
c.Assert(err, IsNil)
c.Check(list.Len(), Equals, 4)
c.Check(list.Add(s.p4), ErrorMatches, "conflict in package.*")
}
func (s *PackageListSuite) TestNewPackageRefList(c *C) { func (s *PackageListSuite) TestNewPackageRefList(c *C) {
s.list.Add(s.p1) s.list.Add(s.p1)
s.list.Add(s.p3) s.list.Add(s.p3)
@@ -119,3 +322,128 @@ func (s *PackageListSuite) TestPackageRefListForeach(c *C) {
c.Check(err, Equals, e) c.Check(err, Equals, e)
} }
func (s *PackageListSuite) TestDiff(c *C) {
db, _ := database.OpenDB(c.MkDir())
coll := NewPackageCollection(db)
packages := []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386"}, //0
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386"}, //1
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all"}, //2
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386"}, //3
&Package{Name: "app", Version: "1.1~bp2", Architecture: "i386"}, //4
&Package{Name: "app", Version: "1.1~bp2", Architecture: "amd64"}, //5
&Package{Name: "xyz", Version: "3.0", Architecture: "sparc"}, //6
}
for _, p := range packages {
coll.Update(p)
}
listA := NewPackageList()
listA.Add(packages[0])
listA.Add(packages[1])
listA.Add(packages[2])
listA.Add(packages[3])
listA.Add(packages[6])
listB := NewPackageList()
listB.Add(packages[0])
listB.Add(packages[2])
listB.Add(packages[4])
listB.Add(packages[5])
reflistA := NewPackageRefListFromPackageList(listA)
reflistB := NewPackageRefListFromPackageList(listB)
diffAA, err := reflistA.Diff(reflistA, coll)
c.Check(err, IsNil)
c.Check(diffAA, HasLen, 0)
diffAB, err := reflistA.Diff(reflistB, coll)
c.Check(err, IsNil)
c.Check(diffAB, HasLen, 4)
c.Check(diffAB[0].Left, IsNil)
c.Check(diffAB[0].Right.String(), Equals, "app-1.1~bp2_amd64")
c.Check(diffAB[1].Left.String(), Equals, "app-1.1~bp1_i386")
c.Check(diffAB[1].Right.String(), Equals, "app-1.1~bp2_i386")
c.Check(diffAB[2].Left.String(), Equals, "dpkg-1.7_i386")
c.Check(diffAB[2].Right, IsNil)
c.Check(diffAB[3].Left.String(), Equals, "xyz-3.0_sparc")
c.Check(diffAB[3].Right, IsNil)
diffBA, err := reflistB.Diff(reflistA, coll)
c.Check(err, IsNil)
c.Check(diffBA, HasLen, 4)
c.Check(diffBA[0].Right, IsNil)
c.Check(diffBA[0].Left.String(), Equals, "app-1.1~bp2_amd64")
c.Check(diffBA[1].Right.String(), Equals, "app-1.1~bp1_i386")
c.Check(diffBA[1].Left.String(), Equals, "app-1.1~bp2_i386")
c.Check(diffBA[2].Right.String(), Equals, "dpkg-1.7_i386")
c.Check(diffBA[2].Left, IsNil)
c.Check(diffBA[3].Right.String(), Equals, "xyz-3.0_sparc")
c.Check(diffBA[3].Left, IsNil)
}
func (s *PackageListSuite) TestMerge(c *C) {
db, _ := database.OpenDB(c.MkDir())
coll := NewPackageCollection(db)
packages := []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386"}, //0
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386"}, //1
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all"}, //2
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386"}, //3
&Package{Name: "app", Version: "1.1~bp2", Architecture: "i386"}, //4
&Package{Name: "app", Version: "1.1~bp2", Architecture: "amd64"}, //5
&Package{Name: "dpkg", Version: "1.0", Architecture: "i386"}, //6
&Package{Name: "xyz", Version: "1.0", Architecture: "sparc"}, //7
}
for _, p := range packages {
coll.Update(p)
}
listA := NewPackageList()
listA.Add(packages[0])
listA.Add(packages[1])
listA.Add(packages[2])
listA.Add(packages[3])
listA.Add(packages[7])
listB := NewPackageList()
listB.Add(packages[0])
listB.Add(packages[2])
listB.Add(packages[4])
listB.Add(packages[5])
listB.Add(packages[6])
reflistA := NewPackageRefListFromPackageList(listA)
reflistB := NewPackageRefListFromPackageList(listB)
mergeAB := reflistA.Merge(reflistB)
mergeBA := reflistB.Merge(reflistA)
toStrSlice := func(reflist *PackageRefList) (result []string) {
result = make([]string, reflist.Len())
for i, r := range reflist.Refs {
result[i] = string(r)
}
return
}
c.Check(toStrSlice(mergeAB), DeepEquals,
[]string{"Pall data 1.1~bp1", "Pamd64 app 1.1~bp2", "Pi386 app 1.1~bp2", "Pi386 dpkg 1.0", "Pi386 lib 1.0", "Psparc xyz 1.0"})
c.Check(toStrSlice(mergeBA), DeepEquals,
[]string{"Pall data 1.1~bp1", "Pamd64 app 1.1~bp2", "Pi386 app 1.1~bp1", "Pi386 dpkg 1.7", "Pi386 lib 1.0", "Psparc xyz 1.0"})
}
+185 -33
View File
@@ -7,29 +7,48 @@ import (
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
) )
// PackageFile is a single file entry in package
type PackageFile struct {
Filename string
Checksums utils.ChecksumInfo
}
// Verify that package file is present and correct
func (f *PackageFile) Verify(packageRepo *Repository) (bool, error) {
poolPath, err := packageRepo.PoolPath(f.Filename, f.Checksums.MD5)
if err != nil {
return false, err
}
st, err := os.Stat(poolPath)
if err != nil {
return false, nil
}
// verify size
// TODO: verify checksum if configured
return st.Size() == f.Checksums.Size, nil
}
// Package is single instance of Debian package // Package is single instance of Debian package
//
// TODO: support source & binary
type Package struct { type Package struct {
Name string Name string
Version string Version string
Filename string
Filesize int64
Architecture string Architecture string
Source string Source string
Provides []string
// Various dependencies // Various dependencies
Depends []string Depends []string
PreDepends []string PreDepends []string
Suggests []string Suggests []string
Recommends []string Recommends []string
// Hashsums of package contents // Files in package
HashMD5 string Files []PackageFile
HashSHA1 string
HashSHA256 string
// Extra information from stanza // Extra information from stanza
Extra Stanza Extra Stanza
} }
@@ -42,7 +61,11 @@ func parseDependencies(input Stanza, key string) []string {
delete(input, key) delete(input, key)
return strings.Split(value, ", ") result := strings.Split(value, ",")
for i := range result {
result[i] = strings.TrimSpace(result[i])
}
return result
} }
// NewPackageFromControlFile creates Package from parsed Debian control file // NewPackageFromControlFile creates Package from parsed Debian control file
@@ -50,30 +73,39 @@ func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{ result := &Package{
Name: input["Package"], Name: input["Package"],
Version: input["Version"], Version: input["Version"],
Filename: input["Filename"],
Architecture: input["Architecture"], Architecture: input["Architecture"],
Source: input["Source"], Source: input["Source"],
HashMD5: input["MD5sum"], Files: make([]PackageFile, 0, 1),
HashSHA1: input["SHA1"],
HashSHA256: input["SHA256"],
} }
delete(input, "Package") delete(input, "Package")
delete(input, "Version") delete(input, "Version")
delete(input, "Filename")
delete(input, "Architecture") delete(input, "Architecture")
delete(input, "Source") delete(input, "Source")
filesize, _ := strconv.ParseInt(input["Size"], 10, 64)
result.Files = append(result.Files, PackageFile{
Filename: input["Filename"],
Checksums: utils.ChecksumInfo{
Size: filesize,
MD5: input["MD5sum"],
SHA1: input["SHA1"],
SHA256: input["SHA256"],
},
})
delete(input, "Filename")
delete(input, "MD5sum") delete(input, "MD5sum")
delete(input, "SHA1") delete(input, "SHA1")
delete(input, "SHA256") delete(input, "SHA256")
result.Filesize, _ = strconv.ParseInt(input["Size"], 10, 64)
delete(input, "Size") delete(input, "Size")
result.Depends = parseDependencies(input, "Depends") result.Depends = parseDependencies(input, "Depends")
result.PreDepends = parseDependencies(input, "Pre-Depends") result.PreDepends = parseDependencies(input, "Pre-Depends")
result.Suggests = parseDependencies(input, "Suggests") result.Suggests = parseDependencies(input, "Suggests")
result.Recommends = parseDependencies(input, "Recommends") result.Recommends = parseDependencies(input, "Recommends")
result.Provides = parseDependencies(input, "Provides")
result.Extra = input result.Extra = input
@@ -82,7 +114,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
// Key returns unique key identifying package // Key returns unique key identifying package
func (p *Package) Key() []byte { func (p *Package) Key() []byte {
return []byte("P" + p.Name + " " + p.Version + " " + p.Architecture) return []byte("P" + p.Architecture + " " + p.Name + " " + p.Version)
} }
// Encode does msgpack encoding of Package // Encode does msgpack encoding of Package
@@ -106,23 +138,49 @@ func (p *Package) String() string {
return fmt.Sprintf("%s-%s_%s", p.Name, p.Version, p.Architecture) return fmt.Sprintf("%s-%s_%s", p.Name, p.Version, p.Architecture)
} }
// MatchesArchitecture checks whether packages matches specified architecture
func (p *Package) MatchesArchitecture(arch string) bool {
if p.Architecture == "all" {
return true
}
return p.Architecture == arch
}
// GetDependencies compiles list of dependenices by flags from options
func (p *Package) GetDependencies(options int) (dependencies []string) {
dependencies = make([]string, 0, 30)
dependencies = append(dependencies, p.Depends...)
dependencies = append(dependencies, p.PreDepends...)
if options&DepFollowRecommends == DepFollowRecommends {
dependencies = append(dependencies, p.Recommends...)
}
if options&DepFollowSuggests == DepFollowSuggests {
dependencies = append(dependencies, p.Suggests...)
}
return
}
// Stanza creates original stanza from package // Stanza creates original stanza from package
func (p *Package) Stanza() (result Stanza) { func (p *Package) Stanza() (result Stanza) {
result = p.Extra.Copy() result = p.Extra.Copy()
result["Package"] = p.Name result["Package"] = p.Name
result["Version"] = p.Version result["Version"] = p.Version
result["Filename"] = p.Filename result["Filename"] = p.Files[0].Filename
result["Architecture"] = p.Architecture result["Architecture"] = p.Architecture
result["Source"] = p.Source result["Source"] = p.Source
if p.HashMD5 != "" { if p.Files[0].Checksums.MD5 != "" {
result["MD5sum"] = p.HashMD5 result["MD5sum"] = p.Files[0].Checksums.MD5
} }
if p.HashSHA1 != "" { if p.Files[0].Checksums.SHA1 != "" {
result["SHA1"] = p.HashSHA1 result["SHA1"] = p.Files[0].Checksums.SHA1
} }
if p.HashSHA256 != "" { if p.Files[0].Checksums.SHA256 != "" {
result["SHA256"] = p.HashSHA256 result["SHA256"] = p.Files[0].Checksums.SHA256
} }
if p.Depends != nil { if p.Depends != nil {
@@ -137,29 +195,123 @@ func (p *Package) Stanza() (result Stanza) {
if p.Recommends != nil { if p.Recommends != nil {
result["Recommends"] = strings.Join(p.Recommends, ", ") result["Recommends"] = strings.Join(p.Recommends, ", ")
} }
if p.Provides != nil {
result["Provides"] = strings.Join(p.Provides, ", ")
}
result["Size"] = fmt.Sprintf("%d", p.Filesize) result["Size"] = fmt.Sprintf("%d", p.Files[0].Checksums.Size)
return return
} }
// Equals compares two packages to be identical // Equals compares two packages to be identical
func (p *Package) Equals(p2 *Package) bool { func (p *Package) Equals(p2 *Package) bool {
return p.Name == p2.Name && p.Version == p2.Version && p.Filename == p2.Filename && if len(p.Files) != len(p2.Files) {
return false
}
for i, f := range p.Files {
if p2.Files[i] != f {
return false
}
}
return p.Name == p2.Name && p.Version == p2.Version &&
p.Architecture == p2.Architecture && utils.StrSlicesEqual(p.Depends, p2.Depends) && p.Architecture == p2.Architecture && utils.StrSlicesEqual(p.Depends, p2.Depends) &&
utils.StrSlicesEqual(p.PreDepends, p2.PreDepends) && utils.StrSlicesEqual(p.Suggests, p2.Suggests) && utils.StrSlicesEqual(p.PreDepends, p2.PreDepends) && utils.StrSlicesEqual(p.Suggests, p2.Suggests) &&
utils.StrSlicesEqual(p.Recommends, p2.Recommends) && utils.StrMapsEqual(p.Extra, p2.Extra) && utils.StrSlicesEqual(p.Recommends, p2.Recommends) && utils.StrMapsEqual(p.Extra, p2.Extra) &&
p.Filesize == p2.Filesize && p.HashMD5 == p2.HashMD5 && p.HashSHA1 == p2.HashSHA1 && p.Source == p2.Source && utils.StrSlicesEqual(p.Provides, p2.Provides)
p.HashSHA256 == p2.HashSHA256 && p.Source == p2.Source
} }
// VerifyFile verifies integrity and existence of local files for the package // LinkFromPool links package file from pool to dist's pool location
func (p *Package) VerifyFile(filepath string) bool { func (p *Package) LinkFromPool(packageRepo *Repository, prefix string, component string) error {
st, err := os.Stat(filepath) poolDir, err := p.PoolDirectory()
if err != nil { if err != nil {
return false return err
} }
return st.Size() == p.Filesize
for i, f := range p.Files {
sourcePath, err := packageRepo.PoolPath(f.Filename, f.Checksums.MD5)
if err != nil {
return err
}
relPath, err := packageRepo.LinkFromPool(prefix, component, sourcePath, poolDir)
if err != nil {
return err
}
p.Files[i].Filename = relPath
}
return nil
}
// PoolDirectory returns directory in package pool for this package files
func (p *Package) PoolDirectory() (string, error) {
source := p.Source
if source == "" {
source = p.Name
}
if len(source) < 2 {
return "", fmt.Errorf("package source %s too short", source)
}
var subdir string
if strings.HasPrefix(source, "lib") {
subdir = source[:4]
} else {
subdir = source[:1]
}
return filepath.Join(subdir, source), nil
}
// PackageDownloadTask is a element of download queue for the package
type PackageDownloadTask struct {
RepoURI string
DestinationPath string
Size int64
}
// DownloadList returns list of missing package files for download in format
// [[srcpath, dstpath]]
func (p *Package) DownloadList(packageRepo *Repository) (result []PackageDownloadTask, err error) {
result = make([]PackageDownloadTask, 0, 1)
for _, f := range p.Files {
poolPath, err := packageRepo.PoolPath(f.Filename, f.Checksums.MD5)
if err != nil {
return nil, err
}
verified, err := f.Verify(packageRepo)
if err != nil {
return nil, err
}
if !verified {
result = append(result, PackageDownloadTask{RepoURI: f.Filename, DestinationPath: poolPath, Size: f.Checksums.Size})
}
}
return result, nil
}
// VerifyFiles verifies that all package files have neen correctly downloaded
func (p *Package) VerifyFiles(packageRepo *Repository) (result bool, err error) {
result = true
for _, f := range p.Files {
result, err = f.Verify(packageRepo)
if err != nil || !result {
return
}
}
return
} }
// PackageCollection does management of packages in DB // PackageCollection does management of packages in DB
+147 -7
View File
@@ -3,9 +3,11 @@ package debian
import ( import (
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"os"
"path/filepath"
) )
var packageStanza = Stanza{"Source": "alien-arena", "Depends": "libc6 (>= 2.7), alien-arena-data (>= 7.40)", "Filename": "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb", "SHA1": "46955e48cad27410a83740a21d766ce362364024", "SHA256": "eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5", "Priority": "extra", "Maintainer": "Debian Games Team <pkg-games-devel@lists.alioth.debian.org>", "Description": "Common files for Alien Arena client and server ALIEN ARENA is a standalone 3D first person online deathmatch shooter\n crafted from the original source code of Quake II and Quake III, released\n by id Software under the GPL license. With features including 32 bit\n graphics, new particle engine and effects, light blooms, reflective water,\n hi resolution textures and skins, hi poly models, stain maps, ALIEN ARENA\n pushes the envelope of graphical beauty rivaling today's top games.\n .\n This package installs the common files for Alien Arena.\n", "Homepage": "http://red.planetarena.org", "Tag": "role::app-data, role::shared-lib, special::auto-inst-parts", "Installed-Size": "456", "Version": "7.40-2", "Replaces": "alien-arena (<< 7.33-1)", "Size": "187518", "MD5sum": "1e8cba92c41420aa7baa8a5718d67122", "Package": "alien-arena-common", "Section": "contrib/games", "Architecture": "i386"} var packageStanza = Stanza{"Source": "alien-arena", "Pre-Depends": "dpkg (>= 1.6)", "Suggests": "alien-arena-mars", "Recommends": "aliean-arena-luna", "Depends": "libc6 (>= 2.7), alien-arena-data (>= 7.40)", "Filename": "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb", "SHA1": "46955e48cad27410a83740a21d766ce362364024", "SHA256": "eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5", "Priority": "extra", "Maintainer": "Debian Games Team <pkg-games-devel@lists.alioth.debian.org>", "Description": "Common files for Alien Arena client and server ALIEN ARENA is a standalone 3D first person online deathmatch shooter\n crafted from the original source code of Quake II and Quake III, released\n by id Software under the GPL license. With features including 32 bit\n graphics, new particle engine and effects, light blooms, reflective water,\n hi resolution textures and skins, hi poly models, stain maps, ALIEN ARENA\n pushes the envelope of graphical beauty rivaling today's top games.\n .\n This package installs the common files for Alien Arena.\n", "Homepage": "http://red.planetarena.org", "Tag": "role::app-data, role::shared-lib, special::auto-inst-parts", "Installed-Size": "456", "Version": "7.40-2", "Replaces": "alien-arena (<< 7.33-1)", "Size": "187518", "MD5sum": "1e8cba92c41420aa7baa8a5718d67122", "Package": "alien-arena-common", "Section": "contrib/games", "Architecture": "i386"}
type PackageSuite struct { type PackageSuite struct {
stanza Stanza stanza Stanza
@@ -17,22 +19,70 @@ func (s *PackageSuite) SetUpTest(c *C) {
s.stanza = packageStanza.Copy() s.stanza = packageStanza.Copy()
} }
func (s *PackageSuite) TestPackageFileVerify(c *C) {
packageRepo := NewRepository(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
poolPath, _ := packageRepo.PoolPath(p.Files[0].Filename, p.Files[0].Checksums.MD5)
result, err := p.Files[0].Verify(packageRepo)
c.Check(err, IsNil)
c.Check(result, Equals, false)
err = os.MkdirAll(filepath.Dir(poolPath), 0755)
c.Assert(err, IsNil)
file, err := os.Create(poolPath)
c.Assert(err, IsNil)
file.WriteString("abcde")
file.Close()
result, err = p.Files[0].Verify(packageRepo)
c.Check(err, IsNil)
c.Check(result, Equals, false)
result, err = p.VerifyFiles(packageRepo)
c.Check(err, IsNil)
c.Check(result, Equals, false)
p.Files[0].Checksums.Size = 5
result, err = p.Files[0].Verify(packageRepo)
c.Check(err, IsNil)
c.Check(result, Equals, true)
result, err = p.VerifyFiles(packageRepo)
c.Check(err, IsNil)
c.Check(result, Equals, true)
}
func (s *PackageSuite) TestNewFromPara(c *C) { func (s *PackageSuite) TestNewFromPara(c *C) {
p := NewPackageFromControlFile(s.stanza) p := NewPackageFromControlFile(s.stanza)
c.Check(p.Name, Equals, "alien-arena-common") c.Check(p.Name, Equals, "alien-arena-common")
c.Check(p.Version, Equals, "7.40-2") c.Check(p.Version, Equals, "7.40-2")
c.Check(p.Architecture, Equals, "i386") c.Check(p.Architecture, Equals, "i386")
c.Check(p.Filename, Equals, "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb") c.Check(p.Provides, DeepEquals, []string(nil))
c.Check(p.Files, HasLen, 1)
c.Check(p.Files[0].Filename, Equals, "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb")
c.Check(p.Files[0].Checksums.Size, Equals, int64(187518))
c.Check(p.Files[0].Checksums.MD5, Equals, "1e8cba92c41420aa7baa8a5718d67122")
c.Check(p.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"}) c.Check(p.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"})
c.Check(p.Suggests, IsNil) }
c.Check(p.Filesize, Equals, int64(187518))
func (s *PackageSuite) TestWithProvides(c *C) {
s.stanza["Provides"] = "arena"
p := NewPackageFromControlFile(s.stanza)
c.Check(p.Name, Equals, "alien-arena-common")
c.Check(p.Provides, DeepEquals, []string{"arena"})
st := p.Stanza()
c.Check(st["Provides"], Equals, "arena")
} }
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("Palien-arena-common 7.40-2 i386")) c.Check(p.Key(), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
} }
func (s *PackageSuite) TestEncodeDecode(c *C) { func (s *PackageSuite) TestEncodeDecode(c *C) {
@@ -60,12 +110,102 @@ func (s *PackageSuite) TestString(c *C) {
func (s *PackageSuite) TestEquals(c *C) { func (s *PackageSuite) TestEquals(c *C) {
p := NewPackageFromControlFile(s.stanza) p := NewPackageFromControlFile(s.stanza)
stanza2 := packageStanza.Copy() p2 := NewPackageFromControlFile(packageStanza.Copy())
p2 := NewPackageFromControlFile(stanza2)
c.Check(p.Equals(p2), Equals, true) c.Check(p.Equals(p2), Equals, true)
p2.Depends = []string{"package1"} p2.Depends = []string{"package1"}
c.Check(p.Equals(p2), Equals, false) c.Check(p.Equals(p2), Equals, false)
p2 = NewPackageFromControlFile(packageStanza.Copy())
p2.Files[0].Checksums.MD5 = "abcdefabcdef"
c.Check(p.Equals(p2), Equals, false)
}
func (s *PackageSuite) TestMatchesArchitecture(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.MatchesArchitecture("i386"), Equals, true)
c.Check(p.MatchesArchitecture("amd64"), Equals, false)
s.stanza = packageStanza.Copy()
s.stanza["Architecture"] = "all"
p = NewPackageFromControlFile(s.stanza)
c.Check(p.MatchesArchitecture("i386"), Equals, true)
c.Check(p.MatchesArchitecture("amd64"), Equals, true)
}
func (s *PackageSuite) TestGetDependencies(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.GetDependencies(0), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)"})
c.Check(p.GetDependencies(DepFollowSuggests), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)", "alien-arena-mars"})
c.Check(p.GetDependencies(DepFollowSuggests|DepFollowRecommends), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)", "aliean-arena-luna", "alien-arena-mars"})
}
func (s *PackageSuite) TestPoolDirectory(c *C) {
p := NewPackageFromControlFile(s.stanza)
dir, err := p.PoolDirectory()
c.Check(err, IsNil)
c.Check(dir, Equals, "a/alien-arena")
p = NewPackageFromControlFile(packageStanza.Copy())
p.Source = ""
dir, err = p.PoolDirectory()
c.Check(err, IsNil)
c.Check(dir, Equals, "a/alien-arena-common")
p = NewPackageFromControlFile(packageStanza.Copy())
p.Source = "libarena"
dir, err = p.PoolDirectory()
c.Check(err, IsNil)
c.Check(dir, Equals, "liba/libarena")
p = NewPackageFromControlFile(packageStanza.Copy())
p.Source = "l"
_, err = p.PoolDirectory()
c.Check(err, ErrorMatches, ".* too short")
}
func (s *PackageSuite) TestLinkFromPool(c *C) {
packageRepo := NewRepository(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
poolPath, _ := packageRepo.PoolPath(p.Files[0].Filename, p.Files[0].Checksums.MD5)
err := os.MkdirAll(filepath.Dir(poolPath), 0755)
c.Assert(err, IsNil)
file, err := os.Create(poolPath)
c.Assert(err, IsNil)
file.Close()
err = p.LinkFromPool(packageRepo, "", "non-free")
c.Check(err, IsNil)
c.Check(p.Files[0].Filename, Equals, "pool/non-free/a/alien-arena/alien-arena-common_7.40-2_i386.deb")
}
func (s *PackageSuite) TestDownloadList(c *C) {
packageRepo := NewRepository(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
p.Files[0].Checksums.Size = 5
poolPath, _ := packageRepo.PoolPath(p.Files[0].Filename, p.Files[0].Checksums.MD5)
list, err := p.DownloadList(packageRepo)
c.Check(err, IsNil)
c.Check(list, DeepEquals, []PackageDownloadTask{
PackageDownloadTask{
RepoURI: "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb",
DestinationPath: poolPath,
Size: 5}})
err = os.MkdirAll(filepath.Dir(poolPath), 0755)
c.Assert(err, IsNil)
file, err := os.Create(poolPath)
c.Assert(err, IsNil)
file.WriteString("abcde")
file.Close()
list, err = p.DownloadList(packageRepo)
c.Check(err, IsNil)
c.Check(list, DeepEquals, []PackageDownloadTask{})
} }
type PackageCollectionSuite struct { type PackageCollectionSuite struct {
+233 -27
View File
@@ -2,8 +2,13 @@ package debian
import ( import (
"bufio" "bufio"
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt" "fmt"
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"log"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@@ -11,6 +16,8 @@ import (
// 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
UUID string
// 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
@@ -24,15 +31,63 @@ type PublishedRepo struct {
} }
// NewPublishedRepo creates new published repository // NewPublishedRepo creates new published repository
func NewPublishedRepo(prefix string, distribution string, component string, architectures []string, snapshot *Snapshot) *PublishedRepo { func NewPublishedRepo(prefix string, distribution string, component string, architectures []string, snapshot *Snapshot) (*PublishedRepo, error) {
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 _, component := range strings.Split(prefix, "/") {
if component == ".." || component == "dists" || component == "pool" {
return nil, fmt.Errorf("invalid prefix %s", prefix)
}
}
return &PublishedRepo{ return &PublishedRepo{
UUID: uuid.New(),
Prefix: prefix, Prefix: prefix,
Distribution: distribution, Distribution: distribution,
Component: component, Component: component,
Architectures: architectures, Architectures: architectures,
SnapshotUUID: snapshot.UUID, SnapshotUUID: snapshot.UUID,
snapshot: snapshot, snapshot: snapshot,
}, nil
}
// String returns human-readable represenation of PublishedRepo
func (p *PublishedRepo) String() string {
var archs string
if len(p.Architectures) > 0 {
archs = fmt.Sprintf(" [%s]", strings.Join(p.Architectures, ", "))
} }
return fmt.Sprintf("%s/%s (%s)%s publishes %s", p.Prefix, p.Distribution, p.Component, archs, p.snapshot.String())
}
// Key returns unique key identifying PublishedRepo
func (p *PublishedRepo) Key() []byte {
return []byte("U" + p.Prefix + ">>" + p.Distribution)
}
// Encode does msgpack encoding of PublishedRepo
func (p *PublishedRepo) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(p)
return buf.Bytes()
}
// Decode decodes msgpack representation into PublishedRepo
func (p *PublishedRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(p)
} }
// 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
@@ -48,15 +103,7 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
} }
// Load all packages // Load all packages
list := NewPackageList() list, err := NewPackageListFromRefList(p.snapshot.RefList(), packageCollection)
err = p.snapshot.RefList().ForEach(func(key []byte) error {
pkg, err := packageCollection.ByKey(key)
if err != nil {
return err
}
return list.Add(pkg)
})
if err != nil { if err != nil {
return fmt.Errorf("unable to load packages: %s", err) return fmt.Errorf("unable to load packages: %s", err)
} }
@@ -65,14 +112,8 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
return fmt.Errorf("repository is empty, can't publish") return fmt.Errorf("repository is empty, can't publish")
} }
if p.Architectures == nil { if len(p.Architectures) == 0 {
p.Architectures = make([]string, 0, 10) p.Architectures = list.Architectures()
list.ForEach(func(pkg *Package) error {
if pkg.Architecture != "all" && !utils.StrSliceHasItem(p.Architectures, pkg.Architecture) {
p.Architectures = append(p.Architectures, pkg.Architecture)
}
return nil
})
} }
if len(p.Architectures) == 0 { if len(p.Architectures) == 0 {
@@ -97,19 +138,12 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
bufWriter := bufio.NewWriter(packagesFile) bufWriter := bufio.NewWriter(packagesFile)
err = list.ForEach(func(pkg *Package) error { err = list.ForEach(func(pkg *Package) error {
if pkg.Architecture == arch || pkg.Architecture == "all" { if pkg.MatchesArchitecture(arch) {
source := pkg.Source err = pkg.LinkFromPool(repo, p.Prefix, p.Component)
if source == "" {
source = pkg.Name
}
path, err := repo.LinkFromPool(p.Prefix, p.Component, pkg.Filename, pkg.HashMD5, source)
if err != nil { if err != nil {
return err return err
} }
pkg.Filename = path
err = pkg.Stanza().WriteTo(bufWriter) err = pkg.Stanza().WriteTo(bufWriter)
if err != nil { if err != nil {
return err return err
@@ -210,3 +244,175 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
return nil return nil
} }
// RemoveFiles removes files that were created by Publish
//
// It can remove prefix fully, and part of pool (for specific component)
func (p *PublishedRepo) RemoveFiles(repo *Repository, removePrefix, removePoolComponent bool) error {
if removePrefix {
err := repo.RemoveDirs(filepath.Join(p.Prefix, "dists"))
if err != nil {
return err
}
return repo.RemoveDirs(filepath.Join(p.Prefix, "pool"))
}
err := repo.RemoveDirs(filepath.Join(p.Prefix, "dists", p.Distribution))
if err != nil {
return err
}
if removePoolComponent {
err = repo.RemoveDirs(filepath.Join(p.Prefix, "pool", p.Component))
if err != nil {
return err
}
}
return nil
}
// PublishedRepoCollection does listing, updating/adding/deleting of PublishedRepos
type PublishedRepoCollection struct {
db database.Storage
list []*PublishedRepo
}
// NewPublishedRepoCollection loads PublishedRepos from DB and makes up collection
func NewPublishedRepoCollection(db database.Storage) *PublishedRepoCollection {
result := &PublishedRepoCollection{
db: db,
}
blobs := db.FetchByPrefix([]byte("U"))
result.list = make([]*PublishedRepo, 0, len(blobs))
for _, blob := range blobs {
r := &PublishedRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding published repo: %s\n", err)
} else {
result.list = append(result.list, r)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *PublishedRepoCollection) Add(repo *PublishedRepo) error {
if collection.CheckDuplicate(repo) != nil {
return fmt.Errorf("published repo with prefix/distribution %s/%s already exists", repo.Prefix, repo.Distribution)
}
err := collection.Update(repo)
if err != nil {
return err
}
collection.list = append(collection.list, repo)
return nil
}
// CheckDuplicate verifies that there's no published repo with the same name
func (collection *PublishedRepoCollection) CheckDuplicate(repo *PublishedRepo) *PublishedRepo {
for _, r := range collection.list {
if r.Prefix == repo.Prefix && r.Distribution == repo.Distribution {
return r
}
}
return nil
}
// Update stores updated information about repo in DB
func (collection *PublishedRepoCollection) Update(repo *PublishedRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
return nil
}
// LoadComplete loads additional information for remote repo
func (collection *PublishedRepoCollection) LoadComplete(repo *PublishedRepo, snapshotCollection *SnapshotCollection) error {
snapshot, err := snapshotCollection.ByUUID(repo.SnapshotUUID)
if err != nil {
return err
}
repo.snapshot = snapshot
return nil
}
// ByPrefixDistribution looks up repository by prefix & distribution
func (collection *PublishedRepoCollection) ByPrefixDistribution(prefix, distribution string) (*PublishedRepo, error) {
for _, r := range collection.list {
if r.Prefix == prefix && r.Distribution == distribution {
return r, nil
}
}
return nil, fmt.Errorf("published repo with prefix/distribution %s/%s not found", prefix, distribution)
}
// ByUUID looks up repository by uuid
func (collection *PublishedRepoCollection) ByUUID(uuid string) (*PublishedRepo, error) {
for _, r := range collection.list {
if r.UUID == uuid {
return r, nil
}
}
return nil, fmt.Errorf("published repo with uuid %s not found", uuid)
}
// ForEach runs method for each repository
func (collection *PublishedRepoCollection) ForEach(handler func(*PublishedRepo) error) error {
var err error
for _, r := range collection.list {
err = handler(r)
if err != nil {
return err
}
}
return err
}
// Len returns number of remote repos
func (collection *PublishedRepoCollection) Len() int {
return len(collection.list)
}
// Remove removes published repository, cleaning up directories, files
func (collection *PublishedRepoCollection) Remove(packageRepo *Repository, prefix, distribution string) error {
repo, err := collection.ByPrefixDistribution(prefix, distribution)
if err != nil {
return err
}
removePrefix := true
removePoolComponent := true
repoPosition := -1
for i, r := range collection.list {
if r == repo {
repoPosition = i
continue
}
if r.Prefix == repo.Prefix {
removePrefix = false
if r.Component == repo.Component {
removePoolComponent = false
}
}
}
err = repo.RemoveFiles(packageRepo, removePrefix, removePoolComponent)
if err != nil {
return err
}
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
return collection.db.Delete(repo.Key())
}
+365 -3
View File
@@ -1,6 +1,7 @@
package debian package debian
import ( import (
"errors"
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"os" "os"
@@ -25,6 +26,7 @@ type PublishedRepoSuite struct {
PackageListMixinSuite PackageListMixinSuite
repo *PublishedRepo repo *PublishedRepo
packageRepo *Repository packageRepo *Repository
snapshot *Snapshot
db database.Storage db database.Storage
packageCollection *PackageCollection packageCollection *PackageCollection
} }
@@ -41,22 +43,91 @@ func (s *PublishedRepoSuite) SetUpTest(c *C) {
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}) repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
repo.packageRefs = s.reflist repo.packageRefs = s.reflist
snapshot, _ := NewSnapshotFromRepository("snap", repo) s.snapshot, _ = NewSnapshotFromRepository("snap", repo)
s.repo = NewPublishedRepo("ppa", "squeeze", "main", nil, snapshot) s.repo, _ = NewPublishedRepo("ppa", "squeeze", "main", nil, s.snapshot)
s.packageCollection = NewPackageCollection(s.db) s.packageCollection = NewPackageCollection(s.db)
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)
poolPath, _ := s.packageRepo.PoolPath(s.p1.Filename, s.p1.HashMD5) poolPath, _ := s.packageRepo.PoolPath(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)
f, err := os.Create(poolPath) f, err := os.Create(poolPath)
c.Assert(err, IsNil) c.Assert(err, IsNil)
f.Close() f.Close()
} }
func (s *PublishedRepoSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
for _, t := range []struct {
prefix string
expected string
errorExpected string
}{
{
prefix: "ppa",
expected: "ppa",
},
{
prefix: "",
expected: ".",
},
{
prefix: "/",
expected: ".",
},
{
prefix: "//",
expected: ".",
},
{
prefix: "//ppa/",
expected: "ppa",
},
{
prefix: "ppa/..",
expected: ".",
},
{
prefix: "ppa/ubuntu/",
expected: "ppa/ubuntu",
},
{
prefix: "ppa/../ubuntu/",
expected: "ubuntu",
},
{
prefix: "../ppa/",
errorExpected: "invalid prefix .*",
},
{
prefix: "../ppa/../ppa/",
errorExpected: "invalid prefix .*",
},
{
prefix: "ppa/dists",
errorExpected: "invalid prefix .*",
},
{
prefix: "ppa/pool",
errorExpected: "invalid prefix .*",
},
} {
repo, err := NewPublishedRepo(t.prefix, "squeeze", "main", nil, s.snapshot)
if t.errorExpected != "" {
c.Check(err, ErrorMatches, t.errorExpected)
} else {
c.Check(repo.Prefix, Equals, t.expected)
}
}
}
func (s *PublishedRepoSuite) TestPublish(c *C) { func (s *PublishedRepoSuite) TestPublish(c *C) {
err := s.repo.Publish(s.packageRepo, s.packageCollection, &NullSigner{}) err := s.repo.Publish(s.packageRepo, s.packageCollection, &NullSigner{})
c.Assert(err, IsNil) c.Assert(err, IsNil)
@@ -93,3 +164,294 @@ func (s *PublishedRepoSuite) TestPublish(c *C) {
_, err = os.Stat(filepath.Join(s.packageRepo.RootPath, "public/ppa/pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb")) _, err = os.Stat(filepath.Join(s.packageRepo.RootPath, "public/ppa/pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb"))
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
func (s *PublishedRepoSuite) TestString(c *C) {
c.Check(s.repo.String(), Equals,
"ppa/squeeze (main) publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze")
repo, _ := NewPublishedRepo("", "squeeze", "main", nil, s.snapshot)
c.Check(repo.String(), Equals,
"./squeeze (main) publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze")
repo, _ = NewPublishedRepo("", "squeeze", "main", []string{"i386", "amd64"}, s.snapshot)
c.Check(repo.String(), Equals,
"./squeeze (main) [i386, amd64] publishes [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze")
}
func (s *PublishedRepoSuite) TestKey(c *C) {
c.Check(s.repo.Key(), DeepEquals, []byte("Uppa>>squeeze"))
}
func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
encoded := s.repo.Encode()
repo := &PublishedRepo{}
err := repo.Decode(encoded)
s.repo.snapshot = nil
c.Assert(err, IsNil)
c.Assert(repo, DeepEquals, s.repo)
}
type PublishedRepoCollectionSuite struct {
PackageListMixinSuite
db database.Storage
snapshotCollection *SnapshotCollection
collection *PublishedRepoCollection
snap1, snap2 *Snapshot
repo1, repo2, repo3 *PublishedRepo
}
var _ = Suite(&PublishedRepoCollectionSuite{})
func (s *PublishedRepoCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.snapshotCollection = NewSnapshotCollection(s.db)
s.snap1 = NewSnapshotFromPackageList("snap1", []*Snapshot{}, NewPackageList(), "desc1")
s.snap2 = NewSnapshotFromPackageList("snap2", []*Snapshot{}, NewPackageList(), "desc2")
s.snapshotCollection.Add(s.snap1)
s.snapshotCollection.Add(s.snap2)
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap1)
s.repo2, _ = NewPublishedRepo("", "anaconda", "main", []string{}, s.snap2)
s.repo3, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap2)
s.collection = NewPublishedRepoCollection(s.db)
}
func (s *PublishedRepoCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoCollectionSuite) TestAddByName(c *C) {
r, err := s.collection.ByPrefixDistribution("ppa", "anaconda")
c.Assert(err, ErrorMatches, "*.not found")
c.Assert(s.collection.Add(s.repo1), IsNil)
c.Assert(s.collection.Add(s.repo1), ErrorMatches, ".*already exists")
c.Assert(s.collection.CheckDuplicate(s.repo2), IsNil)
c.Assert(s.collection.Add(s.repo2), IsNil)
c.Assert(s.collection.Add(s.repo3), ErrorMatches, ".*already exists")
c.Assert(s.collection.CheckDuplicate(s.repo3), Equals, s.repo1)
r, err = s.collection.ByPrefixDistribution("ppa", "anaconda")
c.Assert(err, IsNil)
err = s.collection.LoadComplete(r, s.snapshotCollection)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, s.repo1.String())
collection := NewPublishedRepoCollection(s.db)
r, err = collection.ByPrefixDistribution("ppa", "anaconda")
c.Assert(err, IsNil)
err = s.collection.LoadComplete(r, s.snapshotCollection)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, s.repo1.String())
}
func (s *PublishedRepoCollectionSuite) TestByUUID(c *C) {
r, err := s.collection.ByUUID(s.repo1.UUID)
c.Assert(err, ErrorMatches, "*.not found")
c.Assert(s.collection.Add(s.repo1), IsNil)
r, err = s.collection.ByUUID(s.repo1.UUID)
c.Assert(err, IsNil)
err = s.collection.LoadComplete(r, s.snapshotCollection)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, s.repo1.String())
}
func (s *PublishedRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
c.Assert(s.collection.Update(s.repo1), IsNil)
collection := NewPublishedRepoCollection(s.db)
r, err := collection.ByPrefixDistribution("ppa", "anaconda")
c.Assert(err, IsNil)
c.Assert(r.snapshot, IsNil)
c.Assert(s.collection.LoadComplete(r, s.snapshotCollection), IsNil)
c.Assert(r.snapshot.UUID, Equals, s.repo1.snapshot.UUID)
}
func (s *PublishedRepoCollectionSuite) TestForEachAndLen(c *C) {
s.collection.Add(s.repo1)
count := 0
err := s.collection.ForEach(func(*PublishedRepo) error {
count++
return nil
})
c.Assert(count, Equals, 1)
c.Assert(err, IsNil)
c.Check(s.collection.Len(), Equals, 1)
e := errors.New("c")
err = s.collection.ForEach(func(*PublishedRepo) error {
return e
})
c.Assert(err, Equals, e)
}
type pathExistsChecker struct {
*CheckerInfo
}
var PathExists = &pathExistsChecker{
&CheckerInfo{Name: "PathExists", Params: []string{"path"}},
}
func (checker *pathExistsChecker) Check(params []interface{}, names []string) (result bool, error string) {
_, err := os.Stat(params[0].(string))
return err == nil, ""
}
type PublishedRepoRemoveSuite struct {
PackageListMixinSuite
db database.Storage
snapshotCollection *SnapshotCollection
collection *PublishedRepoCollection
packageRepo *Repository
snap1 *Snapshot
repo1, repo2, repo3, repo4 *PublishedRepo
}
var _ = Suite(&PublishedRepoRemoveSuite{})
func (s *PublishedRepoRemoveSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.snapshotCollection = NewSnapshotCollection(s.db)
s.snap1 = NewSnapshotFromPackageList("snap1", []*Snapshot{}, NewPackageList(), "desc1")
s.snapshotCollection.Add(s.snap1)
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", "main", []string{}, s.snap1)
s.repo2, _ = NewPublishedRepo("", "anaconda", "main", []string{}, s.snap1)
s.repo3, _ = NewPublishedRepo("ppa", "meduza", "main", []string{}, s.snap1)
s.repo4, _ = NewPublishedRepo("ppa", "osminog", "contrib", []string{}, s.snap1)
s.collection = NewPublishedRepoCollection(s.db)
s.collection.Add(s.repo1)
s.collection.Add(s.repo2)
s.collection.Add(s.repo3)
s.collection.Add(s.repo4)
s.packageRepo = NewRepository(c.MkDir())
s.packageRepo.MkDir("ppa/dists/anaconda")
s.packageRepo.MkDir("ppa/dists/meduza")
s.packageRepo.MkDir("ppa/dists/osminog")
s.packageRepo.MkDir("ppa/pool/main")
s.packageRepo.MkDir("ppa/pool/contrib")
s.packageRepo.MkDir("dists/anaconda")
s.packageRepo.MkDir("pool/main")
}
func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
s.repo1.RemoveFiles(s.packageRepo, false, false)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
s.repo1.RemoveFiles(s.packageRepo, false, true)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
s.repo1.RemoveFiles(s.packageRepo, true, true)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
s.repo2.RemoveFiles(s.packageRepo, true, true)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), Not(PathExists))
}
func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
err := s.collection.Remove(s.packageRepo, "ppa", "anaconda")
c.Check(err, IsNil)
_, err = s.collection.ByPrefixDistribution("ppa", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
collection := NewPublishedRepoCollection(s.db)
_, err = collection.ByPrefixDistribution("ppa", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), PathExists)
err = s.collection.Remove(s.packageRepo, "ppa", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
err = s.collection.Remove(s.packageRepo, "ppa", "meduza")
c.Check(err, IsNil)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/main"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveRepo3(c *C) {
err := s.collection.Remove(s.packageRepo, ".", "anaconda")
c.Check(err, IsNil)
_, err = s.collection.ByPrefixDistribution(".", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
collection := NewPublishedRepoCollection(s.db)
_, err = collection.ByPrefixDistribution(".", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.packageRepo.PublicPath(), "dists/"), Not(PathExists))
c.Check(filepath.Join(s.packageRepo.PublicPath(), "pool/"), Not(PathExists))
}
+36 -9
View File
@@ -148,6 +148,8 @@ func (repo *RemoteRepo) Fetch(d utils.Downloader) error {
func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository) error { func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository) error {
list := NewPackageList() list := NewPackageList()
fmt.Printf("Downloading & parsing package files...\n")
// Download and parse all Release files // Download and parse all Release files
for _, component := range repo.Components { for _, component := range repo.Components {
for _, architecture := range repo.Architectures { for _, architecture := range repo.Architectures {
@@ -175,6 +177,8 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
} }
} }
fmt.Printf("Saving packages to database...\n")
// Save package meta information to DB // Save package meta information to DB
err := list.ForEach(func(p *Package) error { err := list.ForEach(func(p *Package) error {
return packageCollection.Update(p) return packageCollection.Update(p)
@@ -184,30 +188,48 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
return fmt.Errorf("unable to save packages to db: %s", err) return fmt.Errorf("unable to save packages to db: %s", err)
} }
// Download all package files fmt.Printf("Building download queue...\n")
ch := make(chan error, list.Len())
// Build download queue
queued := make(map[string]PackageDownloadTask, list.Len())
count := 0 count := 0
downloadSize := int64(0)
err = list.ForEach(func(p *Package) error { err = list.ForEach(func(p *Package) error {
poolPath, err := packageRepo.PoolPath(p.Filename, p.HashMD5) list, err := p.DownloadList(packageRepo)
if err != nil { if err != nil {
return err return err
} }
if !p.VerifyFile(poolPath) { for _, task := range list {
d.Download(repo.PackageURL(p.Filename).String(), poolPath, ch) key := task.RepoURI + "-" + task.DestinationPath
count++ _, found := queued[key]
if !found {
count++
downloadSize += task.Size
queued[key] = task
}
} }
return nil return nil
}) })
if err != nil { if err != nil {
return fmt.Errorf("unable to download packages: %s", err) return fmt.Errorf("unable to build download queue: %s", err)
} }
errors := make([]string, 0) fmt.Printf("Download queue: %d items, %.2f GiB size\n", count, float64(downloadSize)/(1024.0*1024.0*1024.0))
// Download all package files
ch := make(chan error, len(queued))
for _, task := range queued {
d.Download(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch)
}
// Wait for all downloads to finish // Wait for all downloads to finish
errors := make([]string, 0)
for count > 0 { for count > 0 {
err = <-ch err = <-ch
if err != nil { if err != nil {
@@ -217,7 +239,7 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
} }
if len(errors) > 0 { if len(errors) > 0 {
return fmt.Errorf("download errors: %s", strings.Join(errors, ", ")) return fmt.Errorf("download errors:\n %s\n", strings.Join(errors, "\n "))
} }
repo.LastDownloadDate = time.Now() repo.LastDownloadDate = time.Now()
@@ -360,3 +382,8 @@ func (collection *RemoteRepoCollection) ForEach(handler func(*RemoteRepo) error)
} }
return err return err
} }
// Len returns number of remote repos
func (collection *RemoteRepoCollection) Len() int {
return len(collection.list)
}
+6 -3
View File
@@ -144,8 +144,9 @@ func (s *RemoteRepoSuite) TestDownload(c *C) {
pkg, err := s.packageCollection.ByKey(s.repo.packageRefs.Refs[0]) pkg, err := s.packageCollection.ByKey(s.repo.packageRefs.Refs[0])
c.Assert(err, IsNil) c.Assert(err, IsNil)
poolPath, _ := s.packageRepo.PoolPath(pkg.Filename, pkg.HashMD5) result, err := pkg.VerifyFiles(s.packageRepo)
c.Check(pkg.VerifyFile(poolPath), Equals, true) c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "amanda-client") c.Check(pkg.Name, Equals, "amanda-client")
} }
@@ -219,7 +220,7 @@ func (s *RemoteRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
c.Assert(r.NumPackages(), Equals, 3) c.Assert(r.NumPackages(), Equals, 3)
} }
func (s *RemoteRepoCollectionSuite) TestForEach(c *C) { func (s *RemoteRepoCollectionSuite) TestForEachAndLen(c *C) {
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}) repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
s.collection.Add(repo) s.collection.Add(repo)
@@ -231,6 +232,8 @@ func (s *RemoteRepoCollectionSuite) TestForEach(c *C) {
c.Assert(count, Equals, 1) c.Assert(count, Equals, 1)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Check(s.collection.Len(), Equals, 1)
e := errors.New("c") e := errors.New("c")
err = s.collection.ForEach(func(*RemoteRepo) error { err = s.collection.ForEach(func(*RemoteRepo) error {
+12 -22
View File
@@ -5,7 +5,6 @@ import (
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
// Repository directory structure: // Repository directory structure:
@@ -60,30 +59,21 @@ func (r *Repository) CreateFile(path string) (*os.File, error) {
return os.Create(filepath.Join(r.RootPath, "public", path)) return os.Create(filepath.Join(r.RootPath, "public", path))
} }
// RemoveDirs removes directory structure under public path
func (r *Repository) RemoveDirs(path string) error {
filepath := filepath.Join(r.RootPath, "public", path)
fmt.Printf("Removing %s...\n", filepath)
return os.RemoveAll(filepath)
}
// LinkFromPool links package file from pool to dist's pool location // LinkFromPool links package file from pool to dist's pool location
func (r *Repository) LinkFromPool(prefix string, component string, filename string, hashMD5 string, source string) (string, error) { func (r *Repository) LinkFromPool(prefix string, component string, sourcePath string, poolDirectory string) (string, error) {
sourcePath, err := r.PoolPath(filename, hashMD5) baseName := filepath.Base(sourcePath)
if err != nil {
return "", err
}
if len(source) < 2 { relPath := filepath.Join("pool", component, poolDirectory, baseName)
return "", fmt.Errorf("package source %s too short", source) poolPath := filepath.Join(r.RootPath, "public", prefix, "pool", component, poolDirectory)
}
var subdir string err := os.MkdirAll(poolPath, 0755)
if strings.HasPrefix(source, "lib") {
subdir = source[:4]
} else {
subdir = source[:1]
}
baseName := filepath.Base(filename)
relPath := filepath.Join("pool", component, subdir, source, baseName)
poolPath := filepath.Join(r.RootPath, "public", prefix, "pool", component, subdir, source)
err = os.MkdirAll(poolPath, 0755)
if err != nil { if err != nil {
return "", err return "", err
} }
+44 -19
View File
@@ -52,51 +52,76 @@ func (s *RepositorySuite) TestCreateFile(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
func (s *RepositorySuite) TestRemoveDirs(c *C) {
err := s.repo.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
file, err := s.repo.CreateFile("ppa/dists/squeeze/Release")
c.Assert(err, IsNil)
defer file.Close()
err = s.repo.RemoveDirs("ppa/dists/")
_, err = os.Stat(filepath.Join(s.repo.RootPath, "public/ppa/dists/squeeze/Release"))
c.Assert(err, NotNil)
c.Assert(os.IsNotExist(err), Equals, true)
}
func (s *RepositorySuite) TestLinkFromPool(c *C) { func (s *RepositorySuite) TestLinkFromPool(c *C) {
tests := []struct { tests := []struct {
packageFilename string prefix string
MD5 string component string
source string sourcePath string
poolDirectory string
expectedFilename string expectedFilename string
}{ }{
{ // package name regular { // package name regular
packageFilename: "pool/m/mars-invaders_1.03.deb", prefix: "",
MD5: "91b1a1480b90b9e269ca44d897b12575", component: "main",
source: "mars-invaders", sourcePath: "pool/01/ae/mars-invaders_1.03.deb",
poolDirectory: "m/mars-invaders",
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb", expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
}, },
{ // lib-like filename { // lib-like filename
packageFilename: "pool/libm/libmars-invaders_1.03.deb", prefix: "",
MD5: "12c2a1480b90b9e269ca44d897b12575", component: "main",
source: "libmars-invaders", sourcePath: "pool/01/ae/libmars-invaders_1.03.deb",
poolDirectory: "libm/libmars-invaders",
expectedFilename: "pool/main/libm/libmars-invaders/libmars-invaders_1.03.deb", expectedFilename: "pool/main/libm/libmars-invaders/libmars-invaders_1.03.deb",
}, },
{ // duplicate link, shouldn't panic { // duplicate link, shouldn't panic
packageFilename: "pool/m/mars-invaders_1.03.deb", prefix: "",
MD5: "91b1a1480b90b9e269ca44d897b12575", component: "main",
source: "mars-invaders", sourcePath: "pool/01/ae/mars-invaders_1.03.deb",
poolDirectory: "m/mars-invaders",
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb", expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
}, },
{ // prefix & component
prefix: "ppa",
component: "contrib",
sourcePath: "pool/01/ae/libmars-invaders_1.04.deb",
poolDirectory: "libm/libmars-invaders",
expectedFilename: "pool/contrib/libm/libmars-invaders/libmars-invaders_1.04.deb",
},
} }
for _, t := range tests { for _, t := range tests {
poolPath, err := s.repo.PoolPath(t.packageFilename, t.MD5) t.sourcePath = filepath.Join(s.repo.RootPath, t.sourcePath)
err := os.MkdirAll(filepath.Dir(t.sourcePath), 0755)
c.Assert(err, IsNil) c.Assert(err, IsNil)
err = os.MkdirAll(filepath.Dir(poolPath), 0755) file, err := os.Create(t.sourcePath)
c.Assert(err, IsNil)
file, err := os.Create(poolPath)
c.Assert(err, IsNil) c.Assert(err, IsNil)
file.Write([]byte("Contents")) file.Write([]byte("Contents"))
file.Close() file.Close()
path, err := s.repo.LinkFromPool("", "main", t.packageFilename, t.MD5, t.source) path, err := s.repo.LinkFromPool(t.prefix, t.component, t.sourcePath, t.poolDirectory)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(path, Equals, t.expectedFilename) c.Assert(path, Equals, t.expectedFilename)
st, err := os.Stat(filepath.Join(s.repo.RootPath, "public", t.expectedFilename)) st, err := os.Stat(filepath.Join(s.repo.RootPath, "public", t.prefix, t.expectedFilename))
c.Assert(err, IsNil) c.Assert(err, IsNil)
info := st.Sys().(*syscall.Stat_t) info := st.Sys().(*syscall.Stat_t)
+39
View File
@@ -46,6 +46,29 @@ func NewSnapshotFromRepository(name string, repo *RemoteRepo) (*Snapshot, error)
}, nil }, nil
} }
// NewSnapshotFromPackageList creates snapshot from PackageList
func NewSnapshotFromPackageList(name string, sources []*Snapshot, list *PackageList, description string) *Snapshot {
return NewSnapshotFromRefList(name, sources, NewPackageRefListFromPackageList(list), description)
}
// NewSnapshotFromRefList creates snapshot from PackageRefList
func NewSnapshotFromRefList(name string, sources []*Snapshot, list *PackageRefList, description string) *Snapshot {
sourceUUIDs := make([]string, len(sources))
for i := range sources {
sourceUUIDs[i] = sources[i].UUID
}
return &Snapshot{
UUID: uuid.New(),
Name: name,
CreatedAt: time.Now(),
SourceKind: "snapshot",
SourceIDs: sourceUUIDs,
Description: description,
packageRefs: list,
}
}
// String returns string representation of snapshot // String returns string representation of snapshot
func (s *Snapshot) String() string { func (s *Snapshot) String() string {
return fmt.Sprintf("[%s]: %s", s.Name, s.Description) return fmt.Sprintf("[%s]: %s", s.Name, s.Description)
@@ -161,6 +184,16 @@ func (collection *SnapshotCollection) ByName(name string) (*Snapshot, error) {
return nil, fmt.Errorf("snapshot with name %s not found", name) return nil, fmt.Errorf("snapshot with name %s not found", name)
} }
// ByUUID looks up snapshot by UUID
func (collection *SnapshotCollection) ByUUID(uuid string) (*Snapshot, error) {
for _, s := range collection.list {
if s.UUID == uuid {
return s, nil
}
}
return nil, fmt.Errorf("snapshot with uuid %s not found", uuid)
}
// ForEach runs method for each snapshot // ForEach runs method for each snapshot
func (collection *SnapshotCollection) ForEach(handler func(*Snapshot) error) error { func (collection *SnapshotCollection) ForEach(handler func(*Snapshot) error) error {
var err error var err error
@@ -172,3 +205,9 @@ func (collection *SnapshotCollection) ForEach(handler func(*Snapshot) error) err
} }
return err return err
} }
// Len returns number of snapshots in collection
// ForEach runs method for each snapshot
func (collection *SnapshotCollection) Len() int {
return len(collection.list)
}
+30 -2
View File
@@ -24,12 +24,34 @@ func (s *SnapshotSuite) TestNewSnapshotFromRepository(c *C) {
c.Check(snapshot.Name, Equals, "snap1") c.Check(snapshot.Name, Equals, "snap1")
c.Check(snapshot.NumPackages(), Equals, 3) c.Check(snapshot.NumPackages(), Equals, 3)
c.Check(snapshot.RefList().Len(), Equals, 3) c.Check(snapshot.RefList().Len(), Equals, 3)
c.Check(snapshot.SourceKind, Equals, "repo")
c.Check(snapshot.SourceIDs, DeepEquals, []string{s.repo.UUID})
s.repo.packageRefs = nil s.repo.packageRefs = nil
_, err := NewSnapshotFromRepository("snap2", s.repo) _, err := NewSnapshotFromRepository("snap2", s.repo)
c.Check(err, ErrorMatches, ".*not updated") c.Check(err, ErrorMatches, ".*not updated")
} }
func (s *SnapshotSuite) TestNewSnapshotFromPackageList(c *C) {
snap, _ := NewSnapshotFromRepository("snap1", s.repo)
snapshot := NewSnapshotFromPackageList("snap2", []*Snapshot{snap}, s.list, "Pulled")
c.Check(snapshot.Name, Equals, "snap2")
c.Check(snapshot.NumPackages(), Equals, 3)
c.Check(snapshot.SourceKind, Equals, "snapshot")
c.Check(snapshot.SourceIDs, DeepEquals, []string{snap.UUID})
}
func (s *SnapshotSuite) TestNewSnapshotFromRefList(c *C) {
snap, _ := NewSnapshotFromRepository("snap1", s.repo)
snapshot := NewSnapshotFromRefList("snap2", []*Snapshot{snap}, s.reflist, "Merged")
c.Check(snapshot.Name, Equals, "snap2")
c.Check(snapshot.NumPackages(), Equals, 3)
c.Check(snapshot.SourceKind, Equals, "snapshot")
c.Check(snapshot.SourceIDs, DeepEquals, []string{snap.UUID})
}
func (s *SnapshotSuite) TestKey(c *C) { func (s *SnapshotSuite) TestKey(c *C) {
snapshot, _ := NewSnapshotFromRepository("snap1", s.repo) snapshot, _ := NewSnapshotFromRepository("snap1", s.repo)
c.Assert(len(snapshot.Key()), Equals, 37) c.Assert(len(snapshot.Key()), Equals, 37)
@@ -81,7 +103,7 @@ func (s *SnapshotCollectionSuite) TearDownTest(c *C) {
s.db.Close() s.db.Close()
} }
func (s *SnapshotCollectionSuite) TestAddByName(c *C) { func (s *SnapshotCollectionSuite) TestAddByNameByUUID(c *C) {
snapshot, err := s.collection.ByName("snap1") snapshot, err := s.collection.ByName("snap1")
c.Assert(err, ErrorMatches, "*.not found") c.Assert(err, ErrorMatches, "*.not found")
@@ -98,6 +120,10 @@ func (s *SnapshotCollectionSuite) TestAddByName(c *C) {
snapshot, err = collection.ByName("snap1") snapshot, err = collection.ByName("snap1")
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(snapshot.String(), Equals, s.snapshot1.String()) c.Assert(snapshot.String(), Equals, s.snapshot1.String())
snapshot, err = collection.ByUUID(s.snapshot1.UUID)
c.Assert(err, IsNil)
c.Assert(snapshot.String(), Equals, s.snapshot1.String())
} }
func (s *SnapshotCollectionSuite) TestUpdateLoadComplete(c *C) { func (s *SnapshotCollectionSuite) TestUpdateLoadComplete(c *C) {
@@ -112,7 +138,7 @@ func (s *SnapshotCollectionSuite) TestUpdateLoadComplete(c *C) {
c.Assert(snapshot.NumPackages(), Equals, 3) c.Assert(snapshot.NumPackages(), Equals, 3)
} }
func (s *SnapshotCollectionSuite) TestForEach(c *C) { func (s *SnapshotCollectionSuite) TestForEachAndLen(c *C) {
s.collection.Add(s.snapshot1) s.collection.Add(s.snapshot1)
s.collection.Add(s.snapshot2) s.collection.Add(s.snapshot2)
@@ -124,6 +150,8 @@ func (s *SnapshotCollectionSuite) TestForEach(c *C) {
c.Assert(count, Equals, 2) c.Assert(count, Equals, 2)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Check(s.collection.Len(), Equals, 2)
e := errors.New("d") e := errors.New("d")
err = s.collection.ForEach(func(*Snapshot) error { err = s.collection.ForEach(func(*Snapshot) error {
return e return e
+271
View File
@@ -0,0 +1,271 @@
package debian
import (
"fmt"
"strconv"
"strings"
"unicode"
)
// Using documentation from: http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
// CompareVersions compares two package versions
func CompareVersions(ver1, ver2 string) int {
e1, u1, d1 := parseVersion(ver1)
e2, u2, d2 := parseVersion(ver2)
r := compareVersionPart(e1, e2)
if r != 0 {
return r
}
r = compareVersionPart(u1, u2)
if r != 0 {
return r
}
return compareVersionPart(d1, d2)
}
// parseVersions breaks down full version to components (possibly empty)
func parseVersion(ver string) (epoch, upstream, debian string) {
i := strings.LastIndex(ver, "-")
if i != -1 {
debian, ver = ver[i+1:], ver[:i]
}
i = strings.Index(ver, ":")
if i != -1 {
epoch, ver = ver[:i], ver[i+1:]
}
upstream = ver
return
}
// compareLexicographic compares in "Debian lexicographic" way, see below compareVersionPart for details
func compareLexicographic(s1, s2 string) int {
i := 0
l1, l2 := len(s1), len(s2)
for {
if i == l1 && i == l2 {
// s1 equal to s2
break
}
if i == l2 {
// s1 is longer than s2
if s1[i] == '~' {
return -1 // s1 < s2
}
return 1 // s1 > s2
}
if i == l1 {
// s2 is longer than s1
if s2[i] == '~' {
return 1 // s1 > s2
}
return -1 // s1 < s2
}
if s1[i] == s2[i] {
i++
continue
}
if s1[i] == '~' {
return -1
}
if s2[i] == '~' {
return 1
}
c1, c2 := unicode.IsLetter(rune(s1[i])), unicode.IsLetter(rune(s2[i]))
if c1 && !c2 {
return -1
}
if !c1 && c2 {
return 1
}
if s1[i] < s2[i] {
return -1
}
return 1
}
return 0
}
// compareVersionPart compares parts of full version
//
// From Debian Policy Manual:
//
// "The strings are compared from left to right.
//
// First the initial part of each string consisting entirely of non-digit characters is
// determined. These two parts (one of which may be empty) are compared lexically. If a
// difference is found it is returned. The lexical comparison is a comparison of ASCII values
// modified so that all the letters sort earlier than all the non-letters and so that a tilde
// sorts before anything, even the end of a part. For example, the following parts are in sorted
// order from earliest to latest: ~~, ~~a, ~, the empty part.
//
// Then the initial part of the remainder of each string which consists entirely of digit
// characters is determined. The numerical values of these two parts are compared, and any difference
// found is returned as the result of the comparison. For these purposes an empty string (which can only occur at
// the end of one or both version strings being compared) counts as zero.
// These two steps (comparing and removing initial non-digit strings and initial digit strings) are
// repeated until a difference is found or both strings are exhausted."
func compareVersionPart(part1, part2 string) int {
i1, i2 := 0, 0
l1, l2 := len(part1), len(part2)
for {
j1, j2 := i1, i2
for j1 < l1 && !unicode.IsDigit(rune(part1[j1])) {
j1++
}
for j2 < l2 && !unicode.IsDigit(rune(part2[j2])) {
j2++
}
s1, s2 := part1[i1:j1], part2[i2:j2]
r := compareLexicographic(s1, s2)
if r != 0 {
return r
}
i1, i2 = j1, j2
for j1 < l1 && unicode.IsDigit(rune(part1[j1])) {
j1++
}
for j2 < l2 && unicode.IsDigit(rune(part2[j2])) {
j2++
}
s1, s2 = part1[i1:j1], part2[i2:j2]
n1, _ := strconv.Atoi(s1)
n2, _ := strconv.Atoi(s2)
if n1 < n2 {
return -1
}
if n1 > n2 {
return 1
}
i1, i2 = j1, j2
if i1 == l1 && i2 == l2 {
break
}
}
return 0
}
// Version relations
const (
VersionDontCare = iota
VersionLess
VersionLessOrEqual
VersionEqual
VersionGreaterOrEqual
VersionGreater
)
// Dependency is a parsed version of Debian dependency to package
type Dependency struct {
Pkg string
Relation int
Version string
Architecture string
}
// Hash calculates some predefined unique ID of Dependency
func (d *Dependency) Hash() string {
return fmt.Sprintf("%s:%s:%d:%s", d.Architecture, d.Pkg, d.Relation, d.Version)
}
// String produces human-readable representation
func (d *Dependency) String() string {
var rel string
switch d.Relation {
case VersionEqual:
rel = "="
case VersionGreater:
rel = ">>"
case VersionLess:
rel = "<<"
case VersionGreaterOrEqual:
rel = ">="
case VersionLessOrEqual:
rel = "<="
case VersionDontCare:
return fmt.Sprintf("%s [%s]", d.Pkg, d.Architecture)
}
return fmt.Sprintf("%s (%s %s) [%s]", d.Pkg, rel, d.Version, d.Architecture)
}
// ParseDependencyVariants parses dependencies in format "pkg (>= 1.35) | other-package"
func ParseDependencyVariants(variants string) (l []Dependency, err error) {
parts := strings.Split(variants, "|")
l = make([]Dependency, len(parts))
for i, part := range parts {
l[i], err = ParseDependency(strings.TrimSpace(part))
if err != nil {
return nil, err
}
}
return
}
// ParseDependency parses dependency in format "pkg (>= 1.35)" into parts
func ParseDependency(dep string) (d Dependency, err error) {
if !strings.HasSuffix(dep, ")") {
d.Pkg = strings.TrimSpace(dep)
d.Relation = VersionDontCare
return
}
i := strings.Index(dep, "(")
if i == -1 {
err = fmt.Errorf("unable to parse dependency: %s", dep)
return
}
d.Pkg = strings.TrimSpace(dep[0:i])
rel := dep[i+1 : i+2]
if dep[i+2] == '>' || dep[i+2] == '<' || dep[i+2] == '=' {
rel += dep[i+2 : i+3]
d.Version = strings.TrimSpace(dep[i+3 : len(dep)-1])
} else {
d.Version = strings.TrimSpace(dep[i+2 : len(dep)-1])
}
switch rel {
case "<", "<=":
d.Relation = VersionLessOrEqual
case ">", ">=":
d.Relation = VersionGreaterOrEqual
case "<<":
d.Relation = VersionLess
case ">>":
d.Relation = VersionGreater
case "=":
d.Relation = VersionEqual
default:
err = fmt.Errorf("relation unknown: %s", rel)
}
return
}
+186
View File
@@ -0,0 +1,186 @@
package debian
import (
. "launchpad.net/gocheck"
)
type VersionSuite struct {
stanza Stanza
}
var _ = Suite(&VersionSuite{})
func (s *VersionSuite) TestParseVersion(c *C) {
e, u, d := parseVersion("1.3.4")
c.Check([]string{e, u, d}, DeepEquals, []string{"", "1.3.4", ""})
e, u, d = parseVersion("4:1.3:4")
c.Check([]string{e, u, d}, DeepEquals, []string{"4", "1.3:4", ""})
e, u, d = parseVersion("1.3.4-1")
c.Check([]string{e, u, d}, DeepEquals, []string{"", "1.3.4", "1"})
e, u, d = parseVersion("1.3-pre4-1")
c.Check([]string{e, u, d}, DeepEquals, []string{"", "1.3-pre4", "1"})
e, u, d = parseVersion("4:1.3-pre4-1")
c.Check([]string{e, u, d}, DeepEquals, []string{"4", "1.3-pre4", "1"})
}
func (s *VersionSuite) TestCompareLexicographic(c *C) {
c.Check(compareLexicographic("", ""), Equals, 0)
c.Check(compareLexicographic("pre", "pre"), Equals, 0)
c.Check(compareLexicographic("pr", "pre"), Equals, -1)
c.Check(compareLexicographic("pre", "pr"), Equals, 1)
c.Check(compareLexicographic("pra", "prb"), Equals, -1)
c.Check(compareLexicographic("prb", "pra"), Equals, 1)
c.Check(compareLexicographic("prx", "pr+"), Equals, -1)
c.Check(compareLexicographic("pr+", "prx"), Equals, 1)
c.Check(compareLexicographic("pr~", "pra"), Equals, -1)
c.Check(compareLexicographic("pra", "pr~"), Equals, 1)
c.Check(compareLexicographic("~~", "~~a"), Equals, -1)
c.Check(compareLexicographic("~~a", "~"), Equals, -1)
c.Check(compareLexicographic("~", ""), Equals, -1)
c.Check(compareLexicographic("~~a", "~~"), Equals, 1)
c.Check(compareLexicographic("~", "~~a"), Equals, 1)
c.Check(compareLexicographic("", "~"), Equals, 1)
}
func (s *VersionSuite) TestCompareVersionPart(c *C) {
c.Check(compareVersionPart("", ""), Equals, 0)
c.Check(compareVersionPart("pre", "pre"), Equals, 0)
c.Check(compareVersionPart("12", "12"), Equals, 0)
c.Check(compareVersionPart("1.3.5", "1.3.5"), Equals, 0)
c.Check(compareVersionPart("1.3.5-pre1", "1.3.5-pre1"), Equals, 0)
c.Check(compareVersionPart("1.0~beta1~svn1245", "1.0~beta1"), Equals, -1)
c.Check(compareVersionPart("1.0~beta1", "1.0"), Equals, -1)
c.Check(compareVersionPart("1.0~beta1", "1.0~beta1~svn1245"), Equals, 1)
c.Check(compareVersionPart("1.0", "1.0~beta1"), Equals, 1)
c.Check(compareVersionPart("1.pr", "1.pre"), Equals, -1)
c.Check(compareVersionPart("1.pre", "1.pr"), Equals, 1)
c.Check(compareVersionPart("1.pra", "1.prb"), Equals, -1)
c.Check(compareVersionPart("1.prb", "1.pra"), Equals, 1)
c.Check(compareVersionPart("3.prx", "3.pr+"), Equals, -1)
c.Check(compareVersionPart("3.pr+", "3.prx"), Equals, 1)
c.Check(compareVersionPart("3.pr~", "3.pra"), Equals, -1)
c.Check(compareVersionPart("3.pra", "3.pr~"), Equals, 1)
c.Check(compareVersionPart("2~~", "2~~a"), Equals, -1)
c.Check(compareVersionPart("2~~a", "2~"), Equals, -1)
c.Check(compareVersionPart("2~", "2"), Equals, -1)
c.Check(compareVersionPart("2~~a", "2~~"), Equals, 1)
c.Check(compareVersionPart("2~", "2~~a"), Equals, 1)
c.Check(compareVersionPart("2", "2~"), Equals, 1)
}
func (s *VersionSuite) TestCompareVersions(c *C) {
c.Check(CompareVersions("3:1.0~beta1~svn1245-1", "3:1.0~beta1~svn1245-1"), Equals, 0)
c.Check(CompareVersions("1:1.0~beta1~svn1245-1", "3:1.0~beta1~svn1245-1"), Equals, -1)
c.Check(CompareVersions("1:1.0~beta1~svn1245-1", "1.0~beta1~svn1245-1"), Equals, 1)
c.Check(CompareVersions("1.0~beta1~svn1245-1", "1.0~beta1~svn1245-2"), Equals, -1)
c.Check(CompareVersions("3:1.0~beta1~svn1245-1", "3:1.0~beta1-1"), Equals, -1)
c.Check(CompareVersions("1.0~beta1~svn1245", "1.0~beta1"), Equals, -1)
c.Check(CompareVersions("1.0~beta1", "1.0"), Equals, -1)
}
func (s *VersionSuite) TestParseDependency(c *C) {
d, e := ParseDependency("dpkg (>= 1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionGreaterOrEqual)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg(>>1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionGreater)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg (> 1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionGreaterOrEqual)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg (< 1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionLessOrEqual)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg (= 1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionEqual)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg (<< 1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionLess)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg(>>1.6)")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionGreater)
c.Check(d.Version, Equals, "1.6")
d, e = ParseDependency("dpkg ")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionDontCare)
c.Check(d.Version, Equals, "")
d, e = ParseDependency("dpkg(==1.6)")
c.Check(e, ErrorMatches, "relation unknown.*")
d, e = ParseDependency("dpkg==1.6)")
c.Check(e, ErrorMatches, "unable to parse.*")
}
func (s *VersionSuite) TestParseDependencyVariants(c *C) {
l, e := ParseDependencyVariants("dpkg (>= 1.6)")
c.Check(e, IsNil)
c.Check(l, HasLen, 1)
c.Check(l[0].Pkg, Equals, "dpkg")
c.Check(l[0].Relation, Equals, VersionGreaterOrEqual)
c.Check(l[0].Version, Equals, "1.6")
l, e = ParseDependencyVariants("dpkg (>= 1.6) | mailer-agent")
c.Check(e, IsNil)
c.Check(l, HasLen, 2)
c.Check(l[0].Pkg, Equals, "dpkg")
c.Check(l[0].Relation, Equals, VersionGreaterOrEqual)
c.Check(l[0].Version, Equals, "1.6")
c.Check(l[1].Pkg, Equals, "mailer-agent")
c.Check(l[1].Relation, Equals, VersionDontCare)
_, e = ParseDependencyVariants("dpkg(==1.6)")
c.Check(e, ErrorMatches, "relation unknown.*")
}
func (s *VersionSuite) TestDependencyString(c *C) {
d, _ := ParseDependency("dpkg(>>1.6)")
d.Architecture = "i386"
c.Check(d.String(), Equals, "dpkg (>> 1.6) [i386]")
d, _ = ParseDependency("dpkg")
d.Architecture = "i386"
c.Check(d.String(), Equals, "dpkg [i386]")
}
+41 -10
View File
@@ -1,18 +1,19 @@
package main package main
import ( import (
"fmt"
"github.com/gonuts/commander" "github.com/gonuts/commander"
"github.com/gonuts/flag" "github.com/gonuts/flag"
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
"github.com/smira/aptly/debian" "github.com/smira/aptly/debian"
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
// aptly version // aptly version
const Version = "0.1" const Version = "0.2"
var cmd *commander.Command var cmd *commander.Command
@@ -21,7 +22,7 @@ func init() {
UsageLine: os.Args[0], UsageLine: os.Args[0],
Short: "Debian repository management tool", Short: "Debian repository management tool",
Long: ` Long: `
aptly allows to create partial and full mirrors of remote aptly is a tool to create partial and full mirrors of remote
repositories, filter them, merge, upgrade individual packages, repositories, filter them, merge, upgrade individual packages,
take snapshots and publish them back as Debian repositories.`, take snapshots and publish them back as Debian repositories.`,
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
@@ -32,18 +33,30 @@ take snapshots and publish them back as Debian repositories.`,
makeCmdVersion(), makeCmdVersion(),
}, },
} }
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
} }
var context struct { var context struct {
downloader utils.Downloader downloader utils.Downloader
database database.Storage database database.Storage
packageRepository *debian.Repository packageRepository *debian.Repository
dependencyOptions int
architecturesList []string
}
func fatal(err error) {
fmt.Printf("ERROR: %s\n", err)
os.Exit(1)
} }
func main() { func main() {
err := cmd.Flag.Parse(os.Args[1:]) err := cmd.Flag.Parse(os.Args[1:])
if err != nil { if err != nil {
log.Fatalf("%s", err) fatal(err)
} }
configLocations := []string{ configLocations := []string{
@@ -56,28 +69,46 @@ func main() {
if err == nil { if err == nil {
break break
} }
if !os.IsNotExist(err) {
fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
}
} }
if err != nil { if err != nil {
log.Printf("Config file not found, creating default config at %s\n\n", configLocations[0]) fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
utils.SaveConfig(configLocations[0], &utils.Config) utils.SaveConfig(configLocations[0], &utils.Config)
} }
context.dependencyOptions = 0
if utils.Config.DepFollowSuggests || cmd.Flag.Lookup("dep-follow-suggests").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowSuggests
}
if utils.Config.DepFollowRecommends || cmd.Flag.Lookup("dep-follow-recommends").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowRecommends
}
if utils.Config.DepFollowAllVariants || cmd.Flag.Lookup("dep-follow-all-variants").Value.Get().(bool) {
context.dependencyOptions |= debian.DepFollowAllVariants
}
context.architecturesList = utils.Config.Architectures
optionArchitectures := cmd.Flag.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
context.downloader = utils.NewDownloader(utils.Config.DownloadConcurrency) context.downloader = utils.NewDownloader(utils.Config.DownloadConcurrency)
defer context.downloader.Shutdown() defer context.downloader.Shutdown()
// TODO: configure DB dir
context.database, err = database.OpenDB(filepath.Join(utils.Config.RootDir, "db")) context.database, err = database.OpenDB(filepath.Join(utils.Config.RootDir, "db"))
if err != nil { if err != nil {
log.Fatalf("can't open database: %s", err) fatal(fmt.Errorf("can't open database: %s", err))
} }
defer context.database.Close() defer context.database.Close()
// TODO:configure pool dir
context.packageRepository = debian.NewRepository(utils.Config.RootDir) context.packageRepository = debian.NewRepository(utils.Config.RootDir)
err = cmd.Dispatch(os.Args[1:]) err = cmd.Dispatch(cmd.Flag.Args())
if err != nil { if err != nil {
log.Fatalf("%s", err) fatal(err)
} }
} }
+1
View File
@@ -0,0 +1 @@
System test for aptly
+11
View File
@@ -0,0 +1,11 @@
aptly -architectures=i386,amd64 mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
aptly -architectures=i386,amd64 mirror create wheezy-contrib http://mirror.yandex.ru/debian/ wheezy contrib
aptly -architectures=i386,amd64 mirror create wheezy-non-free http://mirror.yandex.ru/debian/ wheezy non-free
aptly -architectures=i386,amd64 mirror create wheezy-updates http://mirror.yandex.ru/debian/ wheezy-updates
aptly -architectures=i386,amd64 mirror create wheezy-backports http://mirror.yandex.ru/debian/ wheezy-backports
aptly mirror update wheezy-main
aptly mirror update wheezy-contrib
aptly mirror update wheezy-non-free
aptly mirror update wheezy-updates
aptly mirror update wheezy-backports
+128
View File
@@ -0,0 +1,128 @@
"""
Test library.
"""
import difflib
import inspect
import json
import subprocess
import os
import shlex
import shutil
import string
class BaseTest(object):
"""
Base class for all tests.
"""
longTest = False
fixturePool = False
fixtureDB = False
expectedCode = 0
configFile = {
"rootDir": "%s/.aptly" % os.environ["HOME"],
"downloadConcurrency": 4,
"architectures": [],
"dependencyFollowSuggests": False,
"dependencyFollowRecommends": False,
"dependencyFollowAllVariants": False
}
configOverride = {}
fixtureDBDir = os.path.join(os.environ["HOME"], "aptly-fixture-db")
fixturePoolDir = os.path.join(os.environ["HOME"], "aptly-fixture-pool")
outputMatchPrepare = None
def test(self):
self.prepare()
self.run()
self.check()
def prepare_remove_all(self):
if os.path.exists(os.path.join(os.environ["HOME"], ".aptly")):
shutil.rmtree(os.path.join(os.environ["HOME"], ".aptly"))
if os.path.exists(os.path.join(os.environ["HOME"], ".aptly.conf")):
os.remove(os.path.join(os.environ["HOME"], ".aptly.conf"))
def prepare_default_config(self):
cfg = self.configFile.copy()
cfg.update(**self.configOverride)
f = open(os.path.join(os.environ["HOME"], ".aptly.conf"), "w")
f.write(json.dumps(cfg))
f.close()
def fixture_available(self):
if self.fixturePool and not os.path.exists(self.fixturePoolDir):
return False
if self.fixtureDB and not os.path.exists(self.fixtureDBDir):
return False
return True
def prepare_fixture(self):
if self.fixturePool:
os.makedirs(os.path.join(os.environ["HOME"], ".aptly"), 0755)
os.symlink(self.fixturePoolDir, os.path.join(os.environ["HOME"], ".aptly", "pool"))
if self.fixtureDB:
shutil.copytree(self.fixtureDBDir, os.path.join(os.environ["HOME"], ".aptly", "db"))
if hasattr(self, "fixtureCmds"):
for cmd in self.fixtureCmds:
self.run_cmd(cmd)
def run(self):
self.output = self.output_processor(self.run_cmd(self.runCmd, self.expectedCode))
def run_cmd(self, command, expected_code=0):
try:
proc = subprocess.Popen(shlex.split(command), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
output, _ = proc.communicate()
if proc.returncode != expected_code:
raise Exception("exit code %d != %d (output: %s)" % (proc.returncode, expected_code, output))
return output
except Exception, e:
raise Exception("Running command %s failed: %s" % (command, str(e)))
def gold_processor(self, gold):
return gold
def output_processor(self, output):
return output
def expand_environ(self, gold):
return string.Template(gold).substitute(os.environ)
def get_gold(self, gold_name="gold"):
gold = os.path.join(os.path.dirname(inspect.getsourcefile(self.__class__)), self.__class__.__name__ + "_" + gold_name)
return self.gold_processor(open(gold, "r").read())
def check_output(self):
self.verify_match(self.get_gold(), self.output, match_prepare=self.outputMatchPrepare)
def check_cmd_output(self, command, gold_name, match_prepare=None):
self.verify_match(self.get_gold(gold_name), self.run_cmd(command), match_prepare)
def verify_match(self, a, b, match_prepare=None):
if match_prepare is not None:
a = match_prepare(a)
b = match_prepare(b)
if a != b:
diff = "".join(difflib.unified_diff([l + "\n" for l in a.split("\n")], [l + "\n" for l in b.split("\n")]))
raise Exception("content doesn't match:\n" + diff + "\n")
def check_file(self):
self.verify_match(self.get_gold(), open(self.checkedFile, "r").read())
check = check_output
def prepare(self):
self.prepare_remove_all()
self.prepare_default_config()
self.prepare_fixture()
Executable
+69
View File
@@ -0,0 +1,69 @@
#!/usr/local/bin/python
import glob
import importlib
import os
import inspect
import sys
from lib import BaseTest
try:
from termcolor import colored
except ImportError:
def colored(s, **kwargs):
return s
def run(include_long_tests=False):
"""
Run system test.
"""
tests = glob.glob("t*_*")
fails = []
numTests = numFailed = numSkipped = 0
for test in tests:
testModule = importlib.import_module(test)
for name in dir(testModule):
o = getattr(testModule, name)
if not (inspect.isclass(o) and issubclass(o, BaseTest) and o is not BaseTest):
continue
t = o()
if t.longTest and not include_long_tests or not t.fixture_available():
numSkipped += 1
continue
numTests += 1
sys.stdout.write("%s:%s... " % (test, o.__name__))
try:
t.test()
except BaseException, e:
numFailed += 1
fails.append((test, t, e, testModule))
sys.stdout.write(colored("FAIL\n", color="red"))
else:
sys.stdout.write(colored("OK\n", color="green"))
print "TESTS: %d SUCCESS: %d FAIL: %d SKIP: %d" % (numTests, numTests - numFailed, numFailed, numSkipped)
if len(fails) > 0:
print "\nFAILURES (%d):" % (len(fails), )
for (test, t, e, testModule) in fails:
print "%s:%s %s" % (test, t.__class__.__name__, testModule.__doc__.strip() + ": " + t.__doc__.strip())
print "ERROR: %s" % (e, )
print "=" * 60
sys.exit(1)
if __name__ == "__main__":
os.chdir(os.path.realpath(os.path.dirname(sys.argv[0])))
include_long_tests = len(sys.argv) > 1 and sys.argv[1] == "--long"
run(include_long_tests)
+1
View File
@@ -0,0 +1 @@
aptly version: 0.2
+13
View File
@@ -0,0 +1,13 @@
"""
Test aptly version
"""
from lib import BaseTest
class VersionTest(BaseTest):
"""
version should match
"""
runCmd = "aptly version"
+1
View File
@@ -0,0 +1 @@
ERROR: error loading config file ${HOME}/.aptly.conf: invalid character 's' looking for beginning of object key string
+8
View File
@@ -0,0 +1,8 @@
{
"rootDir": "${HOME}/.aptly",
"downloadConcurrency": 4,
"architectures": [],
"dependencyFollowSuggests": false,
"dependencyFollowRecommends": false,
"dependencyFollowAllVariants": false
}
+35
View File
@@ -0,0 +1,35 @@
"""
Test config file
"""
import os
from lib import BaseTest
class CreateConfigTest(BaseTest):
"""
new file is generated if missing
"""
runCmd = "aptly"
checkedFile = os.path.join(os.environ["HOME"], ".aptly.conf")
check = BaseTest.check_file
gold_processor = BaseTest.expand_environ
prepare = BaseTest.prepare_remove_all
class BadConfigTest(BaseTest):
"""
broken config file
"""
runCmd = "aptly"
expectedCode = 1
gold_processor = BaseTest.expand_environ
def prepare(self):
self.prepare_remove_all()
f = open(os.path.join(os.environ["HOME"], ".aptly.conf"), "w")
f.write("{some crap")
f.close()
+10
View File
@@ -0,0 +1,10 @@
aptly is a tool to create partial and full mirrors of remote
repositories, filter them, merge, upgrade individual packages,
take snapshots and publish them back as Debian repositories.
Options:
-architectures="": list of architectures to consider during (comma-separated), default to all available
-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-suggests=false: when processing dependencies, follow Suggests
+17
View File
@@ -0,0 +1,17 @@
aptly - Debian repository management tool
Commands:
mirror manage mirrors of remote repositories
publish manage published repositories
snapshot manage snapshots of repositories
version display version
Use "aptly help <command>" for more information about a command.
Options:
-architectures="": list of architectures to consider during (comma-separated), default to all available
-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-suggests=false: when processing dependencies, follow Suggests
@@ -0,0 +1,7 @@
Usage: aptly mirror create <name> <archive url> <distribution> [<component1> ...]
Create records information about new mirror and fetches Release file (it doesn't download packages).
ex:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
+4
View File
@@ -0,0 +1,4 @@
Usage: aptly mirror create <name> <archive url> <distribution> [<component1> ...]
aptly mirror create - create new mirror of Debian repository
+11
View File
@@ -0,0 +1,11 @@
aptly mirror - manage mirrors of remote repositories
Commands:
create create new mirror of Debian repository
list list mirrors of remote repositories
show show details about remote repository mirror
update update packages from remote mirror
Use "mirror help <command>" for more information about a command.
+11
View File
@@ -0,0 +1,11 @@
aptly mirror - manage mirrors of remote repositories
Commands:
create create new mirror of Debian repository
list list mirrors of remote repositories
show show details about remote repository mirror
update update packages from remote mirror
Use "mirror help <command>" for more information about a command.
+47
View File
@@ -0,0 +1,47 @@
"""
Test help screens
"""
from lib import BaseTest
class MainTest(BaseTest):
"""
main
"""
runCmd = "aptly"
class MirrorTest(BaseTest):
"""
main
"""
runCmd = "aptly mirror"
class MirrorCreateTest(BaseTest):
"""
main
"""
runCmd = "aptly mirror create"
class MainHelpTest(BaseTest):
"""
main
"""
runCmd = "aptly help"
class MirrorHelpTest(BaseTest):
"""
main
"""
runCmd = "aptly help mirror"
class MirrorCreateHelpTest(BaseTest):
"""
main
"""
runCmd = "aptly help mirror create"
+4
View File
@@ -0,0 +1,4 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
Mirror [mirror1]: http://mirror.yandex.ru/debian/ wheezy successfully added.
You can run 'aptly mirror update mirror1' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror1
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: main, contrib, non-free
Architectures: amd64, armel, armhf, i386, ia64, kfreebsd-amd64, kfreebsd-i386, mips, mipsel, powerpc, s390, s390x, sparc
Last update: never
Information from release file:
Architectures: amd64 armel armhf i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc
Codename: wheezy
Components: main contrib non-free
Date: Sat, 14 Dec 2013 10:51:30 UTC
Description: Debian 7.3 Released 14 December 2013
Label: Debian
Origin: Debian
Suite: stable
Version: 7.3
+4
View File
@@ -0,0 +1,4 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
Mirror [mirror2]: http://mirror.yandex.ru/debian/ wheezy successfully added.
You can run 'aptly mirror update mirror2' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror2
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: main
Architectures: amd64, armel, armhf, i386, ia64, kfreebsd-amd64, kfreebsd-i386, mips, mipsel, powerpc, s390, s390x, sparc
Last update: never
Information from release file:
Architectures: amd64 armel armhf i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc
Codename: wheezy
Components: main contrib non-free
Date: Sat, 14 Dec 2013 10:51:30 UTC
Description: Debian 7.3 Released 14 December 2013
Label: Debian
Origin: Debian
Suite: stable
Version: 7.3
+4
View File
@@ -0,0 +1,4 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
Mirror [mirror3]: http://mirror.yandex.ru/debian/ wheezy successfully added.
You can run 'aptly mirror update mirror3' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror3
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: main, contrib
Architectures: i386, amd64
Last update: never
Information from release file:
Architectures: amd64 armel armhf i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc
Codename: wheezy
Components: main contrib non-free
Date: Sat, 14 Dec 2013 10:51:30 UTC
Description: Debian 7.3 Released 14 December 2013
Label: Debian
Origin: Debian
Suite: stable
Version: 7.3
+2
View File
@@ -0,0 +1,2 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
ERROR: unable to fetch mirror: component life not available in repo [mirror4]: http://mirror.yandex.ru/debian/ wheezy
+2
View File
@@ -0,0 +1,2 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
ERROR: unable to fetch mirror: architecture nano68 not available in repo [mirror5]: http://mirror.yandex.ru/debian/ wheezy
+2
View File
@@ -0,0 +1,2 @@
Downloading http://mirror.yandex.ru/debian/dists/suslik/Release...
ERROR: unable to fetch mirror: HTTP code 404 while fetching http://mirror.yandex.ru/debian/dists/suslik/Release
+4
View File
@@ -0,0 +1,4 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
Mirror [mirror7]: http://mirror.yandex.ru/debian/ wheezy successfully added.
You can run 'aptly mirror update mirror7' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror7
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: main, contrib
Architectures: i386, amd64
Last update: never
Information from release file:
Architectures: amd64 armel armhf i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc
Codename: wheezy
Components: main contrib non-free
Date: Sat, 14 Dec 2013 10:51:30 UTC
Description: Debian 7.3 Released 14 December 2013
Label: Debian
Origin: Debian
Suite: stable
Version: 7.3
+2
View File
@@ -0,0 +1,2 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
ERROR: unable to add mirror: mirror with name mirror8 already exists
+6
View File
@@ -0,0 +1,6 @@
List of mirrors:
* [mirror1]: http://mirror.yandex.ru/debian/ wheezy
* [mirror2]: http://mirror.yandex.ru/debian/ squeeze
* [mirror3]: http://mirror.yandex.ru/debian/ squeeze
To get more information about mirror, run `aptly mirror show <name>`.
+1
View File
@@ -0,0 +1 @@
No mirrors found, create one with `aptly mirror create ...`.
+18
View File
@@ -0,0 +1,18 @@
Name: mirror1
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: main, contrib, non-free
Architectures: amd64, armel, armhf, i386, ia64, kfreebsd-amd64, kfreebsd-i386, mips, mipsel, powerpc, s390, s390x, sparc
Last update: never
Information from release file:
Architectures: amd64 armel armhf i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 s390x sparc
Codename: wheezy
Components: main contrib non-free
Date: Sat, 14 Dec 2013 10:51:30 UTC
Description: Debian 7.3 Released 14 December 2013
Label: Debian
Origin: Debian
Suite: stable
Version: 7.3
+1
View File
@@ -0,0 +1 @@
ERROR: unable to show: mirror with name mirror-xx not found
+106
View File
@@ -0,0 +1,106 @@
Building download queue...
Download queue: 94 items, 0.13 GiB size
Downloading & parsing package files...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/dists/hardy/Release...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/dists/hardy/main/binary-amd64/Packages.bz2...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/dists/hardy/main/binary-amd64/Packages.gz...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/dists/hardy/main/binary-i386/Packages.bz2...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/dists/hardy/main/binary-i386/Packages.gz...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-driver/alsa-base_1.0.17.dfsg-2ubuntu1~hardy2_all.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-driver/alsa-source_1.0.17.dfsg-2ubuntu1~hardy2_all.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-driver/linux-sound-base_1.0.17.dfsg-2ubuntu1~hardy2_all.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/lib32asound2-dev_1.0.17a-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/lib32asound2_1.0.17a-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/lib64asound2-dev_1.0.17a-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/lib64asound2_1.0.17a-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/libasound2-dev_1.0.17a-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/libasound2-dev_1.0.17a-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/libasound2-doc_1.0.17a-0ubuntu2~hardy1_all.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/libasound2_1.0.17a-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-lib/libasound2_1.0.17a-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-firmware-loaders_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-firmware-loaders_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-tools-gui_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-tools-gui_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-tools_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/alsa-tools_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/ld10k1_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/ld10k1_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/liblo10k1-0_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/liblo10k1-0_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/liblo10k1-dev_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/liblo10k1-dev_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/qlo10k1_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-tools/qlo10k1_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-utils/alsa-utils_1.0.17-0ubuntu2~hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/a/alsa-utils/alsa-utils_1.0.17-0ubuntu2~hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-386_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-generic_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-generic_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-openvz_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-openvz_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-rt_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-rt_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-server_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-server_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-virtual_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-xen_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-19-xen_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-386_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-generic_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-generic_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-openvz_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-openvz_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-rt_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-rt_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-server_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-server_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-virtual_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-xen_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-headers-lum-2.6.24-21-xen_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-386_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-generic_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-generic_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-openvz_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-openvz_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-rt_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-rt_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-server_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-server_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-virtual_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-xen_2.6.24-19.28+hyper1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-19-xen_2.6.24-19.28+hyper1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-386_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-generic_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-generic_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-openvz_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-openvz_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-rt_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-rt_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-server_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-server_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-virtual_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-xen_2.6.24-21.32+hardy1_amd64.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/linux-ubuntu-modules-2.6.24-21-xen_2.6.24-21.32+hardy1_i386.deb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-19-386-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-19-generic-di_2.6.24-19.28+hyper1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-19-generic-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-21-386-di_2.6.24-21.32+hardy1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-21-generic-di_2.6.24-21.32+hardy1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/nic-firmware-2.6.24-21-generic-di_2.6.24-21.32+hardy1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-19-386-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-19-generic-di_2.6.24-19.28+hyper1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-19-generic-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-21-386-di_2.6.24-21.32+hardy1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-21-generic-di_2.6.24-21.32+hardy1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/scsi-firmware-2.6.24-21-generic-di_2.6.24-21.32+hardy1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-19-386-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-19-generic-di_2.6.24-19.28+hyper1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-19-generic-di_2.6.24-19.28+hyper1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-21-386-di_2.6.24-21.32+hardy1_i386.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-21-generic-di_2.6.24-21.32+hardy1_amd64.udeb...
Downloading http://ppa.launchpad.net/alsa-backports/ubuntu/pool/main/l/linux-ubuntu-modules-2.6.24/ubuntu-modules-2.6.24-21-generic-di_2.6.24-21.32+hardy1_i386.udeb...
Mirror `alsa-ppa` has been successfully updated.
Saving packages to database...
+1
View File
@@ -0,0 +1 @@
ERROR: unable to update: mirror with name mirror-xyz not found
+8
View File
@@ -0,0 +1,8 @@
"""
Testing mirror management
"""
from .create import *
from .show import *
from .list import *
from .update import *
+84
View File
@@ -0,0 +1,84 @@
from lib import BaseTest
class CreateMirror1Test(BaseTest):
"""
create mirror: all architectures + all components
"""
runCmd = "aptly mirror create mirror1 http://mirror.yandex.ru/debian/ wheezy"
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror1", "mirror_show")
class CreateMirror2Test(BaseTest):
"""
create mirror: all architectures and 1 component
"""
runCmd = "aptly mirror create mirror2 http://mirror.yandex.ru/debian/ wheezy main"
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror2", "mirror_show")
class CreateMirror3Test(BaseTest):
"""
create mirror: some architectures and 2 components
"""
runCmd = "aptly -architectures=i386,amd64 mirror create mirror3 http://mirror.yandex.ru/debian/ wheezy main contrib"
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror3", "mirror_show")
class CreateMirror4Test(BaseTest):
"""
create mirror: missing component
"""
expectedCode = 1
runCmd = "aptly -architectures=i386,amd64 mirror create mirror4 http://mirror.yandex.ru/debian/ wheezy life"
class CreateMirror5Test(BaseTest):
"""
create mirror: missing architecture
"""
expectedCode = 1
runCmd = "aptly -architectures=i386,nano68 mirror create mirror5 http://mirror.yandex.ru/debian/ wheezy"
class CreateMirror6Test(BaseTest):
"""
create mirror: missing release
"""
expectedCode = 1
runCmd = "aptly mirror create mirror6 http://mirror.yandex.ru/debian/ suslik"
class CreateMirror7Test(BaseTest):
"""
create mirror: architectures fixed via config file
"""
runCmd = "aptly mirror create mirror7 http://mirror.yandex.ru/debian/ wheezy main contrib"
configOverride = {"architectures": ["i386", "amd64"]}
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror7", "mirror_show")
class CreateMirror8Test(BaseTest):
"""
create mirror: already exists
"""
fixtureCmds = [
"aptly mirror create mirror8 http://mirror.yandex.ru/debian/ wheezy main contrib"
]
runCmd = "aptly mirror create mirror8 http://mirror.yandex.ru/debian/ wheezy main contrib"
expectedCode = 1
+20
View File
@@ -0,0 +1,20 @@
from lib import BaseTest
class ListMirror1Test(BaseTest):
"""
list mirrors: regular list
"""
fixtureCmds = [
"aptly mirror create mirror1 http://mirror.yandex.ru/debian/ wheezy",
"aptly mirror create mirror2 http://mirror.yandex.ru/debian/ squeeze contrib",
"aptly -architectures=i386 mirror create mirror3 http://mirror.yandex.ru/debian/ squeeze non-free",
]
runCmd = "aptly mirror list"
class ListMirror2Test(BaseTest):
"""
list mirrors: empty list
"""
runCmd = "aptly mirror list"
+17
View File
@@ -0,0 +1,17 @@
from lib import BaseTest
class ShowMirror1Test(BaseTest):
"""
show mirror: regular mirror
"""
fixtureCmds = ["aptly mirror create mirror1 http://mirror.yandex.ru/debian/ wheezy"]
runCmd = "aptly mirror show mirror1"
class ShowMirror2Test(BaseTest):
"""
show mirror: missing mirror
"""
runCmd = "aptly mirror show mirror-xx"
expectedCode = 1
+23
View File
@@ -0,0 +1,23 @@
from lib import BaseTest
class UpdateMirror1Test(BaseTest):
"""
update mirrors: regular update
"""
longTest = True
fixtureCmds = [
"aptly -architectures=i386,amd64 mirror create alsa-ppa http://ppa.launchpad.net/alsa-backports/ubuntu/ hardy main",
]
runCmd = "aptly mirror update alsa-ppa"
def output_processor(self, output):
return "\n".join(sorted(output.split("\n")))
class UpdateMirror2Test(BaseTest):
"""
update mirrors: no such repo
"""
runCmd = "aptly mirror update mirror-xyz"
expectedCode = 1
@@ -0,0 +1,3 @@
Snapshot snap1 successfully created.
You can run 'aptly publish snapshot snap1' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
ERROR: unable to create snapshot: mirror with name no-such-mirror not found
@@ -0,0 +1 @@
ERROR: unable to add snapshot: snapshot with name snap1 already exists
@@ -0,0 +1,7 @@
Arch | Package | Version in A | Version in B
+ all | init-system-helpers | - | 1.11~bpo70.1
! amd64 | libestr0 | 0.1.1-2 | 0.1.9-1~bpo70+1
! amd64 | rsyslog | 5.8.11-3 | 7.4.4-1~bpo70+1
! i386 | libestr0 | 0.1.1-2 | 0.1.9-1~bpo70+1
! i386 | rsyslog | 5.8.11-3 | 7.4.4-1~bpo70+1

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
ERROR: unable to load snapshot B: snapshot with name snap-no not found
@@ -0,0 +1 @@
ERROR: unable to load snapshot A: snapshot with name snap-no not found
@@ -0,0 +1 @@
Snapshots are identical.
@@ -0,0 +1,7 @@
List of snapshots:
* [snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
* [snap2]: Snapshot from mirror [wheezy-contrib]: http://mirror.yandex.ru/debian/ wheezy
* [snap3]: Merged from sources: 'snap1', 'snap2'
* [snap4]: Pulled into 'snap1' with 'snap2' as source, pull request was: 'mame unrar'
To get more information about snapshot, run `aptly snapshot show <name>`.
@@ -0,0 +1,2 @@
No snapshots found, create one with `aptly snapshot create...`.
@@ -0,0 +1,3 @@
Snapshot snap3 successfully created.
You can run 'aptly publish snapshot snap3' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
Snapshot snap2 successfully created.
You can run 'aptly publish snapshot snap2' to publish snapshot as Debian repository.
@@ -0,0 +1 @@
Snapshots are identical.
@@ -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
@@ -0,0 +1 @@
ERROR: unable to load snapshot: snapshot with name snap2 not found
@@ -0,0 +1 @@
ERROR: unable to create snapshot: snapshot with name snap1 already exists
@@ -0,0 +1,14 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
and result would be saved as new snapshot snap3.
Loading packages (56830)...
Building indexes...
[+] mame-0.146-5_amd64 added
[+] unrar-1:4.1.4-1_amd64 added
[+] mame-0.146-5_i386 added
[+] unrar-1:4.1.4-1_i386 added
Snapshot snap3 successfully created.
You can run 'aptly publish snapshot snap3' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-backports]: http://mirror.yandex.ru/debian/ wheezy-backports
and result would be saved as new snapshot snap3.
Loading packages (58973)...
Building indexes...
[-] rsyslog-5.8.11-3_amd64 removed
[+] rsyslog-7.4.4-1~bpo70+1_amd64 added
[-] libestr0-0.1.1-2_amd64 removed
[+] libestr0-0.1.9-1~bpo70+1_amd64 added
[+] init-system-helpers-1.11~bpo70.1_all added
[-] rsyslog-5.8.11-3_i386 removed
[+] rsyslog-7.4.4-1~bpo70+1_i386 added
[-] libestr0-0.1.1-2_i386 removed
[+] libestr0-0.1.9-1~bpo70+1_i386 added
Snapshot snap3 successfully created.
You can run 'aptly publish snapshot snap3' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-backports]: http://mirror.yandex.ru/debian/ wheezy-backports
and result would be saved as new snapshot snap3.
Loading packages (58973)...
Building indexes...
[-] rsyslog-5.8.11-3_amd64 removed
[+] rsyslog-7.4.4-1~bpo70+1_amd64 added
[-] rsyslog-5.8.11-3_i386 removed
[+] rsyslog-7.4.4-1~bpo70+1_i386 added
Snapshot snap3 successfully created.
You can run 'aptly publish snapshot snap3' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-backports]: http://mirror.yandex.ru/debian/ wheezy-backports
and result would be saved as new snapshot snap3.
Loading packages (58973)...
Building indexes...
[-] rsyslog-5.8.11-3_amd64 removed
[+] rsyslog-7.4.4-1~bpo70+1_amd64 added
[-] libestr0-0.1.1-2_amd64 removed
[+] libestr0-0.1.9-1~bpo70+1_amd64 added
[+] init-system-helpers-1.11~bpo70.1_all added
[-] rsyslog-5.8.11-3_i386 removed
[+] rsyslog-7.4.4-1~bpo70+1_i386 added
[-] libestr0-0.1.1-2_i386 removed
[+] libestr0-0.1.9-1~bpo70+1_i386 added
Not creating snapshot, as dry run was requested.
@@ -0,0 +1,5 @@
List of snapshots:
* [snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
* [snap2]: Snapshot from mirror [wheezy-backports]: http://mirror.yandex.ru/debian/ wheezy-backports
To get more information about snapshot, run `aptly snapshot show <name>`.
@@ -0,0 +1 @@
ERROR: unable to pull: snapshot with name snap-no not found
@@ -0,0 +1 @@
ERROR: unable to pull: snapshot with name snap-no not found
@@ -0,0 +1,17 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-backports]: http://mirror.yandex.ru/debian/ wheezy-backports
and result would be saved as new snapshot snap1.
Loading packages (58973)...
Building indexes...
[-] rsyslog-5.8.11-3_amd64 removed
[+] rsyslog-7.4.4-1~bpo70+1_amd64 added
[-] libestr0-0.1.1-2_amd64 removed
[+] libestr0-0.1.9-1~bpo70+1_amd64 added
[+] init-system-helpers-1.11~bpo70.1_all added
[-] rsyslog-5.8.11-3_i386 removed
[+] rsyslog-7.4.4-1~bpo70+1_i386 added
[-] libestr0-0.1.1-2_i386 removed
[+] libestr0-0.1.9-1~bpo70+1_i386 added
ERROR: unable to create snapshot: snapshot with name snap1 already exists
@@ -0,0 +1,14 @@
Dependencies would be pulled into snapshot:
[snap1]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
from snapshot:
[snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
and result would be saved as new snapshot snap3.
Loading packages (56830)...
Building indexes...
[!] Dependency lunar-landing [amd64] can't be satisfied with source [snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
[!] Dependency mars-landing (>= 1.0) [amd64] can't be satisfied with source [snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
[!] Dependency lunar-landing [i386] can't be satisfied with source [snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
[!] Dependency mars-landing (>= 1.0) [i386] can't be satisfied with source [snap2]: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
Snapshot snap3 successfully created.
You can run 'aptly publish snapshot snap3' to publish snapshot as Debian repository.
File diff suppressed because it is too large Load Diff
+666
View File
@@ -0,0 +1,666 @@
Name: snap1
Created At: 2014-01-24 13:06:43 MSK
Description: Snapshot from mirror [wheezy-non-free]: http://mirror.yandex.ru/debian/ wheezy
Number of packages: 661
Packages:
abs-guide-6.5-1_all
album-4.06-2_all
album-data-4.05-2_all
alien-arena-data-7.53-1_all
amoeba-data-1.1-6_all
assaultcube-data-1.1.0.4+repack1-2.1~deb7u1_all
asterisk-prompt-es-1.4-1_all
atmel-firmware-1.3-4_all
autodocktools-1.5.6~rc3~cvs.20120206-1_all
automake1.9-doc-1.9.6-1_all
bison-doc-1:2.5-1_all
bluez-firmware-1.2-3_all
broadcom-sta-common-5.100.82.112-8_all
broadcom-sta-dkms-5.100.82.112-8_all
broadcom-sta-source-5.100.82.112-8_all
cclib-data-1.0.1-3_all
celestia-common-nonfree-1.6.1-1_all
context-doc-nonfree-2012.06.27-2_all
context-nonfree-2007.03.22-1_all
coq-doc-8.3pl4-1_all
coq-doc-html-8.3pl4-1_all
coq-doc-pdf-8.3pl4-1_all
cpp-4.4-doc-4.4.7-3_all
cpp-4.6-doc-4.6.3-2_all
cpp-4.7-doc-4.7.2-2_all
crash-whitepaper-1.0-1.1_all
cuneiform-common-1.1.0+dfsg-4_all
dahdi-firmware-nonfree-2.6.1-1_all
doc-rfc-20120225-2_all
doc-rfc-experimental-20120225-2_all
doc-rfc-fyi-bcp-20120225-2_all
doc-rfc-informational-20120225-2_all
doc-rfc-misc-20120225-2_all
doc-rfc-old-std-20120225-2_all
doc-rfc-others-20120225-2_all
doc-rfc-std-20120225-2_all
doc-rfc-std-proposed-20120225-2_all
doom-wad-shareware-1.9.fixed-2_all
eagle-data-5.12.0-3_all
ebook-dev-alp-200407-1_all
elmer-doc-2011.09.06-1_all
emacs23-common-non-dfsg-23.4+1-1_all
emacs24-common-non-dfsg-24.1+1-1_all
etoys-4.0.2340-1_all
etoys-doc-4.0.2340-1_all
festlex-oald-1.4.0-3.1_all
festvox-ellpc11k-1.4.0-3_all
firmware-atheros-0.36+wheezy.1_all
firmware-bnx2-0.36+wheezy.1_all
firmware-bnx2x-0.36+wheezy.1_all
firmware-brcm80211-0.36+wheezy.1_all
firmware-intelwimax-0.36+wheezy.1_all
firmware-ipw2x00-0.36+wheezy.1_all
firmware-ivtv-0.36+wheezy.1_all
firmware-iwlwifi-0.36+wheezy.1_all
firmware-libertas-0.36+wheezy.1_all
firmware-linux-0.36+wheezy.1_all
firmware-linux-nonfree-0.36+wheezy.1_all
firmware-myricom-0.36+wheezy.1_all
firmware-netxen-0.36+wheezy.1_all
firmware-qlogic-0.36+wheezy.1_all
firmware-ralink-0.36+wheezy.1_all
firmware-realtek-0.36+wheezy.1_all
foiltex-2.1.4b-3_all
fonts-ipafont-nonfree-jisx0208-00103-19_all
fonts-ipafont-nonfree-uigothic-00203-21_all
fonts-larabie-deco-1:20011216-4_all
fonts-larabie-straight-1:20011216-4_all
fonts-larabie-uncommon-1:20011216-4_all
fonts-mikachan-9.1-8_all
fonts-moe-standard-kai-20120530-1_all
fonts-moe-standard-song-20120530-1_all
frogatto-data-1.2+dfsg-1_all
fsl-4.1.9-7_all
fsl-doc-4.1-4.1.9-7_all
gawk-doc-4.0.1+ds-1_all
gcc-4.4-doc-4.4.7-3_all
gcc-4.6-doc-4.6.3-2_all
gcc-4.7-doc-4.7.2-2_all
gcc-doc-base-4.7.2-2_all
gccgo-4.6-doc-4.6.3-2_all
gccgo-4.7-doc-4.7.2-2_all
gcj-4.6-doc-4.6.3-2_all
gcj-4.7-doc-4.7.2-2_all
gdb-doc-7.4.1-2_all
gfortran-4.4-doc-4.4.7-3_all
gfortran-4.6-doc-4.6.3-2_all
gfortran-4.7-doc-4.7.2-2_all
glibc-doc-reference-2.13-1_all
gliese-3.0.95-2_all
gmp-doc-5.0.5-1_all
gnat-4.4-doc-4.4.7-3_all
gnat-4.6-doc-4.6.3-2_all
gsfonts-other-6.0-4_all
gsl-doc-info-1.15-1_all
gsl-doc-pdf-1.15-1_all
gsl-ref-html-1.15-1_all
gsl-ref-psdoc-1.15-1_all
guile-1.8-doc-non-dfsg-1.8.8+1-1.1_all
hevea-doc-1.10-3_all
hijra-applet-0.2.1-1_all
human-icon-theme-0.28.debian-3.3_all
hwb-1:040412-3_all
icc-profiles-2.0.1-1_all
igv-2.0.30-1_all
jhove-1.6+dfsg-1_all
kstars-data-extra-tycho2-1.1r1-9_all
libcolt-java-1.2.0~nojar-2_all
libcolt-java-doc-1.2.0~nojar-2_all
libcore++-demo-1.7-12_all
libcore++-doc-1.7-12_all
libcupti-doc-4.2.9-2_all
libcwd-doc-1.0.4-1_all
libertas-firmware-9.70.7.p0.0-2_all
libf2j-java-0.8.1-2_all
libgeotiff-epsg-1.3.0-1_all
libjabsorb-java-1.3-2_all
libjai-core-java-doc-1.1.4-3_all
libjai-imageio-core-java-doc-1.2-3_all
libmail-sender-perl-0.8.16-2_all
libstar-parser-perl-0.59-3_all
libttspico-data-1.0+git20110131-2_all
libvideo-info-perl-0.993-2_all
libworldwind-java-0.5.0-10_all
lugaru-data-0~20110520.1+hge4354-1_all
make-doc-3.81-5.1_all
manpages-posix-2.16-1_all
manpages-posix-dev-2.16-1_all
mbrola-af1-0.0.20040426-2_all
mbrola-br1-2.021-1_all
mbrola-br3-2.021-2_all
mbrola-cr1-0.0.19981028-2_all
mbrola-cz2-0.2-2_all
mbrola-de4-0.0.20020812-1_all
mbrola-de5-1.0-1_all
mbrola-de6-0.0.20021125-2_all
mbrola-de7-0.0.20030404-2_all
mbrola-ee1-0.0.20020407-1_all
mbrola-en1-19980910-2_all
mbrola-es1-0.0.19980610-2_all
mbrola-es2-2.069-1_all
mbrola-fr1-2.050-1_all
mbrola-fr4-0.0.19990521-2_all
mbrola-gr1-19990610-1_all
mbrola-gr2-0.0.20010521-2_all
mbrola-hu1-1.002-2_all
mbrola-id1-1-2_all
mbrola-it3-0.1-2_all
mbrola-it4-0.1-2_all
mbrola-la1-0.0.20050615-2_all
mbrola-mx2-0.1-1_all
mbrola-nl2-0.5-2_all
mbrola-pl1-0.1-2_all
mbrola-pt1-1.0-2_all
mbrola-ro1-1.00-2_all
mbrola-sw1-1.00-2_all
mbrola-sw2-1.0-2_all
mbrola-us1-0.3-2_all
mbrola-us2-0.1-2_all
mbrola-us3-0.1-1_all
mbrola-vz1-2-1_all
mess-data-0.146-4_all
mgltools-cadd-1.5.6~rc3~cvs.20120206-1_all
mgltools-dejavu-1.5.6~rc3~cvs.20120206-1_all
mgltools-mglutil-1.5.6~rc3~cvs.20120206-1_all
mgltools-molkit-1.5.6~rc3~cvs.20120206-1_all
mgltools-networkeditor-1.5.6~rc3~cvs.20120206-1_all
mgltools-pmv-1.5.6~rc3~cvs.20120206-1_all
mgltools-pmv-test-1.5.6~rc3~cvs.20120206-1_all
mgltools-pyautodock-1.5.6~rc3~cvs.20120206-1_all
mgltools-pybabel-1.5.6~rc3~cvs.20120206-1_all
mgltools-scenario2-1.5.6~rc3~cvs.20120206-1_all
mgltools-support-1.5.6~rc3~cvs.20120206-1_all
mgltools-symserv-1.5.6~rc3~cvs.20120206-1_all
mgltools-viewerframework-1.5.6~rc3~cvs.20120206-2_all
mgltools-vision-1.5.6~rc3~cvs.20120601-1_all
mgltools-visionlibraries-1.5.6~rc3~cvs.20120601-1_all
mgltools-volume-1.5.6~rc3~cvs.20120206-1_all
mgltools-webservices-1.5.6~rc3~cvs.20120206-1_all
midisport-firmware-1.2-4_all
mpi-specs-20040719-2_all
ngspice-doc-24-1_all
nikto-1:2.1.4-2_all
noshell-4.0.11+notdfsg1-5_all
notion-dev-3+2012042300-1_all
nvidia-cg-doc-3.1.0013-1_all
nvidia-cuda-doc-4.2.9-2_all
ocaml-book-en-1.0-5_all
ocaml-book-fr-1.0-5_all
ocaml-doc-3.12-2_all
ooohg-09.12a-8_all
openttd-opensfx-0.2.3-3_all
os8-2.1-4_all
othman-0.2.7-1_all
out-of-order-1.0-2_all
paml-doc-4.5-1_all
parmetis-doc-3.1.1-4_all
phy-spread-1.0.3-1_all
phylip-doc-1:3.69-1_all
picon-domains-2012.05.09-1_all
picon-misc-2010.01.02-1_all
picon-news-2010.01.02-1_all
picon-unknown-2010.01.02-1_all
picon-usenix-1995.04.13-8_all
picon-users-2012.05.14-1_all
picon-weather-2010.01.02-1_all
ptex-jtex-1.7+1-13_all
python-hijra-0.2.1-1_all
python-okasha-0.2.4-1_all
python-okasha-examples-0.2.4-1_all
python-othman-0.2.7-1_all
redeclipse-data-1.2-1_all
rubybook-0.2.1-1_all
sauerbraten-data-0.0.20100728+repack-1_all
scribus-doc-1.4.0+r17300-1_all
scribus-ng-doc-1.4.0+r17300-1_all
selfhtml-8.1.2-1_all
sgb-doc-1:20090810-1_all
shapetools-tutorial-1.3-3.1_all
sisu-markup-samples-3.0.1-1_all
sl-modem-source-2.9.11~20110321-8+deb7u1_all
snmp-mibs-downloader-1.1_all
spectrum-roms-20081224-3_all
spellcast-doc-1.5_all
stardict-english-czech-20120601-1_all
stardict-german-czech-20120201-1_all
t1-xfree86-nonfree-4.2.1-3.1_all
tads3-common-1:0.13-2_all
tangerine-icon-theme-0.26.debian-3_all
tar-doc-1.26-2_all
texinfo-doc-nonfree-4.13a-1_all
thawab-3.0.13-1_all
ttf-ipafont-jisx0208-00103-19_all
ttf-ipafont-uigothic-00203-21_all
ttf-kochi-gothic-naga10-20030809-14_all
ttf-kochi-mincho-naga10-20030809-14_all
ttf-larabie-deco-1:20011216-4_all
ttf-larabie-straight-1:20011216-4_all
ttf-larabie-uncommon-1:20011216-4_all
ttf-mikachan-9.1-8_all
ttf-xfree86-nonfree-4.2.1-3.1_all
ttf-xfree86-nonfree-syriac-4.2.1-3.1_all
ttytter-2.1.0-1~deb7u1_all
uqm-content-0.6.0+deb1-6_all
uqm-music-0.6.0+deb1-6_all
uqm-voice-0.6.0+deb1-6_all
virtualbox-guest-additions-4.1.18-1_all
virtualbox-guest-additions-iso-4.1.18-1_all
vmtk-1.0.1-1_all
w3-recs-20110107-1_all
worldwind-0.5.0-10_all
worldwind-doc-0.5.0-10_all
xfonts-naga10-1.1-13.1_all
xfonts-x3270-misc-3.3.10ga4-2_all
xmame-sdl-0.146-5_all
xmame-tools-0.146-4_all
xmame-x-0.146-5_all
xmess-sdl-0.146-4_all
xmess-x-0.146-4_all
xml2rfc-1.36-5_all
xtide-data-nonfree-20100529-1_all
yale-5.0.95-2_all
zangband-data-1:2.7.5pre1-8_all
zd1211-firmware-2.21.0.0-1_all
zekr-1.0.0+repack-7_all
zeroc-ice-manual-3.4.2-1_all
3270-common-3.3.10ga4-2+b1_amd64
abyss-1.3.4-3_amd64
agrep-4.17-9_amd64
amd-clinfo-1:12-6+point-3_amd64
amd-libopencl1-1:12-6+point-3_amd64
amd-opencl-dev-1:12-6+point-3_amd64
amd-opencl-icd-1:12-6+point-3_amd64
amd64-microcode-1.20120910-2_amd64
amiwm-0.20.48-8_amd64
blimps-examples-3.9-1_amd64
blimps-utils-3.9-1_amd64
bsdgames-nonfree-2.17-5_amd64
bugsx-1.08-12_amd64
c3270-3.3.10ga4-2+b1_amd64
clustalw-mpi-0.15-2_amd64
conserver-client-8.1.18-2.2_amd64
conserver-server-8.1.18-2.2_amd64
crafty-23.4-6_amd64
cufflinks-1.3.0-2_amd64
cuneiform-1.1.0+dfsg-4_amd64
dynamips-0.2.7-0.2.8RC2-5.1_amd64
embassy-phylip-3.69+20110714-1_amd64
f2j-0.8.1-2_amd64
fatrat-unpack-1.1.3-2_amd64
fglrx-atieventsd-1:12-6+point-3_amd64
fglrx-control-1:12-6+point-3_amd64
fglrx-driver-1:12-6+point-3_amd64
fglrx-glx-1:12-6+point-3_amd64
fglrx-glx-ia32-1:12-6+point-3_amd64
fglrx-modules-dkms-1:12-6+point-3_amd64
fglrx-source-1:12-6+point-3_amd64
frobtads-1:0.13-2_amd64
fsl-4.1-4.1.9-7_amd64
gmap-2012-06-12-1_amd64
intel-microcode-1.20130906.1_amd64
iozone3-397-2_amd64
kic-2.4a-1.1_amd64
libapache2-mod-fastcgi-2.4.7~0910052141-1_amd64
libblimps3-3.9-1_amd64
libblimps3-dev-3.9-1_amd64
libcamlpdf-ocaml-dev-0.5-1+b2_amd64
libcg-3.1.0013-1_amd64
libcggl-3.1.0013-1_amd64
libclamunrar6-0.96.4-1_amd64
libcore++-dev-1.7-12_amd64
libcore++1c2-1.7-12_amd64
libcublas4-4.2.9-2_amd64
libcuda1-304.88-1+deb7u1_amd64
libcuda1-ia32-304.88-1+deb7u1_amd64
libcudart4-4.2.9-2_amd64
libcufft4-4.2.9-2_amd64
libcuinj4-4.2.9-2_amd64
libcuneiform-dev-1.1.0+dfsg-4_amd64
libcuneiform0-1.1.0+dfsg-4_amd64
libcupti-dev-4.2.9-2_amd64
libcupti4-4.2.9-2_amd64
libcurand4-4.2.9-2_amd64
libcusparse4-4.2.9-2_amd64
libcwd-1.0.4-1_amd64
libfglrx-1:12-6+point-3_amd64
libfglrx-amdxvba1-1:12-6+point-3_amd64
libfglrx-ia32-1:12-6+point-3_amd64
libgl1-fglrx-glx-1:12-6+point-3_amd64
libgl1-nvidia-alternatives-304.88-1+deb7u1_amd64
libgl1-nvidia-alternatives-ia32-304.88-1+deb7u1_amd64
libgl1-nvidia-glx-304.88-1+deb7u1_amd64
libgl1-nvidia-glx-ia32-304.88-1+deb7u1_amd64
libgl1-nvidia-legacy-173xx-glx-173.14.35-4_amd64
libgl1-nvidia-legacy-173xx-glx-ia32-173.14.35-4_amd64
libgl1-nvidia-legacy-71xx-glx-71.86.15-3_amd64
libgl1-nvidia-legacy-71xx-glx-ia32-71.86.15-3_amd64
libgl1-nvidia-legacy-96xx-glx-96.43.23-3_amd64
libgl1-nvidia-legacy-96xx-glx-ia32-96.43.23-3_amd64
libglx-nvidia-alternatives-304.88-1+deb7u1_amd64
libjai-core-java-1.1.4-3_amd64
libjai-imageio-core-java-1.2-3_amd64
libmath-random-perl-0.71-3+b1_amd64
libmetis-edf-dev-4.1-2-3_amd64
libmetis-edf4.1-4.1-2-3_amd64
libmotif-dev-2.3.3-8_amd64
libmotif4-2.3.3-8_amd64
libmotif4-dbg-2.3.3-8_amd64
libnauty-dev-2.4r2-1_amd64
libnauty1d-2.4r2-1_amd64
libnpp4-4.2.9-2_amd64
libnvcuvid1-304.88-1+deb7u1_amd64
libnvidia-compiler-304.88-1+deb7u1_amd64
libnvidia-compiler-ia32-304.88-1+deb7u1_amd64
libnvidia-ml1-304.88-1+deb7u1_amd64
libparmetis-dev-3.1.1-4_amd64
libparmetis3.1-3.1.1-4_amd64
libtet1.4-1.4.3-1_amd64
libtet1.4-dev-1.4.3-1_amd64
libtriangle-1.6-1.6-2_amd64
libtriangle-dev-1.6-2_amd64
libttspico-dev-1.0+git20110131-2_amd64
libttspico-utils-1.0+git20110131-2_amd64
libttspico0-1.0+git20110131-2_amd64
libvmtk-dev-1.0.1-1_amd64
libvmtk1.0-1.0.1-1_amd64
libxvbaw-dev-1:12-6+point-3_amd64
libxvmcnvidia1-304.88-1+deb7u1_amd64
madfuload-1.2-4_amd64
mame-0.146-5_amd64
mame-tools-0.146-4_amd64
mbrola-3.01h-6_amd64
mess-0.146-4_amd64
metis-edf-4.1-2-3_amd64
mgltools-bhtree-1.5.6~rc3~cvs.20120206-1_amd64
mgltools-geomutils-1.5.6~rc3~cvs.20120601-1_amd64
mgltools-gle-1.5.6~rc3~cvs.20120601-1_amd64
mgltools-opengltk-1.5.6~rc3~cvs.20120601-2_amd64
mgltools-pyglf-1.5.6~rc3~cvs.20120601-1_amd64
mgltools-sff-1.5.6~rc3~cvs.20120206-1_amd64
mgltools-utpackages-1.5.6~rc3~cvs.20120601-1_amd64
motif-clients-2.3.3-8_amd64
mssstest-3.0-3_amd64
nautilus-dropbox-1.4.0-3_amd64
nauty-2.4r2-1_amd64
netperf-2.4.4-6.1_amd64
ngspice-24-1_amd64
notion-3+2012042300-1_amd64
nttcp-1.47-13_amd64
nvidia-alternative-304.88-1+deb7u1_amd64
nvidia-alternative-legacy-173xx-173.14.35-4_amd64
nvidia-alternative-legacy-71xx-71.86.15-3_amd64
nvidia-alternative-legacy-96xx-96.43.23-3_amd64
nvidia-cg-dev-3.1.0013-1_amd64
nvidia-cg-toolkit-3.1.0013-1_amd64
nvidia-cuda-dev-4.2.9-2_amd64
nvidia-cuda-gdb-4.2.9-2_amd64
nvidia-cuda-toolkit-4.2.9-2_amd64
nvidia-detect-304.88-1+deb7u1_amd64
nvidia-glx-304.88-1+deb7u1_amd64
nvidia-glx-ia32-304.88-1+deb7u1_amd64
nvidia-glx-legacy-71.86.15-3_amd64
nvidia-glx-legacy-173xx-173.14.35-4_amd64
nvidia-glx-legacy-173xx-ia32-173.14.35-4_amd64
nvidia-glx-legacy-71xx-71.86.15-3_amd64
nvidia-glx-legacy-71xx-dev-71.86.15-3_amd64
nvidia-glx-legacy-71xx-ia32-71.86.15-3_amd64
nvidia-glx-legacy-71xx-unsupported-71.86.15-3_amd64
nvidia-glx-legacy-96xx-96.43.23-3_amd64
nvidia-glx-legacy-96xx-ia32-96.43.23-3_amd64
nvidia-glx-legacy-dev-71.86.15-3_amd64
nvidia-glx-legacy-ia32-71.86.15-3_amd64
nvidia-kernel-2.6-amd64-304.88+1_amd64
nvidia-kernel-3.2.0-4-amd64-304.88+1+1+3.2.41-2_amd64
nvidia-kernel-amd64-304.88+1_amd64
nvidia-kernel-dkms-304.88-1+deb7u1_amd64
nvidia-kernel-legacy-173xx-dkms-173.14.35-4_amd64
nvidia-kernel-legacy-173xx-source-173.14.35-4_amd64
nvidia-kernel-legacy-71xx-dkms-71.86.15-3_amd64
nvidia-kernel-legacy-71xx-source-71.86.15-3_amd64
nvidia-kernel-legacy-96xx-dkms-96.43.23-3_amd64
nvidia-kernel-legacy-96xx-source-96.43.23-3_amd64
nvidia-kernel-legacy-source-71.86.15-3_amd64
nvidia-kernel-source-304.88-1+deb7u1_amd64
nvidia-libopencl1-304.88-1+deb7u1_amd64
nvidia-libopencl1-ia32-304.88-1+deb7u1_amd64
nvidia-opencl-common-304.88-1+deb7u1_amd64
nvidia-opencl-dev-4.2.9-2_amd64
nvidia-opencl-icd-304.88-1+deb7u1_amd64
nvidia-opencl-icd-ia32-304.88-1+deb7u1_amd64
nvidia-smi-304.88-1+deb7u1_amd64
nvidia-vdpau-driver-304.88-1+deb7u1_amd64
nvidia-vdpau-driver-ia32-304.88-1+deb7u1_amd64
nvidia-visual-profiler-4.2.9-2_amd64
p7zip-rar-9.20.1~ds.1-3_amd64
paml-4.5-1_amd64
parmetis-test-3.1.1-4_amd64
pgplot5-5.2.2-19_amd64
phylip-1:3.69-1_amd64
powder-117-1_amd64
pptview-8.0-7_amd64
pr3287-3.3.10ga4-2+b1_amd64
python-vmtk-1.0.1-1_amd64
r-cran-maptools-1:0.7-38-1_amd64
rar-2:4.0.b3-1_amd64
s3270-3.3.10ga4-2+b1_amd64
sdlmame-0.146-5_amd64
sdlmame-tools-0.146-4_amd64
seaview-1:4.3.3-3_amd64
seq-gen-1.3.3-1_amd64
sgb-1:20090810-1_amd64
sift-4.0.3b-3_amd64
sl-modem-daemon-2.9.11~20110321-8+deb7u1_amd64
sl-modem-dkms-2.9.11~20110321-8+deb7u1_amd64
sparse-0.4.3+20110419-1_amd64
tads2-dev-1:0.13-2_amd64
tads3-dev-1:0.13-2_amd64
tclspice-24-1_amd64
teamspeak-client-2.0.32-3.1_amd64
tetgen-1.4.3-1_amd64
titantools-4.0.11+notdfsg1-5_amd64
tome-2.3.5-2_amd64
triangle-bin-1.6-2_amd64
trn4-4.0-test77-6_amd64
unace-nonfree-2.5-7_amd64
unrar-1:4.1.4-1_amd64
wap-wml-tools-0.0.4-6_amd64
x3270-3.3.10ga4-2+b1_amd64
xfractint-20.4.10-2_amd64
xserver-xorg-video-nvidia-304.88-1+deb7u1_amd64
xserver-xorg-video-nvidia-legacy-173xx-173.14.35-4_amd64
xserver-xorg-video-nvidia-legacy-71xx-71.86.15-3_amd64
xserver-xorg-video-nvidia-legacy-96xx-96.43.23-3_amd64
xsnow-1:1.42-9_amd64
zangband-1:2.7.5pre1-8_amd64
3270-common-3.3.10ga4-2+b1_i386
amd-clinfo-1:12-6+point-3_i386
amd-libopencl1-1:12-6+point-3_i386
amd-opencl-dev-1:12-6+point-3_i386
amd-opencl-icd-1:12-6+point-3_i386
amd64-microcode-1.20120910-2_i386
amiwm-0.20.48-8_i386
axe-6.1.2-15.1_i386
bsdgames-nonfree-2.17-5_i386
bugsx-1.08-12_i386
c3270-3.3.10ga4-2+b1_i386
conserver-client-8.1.18-2.2_i386
conserver-server-8.1.18-2.2_i386
crafty-23.4-6_i386
cuneiform-1.1.0+dfsg-4_i386
dgen-1.23-12_i386
drdsl-1.2.0-1_i386
dynamips-0.2.7-0.2.8RC2-5.1_i386
eagle-5.12.0-3_i386
f2j-0.8.1-2_i386
fatrat-unpack-1.1.3-2_i386
fglrx-atieventsd-1:12-6+point-3_i386
fglrx-control-1:12-6+point-3_i386
fglrx-driver-1:12-6+point-3_i386
fglrx-glx-1:12-6+point-3_i386
fglrx-modules-dkms-1:12-6+point-3_i386
fglrx-source-1:12-6+point-3_i386
fsl-4.1-4.1.9-7_i386
intel-microcode-1.20130906.1_i386
iozone3-397-2_i386
irpas-0.10-4.1_i386
lgrind-3.67-3_i386
libapache2-mod-fastcgi-2.4.7~0910052141-1_i386
libcamlpdf-ocaml-dev-0.5-1+b2_i386
libcg-3.1.0013-1_i386
libcggl-3.1.0013-1_i386
libclamunrar6-0.96.4-1_i386
libcore++-dev-1.7-12_i386
libcore++1c2-1.7-12_i386
libcublas4-4.2.9-2_i386
libcuda1-304.88-1+deb7u1_i386
libcudart4-4.2.9-2_i386
libcufft4-4.2.9-2_i386
libcuinj4-4.2.9-2_i386
libcuneiform-dev-1.1.0+dfsg-4_i386
libcuneiform0-1.1.0+dfsg-4_i386
libcupti-dev-4.2.9-2_i386
libcupti4-4.2.9-2_i386
libcurand4-4.2.9-2_i386
libcusparse4-4.2.9-2_i386
libcwd-1.0.4-1_i386
libfglrx-1:12-6+point-3_i386
libfglrx-amdxvba1-1:12-6+point-3_i386
libgl1-fglrx-glx-1:12-6+point-3_i386
libgl1-nvidia-alternatives-304.88-1+deb7u1_i386
libgl1-nvidia-glx-304.88-1+deb7u1_i386
libgl1-nvidia-legacy-173xx-glx-173.14.35-4_i386
libgl1-nvidia-legacy-71xx-glx-71.86.15-3_i386
libgl1-nvidia-legacy-96xx-glx-96.43.23-3_i386
libglx-nvidia-alternatives-304.88-1+deb7u1_i386
libgpcl-dev-2.32-1_i386
libgpcl0-2.32-1_i386
libmath-random-perl-0.71-3+b1_i386
libmotif-dev-2.3.3-8_i386
libmotif4-2.3.3-8_i386
libmotif4-dbg-2.3.3-8_i386
libnauty-dev-2.4r2-1_i386
libnauty1d-2.4r2-1_i386
libnpp4-4.2.9-2_i386
libnvcuvid1-304.88-1+deb7u1_i386
libnvidia-compiler-304.88-1+deb7u1_i386
libnvidia-ml1-304.88-1+deb7u1_i386
libparmetis-dev-3.1.1-4_i386
libparmetis3.1-3.1.1-4_i386
libtet1.4-1.4.3-1_i386
libtet1.4-dev-1.4.3-1_i386
libtriangle-1.6-1.6-2_i386
libtriangle-dev-1.6-2_i386
libttspico-dev-1.0+git20110131-2_i386
libttspico-utils-1.0+git20110131-2_i386
libttspico0-1.0+git20110131-2_i386
libvmtk-dev-1.0.1-1_i386
libvmtk1.0-1.0.1-1_i386
libxvbaw-dev-1:12-6+point-3_i386
libxvmcnvidia1-304.88-1+deb7u1_i386
madfuload-1.2-4_i386
mame-0.146-5_i386
mame-tools-0.146-4_i386
martian-modem-20080625-2_i386
martian-modem-source-20080625-2_i386
mbrola-3.01h-6_i386
mess-0.146-4_i386
mgltools-bhtree-1.5.6~rc3~cvs.20120206-1_i386
mgltools-geomutils-1.5.6~rc3~cvs.20120601-1_i386
mgltools-gle-1.5.6~rc3~cvs.20120601-1_i386
mgltools-opengltk-1.5.6~rc3~cvs.20120601-2_i386
mgltools-pyglf-1.5.6~rc3~cvs.20120601-1_i386
mgltools-sff-1.5.6~rc3~cvs.20120206-1_i386
mgltools-utpackages-1.5.6~rc3~cvs.20120601-1_i386
motif-clients-2.3.3-8_i386
mssstest-3.0-3_i386
nautilus-dropbox-1.4.0-3_i386
nauty-2.4r2-1_i386
netperf-2.4.4-6.1_i386
notion-3+2012042300-1_i386
nvidia-alternative-304.88-1+deb7u1_i386
nvidia-alternative-legacy-173xx-173.14.35-4_i386
nvidia-alternative-legacy-71xx-71.86.15-3_i386
nvidia-alternative-legacy-96xx-96.43.23-3_i386
nvidia-cg-dev-3.1.0013-1_i386
nvidia-cg-toolkit-3.1.0013-1_i386
nvidia-cuda-dev-4.2.9-2_i386
nvidia-cuda-gdb-4.2.9-2_i386
nvidia-cuda-toolkit-4.2.9-2_i386
nvidia-detect-304.88-1+deb7u1_i386
nvidia-glx-304.88-1+deb7u1_i386
nvidia-glx-legacy-71.86.15-3_i386
nvidia-glx-legacy-173xx-173.14.35-4_i386
nvidia-glx-legacy-71xx-71.86.15-3_i386
nvidia-glx-legacy-71xx-dev-71.86.15-3_i386
nvidia-glx-legacy-71xx-unsupported-71.86.15-3_i386
nvidia-glx-legacy-96xx-96.43.23-3_i386
nvidia-glx-legacy-dev-71.86.15-3_i386
nvidia-kernel-2.6-486-304.88+1_i386
nvidia-kernel-2.6-686-pae-304.88+1_i386
nvidia-kernel-2.6-amd64-304.88+1_i386
nvidia-kernel-3.2.0-4-486-304.88+1+1+3.2.41-2_i386
nvidia-kernel-3.2.0-4-686-pae-304.88+1+1+3.2.41-2_i386
nvidia-kernel-3.2.0-4-amd64-304.88+1+1+3.2.41-2_i386
nvidia-kernel-486-304.88+1_i386
nvidia-kernel-686-pae-304.88+1_i386
nvidia-kernel-amd64-304.88+1_i386
nvidia-kernel-dkms-304.88-1+deb7u1_i386
nvidia-kernel-legacy-173xx-dkms-173.14.35-4_i386
nvidia-kernel-legacy-173xx-source-173.14.35-4_i386
nvidia-kernel-legacy-71xx-dkms-71.86.15-3_i386
nvidia-kernel-legacy-71xx-source-71.86.15-3_i386
nvidia-kernel-legacy-96xx-dkms-96.43.23-3_i386
nvidia-kernel-legacy-96xx-source-96.43.23-3_i386
nvidia-kernel-legacy-source-71.86.15-3_i386
nvidia-kernel-source-304.88-1+deb7u1_i386
nvidia-libopencl1-304.88-1+deb7u1_i386
nvidia-opencl-common-304.88-1+deb7u1_i386
nvidia-opencl-dev-4.2.9-2_i386
nvidia-opencl-icd-304.88-1+deb7u1_i386
nvidia-smi-304.88-1+deb7u1_i386
nvidia-vdpau-driver-304.88-1+deb7u1_i386
nvidia-visual-profiler-4.2.9-2_i386
p7zip-rar-9.20.1~ds.1-3_i386
parmetis-test-3.1.1-4_i386
pgplot5-5.2.2-19_i386
phylip-1:3.69-1_i386
pptview-8.0-7_i386
pr3287-3.3.10ga4-2+b1_i386
python-vmtk-1.0.1-1_i386
r-cran-maptools-1:0.7-38-1_i386
rar-2:4.0.b3-1_i386
s3270-3.3.10ga4-2+b1_i386
sdlmame-0.146-5_i386
sdlmame-tools-0.146-4_i386
seaview-1:4.3.3-3_i386
seq-gen-1.3.3-1_i386
sgb-1:20090810-1_i386
sl-modem-daemon-2.9.11~20110321-8+deb7u1_i386
sl-modem-dkms-2.9.11~20110321-8+deb7u1_i386
sparse-0.4.3+20110419-1_i386
spellcast-1.0-21_i386
teamspeak-client-2.0.32-3.1_i386
teamspeak-server-2.0.24.1+debian-1.1_i386
tetgen-1.4.3-1_i386
titantools-4.0.11+notdfsg1-5_i386
tome-2.3.5-2_i386
triangle-bin-1.6-2_i386
trn-3.6-23_i386
trn4-4.0-test77-6_i386
unace-nonfree-2.5-7_i386
unrar-1:4.1.4-1_i386
wap-wml-tools-0.0.4-6_i386
x3270-3.3.10ga4-2+b1_i386
xfractint-20.4.10-2_i386
xmame-svga-0.146-5_i386
xserver-xorg-video-nvidia-304.88-1+deb7u1_i386
xserver-xorg-video-nvidia-legacy-173xx-173.14.35-4_i386
xserver-xorg-video-nvidia-legacy-71xx-71.86.15-3_i386
xserver-xorg-video-nvidia-legacy-96xx-96.43.23-3_i386
zangband-1:2.7.5pre1-8_i386
@@ -0,0 +1 @@
ERROR: unable to show: snapshot with name no-such-snapshot not found
@@ -0,0 +1,12 @@
Missing dependencies (11):
fenix [amd64]
fenix-plugins-system [amd64]
ia32-libs-gtk-i386 [amd64]
ia32-libs-i386 [amd64]
kbdcontrol [amd64]
kbdcontrol [i386]
mozart (>= 1.4.0) [amd64]
scsh-0.6 (>= 0.6.6) [amd64]
scsh-0.6 [amd64]
vidcontrol [amd64]
vidcontrol [i386]
@@ -0,0 +1 @@
ERROR: unable to verify: snapshot with name no-such-snapshot not found
@@ -0,0 +1,3 @@
Missing dependencies (2):
kbdcontrol [i386]
vidcontrol [i386]

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