Compare commits

...

179 Commits

Author SHA1 Message Date
Andrey Smirnov 3008147b3a One more "fix" for FreeBSD & gpg. 2014-02-10 23:25:41 +04:00
Andrey Smirnov 421283d161 Output match processor to fix CentOS build was placed incorrectly. 2014-02-10 23:13:49 +04:00
Andrey Smirnov 4020e30f07 Fixing system test on CentOS (different version of gpg). 2014-02-10 22:53:52 +04:00
Andrey Smirnov 766d634fbb terminal.IsTerminal() is not available on FreeBSD until go1.3 2014-02-10 22:48:12 +04:00
Andrey Smirnov 1bba0319cb aptly version 0.3. 2014-02-10 21:12:08 +04:00
Andrey Smirnov 651f6e934c Use temp file with .png extension. 2014-02-10 20:43:05 +04:00
Andrey Smirnov f2a7018335 New command: aptly graph to generate graph of entities created. 2014-02-10 17:33:07 +04:00
Andrey Smirnov 7a3063963c Add note on downloading keys from repository. 2014-02-10 15:29:48 +04:00
Andrey Smirnov 0c85e5252f Ignore timestamp formatting. 2014-02-10 15:29:37 +04:00
Andrey Smirnov d0e73a3e00 Tests for flat repository mirroring. 2014-02-10 15:06:11 +04:00
Andrey Smirnov 955b09a41c Add one more key for flat repos. 2014-02-10 15:05:18 +04:00
Andrey Smirnov 3075addd46 Support for flat repositories. 2014-02-10 14:47:52 +04:00
Andrey Smirnov ecbd146cd3 If repo's distribution is empty, don't use it. 2014-02-10 14:23:55 +04:00
Andrey Smirnov 80100a2eda New wheezy version has been released. 2014-02-10 14:17:27 +04:00
Andrey Smirnov fc51eb8414 Add spaces to SHA1/SHA256 sums in packages. 2014-02-10 13:40:34 +04:00
Andrey Smirnov 8c83706684 Test disable signing & verifying via config file. 2014-02-07 21:09:46 +04:00
Andrey Smirnov 3e5d54f3ef Use config file with all options. 2014-02-07 20:47:29 +04:00
Andrey Smirnov 9b4e858734 Fix root dir location in config file. 2014-02-07 20:46:43 +04:00
Andrey Smirnov 14e66c03a6 Tests for config in other file. 2014-02-07 20:00:43 +04:00
Andrey Smirnov 803570bf26 Support for running individual suites. 2014-02-07 20:00:21 +04:00
Andrey Smirnov 66d1f40a49 Support for config file in any file. 2014-02-07 19:59:26 +04:00
Andrey Smirnov 610940ae5d Mention create empty in command help. 2014-02-07 13:50:20 +04:00
Andrey Smirnov cddce9a5f7 Fix system test: skip gpg. 2014-02-07 13:50:12 +04:00
Andrey Smirnov 36892a2797 Fix tests. 2014-02-07 13:41:53 +04:00
Andrey Smirnov f3bad4ee2c Style fixes. 2014-02-07 13:36:10 +04:00
Andrey Smirnov bb13462f7b Fix error message. 2014-02-07 13:33:15 +04:00
Andrey Smirnov a3dbc8444b Publishing empty snapshot should fail. 2014-02-07 13:33:01 +04:00
Andrey Smirnov 9dccf2a4ee System test for aptly snapshot create empty. 2014-02-07 13:28:09 +04:00
Andrey Smirnov 919dc53814 aptly snapshot create .. empty command 2014-02-07 13:27:47 +04:00
Andrey Smirnov 87c0430628 Import keys as trusted. 2014-02-07 12:17:55 +04:00
Andrey Smirnov ca4736674e Make gpg be less chattly about untrusted keys. 2014-02-07 12:17:33 +04:00
Andrey Smirnov cf3dc6be27 Tests for mirroring repositories with signatures. 2014-02-07 12:06:38 +04:00
Andrey Smirnov d4307ad03c Test on publishing with --skip-signing. 2014-02-07 11:37:01 +04:00
Andrey Smirnov 47225a0e2d Add keyring parameter to supress help messages on empty default keyring. 2014-02-07 10:58:01 +04:00
Andrey Smirnov f4bf144145 Revert "Use non-default keyring in test, so that we don't have extra messages."
This reverts commit ad623f7d74.
2014-02-07 10:57:19 +04:00
Andrey Smirnov ad623f7d74 Use non-default keyring in test, so that we don't have extra messages. 2014-02-07 02:07:49 +04:00
Andrey Smirnov 3a51116881 Use better words. 2014-02-07 02:07:41 +04:00
Andrey Smirnov 39d2dd2483 Update system tests not to fail with GPG verification. 2014-02-07 01:23:11 +04:00
Andrey Smirnov b532cb19ee Verifying and signing releases with GPG, new flags. 2014-02-07 01:22:53 +04:00
Andrey Smirnov f5ee710098 Major rework of GnuPG interface: support verifying, more help to the user. 2014-02-07 01:22:13 +04:00
Andrey Smirnov 7cbba33fe7 Fetching mirror (Release files) with crypto signatures. 2014-02-06 22:12:31 +04:00
Andrey Smirnov a9c812a1b3 Style fix. 2014-02-06 21:58:56 +04:00
Andrey Smirnov 09648044db Test for new config options. 2014-02-06 21:58:40 +04:00
Andrey Smirnov a97365377f New configuration options to disable gpg usage in aptly. 2014-02-06 19:56:26 +04:00
Andrey Smirnov 0e9ccb4481 Publishing repository without signer (no signed files). 2014-02-06 19:55:55 +04:00
Andrey Smirnov c4a30ce0ce Update docs, we have more features. [ci skip] 2014-02-04 14:12:00 +04:00
Andrey Smirnov d350e9edba Fix broken code due to addition of Progress. 2014-02-04 13:06:55 +04:00
Andrey Smirnov ab468bcac4 Use progress when downloading repo. 2014-02-04 12:38:32 +04:00
Andrey Smirnov 679fc3e5fd Bind progress into Downloader. 2014-02-04 12:38:14 +04:00
Andrey Smirnov e99fee3908 Progress reporter: progress bar + messages. 2014-02-04 12:37:55 +04:00
Andrey Smirnov c3bcc9f7cb Tests for downloading from broken mirror (checksums). 2014-02-03 23:16:41 +04:00
Andrey Smirnov 62d3c625ed Support for internal webserver in tests. 2014-02-03 23:15:50 +04:00
Andrey Smirnov 3a9ce36662 Fix broken test: nil.Close() 2014-02-03 21:18:30 +04:00
Andrey Smirnov b4d7f8ec55 Add flag "-ignore-cheksums" to aptly mirror update. 2014-02-03 21:00:12 +04:00
Andrey Smirnov 79eaf5d460 Parsing Release files for checksums of Packages files. 2014-02-03 20:57:34 +04:00
Andrey Smirnov 502ed4366a ignoreMismatch flag for downloading. 2014-02-03 20:56:51 +04:00
Andrey Smirnov d65de9bd30 Rename serve test names. 2014-02-03 17:42:51 +04:00
Andrey Smirnov c1feb4210c Pass expected checksums for package files to downloader. 2014-02-03 17:42:12 +04:00
Andrey Smirnov 360e09cc7d Update to new non-pointer interfaces. 2014-02-03 17:41:54 +04:00
Andrey Smirnov 182a4bd39c Update tests. 2014-02-03 17:41:39 +04:00
Andrey Smirnov 35976f5ff1 Trim hashes when parsing packages, export package expected checksums. 2014-02-03 17:41:12 +04:00
Andrey Smirnov 982a25992e Checksum verification while downloading files. 2014-02-03 17:40:41 +04:00
Andrey Smirnov 08123ef5e3 Use values instead of pointers and use io.MultiWriter instead of homegrown impl. 2014-02-03 17:39:49 +04:00
Andrey Smirnov 0dd44f98b8 Restore old function check_file. 2014-02-02 20:08:51 +04:00
Andrey Smirnov 996fc445be Refactor download test to use internal HTTP server. 2014-01-31 22:26:26 +04:00
Andrey Smirnov df0a678863 Refactor checksum calculation to implement transparent checksum writer. 2014-01-31 21:46:38 +04:00
Andrey Smirnov 89ac907ca3 Sytem test for aptly publish list/drop. 2014-01-31 21:14:48 +04:00
Andrey Smirnov 7ce8b0ff7b Add note that repo has been dropped. 2014-01-31 21:14:35 +04:00
Andrey Smirnov a1258f2125 Revert: run all tests. 2014-01-31 21:14:13 +04:00
Andrey Smirnov 59d72c8112 System tests for aptly publish snapshot. 2014-01-31 19:04:44 +04:00
Andrey Smirnov 5bd0546fb7 Add note about aptly serve to command output 2014-01-31 19:04:25 +04:00
Andrey Smirnov 67422642ef Fix order of fields. 2014-01-31 19:03:50 +04:00
Andrey Smirnov de5e39818e Fix description to be more readable. 2014-01-31 19:03:30 +04:00
Andrey Smirnov cfdcf84fa5 Fix help message. 2014-01-30 13:16:35 +04:00
Andrey Smirnov 043ed13c18 aptly serve command: handle publishing of repositories, with system test. 2014-01-30 13:10:19 +04:00
Andrey Smirnov 0271456ca4 Add 2014 to LICENSE file. 2014-01-30 11:59:19 +04:00
Andrey Smirnov 08bf46e486 Clarify command line usage a bit. 2014-01-29 19:22:56 +04:00
Andrey Smirnov 93eef2cc80 aptly snapshot drop with system tests. 2014-01-29 19:04:53 +04:00
Andrey Smirnov c4f1179b65 Lookup PublishedRepo by snapshot. 2014-01-29 18:11:40 +04:00
Andrey Smirnov 2487d5da37 Fix option name in error message. 2014-01-29 18:05:22 +04:00
Andrey Smirnov 772035ad44 Find snapshot by source. 2014-01-29 18:00:53 +04:00
Andrey Smirnov e49afbcd2d Snapshot dropping. 2014-01-29 17:13:42 +04:00
Andrey Smirnov 1803252f33 Command aptly mirror drop with system tests. 2014-01-29 16:57:10 +04:00
Andrey Smirnov 555256c1fe Search snapshots by source repo. 2014-01-29 16:16:24 +04:00
Andrey Smirnov 26267802e9 Dropping remote repos. 2014-01-29 15:57:19 +04:00
Andrey Smirnov 69eb6a85b0 Add comment to exported function. 2014-01-29 14:34:56 +04:00
Andrey Smirnov 78c7bf6af1 Double delete is not a problem. 2014-01-29 14:34:30 +04:00
Andrey Smirnov d5bc13751b Update version in test as well. 2014-01-28 19:21:42 +04:00
Andrey Smirnov b04027edad Bump version: waiting for 0.3 2014-01-28 18:33:49 +04:00
Andrey Smirnov 5c6e4fffc4 Better words. 2014-01-28 13:02:05 +04:00
Andrey Smirnov 0da53a2d87 Add '+' to list of skipped symbols. 2014-01-28 12:41:31 +04:00
Andrey Smirnov db4ccce9e4 Remove updated at while comparing. 2014-01-28 12:35:20 +04:00
Andrey Smirnov c538ca8cc6 Add options --with-packages to show list of packages in snapshot & mirror. 2014-01-28 12:26:05 +04:00
Andrey Smirnov 00bb27fcea Don't run fixtured DB on go1.1. 2014-01-27 20:05:01 +04:00
Andrey Smirnov b571e4d79b Clone from https url. 2014-01-27 19:51:55 +04:00
Andrey Smirnov 29cb5d9c9e Prepare Travis with all files to run aptly system tests with DB fixture. 2014-01-27 19:45:35 +04:00
Andrey Smirnov 899a28cc27 Clarify go version required in README. #2 2014-01-25 23:10:22 +04:00
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
212 changed files with 471202 additions and 737 deletions
+2
View File
@@ -24,3 +24,5 @@ _testmain.go
coverage.html coverage.html
coverage*.out coverage*.out
*.pyc
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2013 Andrey Smirnov. All rights reserved. Copyright 2013-2014 Andrey Smirnov. All rights reserved.
MIT License MIT License
+11 -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,14 @@ check:
go tool vet -all=true . go tool vet -all=true .
golint . golint .
travis: $(TRAVIS_TARGET) system-test:
ifeq ($(GOVERSION), go1.2)
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
endif
go install
PATH=$(BINPATH):$(PATH) python system/run.py --long
travis: $(TRAVIS_TARGET) system-test
test: test:
go test -v ./... -gocheck.v=true go test -v ./... -gocheck.v=true
+10 -342
View File
@@ -10,366 +10,34 @@ 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:
* source packages, debian-installer and translations not supported yet * source packages, debian-installer and translations not supported yet
* checksums and signature are not verified while downloading * signature is not verified while downloading
* deleting created items is not implemented
* cleaning up stale files is not implemented * cleaning up stale files is not implemented
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 (go 1.1+ required)::
-------------
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:: If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_.
{
"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``.
+174
View File
@@ -0,0 +1,174 @@
package main
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
graph := gographviz.NewGraph()
graph.SetDir(true)
graph.SetName("aptly")
fmt.Printf("Loading mirrors...\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
err := repoCollection.LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())),
})
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
err := snapshotCollection.LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
})
if snapshot.SourceKind == "repo" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
} else if snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
publishedCollection := debian.NewPublishedRepoCollection(context.database)
publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
})
graph.AddEdge(graphvizEscape(repo.SnapshotUUID), "", graphvizEscape(repo.UUID), "", true, nil)
return nil
})
fmt.Printf("Generating graph...\n")
buf := bytes.NewBufferString(graph.String())
tempfile, err := ioutil.TempFile("", "aptly-graph")
if err != nil {
return err
}
tempfile.Close()
os.Remove(tempfile.Name())
tempfilename := tempfile.Name() + ".png"
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
}
_, err = io.Copy(stdin, buf)
if err != nil {
return err
}
err = stdin.Close()
if err != nil {
return err
}
err = command.Wait()
if err != nil {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
}
return err
}
func makeCmdGraph() *commander.Command {
cmd := &commander.Command{
Run: aptlyGraph,
UsageLine: "graph",
Short: "display graph of dependencies between aptly objects (requires graphviz)",
Long: `
Command graph displays relationship between mirrors, snapshots and published repositories using
graphviz package to render graph as image.
ex:
$ aptly graph
`,
Flag: *flag.NewFlagSet("aptly-graph", flag.ExitOnError),
}
return cmd
}
+163 -29
View File
@@ -5,9 +5,48 @@ 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"
) )
func getVerifier(cmd *commander.Command) (utils.Verifier, error) {
if utils.Config.GpgDisableVerify || cmd.Flag.Lookup("ignore-signatures").Value.Get().(bool) {
return nil, nil
}
verifier := &utils.GpgVerifier{}
for _, keyRing := range keyRings.keyRings {
verifier.AddKeyring(keyRing)
}
err := verifier.InitKeyring()
if err != nil {
return nil, err
}
return verifier, nil
}
type keyRingsFlag struct {
keyRings []string
}
func (k *keyRingsFlag) Set(value string) error {
k.keyRings = append(k.keyRings, value)
return nil
}
func (k *keyRingsFlag) Get() interface{} {
return k.keyRings
}
func (k *keyRingsFlag) String() string {
return strings.Join(k.keyRings, ",")
}
var keyRings = keyRingsFlag{}
func aptlyMirrorList(cmd *commander.Command, args []string) error { func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
@@ -15,15 +54,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)
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 { repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
fmt.Printf(" * %s\n", repo) repos[i] = repo.String()
i++
return nil return nil
}) })
fmt.Printf("\nTo get more information about repository, run `aptly mirror show <name>`.\n") 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,18 +85,17 @@ 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)
} }
err = repo.Fetch(context.downloader) verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil { if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err) return fmt.Errorf("unable to fetch mirror: %s", err)
} }
@@ -94,9 +144,19 @@ 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])
} }
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err return err
} }
@@ -120,14 +180,21 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
} }
err = repo.Fetch(context.downloader) ignoreMismatch := cmd.Flag.Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil { if err != nil {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
} }
packageCollection := debian.NewPackageCollection(context.database) packageCollection := debian.NewPackageCollection(context.database)
err = repo.Download(context.downloader, packageCollection, context.packageRepository) err = repo.Download(context.downloader, packageCollection, context.packageRepository, ignoreMismatch)
if err != nil { if err != nil {
return fmt.Errorf("unable to update: %s", err) return fmt.Errorf("unable to update: %s", err)
} }
@@ -141,23 +208,64 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return err return err
} }
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshots := snapshotCollection.ByRemoteRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Mirror `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete mirror with snapshots, use -force to override")
}
}
err = repoCollection.Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Mirror `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command { 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")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd return cmd
} }
func makeCmdMirrorList() *commander.Command { func makeCmdMirrorList() *commander.Command {
@@ -166,7 +274,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,34 +289,60 @@ 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),
} }
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd return cmd
} }
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),
} }
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete remote repository mirror",
Long: `
Drop deletes information about remote repository mirror. Package data is not deleted
(it could be still used by other mirrors or snapshots).
ex:
$ aptly mirror drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force mirror deletion even if used by snapshots")
return cmd return cmd
} }
@@ -220,7 +354,7 @@ func makeCmdMirror() *commander.Command {
makeCmdMirrorCreate(), makeCmdMirrorCreate(),
makeCmdMirrorList(), makeCmdMirrorList(),
makeCmdMirrorShow(), makeCmdMirrorShow(),
//makeCmdMirrorDestroy(), makeCmdMirrorDrop(),
makeCmdMirrorUpdate(), makeCmdMirrorUpdate(),
}, },
Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError),
+155 -14
View File
@@ -6,9 +6,31 @@ 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"
) )
func getSigner(cmd *commander.Command) (utils.Signer, error) {
if cmd.Flag.Lookup("skip-signing").Value.Get().(bool) || utils.Config.GpgDisableSign {
return nil, nil
}
signer := &utils.GpgSigner{}
key := cmd.Flag.Lookup("gpg-key").Value.String()
if key != "" {
signer.SetKey(key)
}
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func aptlyPublishSnapshot(cmd *commander.Command, args []string) error { func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) < 1 || len(args) > 2 { if len(args) < 1 || len(args) > 2 {
@@ -25,6 +47,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 {
@@ -57,26 +81,38 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
if distribution == "" { if distribution == "" {
if sourceRepo != nil { if sourceRepo != nil {
distribution = sourceRepo.Distribution distribution = sourceRepo.Distribution
} else { }
if distribution == "" {
return fmt.Errorf("unable to guess distribution name, please specify explicitly") return fmt.Errorf("unable to guess distribution name, please specify explicitly")
} }
} }
var architecturesList []string published, err := debian.NewPublishedRepo(prefix, distribution, component, context.architecturesList, snapshot)
architectures := cmd.Flag.Lookup("architectures").Value.String() if err != nil {
if architectures != "" { return fmt.Errorf("unable to publish: %s", err)
architecturesList = strings.Split(architectures, ",")
} }
signer := &utils.GpgSigner{} duplicate := publishedCollecton.CheckDuplicate(published)
signer.SetKey(cmd.Flag.Lookup("gpg-key").Value.String()) if duplicate != nil {
publishedCollecton.LoadComplete(duplicate, snapshotCollection)
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
published := debian.NewPublishedRepo(prefix, distribution, component, architecturesList, snapshot) signer, err := getSigner(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
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, "/") {
@@ -88,6 +124,75 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
fmt.Printf("Now you can add following line to apt sources:\n") fmt.Printf("Now you can add following line to apt sources:\n")
fmt.Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component) fmt.Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component)
fmt.Printf("Don't forget to add your GPG key to apt with apt-key.\n") fmt.Printf("Don't forget to add your GPG key to apt with apt-key.\n")
fmt.Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollecton.Len() == 0 {
fmt.Printf("No snapshots have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
published := make(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)
}
fmt.Printf("\nPublished repositroy has been removed successfully.\n")
return err return err
} }
@@ -95,20 +200,54 @@ func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
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")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
return cmd
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
Short: "removes files of published repository",
Long: `
Command removes whatever has been published under specified prefix and distribution name.
ex.
$ aptly publish drop wheezy
`,
Flag: *flag.NewFlagSet("aptly-publish-drop", flag.ExitOnError),
}
return cmd
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "displays list of published repositories",
Long: `
Display command displays list of currently published snapshots with information about published root.
ex.
$ aptly publish list
`,
Flag: *flag.NewFlagSet("aptly-publish-list", flag.ExitOnError),
}
return cmd return cmd
} }
@@ -119,6 +258,8 @@ func makeCmdPublish() *commander.Command {
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),
} }
+106
View File
@@ -0,0 +1,106 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"net"
"net/http"
"os"
"sort"
)
func aptlyServe(cmd *commander.Command, args []string) error {
var err error
publishedCollection := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollection.Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := cmd.Flag.Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen)
if err != nil {
return fmt.Errorf("wrong -listen specification: %s", err)
}
if listenHost == "" {
listenHost, err = os.Hostname()
if err != nil {
listenHost = "localhost"
}
}
fmt.Printf("Serving published repositories, recommended apt sources list:\n\n")
sources := make(sort.StringSlice, 0, publishedCollection.Len())
published := make(map[string]*debian.PublishedRepo, publishedCollection.Len())
err = publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
sources = append(sources, repo.String())
published[repo.String()] = repo
return nil
})
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
sort.Strings(sources)
for _, source := range sources {
repo := published[source]
prefix := repo.Prefix
if prefix == "." {
prefix = ""
} else {
prefix += "/"
}
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component)
}
context.database.Close()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
err = http.ListenAndServe(listen, http.FileServer(http.Dir(context.packageRepository.PublicPath())))
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
return nil
}
func makeCmdServe() *commander.Command {
cmd := &commander.Command{
Run: aptlyServe,
UsageLine: "serve",
Short: "start embedded HTTP server to serve published repositories",
Long: `
Command serve starts embedded HTTP server (not suitable for real production usage) to serve
contents of public/ subdirectory of aptly's root that contains published repositories.
ex:
$ aptly serve -listen=:8080
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
}
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
return cmd
}
+585 -29
View File
@@ -5,17 +5,20 @@ 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 {
var err error var (
err error
snapshot *debian.Snapshot
)
if len(args) < 4 || args[1] != "from" || args[2] != "mirror" { if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
cmd.Usage() // aptly snapshot create snap from mirror mirror
return err repoName, snapshotName := args[3], args[0]
}
repoName, mirrorName := args[3], args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database) repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(repoName) repo, err := repoCollection.ByName(repoName)
@@ -28,10 +31,21 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to create snapshot: %s", err) return fmt.Errorf("unable to create snapshot: %s", err)
} }
snapshot, err := debian.NewSnapshotFromRepository(mirrorName, repo) snapshot, err = debian.NewSnapshotFromRepository(snapshotName, repo)
if err != nil { if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err) return fmt.Errorf("unable to create snapshot: %s", err)
} }
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := debian.NewPackageList()
snapshot = debian.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database) snapshotCollection := debian.NewSnapshotCollection(context.database)
@@ -52,16 +66,31 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
return err return err
} }
snapshotCollection := debian.NewSnapshotCollection(context.database)
if snapshotCollection.Len() > 0 {
fmt.Printf("List of snapshots:\n") fmt.Printf("List of snapshots:\n")
snapshotCollection := debian.NewSnapshotCollection(context.database) snapshots := make(sort.StringSlice, snapshotCollection.Len())
i := 0
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error { snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
fmt.Printf(" * %s\n", snapshot) snapshots[i] = snapshot.String()
i++
return nil 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") 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 {
@@ -88,35 +117,457 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST")) fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description) fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages()) fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database) withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
err = snapshot.RefList().ForEach(func(key []byte) error { return err
p, err := packageCollection.ByKey(key) }
if err != nil {
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err return err
} }
fmt.Printf(" %s\n", p)
return nil 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 { if err != nil {
return fmt.Errorf("unable to load packages: %s", err) 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 aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
publishedRepoCollection := debian.NewPublishedRepoCollection(context.database)
published := publishedRepoCollection.BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = publishedRepoCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: snapshot is published")
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
fmt.Printf("Snapshot `%s` was used as a source in following snapshots:\n", snapshot.Name)
for _, snap := range snapshots {
fmt.Printf(" * %s\n", snap)
}
return fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use -force to override")
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err 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> | create <name> empty",
Short: "creates snapshot out of any mirror", Short: "creates immutable snapshot of mirror contents",
Long: ` Long: `
Create makes persistent immutable snapshot of repository mirror state in givent moment of time. Command create .. from mirror makes persistent immutable snapshot of remote repository mirror. Snapshot could be
published or further modified using merge, pull and other aptly features.
ex: Command create .. empty creates empty snapshot that could be used as a basis for snapshot pull operations, for example.
$ aptly snapshot create <name> from mirror <mirror-name> As snapshots are immutable, creating one empty snapshot should be enough.
ex.
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`, `,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
} }
@@ -131,7 +582,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,17 +596,118 @@ 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),
} }
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verifies that dependencies are satisfied in snapshot",
Long: `
Verify does depenency resolution in snapshot, possibly using additional snapshots as dependency sources.
All unsatisfied dependencies are returned.
ex.
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
Flag: *flag.NewFlagSet("aptly-snapshot-verify", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
Short: "performs partial upgrades (pulls new packages) from another snapshot",
Long: `
Command pull pulls new packages along with its dependencies in <name> snapshot
from <source> snapshot. Also can upgrade package version from one snapshot into
another, once again along with dependencies. New snapshot <destination> is created as result of this
process. Packages could be specified simply as 'package-name' or as dependency 'package-name (>= version)'.
ex.
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
return cmd
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "calculates difference in packages between two snapshots",
Long: `
Command diff shows list of missing and new packages, difference in package versions between two snapshots.
ex.
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots into one, replacing matching packages",
Long: `
Merge merges several snapshots into one. Merge happens from left to right. Packages with the same
name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
If run with only one source snapshot, merge copies source into destination.
ex.
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-merge", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about snapshot. If snapshot is published,
it can't be dropped.
ex.
$ aptly snapshot drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "remove snapshot even if it was used as source for other snapshots")
return cmd return cmd
} }
@@ -167,7 +719,11 @@ func makeCmdSnapshot() *commander.Command {
makeCmdSnapshotCreate(), makeCmdSnapshotCreate(),
makeCmdSnapshotList(), makeCmdSnapshotList(),
makeCmdSnapshotShow(), makeCmdSnapshotShow(),
//makeCmdSnapshotDestroy(), makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
}, },
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
} }
+27
View File
@@ -0,0 +1,27 @@
package main
import (
"fmt"
"github.com/smira/aptly/debian"
)
//ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *debian.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database)
err = reflist.ForEach(func(key []byte) error {
p, err := packageCollection.ByKey(key)
if err != nil {
return err
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
+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)
+19
View File
@@ -45,6 +45,25 @@ 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")
err = s.db.Delete(key)
c.Assert(err, IsNil)
}
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{})
+2 -1
View File
@@ -11,7 +11,8 @@ import (
type Stanza map[string]string type Stanza map[string]string
// Canonical order of fields in stanza // Canonical order of fields in stanza
var canocialOrder = []string{"Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer", "Architecture"} var canocialOrder = []string{"Origin", "Label", "Suite", "Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer",
"Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256"}
// Copy returns copy of Stanza // Copy returns copy of Stanza
func (s Stanza) Copy() (result Stanza) { func (s Stanza) Copy() (result Stanza) {
+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: strings.TrimSpace(input["MD5sum"]),
SHA1: strings.TrimSpace(input["SHA1"]),
SHA256: strings.TrimSpace(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
Checksums utils.ChecksumInfo
}
// 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, Checksums: f.Checksums})
}
}
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
+151 -7
View File
@@ -2,10 +2,13 @@ package debian
import ( import (
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
. "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 +20,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 +111,105 @@ 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,
Checksums: utils.ChecksumInfo{Size: 5,
MD5: "1e8cba92c41420aa7baa8a5718d67122",
SHA1: "46955e48cad27410a83740a21d766ce362364024",
SHA256: "eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5"}}})
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 {
+249 -30
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,38 +103,24 @@ 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)
} }
if list.Len() == 0 { if list.Len() == 0 {
return fmt.Errorf("repository is empty, can't publish") return fmt.Errorf("snapshot is empty")
} }
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 {
return fmt.Errorf("unable to figure out list of architectures, please supply explicit list") return fmt.Errorf("unable to figure out list of architectures, please supply explicit list")
} }
generatedFiles := map[string]*utils.ChecksumInfo{} generatedFiles := map[string]utils.ChecksumInfo{}
// For all architectures, generate release file // For all architectures, generate release file
for _, arch := range p.Architectures { for _, arch := range p.Architectures {
@@ -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
@@ -167,7 +201,7 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST") release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
release["Components"] = p.Component release["Components"] = p.Component
release["Architectures"] = strings.Join(p.Architectures, " ") release["Architectures"] = strings.Join(p.Architectures, " ")
release["Description"] = "Generated by aptly\n" release["Description"] = " Generated by aptly\n"
release["MD5Sum"] = "\n" release["MD5Sum"] = "\n"
release["SHA1"] = "\n" release["SHA1"] = "\n"
release["SHA256"] = "\n" release["SHA256"] = "\n"
@@ -198,6 +232,7 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
releaseFilename := releaseFile.Name() releaseFilename := releaseFile.Name()
releaseFile.Close() releaseFile.Close()
if signer != nil {
err = signer.DetachedSign(releaseFilename, releaseFilename+".gpg") err = signer.DetachedSign(releaseFilename, releaseFilename+".gpg")
if err != nil { if err != nil {
return fmt.Errorf("unable to sign Release file: %s", err) return fmt.Errorf("unable to sign Release file: %s", err)
@@ -207,6 +242,190 @@ func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageColl
if err != nil { if err != nil {
return fmt.Errorf("unable to sign Release file: %s", err) return fmt.Errorf("unable to sign Release file: %s", err)
} }
}
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)
}
// BySnapshot looks up repository by snapshot source
func (collection *PublishedRepoCollection) BySnapshot(snapshot *Snapshot) []*PublishedRepo {
result := make([]*PublishedRepo, 0)
for _, r := range collection.list {
if r.SnapshotUUID == snapshot.UUID {
result = append(result, r)
}
}
return result
}
// 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())
}
+384 -3
View File
@@ -1,14 +1,32 @@
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"
"path/filepath" "path/filepath"
) )
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 NullSigner struct{} type NullSigner struct{}
func (n *NullSigner) Init() error {
return nil
}
func (n *NullSigner) SetKey(keyRef string) { func (n *NullSigner) SetKey(keyRef string) {
} }
@@ -25,6 +43,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 +60,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 +181,296 @@ 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) TestPublishNoSigner(c *C) {
err := s.repo.Publish(s.packageRepo, s.packageCollection, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.packageRepo.RootPath, "public/ppa/dists/squeeze/Release"), PathExists)
}
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) TestAddByPrefixDistribution(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)
}
func (s *PublishedRepoCollectionSuite) TestBySnapshot(c *C) {
c.Check(s.collection.Add(s.repo1), IsNil)
c.Check(s.collection.Add(s.repo2), IsNil)
c.Check(s.collection.BySnapshot(s.snap1), DeepEquals, []*PublishedRepo{s.repo1})
c.Check(s.collection.BySnapshot(s.snap2), DeepEquals, []*PublishedRepo{s.repo2})
}
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))
}
+222 -22
View File
@@ -10,6 +10,8 @@ import (
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"log" "log"
"net/url" "net/url"
"os"
"strconv"
"strings" "strings"
"time" "time"
) )
@@ -35,6 +37,8 @@ type RemoteRepo struct {
Meta Stanza Meta Stanza
// Last update date // Last update date
LastDownloadDate time.Time LastDownloadDate time.Time
// Checksums for release files
ReleaseFiles map[string]utils.ChecksumInfo
// "Snapshot" of current list of packages // "Snapshot" of current list of packages
packageRefs *PackageRefList packageRefs *PackageRefList
// Parsed archived root // Parsed archived root
@@ -56,6 +60,17 @@ func NewRemoteRepo(name string, archiveRoot string, distribution string, compone
if err != nil { if err != nil {
return nil, err return nil, err
} }
if result.Distribution == "." || result.Distribution == "./" {
// flat repo
result.Distribution = ""
result.Architectures = nil
if len(result.Components) > 0 {
return nil, fmt.Errorf("components aren't supported for flat repos")
}
result.Components = nil
}
return result, nil return result, nil
} }
@@ -70,6 +85,11 @@ func (repo *RemoteRepo) String() string {
return fmt.Sprintf("[%s]: %s %s", repo.Name, repo.ArchiveRoot, repo.Distribution) return fmt.Sprintf("[%s]: %s %s", repo.Name, repo.ArchiveRoot, repo.Distribution)
} }
// IsFlat determines if repository is flat
func (repo *RemoteRepo) IsFlat() bool {
return repo.Distribution == ""
}
// NumPackages return number of packages retrived from remore repo // NumPackages return number of packages retrived from remore repo
func (repo *RemoteRepo) NumPackages() int { func (repo *RemoteRepo) NumPackages() int {
if repo.packageRefs == nil { if repo.packageRefs == nil {
@@ -78,10 +98,27 @@ func (repo *RemoteRepo) NumPackages() int {
return repo.packageRefs.Len() return repo.packageRefs.Len()
} }
// ReleaseURL returns URL to Release file in repo root // RefList returns package list for repo
// TODO: InRelease, Release.gz, Release.bz2 handling func (repo *RemoteRepo) RefList() *PackageRefList {
func (repo *RemoteRepo) ReleaseURL() *url.URL { return repo.packageRefs
path := &url.URL{Path: fmt.Sprintf("dists/%s/Release", repo.Distribution)} }
// ReleaseURL returns URL to Release* files in repo root
func (repo *RemoteRepo) ReleaseURL(name string) *url.URL {
var path *url.URL
if !repo.IsFlat() {
path = &url.URL{Path: fmt.Sprintf("dists/%s/%s", repo.Distribution, name)}
} else {
path = &url.URL{Path: name}
}
return repo.archiveRootURL.ResolveReference(path)
}
// FlatBinaryURL returns URL to Package files for flat repo
func (repo *RemoteRepo) FlatBinaryURL() *url.URL {
path := &url.URL{Path: "Packages"}
return repo.archiveRootURL.ResolveReference(path) return repo.archiveRootURL.ResolveReference(path)
} }
@@ -100,12 +137,57 @@ func (repo *RemoteRepo) PackageURL(filename string) *url.URL {
} }
// Fetch updates information about repository // Fetch updates information about repository
func (repo *RemoteRepo) Fetch(d utils.Downloader) error { func (repo *RemoteRepo) Fetch(d utils.Downloader, verifier utils.Verifier) error {
// Download release file to temporary URL var (
release, err := utils.DownloadTemp(d, repo.ReleaseURL().String()) release *os.File
err error
)
if verifier == nil {
// 0. Just download release file to temporary URL
release, err = utils.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil { if err != nil {
return err return err
} }
} else {
// 1. try InRelease file
inrelease, err := utils.DownloadTemp(d, repo.ReleaseURL("InRelease").String())
if err != nil {
goto splitsignature
}
defer inrelease.Close()
release, err = verifier.VerifyClearsigned(inrelease)
if err != nil {
goto splitsignature
}
goto ok
splitsignature:
// 2. try Release + Release.gpg
release, err = utils.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil {
return err
}
releasesig, err := utils.DownloadTemp(d, repo.ReleaseURL("Release.gpg").String())
if err != nil {
return err
}
err = verifier.VerifyDetachedSignature(releasesig, release)
if err != nil {
return err
}
_, err = release.Seek(0, 0)
if err != nil {
return err
}
}
ok:
defer release.Close() defer release.Close()
sreader := NewControlFileReader(release) sreader := NewControlFileReader(release)
@@ -114,6 +196,7 @@ func (repo *RemoteRepo) Fetch(d utils.Downloader) error {
return err return err
} }
if !repo.IsFlat() {
architectures := strings.Split(stanza["Architectures"], " ") architectures := strings.Split(stanza["Architectures"], " ")
if len(repo.Architectures) == 0 { if len(repo.Architectures) == 0 {
repo.Architectures = architectures repo.Architectures = architectures
@@ -135,23 +218,81 @@ func (repo *RemoteRepo) Fetch(d utils.Downloader) error {
return err return err
} }
} }
}
repo.ReleaseFiles = make(map[string]utils.ChecksumInfo)
parseSums := func(field string, setter func(sum *utils.ChecksumInfo, data string)) error {
for _, line := range strings.Split(stanza[field], "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) != 3 {
return fmt.Errorf("unparseable hash sum line: %#v", line)
}
size, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return fmt.Errorf("unable to parse size: %s", err)
}
sum := repo.ReleaseFiles[parts[2]]
sum.Size = size
setter(&sum, parts[0])
repo.ReleaseFiles[parts[2]] = sum
}
delete(stanza, field)
return nil
}
err = parseSums("MD5Sum", func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data })
if err != nil {
return err
}
err = parseSums("SHA1", func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data })
if err != nil {
return err
}
err = parseSums("SHA256", func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data })
if err != nil {
return err
}
delete(stanza, "MD5Sum")
delete(stanza, "SHA1")
delete(stanza, "SHA256")
repo.Meta = stanza repo.Meta = stanza
return nil return nil
} }
// Download downloads all repo files // Download downloads all repo files
func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository) error { func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository, ignoreMismatch bool) error {
list := NewPackageList() list := NewPackageList()
// Download and parse all Release files d.GetProgress().Printf("Downloading & parsing package files...\n")
// Download and parse all Packages files
packagesURLs := []string{}
if repo.IsFlat() {
packagesURLs = append(packagesURLs, repo.FlatBinaryURL().String())
} else {
for _, component := range repo.Components { for _, component := range repo.Components {
for _, architecture := range repo.Architectures { for _, architecture := range repo.Architectures {
packagesReader, packagesFile, err := utils.DownloadTryCompression(d, repo.BinaryURL(component, architecture).String()) packagesURLs = append(packagesURLs, repo.BinaryURL(component, architecture).String())
}
}
}
for _, url := range packagesURLs {
packagesReader, packagesFile, err := utils.DownloadTryCompression(d, url, repo.ReleaseFiles, ignoreMismatch)
if err != nil { if err != nil {
return err return err
} }
@@ -173,10 +314,14 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
list.Add(p) list.Add(p)
} }
} }
}
d.GetProgress().Printf("Saving packages to database...\n")
d.GetProgress().InitBar(int64(list.Len()), false)
// 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 {
d.GetProgress().AddBar(1)
return packageCollection.Update(p) return packageCollection.Update(p)
}) })
@@ -184,30 +329,52 @@ 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 d.GetProgress().ShutdownBar()
ch := make(chan error, list.Len())
d.GetProgress().Printf("Building download queue...\n")
// 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
_, found := queued[key]
if !found {
count++ count++
downloadSize += task.Checksums.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) d.GetProgress().Printf("Download queue: %d items, %.2f GiB size\n", count, float64(downloadSize)/(1024.0*1024.0*1024.0))
d.GetProgress().InitBar(downloadSize, true)
// Download all package files
ch := make(chan error, len(queued))
for _, task := range queued {
d.DownloadWithChecksum(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch, task.Checksums, ignoreMismatch)
}
// 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 {
@@ -216,8 +383,10 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
count-- count--
} }
d.GetProgress().ShutdownBar()
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 +529,34 @@ 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)
}
// Drop removes remote repo from collection
func (collection *RemoteRepoCollection) Drop(repo *RemoteRepo) error {
repoPosition := -1
for i, r := range collection.list {
if r == repo {
repoPosition = i
break
}
}
if repoPosition == -1 {
panic("repo not found!")
}
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
err := collection.db.Delete(repo.Key())
if err != nil {
return err
}
return collection.db.Delete(repo.RefKey())
}
+156 -15
View File
@@ -4,7 +4,10 @@ import (
"errors" "errors"
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"io"
"io/ioutil"
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"os"
"testing" "testing"
) )
@@ -13,6 +16,29 @@ func Test(t *testing.T) {
TestingT(t) TestingT(t)
} }
type NullVerifier struct {
}
func (n *NullVerifier) InitKeyring() error {
return nil
}
func (n *NullVerifier) AddKeyring(keyring string) {
}
func (n *NullVerifier) VerifyDetachedSignature(signature, cleartext io.Reader) error {
return nil
}
func (n *NullVerifier) VerifyClearsigned(clearsigned io.Reader) (text *os.File, err error) {
text, _ = ioutil.TempFile("", "aptly-test")
io.Copy(text, clearsigned)
text.Seek(0, 0)
os.Remove(text.Name())
return
}
type PackageListMixinSuite struct { type PackageListMixinSuite struct {
p1, p2, p3 *Package p1, p2, p3 *Package
list *PackageList list *PackageList
@@ -40,6 +66,7 @@ func (s *PackageListMixinSuite) SetUpPackages() {
type RemoteRepoSuite struct { type RemoteRepoSuite struct {
PackageListMixinSuite PackageListMixinSuite
repo *RemoteRepo repo *RemoteRepo
flat *RemoteRepo
downloader *utils.FakeDownloader downloader *utils.FakeDownloader
db database.Storage db database.Storage
packageCollection *PackageCollection packageCollection *PackageCollection
@@ -50,6 +77,7 @@ var _ = Suite(&RemoteRepoSuite{})
func (s *RemoteRepoSuite) SetUpTest(c *C) { func (s *RemoteRepoSuite) SetUpTest(c *C) {
s.repo, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}) s.repo, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
s.flat, _ = NewRemoteRepo("exp42", "http://repos.express42.com/virool/precise/", "./", []string{}, []string{})
s.downloader = utils.NewFakeDownloader().ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release", exampleReleaseFile) s.downloader = utils.NewFakeDownloader().ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release", exampleReleaseFile)
s.db, _ = database.OpenDB(c.MkDir()) s.db, _ = database.OpenDB(c.MkDir())
s.packageCollection = NewPackageCollection(s.db) s.packageCollection = NewPackageCollection(s.db)
@@ -66,42 +94,100 @@ func (s *RemoteRepoSuite) TestInvalidURL(c *C) {
c.Assert(err, ErrorMatches, ".*hexadecimal escape in host.*") c.Assert(err, ErrorMatches, ".*hexadecimal escape in host.*")
} }
func (s *RemoteRepoSuite) TestFlatCreation(c *C) {
c.Check(s.flat.Distribution, Equals, "")
c.Check(s.flat.Architectures, IsNil)
c.Check(s.flat.Components, IsNil)
_, err := NewRemoteRepo("fl", "http://some.repo/", "./", []string{"main"}, []string{})
c.Check(err, ErrorMatches, "components aren't supported for flat repos")
}
func (s *RemoteRepoSuite) TestNumPackages(c *C) { func (s *RemoteRepoSuite) TestNumPackages(c *C) {
c.Check(s.repo.NumPackages(), Equals, 0) c.Check(s.repo.NumPackages(), Equals, 0)
s.repo.packageRefs = s.reflist s.repo.packageRefs = s.reflist
c.Check(s.repo.NumPackages(), Equals, 3) c.Check(s.repo.NumPackages(), Equals, 3)
} }
func (s *RemoteRepoSuite) TestIsFlat(c *C) {
c.Check(s.repo.IsFlat(), Equals, false)
c.Check(s.flat.IsFlat(), Equals, true)
}
func (s *RemoteRepoSuite) TestRefList(c *C) {
s.repo.packageRefs = s.reflist
c.Check(s.repo.RefList(), Equals, s.reflist)
}
func (s *RemoteRepoSuite) TestReleaseURL(c *C) { func (s *RemoteRepoSuite) TestReleaseURL(c *C) {
c.Assert(s.repo.ReleaseURL().String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/Release") c.Assert(s.repo.ReleaseURL("Release").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/Release")
c.Assert(s.repo.ReleaseURL("InRelease").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/InRelease")
c.Assert(s.flat.ReleaseURL("Release").String(), Equals, "http://repos.express42.com/virool/precise/Release")
} }
func (s *RemoteRepoSuite) TestBinaryURL(c *C) { func (s *RemoteRepoSuite) TestBinaryURL(c *C) {
c.Assert(s.repo.BinaryURL("main", "amd64").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/main/binary-amd64/Packages") c.Assert(s.repo.BinaryURL("main", "amd64").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/main/binary-amd64/Packages")
} }
func (s *RemoteRepoSuite) TestFlatBinaryURL(c *C) {
c.Assert(s.flat.FlatBinaryURL().String(), Equals, "http://repos.express42.com/virool/precise/Packages")
}
func (s *RemoteRepoSuite) TestPackageURL(c *C) { func (s *RemoteRepoSuite) TestPackageURL(c *C) {
c.Assert(s.repo.PackageURL("pool/main/0/0ad/0ad_0~r11863-2_i386.deb").String(), Equals, c.Assert(s.repo.PackageURL("pool/main/0/0ad/0ad_0~r11863-2_i386.deb").String(), Equals,
"http://mirror.yandex.ru/debian/pool/main/0/0ad/0ad_0~r11863-2_i386.deb") "http://mirror.yandex.ru/debian/pool/main/0/0ad/0ad_0~r11863-2_i386.deb")
} }
func (s *RemoteRepoSuite) TestFetch(c *C) { func (s *RemoteRepoSuite) TestFetch(c *C) {
err := s.repo.Fetch(s.downloader) err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(s.repo.Architectures, DeepEquals, []string{"amd64", "armel", "armhf", "i386", "powerpc"}) c.Assert(s.repo.Architectures, DeepEquals, []string{"amd64", "armel", "armhf", "i386", "powerpc"})
c.Assert(s.repo.Components, DeepEquals, []string{"main"}) c.Assert(s.repo.Components, DeepEquals, []string{"main"})
c.Assert(s.downloader.Empty(), Equals, true) c.Assert(s.downloader.Empty(), Equals, true)
c.Check(s.repo.ReleaseFiles, HasLen, 39)
c.Check(s.repo.ReleaseFiles["main/binary-i386/Packages.bz2"], DeepEquals,
utils.ChecksumInfo{
Size: 734,
MD5: "7954ed80936429687122b554620c1b5b",
SHA1: "95a463a0739bf9ff622c8d68f6e4598d400f5248",
SHA256: "377890a26f99db55e117dfc691972dcbbb7d8be1630c8fc8297530c205377f2b"})
}
func (s *RemoteRepoSuite) TestFetchNullVerifier1(c *C) {
downloader := utils.NewFakeDownloader()
downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/InRelease", errors.New("404"))
downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release", exampleReleaseFile)
downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release.gpg", "GPG")
err := s.repo.Fetch(downloader, &NullVerifier{})
c.Assert(err, IsNil)
c.Assert(s.repo.Architectures, DeepEquals, []string{"amd64", "armel", "armhf", "i386", "powerpc"})
c.Assert(s.repo.Components, DeepEquals, []string{"main"})
c.Assert(downloader.Empty(), Equals, true)
}
func (s *RemoteRepoSuite) TestFetchNullVerifier2(c *C) {
downloader := utils.NewFakeDownloader()
downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/InRelease", exampleReleaseFile)
err := s.repo.Fetch(downloader, &NullVerifier{})
c.Assert(err, IsNil)
c.Assert(s.repo.Architectures, DeepEquals, []string{"amd64", "armel", "armhf", "i386", "powerpc"})
c.Assert(s.repo.Components, DeepEquals, []string{"main"})
c.Assert(downloader.Empty(), Equals, true)
} }
func (s *RemoteRepoSuite) TestFetchWrongArchitecture(c *C) { func (s *RemoteRepoSuite) TestFetchWrongArchitecture(c *C) {
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{"xyz"}) s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{"xyz"})
err := s.repo.Fetch(s.downloader) err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, ErrorMatches, "architecture xyz not available in repo.*") c.Assert(err, ErrorMatches, "architecture xyz not available in repo.*")
} }
func (s *RemoteRepoSuite) TestFetchWrongComponent(c *C) { func (s *RemoteRepoSuite) TestFetchWrongComponent(c *C) {
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"xyz"}, []string{"i386"}) s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"xyz"}, []string{"i386"})
err := s.repo.Fetch(s.downloader) err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, ErrorMatches, "component xyz not available in repo.*") c.Assert(err, ErrorMatches, "component xyz not available in repo.*")
} }
@@ -128,7 +214,7 @@ func (s *RemoteRepoSuite) TestRefKey(c *C) {
func (s *RemoteRepoSuite) TestDownload(c *C) { func (s *RemoteRepoSuite) TestDownload(c *C) {
s.repo.Architectures = []string{"i386"} s.repo.Architectures = []string{"i386"}
err := s.repo.Fetch(s.downloader) err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, IsNil) c.Assert(err, IsNil)
s.downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages.bz2", errors.New("HTTP 404")) s.downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages.bz2", errors.New("HTTP 404"))
@@ -136,7 +222,7 @@ func (s *RemoteRepoSuite) TestDownload(c *C) {
s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages", examplePackagesFile) s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages", examplePackagesFile)
s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb", "xyz") s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb", "xyz")
err = s.repo.Download(s.downloader, s.packageCollection, s.packageRepo) err = s.repo.Download(s.downloader, s.packageCollection, s.packageRepo, false)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(s.downloader.Empty(), Equals, true) c.Assert(s.downloader.Empty(), Equals, true)
c.Assert(s.repo.packageRefs, NotNil) c.Assert(s.repo.packageRefs, NotNil)
@@ -144,8 +230,35 @@ 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")
}
func (s *RemoteRepoSuite) TestDownloadFlat(c *C) {
downloader := utils.NewFakeDownloader()
downloader.ExpectResponse("http://repos.express42.com/virool/precise/Release", exampleReleaseFile)
downloader.ExpectError("http://repos.express42.com/virool/precise/Packages.bz2", errors.New("HTTP 404"))
downloader.ExpectError("http://repos.express42.com/virool/precise/Packages.gz", errors.New("HTTP 404"))
downloader.ExpectResponse("http://repos.express42.com/virool/precise/Packages", examplePackagesFile)
downloader.ExpectResponse("http://repos.express42.com/virool/precise/pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb", "xyz")
err := s.flat.Fetch(downloader, nil)
c.Assert(err, IsNil)
err = s.flat.Download(downloader, s.packageCollection, s.packageRepo, false)
c.Assert(err, IsNil)
c.Assert(downloader.Empty(), Equals, true)
c.Assert(s.flat.packageRefs, NotNil)
pkg, err := s.packageCollection.ByKey(s.flat.packageRefs.Refs[0])
c.Assert(err, IsNil)
result, err := pkg.VerifyFiles(s.packageRepo)
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 +332,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 +344,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 {
@@ -239,6 +354,32 @@ func (s *RemoteRepoCollectionSuite) TestForEach(c *C) {
c.Assert(err, Equals, e) c.Assert(err, Equals, e)
} }
func (s *RemoteRepoCollectionSuite) TestDrop(c *C) {
repo1, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
s.collection.Add(repo1)
repo2, _ := NewRemoteRepo("tyndex", "http://mirror.yandex.ru/debian/", "wheezy", []string{"main"}, []string{})
s.collection.Add(repo2)
r1, _ := s.collection.ByUUID(repo1.UUID)
c.Check(r1, Equals, repo1)
err := s.collection.Drop(repo1)
c.Check(err, IsNil)
_, err = s.collection.ByUUID(repo1.UUID)
c.Check(err, ErrorMatches, "mirror .* not found")
collection := NewRemoteRepoCollection(s.db)
_, err = collection.ByName("yandex")
c.Check(err, ErrorMatches, "mirror .* not found")
r2, _ := collection.ByName("tyndex")
c.Check(r2.String(), Equals, repo2.String())
c.Check(func() { s.collection.Drop(repo1) }, Panics, "repo not found!")
}
const exampleReleaseFile = `Origin: LP-PPA-agenda-developers-daily const exampleReleaseFile = `Origin: LP-PPA-agenda-developers-daily
Label: Agenda Daily Builds Label: Agenda Daily Builds
Suite: precise Suite: precise
@@ -261,7 +402,7 @@ MD5Sum:
c63d31e8e3a5650c29a7124e541d6c23 134 main/binary-armhf/Release c63d31e8e3a5650c29a7124e541d6c23 134 main/binary-armhf/Release
4059d198768f9f8dc9372dc1c54bc3c3 14 main/binary-armhf/Packages.bz2 4059d198768f9f8dc9372dc1c54bc3c3 14 main/binary-armhf/Packages.bz2
d41d8cd98f00b204e9800998ecf8427e 0 main/binary-armhf/Packages d41d8cd98f00b204e9800998ecf8427e 0 main/binary-armhf/Packages
708fc548e709eea0dfd2d7edb6098829 1344 main/binary-i386/Packages c8d336856df67d509032bb54145c2f89 826 main/binary-i386/Packages
92262f0668b265401291f0467bc93763 133 main/binary-i386/Release 92262f0668b265401291f0467bc93763 133 main/binary-i386/Release
7954ed80936429687122b554620c1b5b 734 main/binary-i386/Packages.bz2 7954ed80936429687122b554620c1b5b 734 main/binary-i386/Packages.bz2
e2eef4fe7d285b12c511adfa3a39069e 641 main/binary-i386/Packages.gz e2eef4fe7d285b12c511adfa3a39069e 641 main/binary-i386/Packages.gz
@@ -301,7 +442,7 @@ SHA1:
585a452e27c2e7e047c49d4b0a7459d8c627aa08 134 main/binary-armhf/Release 585a452e27c2e7e047c49d4b0a7459d8c627aa08 134 main/binary-armhf/Release
64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 main/binary-armhf/Packages.bz2 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 main/binary-armhf/Packages.bz2
da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-armhf/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-armhf/Packages
2bfad956c2d2437924a8527970858c59823451b7 1344 main/binary-i386/Packages 1d2f0cd7a3c9e687b853eb277e241cd712b6e3b1 826 main/binary-i386/Packages
16020809662f9bda36eb516d0995658dd94d1ad5 133 main/binary-i386/Release 16020809662f9bda36eb516d0995658dd94d1ad5 133 main/binary-i386/Release
95a463a0739bf9ff622c8d68f6e4598d400f5248 734 main/binary-i386/Packages.bz2 95a463a0739bf9ff622c8d68f6e4598d400f5248 734 main/binary-i386/Packages.bz2
bf8c0dec9665ba78311c97cae1755d4b2e60af76 641 main/binary-i386/Packages.gz bf8c0dec9665ba78311c97cae1755d4b2e60af76 641 main/binary-i386/Packages.gz
@@ -341,7 +482,7 @@ SHA256:
d25382b633c4a1621f8df6ce86e5c63da2e506a377e05ae9453238bb18191540 134 main/binary-armhf/Release d25382b633c4a1621f8df6ce86e5c63da2e506a377e05ae9453238bb18191540 134 main/binary-armhf/Release
d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 main/binary-armhf/Packages.bz2 d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 main/binary-armhf/Packages.bz2
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-armhf/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-armhf/Packages
9cd4bad3462e795bad509a44bae48622f2e9c9e56aafc999419cc5221f087dc8 1344 main/binary-i386/Packages b1bb341bb613363ca29440c2eb9c08a9289de5458209990ec502ed27711a83a2 826 main/binary-i386/Packages
e5aaceaac5ecb59143a4b4ed2bf700fe85d6cf08addd10cf2058bde697b7b219 133 main/binary-i386/Release e5aaceaac5ecb59143a4b4ed2bf700fe85d6cf08addd10cf2058bde697b7b219 133 main/binary-i386/Release
377890a26f99db55e117dfc691972dcbbb7d8be1630c8fc8297530c205377f2b 734 main/binary-i386/Packages.bz2 377890a26f99db55e117dfc691972dcbbb7d8be1630c8fc8297530c205377f2b 734 main/binary-i386/Packages.bz2
6361e8efc67d2e7c1a8db45388aec0311007c0a1bd96698623ddeb5ed0bdc914 641 main/binary-i386/Packages.gz 6361e8efc67d2e7c1a8db45388aec0311007c0a1bd96698623ddeb5ed0bdc914 641 main/binary-i386/Packages.gz
@@ -385,7 +526,7 @@ Section: utils
Priority: optional Priority: optional
Filename: pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb Filename: pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb
Size: 3 Size: 3
MD5sum: cdc997dc06126e18ea9ba843efed9811 MD5sum: d16fb36f0911f878998c136191af705e
SHA1: 049ba341d520c447fa2e6a1f8c871b3dbbe00106 SHA1: 66b27417d37e024c46526c2f6d358a754fc552f3
SHA256: 4487115ca47fe9acd95355b9278f30e18c53f33c385252252d3d7948d650d1d0 SHA256: 3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
` `
+13 -23
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
} }
@@ -98,6 +88,6 @@ func (r *Repository) LinkFromPool(prefix string, component string, filename stri
} }
// ChecksumsForFile proxies requests to utils.ChecksumsForFile, joining public path // ChecksumsForFile proxies requests to utils.ChecksumsForFile, joining public path
func (r *Repository) ChecksumsForFile(path string) (*utils.ChecksumInfo, error) { func (r *Repository) ChecksumsForFile(path string) (utils.ChecksumInfo, error) {
return utils.ChecksumsForFile(filepath.Join(r.RootPath, "public", path)) return utils.ChecksumsForFile(filepath.Join(r.RootPath, "public", path))
} }
+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)
+90
View File
@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/smira/aptly/database" "github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"log" "log"
"time" "time"
@@ -46,6 +47,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 +185,40 @@ 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)
}
// ByRemoteRepoSource looks up snapshots that have specified RepoteRepo as a source
func (collection *SnapshotCollection) ByRemoteRepoSource(repo *RemoteRepo) []*Snapshot {
result := make([]*Snapshot, 0)
for _, s := range collection.list {
if s.SourceKind == "repo" && utils.StrSliceHasItem(s.SourceIDs, repo.UUID) {
result = append(result, s)
}
}
return result
}
// BySnapshotSource looks up snapshots that have specified snapshot as a source
func (collection *SnapshotCollection) BySnapshotSource(snapshot *Snapshot) []*Snapshot {
result := make([]*Snapshot, 0)
for _, s := range collection.list {
if s.SourceKind == "snapshot" && utils.StrSliceHasItem(s.SourceIDs, snapshot.UUID) {
result = append(result, s)
}
}
return result
}
// 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 +230,35 @@ 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)
}
// Drop removes snapshot from collection
func (collection *SnapshotCollection) Drop(snapshot *Snapshot) error {
snapshotPosition := -1
for i, s := range collection.list {
if s == snapshot {
snapshotPosition = i
break
}
}
if snapshotPosition == -1 {
panic("snapshot not found!")
}
collection.list[len(collection.list)-1], collection.list[snapshotPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
err := collection.db.Delete(snapshot.Key())
if err != nil {
return err
}
return collection.db.Delete(snapshot.RefKey())
}
+79 -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,9 +150,60 @@ 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
}) })
c.Assert(err, Equals, e) c.Assert(err, Equals, e)
} }
func (s *SnapshotCollectionSuite) TestFindByRemoteRepoSource(c *C) {
c.Assert(s.collection.Add(s.snapshot1), IsNil)
c.Assert(s.collection.Add(s.snapshot2), IsNil)
c.Check(s.collection.ByRemoteRepoSource(s.repo1), DeepEquals, []*Snapshot{s.snapshot1})
c.Check(s.collection.ByRemoteRepoSource(s.repo2), DeepEquals, []*Snapshot{s.snapshot2})
repo3, _ := NewRemoteRepo("other", "http://mirror.yandex.ru/debian/", "lenny", []string{"main"}, []string{})
c.Check(s.collection.ByRemoteRepoSource(repo3), DeepEquals, []*Snapshot{})
}
func (s *SnapshotCollectionSuite) TestFindSnapshotSource(c *C) {
snapshot3 := NewSnapshotFromRefList("snap3", []*Snapshot{s.snapshot1, s.snapshot2}, s.reflist, "desc1")
snapshot4 := NewSnapshotFromRefList("snap4", []*Snapshot{s.snapshot1}, s.reflist, "desc2")
snapshot5 := NewSnapshotFromRefList("snap5", []*Snapshot{snapshot3}, s.reflist, "desc3")
c.Assert(s.collection.Add(s.snapshot1), IsNil)
c.Assert(s.collection.Add(s.snapshot2), IsNil)
c.Assert(s.collection.Add(snapshot3), IsNil)
c.Assert(s.collection.Add(snapshot4), IsNil)
c.Assert(s.collection.Add(snapshot5), IsNil)
c.Check(s.collection.BySnapshotSource(s.snapshot1), DeepEquals, []*Snapshot{snapshot3, snapshot4})
c.Check(s.collection.BySnapshotSource(s.snapshot2), DeepEquals, []*Snapshot{snapshot3})
c.Check(s.collection.BySnapshotSource(snapshot5), DeepEquals, []*Snapshot{})
}
func (s *SnapshotCollectionSuite) TestDrop(c *C) {
s.collection.Add(s.snapshot1)
s.collection.Add(s.snapshot2)
snap, _ := s.collection.ByUUID(s.snapshot1.UUID)
c.Check(snap, Equals, s.snapshot1)
err := s.collection.Drop(s.snapshot1)
c.Check(err, IsNil)
_, err = s.collection.ByUUID(s.snapshot1.UUID)
c.Check(err, ErrorMatches, "snapshot .* not found")
collection := NewSnapshotCollection(s.db)
_, err = collection.ByUUID(s.snapshot1.UUID)
c.Check(err, ErrorMatches, "snapshot .* not found")
c.Check(func() { s.collection.Drop(s.snapshot1) }, Panics, "snapshot not found!")
}
+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]")
}
+53 -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.3"
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),
@@ -29,23 +30,46 @@ take snapshots and publish them back as Debian repositories.`,
makeCmdMirror(), makeCmdMirror(),
makeCmdSnapshot(), makeCmdSnapshot(),
makeCmdPublish(), makeCmdPublish(),
makeCmdServe(),
makeCmdGraph(),
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")
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
} }
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)
} }
configLocation := cmd.Flag.Lookup("config").Value.String()
if configLocation != "" {
err = utils.LoadConfig(configLocation, &utils.Config)
if err != nil {
fatal(err)
}
} else {
configLocations := []string{ configLocations := []string{
filepath.Join(os.Getenv("HOME"), ".aptly.conf"), filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
"/etc/aptly.conf", "/etc/aptly.conf",
@@ -56,28 +80,47 @@ 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
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.5 (GNU/Linux)
mQGiBE9p65cRBACFOL5YS4kW6xieXa98meE+RVu1hfBi1n7ajAy+ZQOfNa2Xb9if
H8WAcwJD4cTZYB19/O/xVxCYf1hnC/T34XGC5PUzMzBDKde86UDqvT4YNHDQA/E1
I6UUzE0MgLINO4Dt7Mw62koVrlPXklc2Zn83ucZB7YgBzJOIBFUQLghikwCgnubS
n/9lw8Hm8CIsg4nWtHwHGPED/jXIsH7ON3PBx2wIdRsealsx5sPGHQSlq1grRHcN
YT5/glXmVqnexY/+IFhcpjjb3vMMQ5LYq8+bDWGVMQx3GZrJs66rwPbo4kZ92OdC
RTnY/nznJlf5gS86DaFl+NFuO7F1k8ju4CurXXGXPF7nk8cgV6CrYHz1AtNyLVqa
306IA/9j9rdD/MY9SYT16eFMo7C2ieIS0RxxU3q9w0e8EucQKiHWMtjTPJ0Ik0GO
TY5lAPasnD6ZBA15XSiTi2Ck2QoZQZCxdtId/nL7lNG4+vQ8HACNDkxxK4yHJiFa
frMdlWi5cYgAMYzbYPekbhaamDR7Gh4NU7z72QZTPELKyZD/pbRGaG9tZTpEZWVw
RGl2ZXIxOTc1IE9CUyBQcm9qZWN0IDxob21lOkRlZXBEaXZlcjE5NzVAYnVpbGQu
b3BlbnN1c2Uub3JnPohlBBMRAgAmBQJPaeuXAhsDBQkEHrAABgsJCAcDAgQVAggD
BBYCAwECHgECF4AACgkQuCWWGBBIwf+tEgCcDEzTAilZDvOr0OKHHmguuKFXoHMA
ljX7B6nKOYoiHoGpBeOwr8U5ZB6IRgQTEQIABgUCT2nrlwAKCRA7MBG3a51lI7Rt
AJ4mYeomQiHHfd+7c8T0JhbGKUIDlACglHyTlouU5vCpUEHDyLvwrHFylpk=
=b/6f
-----END PGP PUBLIC KEY BLOCK-----
+14
View File
@@ -0,0 +1,14 @@
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
aptly mirror create gnuplot-maverick http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick
aptly mirror update gnuplot-maverick
+221
View File
@@ -0,0 +1,221 @@
"""
Test library.
"""
import difflib
import inspect
import json
import subprocess
import os
import posixpath
import shlex
import shutil
import string
import threading
import urllib
import SocketServer
import SimpleHTTPServer
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
class FileHTTPServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = path.split('?', 1)[0]
path = path.split('#', 1)[0]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.rootPath
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir):
continue
path = os.path.join(path, word)
return path
def log_message(self, format, *args):
pass
class BaseTest(object):
"""
Base class for all tests.
"""
longTest = False
fixturePool = False
fixtureDB = False
fixtureGpg = False
fixtureWebServer = False
expectedCode = 0
configFile = {
"rootDir": "%s/.aptly" % os.environ["HOME"],
"downloadConcurrency": 4,
"architectures": [],
"dependencyFollowSuggests": False,
"dependencyFollowRecommends": False,
"dependencyFollowAllVariants": False,
"gpgDisableVerify": False,
"gpgDisableSign": 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"))
if os.path.exists(os.path.join(os.environ["HOME"], ".gnupg", "aptlytest.gpg")):
os.remove(os.path.join(os.environ["HOME"], ".gnupg", "aptlytest.gpg"))
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 self.fixtureWebServer:
self.webServerUrl = self.start_webserver(os.path.join(os.path.dirname(inspect.getsourcefile(self.__class__)),
self.fixtureWebServer))
if self.fixtureGpg:
self.run_cmd(["gpg", "--no-default-keyring", "--trust-model", "always", "--batch", "--keyring", "aptlytest.gpg", "--import",
os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "files", "debian-archive-keyring.gpg"),
os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "files", "flat.key")])
if hasattr(self, "fixtureCmds"):
if self.fixtureWebServer:
params = {'url': self.webServerUrl}
self.fixtureCmds = [string.Template(cmd).substitute(params) for cmd in 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:
if not hasattr(command, "__iter__"):
command = shlex.split(command)
environ = os.environ.copy()
environ["LC_ALL"] = "C"
proc = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=environ)
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, expected_code=0):
self.verify_match(self.get_gold(gold_name), self.run_cmd(command, expected_code=expected_code), match_prepare)
def read_file(self, path):
with open(os.path.join(os.environ["HOME"], ".aptly", path), "r") as f:
return f.read()
def check_file_contents(self, path, gold_name, match_prepare=None):
contents = self.read_file(path)
self.verify_match(self.get_gold(gold_name), contents, match_prepare=match_prepare)
def check_file(self):
self.verify_match(self.get_gold(), open(self.checkedFile, "r").read())
def check_exists(self, path):
if not os.path.exists(os.path.join(os.environ["HOME"], ".aptly", path)):
raise Exception("path %s doesn't exist" % (path, ))
def check_not_exists(self, path):
if os.path.exists(os.path.join(os.environ["HOME"], ".aptly", path)):
raise Exception("path %s exists" % (path, ))
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")
check = check_output
def prepare(self):
self.prepare_remove_all()
self.prepare_default_config()
self.prepare_fixture()
def start_webserver(self, directory):
FileHTTPServerRequestHandler.rootPath = directory
self.webserver = ThreadedTCPServer(("localhost", 0), FileHTTPServerRequestHandler)
server_thread = threading.Thread(target=self.webserver.serve_forever)
server_thread.daemon = True
server_thread.start()
return "http://%s:%d/" % self.webserver.server_address
def shutdown(self):
if hasattr(self, 'webserver'):
self.shutdown_webserver()
def shutdown_webserver(self):
self.webserver.shutdown()
Executable
+78
View File
@@ -0,0 +1,78 @@
#!/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, tests=None):
"""
Run system test.
"""
if tests is None:
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"))
t.shutdown()
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 = False
tests = None
if len(sys.argv) > 1:
if sys.argv[1] == "--long":
include_long_tests = True
else:
tests = sys.argv[1:]
run(include_long_tests, tests)
+1
View File
@@ -0,0 +1 @@
aptly version: 0.3
+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
+20
View File
@@ -0,0 +1,20 @@
aptly - Debian repository management tool
Commands:
graph display graph of dependencies between aptly objects (requires graphviz)
mirror manage mirrors of remote repositories
publish manage published repositories
serve start embedded HTTP server to serve 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
-config="": location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)
-dep-follow-all-variants=false: when processing dependencies, follow a & b if depdency is 'a|b'
-dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-suggests=false: when processing dependencies, follow Suggests
@@ -0,0 +1 @@
ERROR: open nosuchfile.conf: no such file or directory
+10
View File
@@ -0,0 +1,10 @@
{
"rootDir": "${HOME}/.aptly",
"downloadConcurrency": 4,
"architectures": [],
"dependencyFollowSuggests": false,
"dependencyFollowRecommends": false,
"dependencyFollowAllVariants": false,
"gpgDisableSign": false,
"gpgDisableVerify": false
}
+53
View File
@@ -0,0 +1,53 @@
"""
Test config file
"""
import os
import inspect
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()
class ConfigInFileTest(BaseTest):
"""
config in other file test
"""
runCmd = ["aptly", "-config=%s" % (os.path.join(os.path.dirname(inspect.getsourcefile(BadConfigTest)), "aptly.conf"), )]
prepare = BaseTest.prepare_remove_all
class ConfigInMissingFileTest(BaseTest):
"""
config in other file test
"""
runCmd = ["aptly", "-config=nosuchfile.conf"]
expectedCode = 1
prepare = BaseTest.prepare_remove_all
+10
View File
@@ -0,0 +1,10 @@
{
"rootDir": "/tmp/aptly",
"downloadConcurrency": 4,
"architectures": [],
"dependencyFollowSuggests": false,
"dependencyFollowRecommends": false,
"dependencyFollowAllVariants": false,
"gpgDisableSign": false,
"gpgDisableVerify": false
}
+11
View File
@@ -0,0 +1,11 @@
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
-config="": location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)
-dep-follow-all-variants=false: when processing dependencies, follow a & b if depdency is 'a|b'
-dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-suggests=false: when processing dependencies, follow Suggests
+20
View File
@@ -0,0 +1,20 @@
aptly - Debian repository management tool
Commands:
graph display graph of dependencies between aptly objects (requires graphviz)
mirror manage mirrors of remote repositories
publish manage published repositories
serve start embedded HTTP server to serve 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
-config="": location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)
-dep-follow-all-variants=false: when processing dependencies, follow a & b if depdency is 'a|b'
-dep-follow-recommends=false: when processing dependencies, follow Recommends
-dep-follow-suggests=false: when processing dependencies, follow Suggests
+11
View File
@@ -0,0 +1,11 @@
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
Options:
-ignore-signatures=false: disable verification of Release file signatures
-keyring=: gpg keyring to use when verifying Release file (could be specified multiple times)
+8
View File
@@ -0,0 +1,8 @@
Usage: aptly mirror create <name> <archive url> <distribution> [<component1> ...]
aptly mirror create - create new mirror of Debian repository
Options:
-ignore-signatures=false: disable verification of Release file signatures
-keyring=: gpg keyring to use when verifying Release file (could be specified multiple times)
+12
View File
@@ -0,0 +1,12 @@
aptly mirror - manage mirrors of remote repositories
Commands:
create create new mirror of Debian repository
drop delete remote repository mirror
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.
+12
View File
@@ -0,0 +1,12 @@
aptly mirror - manage mirrors of remote repositories
Commands:
create create new mirror of Debian repository
drop delete remote repository mirror
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"
+10
View File
@@ -0,0 +1,10 @@
Downloading http://mirror.yandex.ru/debian-backports/dists/squeeze-backports/InRelease...
gpgv: keyblock resource `${HOME}/.gnupg/aptlytest.gpg': file open error
gpgv: RSA key ID 46925553
gpgv: Can't check signature: public key not found
Downloading http://mirror.yandex.ru/debian-backports/dists/squeeze-backports/Release...
Downloading http://mirror.yandex.ru/debian-backports/dists/squeeze-backports/Release.gpg...
gpgv: keyblock resource `${HOME}/.gnupg/aptlytest.gpg': file open error
gpgv: RSA key ID 46925553
gpgv: Can't check signature: public key not found
ERROR: unable to fetch mirror: verification of detached signature failed: exit status 2
+10
View File
@@ -0,0 +1,10 @@
Downloading http://mirror.yandex.ru/debian/dists/squeeze/InRelease...
Downloading http://mirror.yandex.ru/debian/dists/squeeze/Release...
Downloading http://mirror.yandex.ru/debian/dists/squeeze/Release.gpg...
gpgv: RSA key ID 473041FA
gpgv: Good signature from "Debian Archive Automatic Signing Key (6.0/squeeze) <ftpmaster@debian.org>"
gpgv: RSA key ID B98321F9
gpgv: Good signature from "Squeeze Stable Release Key <debian-release@lists.debian.org>"
Mirror [mirror11]: http://mirror.yandex.ru/debian/ squeeze successfully added.
You can run 'aptly mirror update mirror11' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror11
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: squeeze
Components: main, contrib, non-free
Architectures: amd64, armel, i386, ia64, kfreebsd-amd64, kfreebsd-i386, mips, mipsel, powerpc, s390, sparc
Last update: never
Information from release file:
Architectures: amd64 armel i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 sparc
Codename: squeeze
Components: main contrib non-free
Date: Sat, 19 Oct 2013 13:54:21 UTC
Description: Debian 6.0.8 Released 19 October 2013
Label: Debian
Origin: Debian
Suite: oldstable
Version: 6.0.8
@@ -0,0 +1,9 @@
Downloading http://mirror.yandex.ru/debian/dists/squeeze/InRelease...
Downloading http://mirror.yandex.ru/debian/dists/squeeze/Release...
Downloading http://mirror.yandex.ru/debian/dists/squeeze/Release.gpg...
gpgv: keyblock resource `${HOME}/.gnupg/aptlytest.gpg': file open error
gpgv: RSA key ID 473041FA
gpgv: Can't check signature: public key not found
gpgv: RSA key ID B98321F9
gpgv: Can't check signature: public key not found
ERROR: unable to fetch mirror: verification of detached signature failed: exit status 2
@@ -0,0 +1,4 @@
Downloading http://mirror.yandex.ru/debian/dists/wheezy/Release...
Mirror [mirror13]: http://mirror.yandex.ru/debian/ wheezy successfully added.
You can run 'aptly mirror update mirror13' to download repository contents.
@@ -0,0 +1,18 @@
Name: mirror13
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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
@@ -0,0 +1,8 @@
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/InRelease...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Release...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Release.gpg...
gpgv: Signature made Tue May 21 23:01:30 2013 MSK using DSA key ID 1048C1FF
gpgv: Good signature from "home:DeepDiver1975 OBS Project <home:DeepDiver1975@build.opensuse.org>"
Mirror [mirror14]: http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/ successfully added.
You can run 'aptly mirror update mirror14' to download repository contents.
@@ -0,0 +1,14 @@
Name: mirror14
Archive Root URL: http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/
Distribution:
Components:
Architectures:
Last update: never
Information from release file:
Date: Tue May 21 21:01:30 2013
Description: Open Build Service home:DeepDiver1975 xUbuntu_10.04
Label: DeepDiver1975's Home Project (xUbuntu_10.04)
Origin: Open Build Service home:DeepDiver1975 xUbuntu_10.04
Version: 0.00
@@ -0,0 +1 @@
ERROR: unable to create mirror: components aren't supported for flat repos
+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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
+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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
+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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
+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
+3
View File
@@ -0,0 +1,3 @@
Downloading http://mirror.yandex.ru/debian/dists/suslik/InRelease...
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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
+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
+9
View File
@@ -0,0 +1,9 @@
Downloading http://mirror.yandex.ru/debian-backports/dists/squeeze-backports/InRelease...
gpgv: Signature made Fri Feb 7 06:56:50 2014 MSK using RSA key ID 46925553
gpgv: Good signature from "Debian Archive Automatic Signing Key (7.0/wheezy) <ftpmaster@debian.org>"
gpg: Signature made Fri Feb 7 06:56:50 2014 MSK using RSA key ID 46925553
gpg: Good signature from "Debian Archive Automatic Signing Key (7.0/wheezy) <ftpmaster@debian.org>"
gpg: WARNING: Using untrusted key!
Mirror [mirror9]: http://mirror.yandex.ru/debian-backports/ squeeze-backports successfully added.
You can run 'aptly mirror update mirror9' to download repository contents.
@@ -0,0 +1,20 @@
Name: mirror9
Archive Root URL: http://mirror.yandex.ru/debian-backports/
Distribution: squeeze-backports
Components: main, contrib, non-free
Architectures: amd64, armel, i386, ia64, kfreebsd-amd64, kfreebsd-i386, mips, mipsel, powerpc, s390, sparc
Last update: never
Information from release file:
Architectures: amd64 armel i386 ia64 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390 sparc
ButAutomaticUpgrades: yes
Codename: squeeze-backports
Components: main contrib non-free
Date: Fri, 07 Feb 2014 02:56:49 UTC
Description: Backports for the Squeeze Distribution
Label: Debian Backports
NotAutomatic: yes
Origin: Debian Backports
Suite: squeeze-backports
Valid-Until: Fri, 14 Feb 2014 02:56:49 UTC
+1
View File
@@ -0,0 +1 @@
Mirror `mirror1` has been removed.
@@ -0,0 +1 @@
ERROR: unable to show: mirror with name mirror1 not found
+3
View File
@@ -0,0 +1,3 @@
Mirror `wheezy-main` was used to create following snapshots:
* [wheez]: Snapshot from mirror [wheezy-main]: http://mirror.yandex.ru/debian/ wheezy
ERROR: won't delete mirror with snapshots, use -force to override
+1
View File
@@ -0,0 +1 @@
Mirror `wheezy-main` has been removed.
+1
View File
@@ -0,0 +1 @@
ERROR: unable to drop: mirror with name mirror1 not found
+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, 08 Feb 2014 10:36:03 UTC
Description: Debian 7.4 Released 08 February 2014
Label: Debian
Origin: Debian
Suite: stable
Version: 7.4
+1
View File
@@ -0,0 +1 @@
ERROR: unable to show: mirror with name mirror-xx not found
+345
View File
@@ -0,0 +1,345 @@
Name: wheezy-contrib
Archive Root URL: http://mirror.yandex.ru/debian/
Distribution: wheezy
Components: contrib
Architectures: i386, amd64
Last update: 2014-01-21 19:09:24 MSK
Number of packages: 325
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
Packages:
bgoffice-dict-downloader-0.09_all
biomaj-watcher-1.2.1-1_all
cicero-0.7.2-2_all
cl-sql-oracle-6.2.0-1_all
cl-umlisp-1:2007ac.2-6_all
cl-umlisp-orf-3.3.2-3_all
cltl-1.0.26_all
crafty-bitmaps-1.0-1_all
crafty-books-medium-1.0.debian1-2_all
crafty-books-medtosmall-1.0.debian1-2_all
crafty-books-small-1.0.debian1-2_all
cytadela-data-1.0.1-2_all
dynagen-0.11.0-6_all
dynare-matlab-4.3.0-2_all
esix-1-2_all
festvox-don-1.4.0-4_all
festvox-en1-1.95-1_all
festvox-rablpc16k-1.4.0-2_all
festvox-rablpc8k-1.4.0-2_all
festvox-us1-1.95-1_all
festvox-us2-1.95-1_all
festvox-us3-1.95-1_all
firmware-b43-installer-1:015-14.1_all
firmware-b43-lpphy-installer-1:015-14.1_all
firmware-b43legacy-installer-1:015-14.1_all
game-data-packager-30_all
geoip-database-contrib-1.8_all
gns3-0.7.4-1_all
gnuvd-gnome-1.0.11-1_all
googleearth-package-0.7.0_all
horae-071~svn536-1_all
hts-voice-nitech-jp-atr503-m001-1.04-1_all
hyperspec-1.30+nmu2_all
ifeffit-doc-2:1.2.11d-8_all
imgtex-0.20050123-8_all
java-package-0.50+nmu2_all
kcemu-common-0.5.1+dfsg-5_all
libgooglecharts-ruby-1.6.8-2_all
libgooglecharts-ruby1.8-1.6.8-2_all
libjlapack-java-0.8~dfsg-1_all
libmtj-java-0.9.14~dfsg-2_all
libmtj-java-doc-0.9.14~dfsg-2_all
libnetlib-java-0.9.3-1_all
libviennacl-dev-1.2.0-2_all
libviennacl-doc-1.2.0-2_all
linux-wlan-ng-firmware-0.2.9+dfsg-5_all
mathematica-fonts-16_all
matlab-gdf-0.1.2-2_all
matlab-support-0.0.18_all
mess-desktop-entries-0.2-2_all
netdisco-mibs-installer-1.7.1_all
opendict-plugins-lingvosoft-0.8-2_all
ora2pg-8.11-1_all
phoronix-test-suite-3.8.0-1_all
playonlinux-4.1.1-1_all
premail-0.46-9_all
python-ldap-doc-2.3-2.2_all
python-pycuda-doc-2012.1-1_all
python-pycuda-headers-2012.1-1_all
python-pyopencl-doc-2012.1.dfsg-1_all
python-pyopencl-headers-2012.1.dfsg-1_all
qmhandle-1.3.2-1_all
quake-2_all
quake-server-2_all
quake3-1.4_all
quake3-server-1.4_all
raccoon-1.0-1_all
ruby-googlecharts-1.6.8-2_all
sabnzbdplus-0.6.15-1_all
sabnzbdplus-theme-classic-0.6.15-1_all
sabnzbdplus-theme-iphone-0.6.15-1_all
sabnzbdplus-theme-mobile-0.6.15-1_all
sabnzbdplus-theme-plush-0.6.15-1_all
sabnzbdplus-theme-smpl-0.6.15-1_all
sapgui-package-0.0.10_all
sauerbraten-wake6-1.0-1.1_all
sdic-2.1.3-22_all
sdic-edict-2.1.3-22_all
sdic-eijiro-2.1.3-22_all
sdic-gene95-2.1.3-22_all
series60-remote-0.4.0+dfsg.1-1_all
sixpack-1:0.68-1_all
sqldeveloper-package-0.2.4_all
sugar-etoys-activity-116-3_all
susv2-1.1_all
susv3-6.1_all
tightvnc-java-1.2.7-8_all
ttf-mathematica4.1-16_all
ttf-mscorefonts-installer-3.4+nmu1_all
ttf-root-installer-5.34.00-2_all
uqm-russian-1.0.2-5_all
vmware-manager-0.2.0-3_all
vnc-java-3.3.3r2-8_all
vusb-analyzer-1.1-3_all
winetricks-0.0+20121030+svn918-1_all
wnn7egg-1.02-8_all
x-pgp-sig-el-1.3.5.1-4.1_all
alien-arena-7.53+dfsg-3_amd64
alien-arena-server-7.53+dfsg-3_amd64
alsa-firmware-loaders-1.0.25-2_amd64
amoeba-1.1-26_amd64
assaultcube-1.1.0.4+dfsg2-1_amd64
atari800-2.2.1-2_amd64
b43-fwcutter-1:015-14.1_amd64
basilisk2-0.9.20120331-2_amd64
boinc-nvidia-cuda-7.0.27+dfsg-5_amd64
cbedic-4.0-3_amd64
chocolate-doom-1.7.0-3+b1_amd64
conky-all-1.9.0-2_amd64
cpp-doc-5:4_amd64
cytadela-1.0.1-2_amd64
cytadela-dbg-1.0.1-2_amd64
dosemu-1.4.0+svn.2080-1_amd64
e-uae-0.8.29-WIP4-10_amd64
e-uae-dbg-0.8.29-WIP4-10_amd64
easyspice-0.6.8-2_amd64
exult-1.2-15.2_amd64
exult-studio-1.2-15.2_amd64
flashplugin-nonfree-1:3.2_amd64
frogatto-1.2+dfsg-1+b1_amd64
gcc-doc-5:4_amd64
gcj-doc-5:4_amd64
gfortran-doc-5:4_amd64
glx-alternative-fglrx-0.2.2_amd64
glx-alternative-mesa-0.2.2_amd64
glx-alternative-nvidia-0.2.2_amd64
glx-diversions-0.2.2_amd64
gnat-doc-5:4_amd64
gnome-speech-swift-1:0.4.25-5_amd64
gnome-video-arcade-0.8.3-1_amd64
gnuboy-sdl-1.0.3-6.1_amd64
gnuboy-svga-1.0.3-6.1_amd64
gnuboy-x-1.0.3-6.1_amd64
gnuvd-1.0.11-1_amd64
gobi-loader-0.6-1_amd64
gtktrain-0.9b-13_amd64
hannah-foo2zjs-1:1_amd64
ifeffit-2:1.2.11d-8_amd64
isdnactivecards-1:3.9.20060704-11_amd64
isight-firmware-tools-1.6-1_amd64
iucode-tool-0.8.3-1_amd64
ivtv-utils-1.4.1-2_amd64
kcemu-0.5.1+dfsg-5_amd64
libcplgasgano20-6.1.1-2_amd64
libdbd-oracle-perl-1.44-1_amd64
libifeffit-perl-2:1.2.11d-8_amd64
libpgplot-perl-1:2.21-3_amd64
libsocl-contrib-1.0-1.0.1+dfsg-1_amd64
libstarpu-contrib-1.0-1.0.1+dfsg-1_amd64
libstarpu-contrib-dev-1.0.1+dfsg-1_amd64
libstarpu-contribfft-1.0-1.0.1+dfsg-1_amd64
libstarpu-contribmpi-1.0-1.0.1+dfsg-1_amd64
libsuitesparse-metis-3.1.0-3.1.0-2_amd64
libsuitesparse-metis-dbg-3.1.0-2_amd64
libsuitesparse-metis-dev-3.1.0-2_amd64
libtrain-bin-0.9b-11_amd64
libtrain-dev-0.9b-11_amd64
libtrain1-0.9b-11_amd64
libxnvctrl-dev-304.88-1_amd64
libxnvctrl0-304.88-1_amd64
libydpdict2-1.0.2-1_amd64
libydpdict2-dev-1.0.2-1_amd64
lugaru-0~20110520.1+hge4354+dfsg-3_amd64
microcode.ctl-1.18~0+nmu2_amd64
nvidia-installer-cleanup-20120630+3_amd64
nvidia-kernel-common-20120630+3_amd64
nvidia-settings-304.88-1_amd64
nvidia-settings-legacy-173xx-173.14.35-2_amd64
nvidia-support-20120630+3_amd64
nvidia-xconfig-304.48-1_amd64
pidgin-skype-20110407+svn628+dfsg-1_amd64
pidgin-skype-dbg-20110407+svn628+dfsg-1_amd64
prism2-usb-firmware-installer-0.2.9+dfsg-5_amd64
pvpgn-1.8.1-2.1+b1_amd64
python-ifeffit-2:1.2.11d-8_amd64
python-pycuda-2012.1-1_amd64
python-pyopencl-2012.1.dfsg-1_amd64
python3-pyopencl-2012.1.dfsg-1_amd64
r-cran-surveillance-1.2-1-3_amd64
redeclipse-1.2-3_amd64
redeclipse-dbg-1.2-3_amd64
redeclipse-server-1.2-3_amd64
redeclipse-server-dbg-1.2-3_amd64
reminiscence-0.2.1-1_amd64
rocksndiamonds-3.3.0.1+dfsg1-2.2_amd64
rott-1.1.2-1_amd64
ruby-pgplot-0.1.3-6_amd64
ruby-pgplot-dbg-0.1.3-6_amd64
sandboxgamemaker-2.7.1+dfsg-2_amd64
sauerbraten-0.0.20100728.dfsg+repack-3_amd64
sauerbraten-dbg-0.0.20100728.dfsg+repack-3_amd64
sauerbraten-server-0.0.20100728.dfsg+repack-3_amd64
spectemu-common-0.94a-15_amd64
spectemu-svga-0.94a-15_amd64
spectemu-x11-0.94a-15_amd64
starpu-contrib-examples-1.0.1+dfsg-1_amd64
starpu-contrib-tools-1.0.1+dfsg-1_amd64
uae-0.8.29-7_amd64
uae-dbg-0.8.29-7_amd64
uqm-0.6.2.dfsg-9_amd64
vice-2.3.dfsg-4_amd64
vmware-view-open-client-4.5.0-297975+dfsg-4+b1_amd64
vor-0.5.5-2_amd64
wdq2wav-0.8.3-2_amd64
wolf4sdl-1.7+svn262+dfsg1-1_amd64
xserver-xorg-video-ivtv-1.1.2-1+b3_amd64
xserver-xorg-video-ivtv-dbg-1.1.2-1+b3_amd64
xtrs-4.9c-3.4_amd64
xvba-va-driver-0.8.0-5_amd64
ydpdict-1.0.0-2_amd64
alien-arena-7.53+dfsg-3_i386
alien-arena-server-7.53+dfsg-3_i386
alsa-firmware-loaders-1.0.25-2_i386
amoeba-1.1-26_i386
assaultcube-1.1.0.4+dfsg2-1_i386
atari800-2.2.1-2_i386
b43-fwcutter-1:015-14.1_i386
basilisk2-0.9.20120331-2_i386
boinc-nvidia-cuda-7.0.27+dfsg-5_i386
cbedic-4.0-3_i386
chocolate-doom-1.7.0-3_i386
conky-all-1.9.0-2_i386
cpp-doc-5:4_i386
cytadela-1.0.1-2_i386
cytadela-dbg-1.0.1-2_i386
dosemu-1.4.0+svn.2080-1_i386
e-uae-0.8.29-WIP4-10_i386
e-uae-dbg-0.8.29-WIP4-10_i386
easyspice-0.6.8-2_i386
exult-1.2-15.2_i386
exult-studio-1.2-15.2_i386
flashplugin-nonfree-1:3.2_i386
flashplugin-nonfree-extrasound-0.0.svn2431-3_i386
frogatto-1.2+dfsg-1_i386
gcc-doc-5:4_i386
gcj-doc-5:4_i386
gfortran-doc-5:4_i386
glx-alternative-fglrx-0.2.2_i386
glx-alternative-mesa-0.2.2_i386
glx-alternative-nvidia-0.2.2_i386
glx-diversions-0.2.2_i386
gnat-doc-5:4_i386
gnome-speech-dectalk-1:0.4.25-5_i386
gnome-speech-ibmtts-1:0.4.25-5_i386
gnome-speech-swift-1:0.4.25-5_i386
gnome-video-arcade-0.8.3-1_i386
gnuboy-sdl-1.0.3-6.1_i386
gnuboy-svga-1.0.3-6.1_i386
gnuboy-x-1.0.3-6.1_i386
gnuvd-1.0.11-1_i386
gobi-loader-0.6-1_i386
gtktrain-0.9b-13_i386
hannah-foo2zjs-1:1_i386
ifeffit-2:1.2.11d-8_i386
isdnactivecards-1:3.9.20060704-11_i386
isight-firmware-tools-1.6-1_i386
iucode-tool-0.8.3-1_i386
ivtv-utils-1.4.1-2_i386
kcemu-0.5.1+dfsg-5_i386
libcplgasgano20-6.1.1-2_i386
libdbd-oracle-perl-1.44-1_i386
libifeffit-perl-2:1.2.11d-8_i386
libpgplot-perl-1:2.21-3_i386
libsocl-contrib-1.0-1.0.1+dfsg-1_i386
libstarpu-contrib-1.0-1.0.1+dfsg-1_i386
libstarpu-contrib-dev-1.0.1+dfsg-1_i386
libstarpu-contribfft-1.0-1.0.1+dfsg-1_i386
libstarpu-contribmpi-1.0-1.0.1+dfsg-1_i386
libsuitesparse-metis-3.1.0-3.1.0-2_i386
libsuitesparse-metis-dbg-3.1.0-2_i386
libsuitesparse-metis-dev-3.1.0-2_i386
libtrain-bin-0.9b-11_i386
libtrain-dev-0.9b-11_i386
libtrain1-0.9b-11_i386
libxnvctrl-dev-304.88-1_i386
libxnvctrl0-304.88-1_i386
libydpdict2-1.0.2-1_i386
libydpdict2-dev-1.0.2-1_i386
lugaru-0~20110520.1+hge4354+dfsg-3_i386
microcode.ctl-1.18~0+nmu2_i386
nvidia-installer-cleanup-20120630+3_i386
nvidia-kernel-common-20120630+3_i386
nvidia-settings-304.88-1_i386
nvidia-settings-legacy-173xx-173.14.35-2_i386
nvidia-support-20120630+3_i386
nvidia-xconfig-304.48-1_i386
pidgin-skype-20110407+svn628+dfsg-1_i386
pidgin-skype-dbg-20110407+svn628+dfsg-1_i386
prism2-usb-firmware-installer-0.2.9+dfsg-5_i386
pvpgn-1.8.1-2.1+b1_i386
python-ifeffit-2:1.2.11d-8_i386
python-pycuda-2012.1-1_i386
python-pyopencl-2012.1.dfsg-1_i386
python3-pyopencl-2012.1.dfsg-1_i386
q-tools-0.4-1_i386
r-cran-surveillance-1.2-1-3_i386
redeclipse-1.2-3_i386
redeclipse-dbg-1.2-3_i386
redeclipse-server-1.2-3_i386
redeclipse-server-dbg-1.2-3_i386
reminiscence-0.2.1-1_i386
rocksndiamonds-3.3.0.1+dfsg1-2.2_i386
rott-1.1.2-1_i386
sandboxgamemaker-2.7.1+dfsg-2_i386
sauerbraten-0.0.20100728.dfsg+repack-3_i386
sauerbraten-dbg-0.0.20100728.dfsg+repack-3_i386
sauerbraten-server-0.0.20100728.dfsg+repack-3_i386
spectemu-common-0.94a-15_i386
spectemu-svga-0.94a-15_i386
spectemu-x11-0.94a-15_i386
starpu-contrib-examples-1.0.1+dfsg-1_i386
starpu-contrib-tools-1.0.1+dfsg-1_i386
uae-0.8.29-7_i386
uae-dbg-0.8.29-7_i386
uqm-0.6.2.dfsg-9_i386
vice-2.3.dfsg-4_i386
vmware-view-open-client-4.5.0-297975+dfsg-4+b1_i386
vor-0.5.5-2_i386
wdq2wav-0.8.3-2_i386
wolf4sdl-1.7+svn262+dfsg1-1_i386
xserver-xorg-video-ivtv-1.1.2-1+b3_i386
xserver-xorg-video-ivtv-dbg-1.1.2-1+b3_i386
xtrs-4.9c-3.4_i386
xvba-va-driver-0.8.0-5_i386
ydpdict-1.0.0-2_i386
+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
+6
View File
@@ -0,0 +1,6 @@
Downloading ${url}dists/hardy/Release...
Downloading & parsing package files...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.bz2...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.gz...
Downloading ${url}dists/hardy/main/binary-amd64/Packages...
ERROR: unable to update: ${url}dists/hardy/main/binary-amd64/Packages: sha256 hash mismatch "494414ded24da13c451b13b424928821351c78fce49f93d9e1b55f102790c206" != "8a21688ae769f2b4ffcaa366409f679d"
+7
View File
@@ -0,0 +1,7 @@
Downloading ${url}dists/hardy/Release...
Downloading & parsing package files...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.bz2...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.gz...
Downloading ${url}dists/hardy/main/binary-amd64/Packages...
WARNING: ${url}dists/hardy/main/binary-amd64/Packages: sha256 hash mismatch "494414ded24da13c451b13b424928821351c78fce49f93d9e1b55f102790c206" != "8a21688ae769f2b4ffcaa366409f679d"
ERROR: unable to update: malformed stanza syntax
+12
View File
@@ -0,0 +1,12 @@
Downloading ${url}dists/hardy/Release...
Downloading & parsing package files...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.bz2...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.gz...
Downloading ${url}dists/hardy/main/binary-amd64/Packages...
Saving packages to database...
Building download queue...
Download queue: 1 items, 0.00 GiB size
Downloading ${url}pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb...
ERROR: unable to update: download errors:
${url}pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb: sha1 hash mismatch "8d3a014000038725d6daf8771b42a0784253688f" != "66b27417d37e024c46526c2f6d358a754fc552f3"
+12
View File
@@ -0,0 +1,12 @@
Downloading ${url}dists/hardy/Release...
Downloading & parsing package files...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.bz2...
Downloading ${url}dists/hardy/main/binary-amd64/Packages.gz...
Downloading ${url}dists/hardy/main/binary-amd64/Packages...
Saving packages to database...
Building download queue...
Download queue: 1 items, 0.00 GiB size
Downloading ${url}pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb...
WARNING: ${url}pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb: sha1 hash mismatch "8d3a014000038725d6daf8771b42a0784253688f" != "66b27417d37e024c46526c2f6d358a754fc552f3"
Mirror `failure` has been successfully updated.
+18
View File
@@ -0,0 +1,18 @@
Building download queue...
Download queue: 4 items, 0.00 GiB size
Downloading & parsing package files...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/InRelease...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Packages.bz2...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Packages.gz...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Release...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/Release.gpg...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/amd64/libiniparser-dev_3.1-1_amd64.deb...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/amd64/libiniparser_3.1-1_amd64.deb...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/i386/libiniparser-dev_3.1-1_i386.deb...
Downloading http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/i386/libiniparser_3.1-1_i386.deb...
Mirror `flat` has been successfully updated.
Saving packages to database...
gpgv: Good signature from "home:DeepDiver1975 OBS Project <home:DeepDiver1975@build.opensuse.org>"
gpgv: Signature made Tue May 21 23:01:30 2013 MSK using DSA key ID 1048C1FF
+9
View File
@@ -0,0 +1,9 @@
"""
Testing mirror management
"""
from .create import *
from .show import *
from .list import *
from .update import *
from .drop import *
+170
View File
@@ -0,0 +1,170 @@
import re
from lib import BaseTest
class CreateMirror1Test(BaseTest):
"""
create mirror: all architectures + all components
"""
runCmd = "aptly mirror create --ignore-signatures 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 --ignore-signatures 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 --ignore-signatures 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 --ignore-signatures mirror4 http://mirror.yandex.ru/debian/ wheezy life"
class CreateMirror5Test(BaseTest):
"""
create mirror: missing architecture
"""
expectedCode = 1
runCmd = "aptly -architectures=i386,nano68 mirror create --ignore-signatures mirror5 http://mirror.yandex.ru/debian/ wheezy"
class CreateMirror6Test(BaseTest):
"""
create mirror: missing release
"""
expectedCode = 1
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror6 http://mirror.yandex.ru/debian/ suslik"
class CreateMirror7Test(BaseTest):
"""
create mirror: architectures fixed via config file
"""
runCmd = "aptly mirror create --ignore-signatures 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 --ignore-signatures mirror8 http://mirror.yandex.ru/debian/ wheezy main contrib"
]
runCmd = "aptly mirror create --ignore-signatures mirror8 http://mirror.yandex.ru/debian/ wheezy main contrib"
expectedCode = 1
class CreateMirror9Test(BaseTest):
"""
create mirror: repo with InRelease verification
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror9 http://mirror.yandex.ru/debian-backports/ squeeze-backports"
fixtureGpg = True
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using|Warning: using insecure memory!\n', '', s)
def check(self):
def removeDates(s):
return re.sub(r"(Date|Valid-Until): [,0-9:+A-Za-z -]+\n", "", s)
self.check_output()
self.check_cmd_output("aptly mirror show mirror9", "mirror_show", match_prepare=removeDates)
class CreateMirror10Test(BaseTest):
"""
create mirror: repo with InRelease verification, failure
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror10 http://mirror.yandex.ru/debian-backports/ squeeze-backports"
fixtureGpg = False
gold_processor = BaseTest.expand_environ
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using|gpgv: keyblock resource .*$|gpgv: Can\'t check signature: .*$', '', s, flags=re.MULTILINE)
expectedCode = 1
class CreateMirror11Test(BaseTest):
"""
create mirror: repo with Release + Release.gpg verification
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror11 http://mirror.yandex.ru/debian/ squeeze"
fixtureGpg = True
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using', '', s)
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror11", "mirror_show")
class CreateMirror12Test(BaseTest):
"""
create mirror: repo with Release+Release.gpg verification, failure
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror12 http://mirror.yandex.ru/debian/ squeeze"
fixtureGpg = False
gold_processor = BaseTest.expand_environ
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using|gpgv: keyblock resource .*$|gpgv: Can\'t check signature: .*$', '', s, flags=re.MULTILINE)
expectedCode = 1
class CreateMirror13Test(BaseTest):
"""
create mirror: skip verification using config file
"""
runCmd = "aptly mirror create mirror13 http://mirror.yandex.ru/debian/ wheezy"
configOverride = {"gpgDisableVerify": True}
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror13", "mirror_show")
class CreateMirror14Test(BaseTest):
"""
create mirror: flat repository
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror14 http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/ ./"
fixtureGpg = True
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using', '', s)
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror14", "mirror_show")
class CreateMirror15Test(BaseTest):
"""
create mirror: flat repository + components
"""
runCmd = "aptly mirror create --keyring=aptlytest.gpg mirror14 http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/ ./ main"
expectedCode = 1
+46
View File
@@ -0,0 +1,46 @@
from lib import BaseTest
class DropMirror1Test(BaseTest):
"""
drop mirror: regular list
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures mirror1 http://mirror.yandex.ru/debian/ wheezy",
]
runCmd = "aptly mirror drop mirror1"
def check(self):
self.check_output()
self.check_cmd_output("aptly mirror show mirror1", "mirror_show", expected_code=1)
class DropMirror2Test(BaseTest):
"""
drop mirror: in use by snapshots
"""
fixtureDB = True
fixtureCmds = [
"aptly snapshot create wheez from mirror wheezy-main"
]
runCmd = "aptly mirror drop wheezy-main"
expectedCode = 1
class DropMirror3Test(BaseTest):
"""
drop mirror: force
"""
fixtureDB = True
fixtureCmds = [
"aptly snapshot create wheez from mirror wheezy-main"
]
runCmd = "aptly mirror drop --force wheezy-main"
class DropMirror4Test(BaseTest):
"""
drop mirror: no such mirror
"""
runCmd = "aptly mirror drop mirror1"
expectedCode = 1
+20
View File
@@ -0,0 +1,20 @@
from lib import BaseTest
class ListMirror1Test(BaseTest):
"""
list mirrors: regular list
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures mirror1 http://mirror.yandex.ru/debian/ wheezy",
"aptly mirror create --ignore-signatures mirror2 http://mirror.yandex.ru/debian/ squeeze contrib",
"aptly -architectures=i386 mirror create --ignore-signatures mirror3 http://mirror.yandex.ru/debian/ squeeze non-free",
]
runCmd = "aptly mirror list"
class ListMirror2Test(BaseTest):
"""
list mirrors: empty list
"""
runCmd = "aptly mirror list"
+27
View File
@@ -0,0 +1,27 @@
from lib import BaseTest
import re
class ShowMirror1Test(BaseTest):
"""
show mirror: regular mirror
"""
fixtureCmds = ["aptly mirror create --ignore-signatures 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
class ShowMirror3Test(BaseTest):
"""
show mirror: regular mirror with packages
"""
fixtureDB = True
runCmd = "aptly mirror show --with-packages wheezy-contrib"
outputMatchPrepare = lambda _, s: re.sub(r"Last update: [0-9:+A-Za-z -]+\n", "", s)
@@ -0,0 +1,15 @@
Origin: test
Label: failure
Suite: test
Version: 6.0.8
Codename: hardy
Date: Sat, 19 Oct 2013 13:54:21 UTC
Architectures: amd64
Components: main
Description: Debian 6.0.8 Released 19 October 2013
MD5Sum:
2e3817293fc275dbee74bd71ce6eb056 4 main/binary-amd64/Packages
SHA1:
4d13fcc6eda389d4d679602171e11593eadae9b9 4 main/binary-amd64/Packages
SHA256:
8a21688ae769f2b4ffcaa366409f679d 4 main/binary-amd64/Packages
@@ -0,0 +1,11 @@
Origin: test
Label: failure
Suite: test
Version: 6.0.8
Codename: hardy
Date: Sat, 19 Oct 2013 13:54:21 UTC
Architectures: amd64
Components: main
Description: Debian 6.0.8 Released 19 October 2013
MD5Sum:
846549680001f5c632b6ee8e0f183825 827 main/binary-amd64/Packages
@@ -0,0 +1,19 @@
Package: amanda-client
Source: amanda
Version: 1:3.3.1-3~bpo60+1
Installed-Size: 880
Maintainer: Bdale Garbee <bdale@gag.com>
Architecture: amd64
Replaces: amanda-common (<< 1:2.5.2p1-3)
Depends: libc6 (>= 2.3), libcurl3 (>= 7.16.2-1), libglib2.0-0 (>= 2.12.0), libreadline6 (>= 6.0), libssl0.9.8 (>= 0.9.8m-1), amanda-common (= 1:3.3.1-3~bpo60+1)
Suggests: gnuplot, dump, smbclient
Conflicts: amanda, amanda-common (<< 1:2.5.2p1-3)
Description: Advanced Maryland Automatic Network Disk Archiver (Client)
Description-md5: 21af3684379a64cacc51c39152ab1062
Section: utils
Priority: optional
Filename: pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb
Size: 30
MD5sum: 4f7223ebadee9fb57b6796570d60638f
SHA1: 66b27417d37e024c46526c2f6d358a754fc552f3
SHA256: 3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
@@ -0,0 +1 @@
012345678901234567890123456789
+99
View File
@@ -0,0 +1,99 @@
import string
import re
from lib import BaseTest
class UpdateMirror1Test(BaseTest):
"""
update mirrors: regular update
"""
longTest = True
fixtureCmds = [
"aptly -architectures=i386,amd64 mirror create --ignore-signatures alsa-ppa http://ppa.launchpad.net/alsa-backports/ubuntu/ hardy main",
]
runCmd = "aptly mirror update --ignore-signatures 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
class UpdateMirror3Test(BaseTest):
"""
update mirrors: wrong checksum in release file
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures failure ${url} hardy main",
]
fixtureWebServer = "test_release"
runCmd = "aptly mirror update --ignore-signatures failure"
expectedCode = 1
def gold_processor(self, gold):
return string.Template(gold).substitute({'url': self.webServerUrl})
class UpdateMirror4Test(BaseTest):
"""
update mirrors: wrong checksum in release file, but ignore
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures failure ${url} hardy main",
]
fixtureWebServer = "test_release"
runCmd = "aptly mirror update -ignore-checksums --ignore-signatures failure"
expectedCode = 1
def gold_processor(self, gold):
return string.Template(gold).substitute({'url': self.webServerUrl})
class UpdateMirror5Test(BaseTest):
"""
update mirrors: wrong checksum in package
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures failure ${url} hardy main",
]
fixtureWebServer = "test_release2"
runCmd = "aptly mirror update --ignore-signatures failure"
expectedCode = 1
def gold_processor(self, gold):
return string.Template(gold).substitute({'url': self.webServerUrl})
class UpdateMirror6Test(BaseTest):
"""
update mirrors: wrong checksum in package, but ignore
"""
fixtureCmds = [
"aptly mirror create --ignore-signatures failure ${url} hardy main",
]
fixtureWebServer = "test_release2"
runCmd = "aptly mirror update -ignore-checksums --ignore-signatures failure"
def gold_processor(self, gold):
return string.Template(gold).substitute({'url': self.webServerUrl})
class UpdateMirror7Test(BaseTest):
"""
update mirrors: flat repository
"""
fixtureGpg = True
fixtureCmds = [
"aptly mirror create --keyring=aptlytest.gpg flat http://download.opensuse.org/repositories/home:/DeepDiver1975/xUbuntu_10.04/ ./",
]
runCmd = "aptly mirror update --keyring=aptlytest.gpg flat"
outputMatchPrepare = lambda _, s: re.sub(r'Signature made .* using', '', s)
def output_processor(self, output):
return "\n".join(sorted(output.split("\n")))

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