Compare commits

..

670 Commits

Author SHA1 Message Date
Andrey Smirnov 625a38c578 aptly version 0.7 2014-07-29 00:33:53 +04:00
Andrey Smirnov 03a79ebe4c Update goamz to fixed version with signing & encoding. #15 2014-07-28 23:41:59 +04:00
Andrey Smirnov 60fa0aa68e Update command usage. 2014-07-28 19:17:21 +04:00
Andrey Smirnov 04bd9929e1 Update man page: S3, package queries. 2014-07-28 19:17:10 +04:00
Andrey Smirnov 8407e70347 Fix system tests. #15 2014-07-28 16:20:38 +04:00
Andrey Smirnov bf91744078 <endpoint> in command usage. #15 2014-07-28 15:03:55 +04:00
Andrey Smirnov 2c470c1535 Rename config option to endpoint. #15 2014-07-28 15:01:51 +04:00
Andrey Smirnov a18011bdc0 Update goamz: fixed bug with '+' in filenames. #15 2014-07-27 02:49:05 +04:00
Andrey Smirnov af8af0f3d7 Fix tests on aptly mirror edit. #63 2014-07-26 18:22:47 +04:00
Andrey Smirnov 89d26b7dc6 Man for aptly mirror edit. #63 2014-07-26 18:02:01 +04:00
Andrey Smirnov 8649ee3b37 Command aptly mirror edit with tests. #63 2014-07-26 17:59:46 +04:00
Andrey Smirnov b9c8a8d9da System tests for mirror/repo/snapshot rename commands. #63 2014-07-26 17:28:16 +04:00
Andrey Smirnov c5922737ed Man page updates for 'rename' commands. #63 2014-07-26 17:12:00 +04:00
Andrey Smirnov 772111ad26 Commands mirror/repo/snapshot rename. #63 2014-07-26 17:11:26 +04:00
Andrey Smirnov d7ef1a0c4b Allow saving snapshot without package refs loaded. #63 2014-07-26 17:09:47 +04:00
Andrey Smirnov bd221bf869 Sort dependencies. 2014-07-26 16:42:59 +04:00
Andrey Smirnov 0485a36de1 Add Recommends: dependency on bzip2. #84 2014-07-26 01:44:30 +04:00
Andrey Smirnov 77d6a10984 Implementation of Rename method for S3 PublishedStorage. #15 2014-07-26 01:11:23 +04:00
Andrey Smirnov 8015966663 Optimize package encoding/decoding a bit by reusing codec handle. 2014-07-25 16:46:52 +04:00
Andrey Smirnov 94114f2c3d Use idiomatic chan struct{} when there's nothing to send. 2014-07-24 01:25:59 +04:00
Andrey Smirnov 2906369a3b Reuse default HTTP transport options. 2014-07-24 01:17:58 +04:00
Andrey Smirnov 521c52f600 Remove unused field. 2014-07-24 01:15:01 +04:00
Andrey Smirnov 52bb33dc69 Fix bugs with prefix/storage parsing. #15 2014-07-22 00:27:49 +04:00
Andrey Smirnov 71d90947c9 Remove debugging output. #15 2014-07-22 00:27:38 +04:00
Andrey Smirnov b3a4936e06 Fix system tests. 2014-07-21 18:14:09 +04:00
Andrey Smirnov 237d25fe5b Fix issue with ETag/MD5 comparison, add extra info in error messages. #15 2014-07-21 17:43:42 +04:00
Andrey Smirnov de0954732a Style fix. 2014-07-21 17:43:35 +04:00
Andrey Smirnov 915b0d1697 Integrate PublishedRepos with storages & context. #15 2014-07-21 17:43:12 +04:00
Andrey Smirnov 6d026afc69 Support for multiple storages in PublishedRepository. #15 2014-07-21 17:19:13 +04:00
Andrey Smirnov 27a5578d30 Fix system tests. #15 2014-07-18 19:21:29 +04:00
Andrey Smirnov 96e878a2e0 Separate out LocalPublishedStorage interface. #15 2014-07-18 17:44:54 +04:00
Andrey Smirnov 7a7bb56557 Config options for S3 storage. #15 2014-07-18 17:37:08 +04:00
Andrey Smirnov 076ecd586f Fix style issues. #15 2014-07-17 18:09:13 +04:00
Andrey Smirnov c54406e29f First version of PublishedStorage for S3. #15 2014-07-17 18:05:38 +04:00
Andrey Smirnov b260b0010a Refactoring: add MD5 to LinkFromPool. #15 2014-07-17 18:04:56 +04:00
Andrey Smirnov fbf1bc14b7 Refactoring PublishedStorage interface: leave operations suitable for S3. #15 2014-07-17 00:54:44 +04:00
Andrey Smirnov f12cf935ba GPG signer shouldn't report full path name. #15 2014-07-17 00:53:36 +04:00
Andrey Smirnov 4e169c3d10 Update system tests on command help. #64 2014-07-16 14:10:40 +04:00
Andrey Smirnov ea2bfea2a3 Man page with new flags for aptly mirror create. #64 2014-07-16 13:58:07 +04:00
Andrey Smirnov cf4619784e System tests for mirror show & update with filters. #64 2014-07-16 13:58:02 +04:00
Andrey Smirnov 69ad2ccd84 System tests for mirror create with filter. #64 2014-07-16 13:45:46 +04:00
Andrey Smirnov fe1046a7a3 Support for filters in mirror create/update/show. #64 2014-07-16 13:28:11 +04:00
Andrey Smirnov ce1df9447d Support for filters in RemoteRepo: filtering mirror contents by query. #62 2014-07-16 02:27:29 +04:00
Andrey Smirnov 2a7a2de84a Allow empty source in PackageList.Filter. #64 2014-07-16 02:23:55 +04:00
Andrey Smirnov 238bdfad96 Add String() method for queries. 2014-07-15 21:49:30 +04:00
Andrey Smirnov 56d777af0a Introduce regexp query matching. 2014-07-15 19:00:06 +04:00
Andrey Smirnov a632469890 Support for quoted string arguments. 2014-07-14 23:20:31 +04:00
Andrey Smirnov 3601cc15ed Fix unit-tests for new $Architecture matching. 2014-07-14 19:45:27 +04:00
Andrey Smirnov 61cd4c6af1 Use special matcher for $Architecture, so that 'any' matches any arch. 2014-07-14 19:39:19 +04:00
Andrey Smirnov 401bb768d7 Fix bug with architectures query: it was always true. 2014-07-14 19:37:12 +04:00
Andrey Smirnov 5880d11899 Fix refactoring leftover bug. 2014-07-14 19:16:02 +04:00
Andrey Smirnov ed6e261bd0 Rewrite snapshot pull to use PackageList.Filter instead of homebrew algorithm. 2014-07-14 19:02:15 +04:00
Andrey Smirnov fb660efeb5 Make list sort really stable: if all properties match, use architecture
as sort key.
2014-07-14 18:51:07 +04:00
Andrey Smirnov 80de65f28d Fix system test. #62 2014-07-13 16:19:03 +04:00
Andrey Smirnov 9893e4af3d Add flag to control downlod limit in aptly mirror update. #62 2014-07-13 16:11:18 +04:00
Andrey Smirnov 7416cc403d Update system test config file. #62 2014-07-13 16:11:07 +04:00
Andrey Smirnov 83ceee1e3f Remove debug output. #62 2014-07-13 16:10:53 +04:00
Andrey Smirnov a54a366c95 New config setting: downloadSpeedLimit to limit download speed. #62 2014-07-13 15:47:44 +04:00
Andrey Smirnov fb1e28b91b Setting for downloader to limit download speed to specified level. #62 2014-07-12 23:56:32 +04:00
Andrey Smirnov 86206df58d Use flowcontrol library. #62 2014-07-12 23:55:54 +04:00
Andrey Smirnov 1d49a717b9 Unit-tests for queries. 2014-07-12 23:26:08 +04:00
Andrey Smirnov 3b0b0b76ec One more fix for Debian 7.6 2014-07-12 23:18:20 +04:00
Andrey Smirnov 904b9e101b Update tests: wheezy 7.6 released. 2014-07-12 21:54:13 +04:00
Andrey Smirnov 9fb8a0ea4b Capturing results for other command output. 2014-07-12 21:53:47 +04:00
Andrey Smirnov bc27c6e14d System test on using complex query when importing. 2014-07-12 18:02:57 +04:00
Andrey Smirnov ae3c98c210 Implementation for FieldQuery. 2014-07-12 18:02:33 +04:00
Andrey Smirnov 34f545b8cf Package index may not be indexed when schanning. 2014-07-12 18:02:13 +04:00
Andrey Smirnov d523d2b415 Package.GetField implementation for querying. 2014-07-12 18:00:13 +04:00
Andrey Smirnov e320ac31d5 Switch aptly repo move/copy/import/remove to use new filters based on queries. 2014-07-12 13:58:38 +04:00
Andrey Smirnov e08d44ff0a Fix bug with matching in Search method. 2014-07-12 13:58:22 +04:00
Andrey Smirnov 898870038a Rename s/Searchable/Fast/ 2014-07-12 00:30:53 +04:00
Andrey Smirnov c485cf41f7 PkgQueries, concept of 'Searchable', rewrite Filter using PackageQueries. 2014-07-12 00:15:33 +04:00
Andrey Smirnov d54ef1e921 Fix bug with special chars handling in strings, detect package key queries,
arch condition for dependency-like queries.
2014-07-12 00:14:49 +04:00
Andrey Smirnov b42fd71acf MatchesDependency should check on Provides: as well. 2014-07-11 16:30:41 +04:00
Andrey Smirnov ede5449440 Refactoring: move query tree to deb package. 2014-07-11 00:09:31 +04:00
Andrey Smirnov eef49516ef Fix bugs after style fixes. 2014-07-10 23:31:53 +04:00
Andrey Smirnov e745747370 Style fixes as suggested by tools. 2014-07-10 21:34:52 +04:00
Andrey Smirnov 7e5b2ae8f5 Bugfix: unit-test was creating dirs in source directory. 2014-07-10 21:31:12 +04:00
Andrey Smirnov ada3ae0094 Introduce query language (resembling reprepro syntax). 2014-07-10 21:28:02 +04:00
Andrey Smirnov d262a131cc Preparation for query matching: introduce Regexp + PatternMatch. 2014-07-10 21:16:30 +04:00
Andrey Smirnov f0e69144ed Merge branch 'simonaquino-pull_multiple_packages' 2014-07-10 00:56:16 +04:00
Andrey Smirnov a7cb40ee7a System tests for aptly snapshot pull -all-matches. #70 2014-07-10 00:55:53 +04:00
Andrey Smirnov 2a9b2f87f9 Update man page. #70 2014-07-10 00:48:24 +04:00
Andrey Smirnov 9af10bc422 Refactoring: simplification. #70 2014-07-10 00:43:42 +04:00
Andrey Smirnov bdbb5acb11 Remove 'allMatches' on version equal. #70 2014-07-10 00:20:28 +04:00
Andrey Smirnov 81d506b226 Dependencies should be matched for each package one by one. #70 2014-07-10 00:13:19 +04:00
Andrey Smirnov 1c30b2b9de Simplification: we are already able to search for all packages. #70 2014-07-10 00:10:53 +04:00
Andrey Smirnov 566604d4ba Revert changes related to NextVersion. #70
It would be done in a bit different way: by introducing powerful search queries.
2014-07-10 00:04:48 +04:00
Andrey Smirnov 58a57f2b2c Merge branch 'pull_multiple_packages' of https://github.com/simonaquino/aptly into simonaquino-pull_multiple_packages 2014-07-09 18:16:46 +04:00
Andrey Smirnov 20d744f398 Fix system tests (whitespace). 2014-07-08 13:12:10 +04:00
Andrey Smirnov 360981de4a Merge branch 'simonaquino-sort_snapshots_time' 2014-07-07 23:37:40 +04:00
Andrey Smirnov 79016f7f98 Slight refactoring, make wrong param real error. #73 2014-07-07 23:36:59 +04:00
Andrey Smirnov 1a92d8bfe9 Add --capture to auto-create 'gold' results when fail. 2014-07-07 23:29:36 +04:00
Andrey Smirnov d3707b4cfe Add system test on wrong --sort parameter. #73 2014-07-07 23:29:21 +04:00
Andrey Smirnov de1fa85127 Update man page. #73 2014-07-07 23:14:32 +04:00
Andrey Smirnov d9b35cea01 Allow running system tests by mask.
E.g. system/run.py 'ListSnapshot*'.
2014-07-07 23:13:49 +04:00
Andrey Smirnov b75b4d1488 Merge branch 'sort_snapshots_time' of https://github.com/simonaquino/aptly into simonaquino-sort_snapshots_time 2014-07-07 18:18:58 +04:00
Andrey Smirnov da55f18b0e Fix system tests. 2014-07-02 10:10:39 +04:00
Andrey Smirnov 165dd0053e Merge branch 'specify_long_tests' of https://github.com/simonaquino/aptly 2014-07-02 10:03:19 +04:00
Andrey Smirnov 22a4e6b67b Update goleveldb to fixed version (dropping data after compaction + recover). syndtr/goleveldb#53 2014-07-02 09:54:20 +04:00
Simon Aquino 20513e1c16 Specify individual long tests to run
The test script currently only allows to run all the long test. This
change will allow a user to specify individual long tests to run.
2014-07-01 00:51:58 +01:00
Simon Aquino b4ea963744 List snapshots by time: added integration tests
Added a couple of integration tests for the new list snapshot by
creation time feature.
2014-06-30 20:23:20 +01:00
Simon Aquino 429788db0f List snapshots by time
Users now have the choice of listing the snapshot by time as well as
name (default behaviour).
An additional flag has been added '--sort=' which controls the sort
method applied to the list produced by aptly snapshot list.

The possible values are:
--sort=name (default): sorts the snapshot list by name (lexicographic
order)
--sort=time: sorts the snapshot list in chronological order (oldest to
newest)
2014-06-30 19:25:13 +01:00
Andrey Smirnov 1e70e954da System test on pulling latest version by default. #67 2014-06-29 10:12:18 +04:00
Andrey Smirnov 319f3e6bb2 Updated fixture: sensu mirror, new mirror contents. 2014-06-29 09:56:30 +04:00
Andrey Smirnov 56915c4357 Fix order of Component & Archive fields. 2014-06-29 09:55:51 +04:00
Simon Aquino e1348ab88f Merge smira/master into pull_multiple_packages
Resolved conflicts arisen following smira's new commits into master.
2014-06-28 01:34:28 +01:00
Andrey Smirnov 026dc540d2 Merge branch 'simonaquino-deterministic_package_search' 2014-06-28 00:37:33 +04:00
Andrey Smirnov 44ce4c8a77 Insert into right position when adding as well. #67 2014-06-28 00:26:56 +04:00
Andrey Smirnov 980102462b Simplify Makefile. 2014-06-28 00:10:35 +04:00
Andrey Smirnov 86b0860463 Enable system tests fixture under go1.3 as well. #72 2014-06-27 23:53:37 +04:00
Andrey Smirnov e311d41dd7 Add Simon Aquino to authors. #67 2014-06-27 23:49:05 +04:00
Andrey Smirnov c3ce886990 Merge branch 'deterministic_package_search' of https://github.com/simonaquino/aptly into simonaquino-deterministic_package_search 2014-06-27 23:47:57 +04:00
Andrey Smirnov 959ecf696c Build under go1.3 as well. #72 2014-06-27 23:39:32 +04:00
Andrey Smirnov 48d01f5700 Fix reference to home directory. #24 2014-06-27 23:30:04 +04:00
Andrey Smirnov aeecc1ec91 System test on conflicting files when publishing. #65 2014-06-27 22:29:01 +04:00
Andrey Smirnov 685a4de4e7 Fix link to Ryan Uber's profile. 2014-06-27 22:18:03 +04:00
Andrey Smirnov 667efc2b90 Add AUTHORS files. 2014-06-27 21:44:43 +04:00
Simon Aquino 3cf281965b Implementation of all-matches functionality + tests
When performing an *aptly snapshot pull*, users might list dependency
versions that can potentially match multiple packages in the source
snapshot. However, the current implementation of the 'snapshot pull'
command only allows one package to be pulled from a snapshot at a time
for a given dependency.

The newly implemented all-matches flag allows users to pull all the
matching packages from a source snapshot, provided that they satisfy the
version requirements indicated by the dependencies.

The all-matches flag defaults to false and only produces the described
behaviour when it is explicitly set to true.
2014-06-27 03:36:03 +01:00
Simon Aquino e19a615641 In PackageList, sort the package version from latest to oldest
This enables us to return the latest version of a package when no version is specified rather than the oldest.
Therefore, we don't need to modify the search algorithm to return the latest version of a package.
2014-06-13 12:06:33 +01:00
Simon Aquino ff77fbf5d9 Sort PackagesList by name and version 2014-06-13 11:42:20 +01:00
Andrey Smirnov 856dd7021c System tests for publish update empty -> empty. #66 2014-06-11 20:42:13 +04:00
Andrey Smirnov ebc47f7d5d Add unit-test. #65 2014-06-11 20:32:45 +04:00
Andrey Smirnov 082fda62b5 Add unit-test. #66 2014-06-11 20:28:56 +04:00
Andrey Smirnov 3199fd85fb Fix publish updating (switching) for empty -> empty scenario. #66 2014-06-11 20:27:49 +04:00
Andrey Smirnov 0c6951fcd2 When linking, check that inode file matches if linking to same file. #65
Otherwise files from conflicting packages might override each other in the published
pool. This is explicitly POSIX-only.
2014-06-11 20:03:34 +04:00
Andrey Smirnov 35e57026ac Version bump: 0.7~dev 2014-06-11 20:02:50 +04:00
Andrey Smirnov 28e050c14e Fix style warnings. 2014-06-07 18:00:36 +04:00
Andrey Smirnov 8fb399026d Version bump for 0.6. 2014-06-07 17:58:33 +04:00
Andrey Smirnov e15f23962a Add missing files. #61 2014-06-07 17:27:24 +04:00
Andrey Smirnov 81af5882b9 Test on per-component/arch Release contents. #61 2014-06-07 17:19:02 +04:00
Andrey Smirnov e554d8befa Update tests with introduction of Release files under each arch/component. #61 2014-06-07 17:01:37 +04:00
Andrey Smirnov 17c564358a Refactoring, support for atomic updates and checksumming. #61 2014-06-07 16:44:54 +04:00
Andrey Smirnov 2e4c1c491e Merge branch 'debinst' of https://github.com/ryanuber/aptly into ryanuber-debinst
Conflicts:
	deb/publish_test.go
2014-06-07 16:34:39 +04:00
Andrey Smirnov e7230d9ee6 One more test fix. #36 2014-06-07 16:28:15 +04:00
Andrey Smirnov 0f1074a721 Fix tests. #36 2014-06-07 16:17:59 +04:00
Andrey Smirnov 17ed34fdaa System tests for aptly publish switch with multiple repositories. #36 2014-06-07 15:18:58 +04:00
Andrey Smirnov 17b320eac4 Sort components when doing string representation. #36 2014-06-07 15:18:36 +04:00
Andrey Smirnov e3a71c81e1 Both components should contain same architectures. #36 2014-06-06 03:05:22 +04:00
Andrey Smirnov bf900deb4b Remove whitespace. 2014-06-06 03:03:58 +04:00
Andrey Smirnov 142387311b Tests for aptly publish update and multi-component repository. #36 2014-06-06 03:00:44 +04:00
Andrey Smirnov 68fbb0cbb9 Fix tests, don't break pool contents. #36 2014-06-06 03:00:29 +04:00
Andrey Smirnov 835da9cb3c One more test fix: duplicate archs. #36 2014-06-06 02:37:09 +04:00
Andrey Smirnov 7cd0d394d4 Test fix: source shouldn't be part of Architectures: field. #36 2014-06-06 02:25:11 +04:00
Andrey Smirnov 2040be2f8a Deduplicate architectures when guessed. #36 2014-06-06 02:14:15 +04:00
Andrey Smirnov 1957c811e8 Tests for multi-component and aptly publish list/snapshot/repo, aptly serve. #36 2014-06-06 02:09:13 +04:00
Andrey Smirnov 2dae9b01a1 Grammar fix. #36 2014-06-06 02:09:00 +04:00
Andrey Smirnov 9a34b4ff1f Update commands to handle multiple components repositories. #36 2014-06-04 17:43:16 +04:00
Andrey Smirnov d218159455 Multiple connections for published repo. #36 2014-06-04 17:06:34 +04:00
Andrey Smirnov 8be6911238 Fix multiple component in aptly graph. #36 2014-06-04 17:04:05 +04:00
Andrey Smirnov 20a7c5ae2d A bit more unit-tests with multi-component repositories. #36 2014-06-04 01:07:32 +04:00
Andrey Smirnov e161313efa Fix aptly serve: correct components list. #36 2014-06-03 17:25:15 +04:00
Andrey Smirnov 7192049c16 Update to new PublishedRepo with multiple components. #36
Multiple component publishing doesn't work yet, but old features are working.
2014-06-03 17:09:00 +04:00
Andrey Smirnov ee71b93669 Major change: published repo now supports multiple components <> snapshots (local repos). #36 2014-06-03 14:34:26 +04:00
Andrey Smirnov 43ee735aa4 Fix error capitalization. 2014-06-03 14:33:19 +04:00
Andrey Smirnov da5b0c9a66 Test fixes. #55 2014-05-31 21:27:36 +04:00
Andrey Smirnov e1dbab6988 Allow publishing of empty snapshots and local repos. #55 2014-05-31 21:13:30 +04:00
Andrey Smirnov bcdfb7d99a Fix system tests: update test data. 2014-05-31 19:37:31 +04:00
Andrey Smirnov ac85a0897a Update gographviz to fixed version with XML escaping. #58 2014-05-30 22:08:11 +04:00
Ryan Uber 9a4543500c Handle source repos while creating dist release file 2014-05-29 22:24:19 -07:00
Ryan Uber b0f9a4a419 Added tests for Release file in distribution directory 2014-05-29 22:07:59 -07:00
Ryan Uber 71ea2be6c1 deb: added Release file to each individual arch dir for d-i. 2014-05-29 14:04:06 -07:00
Andrey Smirnov b717caeda4 Make 'Package:' line field first when serializing stanza. #49 2014-05-30 00:43:19 +04:00
Andrey Smirnov fcc283bdb1 Regenerate man page (-no-remove for aptly snapshot merge). #57 2014-05-30 00:05:15 +04:00
Andrey Smirnov d3d41dd1c9 Add missing files. #57 2014-05-29 20:49:35 +04:00
Andrey Smirnov c72ef05a2a Support for -no-remove while merging snapshots. #57 2014-05-29 18:05:52 +04:00
Andrey Smirnov a1e360b07b Conflict detection for packages in one list. #60 2014-05-29 18:01:07 +04:00
Andrey Smirnov 90bba977d7 Introduce Package.ShortKey(): packages should have no conflict on that key in one list. #60 2014-05-29 18:00:14 +04:00
Andrey Smirnov dc248c5603 Fix diff algorithm for reflists with duplicate entries. #57 2014-05-29 17:58:59 +04:00
Andrey Smirnov f007465d18 Change the way package key works: now it includes FilesHash. #60
Now duplicate packages (the same name/version/arch) but with different set of files
would be handled as separate entities.
2014-05-29 16:49:05 +04:00
Andrey Smirnov 869e83713d Remove debugging output. #51 2014-05-29 00:49:26 +04:00
Andrey Smirnov 7b9e3429fd Use gographviz escaping instead of homegrown escape function. #51 #58
It doesn't fix the bug #58, waiting for maintainer of gographviz to
accept the patch: https://code.google.com/p/gographviz/issues/detail?id=2
2014-05-29 00:47:37 +04:00
Andrey Smirnov 5b75dbc481 Fix system tests. #54 2014-05-18 00:16:00 +04:00
Andrey Smirnov d96839f99d Fix unit tests. #54 2014-05-17 22:47:47 +04:00
Andrey Smirnov 8b2920d5dd aptly repo add now exists with non-zero exit code if some files fail to add. #54 2014-05-17 22:23:20 +04:00
Andrey Smirnov 4240b134e6 Fix spelling. 2014-05-17 22:08:31 +04:00
Andrey Smirnov 1d31a5c25f Don't use fixed cap, as it might be more than length. #53 2014-05-16 00:29:43 +04:00
Andrey Smirnov 05a42f4cba aptly exits with 2 on command/flag parse error. #52 2014-05-16 00:22:51 +04:00
Andrey Smirnov 2cbb486f6b Update tests with new Varnish repo. 2014-05-13 13:39:14 +04:00
Andrey Smirnov d0ff11390b Revert "Version bump for 0.5.1."
This reverts commit b5d025f141.
2014-05-13 12:29:13 +04:00
Andrey Smirnov b5d025f141 Version bump for 0.5.1. 2014-05-10 18:10:19 +04:00
Andrey Smirnov 3c7a2281b2 Fix "production" libraries in Gomfile with exact SHA. 2014-05-10 18:08:12 +04:00
Andrey Smirnov be3ad21fbe Add target to build "all-in-one" source tarball for Debian. 2014-05-10 18:06:23 +04:00
Andrey Smirnov 5301e8a341 Fix pool directory for packages with version in Source: field. #44 2014-05-10 17:25:44 +04:00
Andrey Smirnov 49eed59238 Fix test on command line help. #47 2014-05-10 17:13:49 +04:00
Andrey Smirnov 3e78240b39 Remove debugging output. #47 2014-05-10 17:07:17 +04:00
Andrey Smirnov 10bbefeb25 Fix support for flat format repositories in subdirectories with common pool. #47 2014-05-10 16:56:50 +04:00
Andrey Smirnov 35eac72226 Missed file. #46 2014-05-10 13:35:06 +04:00
Andrey Smirnov 5371f94b7a Debian 7.5 has been released, update tests. 2014-05-10 13:34:46 +04:00
Andrey Smirnov 53adf39d89 Bring back automatic HTTP_PROXY setting from environment. #46 2014-05-10 13:27:01 +04:00
Andrey Smirnov bc7972ff68 Version bump for 0.6~dev. 2014-04-24 14:24:03 +04:00
Andrey Smirnov 21e8aa5519 Remove comments. 2014-04-24 01:31:31 +04:00
Andrey Smirnov f0825d93be aptly 0.5 2014-04-24 01:17:14 +04:00
Andrey Smirnov 9e538d9475 aptly version 0.5 2014-04-24 01:15:59 +04:00
Andrey Smirnov 042602f991 Update man page. #42 2014-04-24 01:03:15 +04:00
Andrey Smirnov e8a894bc88 More tests for merging. #42 2014-04-24 01:01:14 +04:00
Andrey Smirnov 59647fe6d0 Fix subtle bug in .Merge: if there are duplicate name-arch on the left, override them all. #42 2014-04-24 00:57:17 +04:00
Andrey Smirnov 37a6fb336a PackageList doesn't allow duplicates and PackageRefList does sorting on keys. #42 2014-04-23 23:47:32 +04:00
Andrey Smirnov 7c2faafa91 Simplify implementation (and improve performance) by using the fact that RefList is always sorted. #42 2014-04-23 23:46:33 +04:00
Andrey Smirnov 87295c6580 Move command description from man file to command help. #42
Man file is auto-generated from inline command help.

N.B. I should document this in Contributing.
2014-04-23 23:30:21 +04:00
Andrey Smirnov 4ce4923f58 Merge branch 'f-newestpkg' of https://github.com/ryanuber/aptly into ryanuber-lastest-merge 2014-04-23 23:21:39 +04:00
Andrey Smirnov 2a83596307 Merge pull request #43 from ryanuber/make_package
Fix path to manpage in Makefile
2014-04-23 22:56:37 +04:00
Ryan Uber 5f29cb202a Fix path to manpage in Makefile 2014-04-23 00:50:06 -07:00
Ryan Uber 3800f2c957 Added CLI test for snapshot merge with latest flag 2014-04-23 00:25:44 -07:00
Ryan Uber 6c3b2f686e snapshot: FilterLatestRefs returns nothing as it deals with a pointer. 2014-04-22 23:33:30 -07:00
Ryan Uber 708fd800df snapshot: separate test for FilterLatestRefs 2014-04-22 23:04:45 -07:00
Ryan Uber 385ac1afd0 snapshot: explicity call FilterLatestRefs() where needed rather than calling from Merge() 2014-04-22 22:28:14 -07:00
Ryan Uber d9f8673286 snapshot: break out FilterLatestPackages to its own function 2014-04-22 19:17:10 -07:00
Ryan Uber d1cc562f3c cmd/snapshot_merge: reword -latest flag to match man page 2014-04-22 17:48:53 -07:00
Ryan Uber e6992d822d Updated man page for snapshot merging with -latest flag 2014-04-22 17:46:29 -07:00
Ryan Uber 0d8debe7b6 snapshot: simplify merging latest packages 2014-04-22 17:44:03 -07:00
Ryan Uber 89eafd1b21 Initial pass at testing merged snapshotting with -latest flag 2014-04-22 17:44:03 -07:00
Ryan Uber 1a735e849b snapshot: alter result generation in Merge() to accommodate snapshot diff 2014-04-22 17:44:03 -07:00
Ryan Uber a93052aa8a snapshot: merge adjustments for -latest flag 2014-04-22 17:44:03 -07:00
Ryan Uber 133d67bffa snapshot: newest -> latest 2014-04-22 17:44:03 -07:00
Ryan Uber 60d48e890c snapshot: Move 'newest' logic out of main compilation loop and evaluate the reflist afterward. 2014-04-22 17:44:03 -07:00
Ryan Uber dbcfd6f58b snapshot: keep a tab of seen packages and only include the latest copy during merge 2014-04-22 17:44:03 -07:00
Ryan Uber cd369f5fa0 snapshot: add cli flag for taking newest during merge 2014-04-22 17:44:03 -07:00
Ryan Uber 992a5cee37 snapshot: first pass at newest-wins functionality. 2014-04-22 17:44:03 -07:00
Andrey Smirnov fb8686a634 Update man page. #8 2014-04-22 20:20:52 +04:00
Andrey Smirnov cc8baec317 Command aptly publish switch to switch published snapshot. #8 2014-04-22 20:17:21 +04:00
Andrey Smirnov 1200e9cc95 Command aptly publish update: update local repo published in-place. #8 2014-04-22 18:35:20 +04:00
Andrey Smirnov 3342ce490a System tests for cleaning up prefix/component. #8 2014-04-22 17:51:29 +04:00
Andrey Smirnov 4541e0bdae Fix misprint. #8 2014-04-22 17:51:22 +04:00
Andrey Smirnov 522684aabb Use progress for printing. #8 2014-04-22 17:19:39 +04:00
Andrey Smirnov 8963cd8027 Use progress when printing. #8 2014-04-22 17:19:10 +04:00
Andrey Smirnov 9445f3a0fa Basis for repo re-publishing, cleaning up prefix + component published package pool. #8 2014-04-22 17:07:25 +04:00
Andrey Smirnov d69eaeff4e New methods for public root: Filelist and Remove. #8 2014-04-22 17:05:32 +04:00
Andrey Smirnov 1bac201687 Refactoring: build pool path in Package. #8 2014-04-22 16:20:51 +04:00
Andrey Smirnov 45335da0ed Fix system tests. #41 2014-04-22 11:41:46 +04:00
Andrey Smirnov b10aeacfc0 Merge branch 'docfix' of https://github.com/Temikus/aptly 2014-04-22 11:28:34 +04:00
Artem Yakimenko d9f4686e2c Fixing minor spelling/grammar issues in documentation. 2014-04-22 11:08:57 +04:00
Andrey Smirnov 7eb2fdf425 Re-publishing for local repositories. #8
Cleanup part is missing.
2014-04-22 10:49:40 +04:00
Andrey Smirnov c70c196420 Renaming files in public area. #8 2014-04-22 10:40:17 +04:00
Andrey Smirnov e81f86f942 Slight refactoring, add oldRefs. #8 2014-04-21 21:26:44 +04:00
Andrey Smirnov 6352ce30ed Disable compression when downloading, otherwise HTTP client may do "decompression" on its own. #33 2014-04-16 15:55:04 +04:00
Andrey Smirnov cefc3cc2dc Fix test. #31 2014-04-16 00:22:11 +04:00
Andrey Smirnov 376bb69803 Updated man page. #31 2014-04-15 23:54:33 +04:00
Andrey Smirnov e33f5792e1 Add -raw for aptly publish list. #31 2014-04-15 23:53:53 +04:00
Andrey Smirnov 8d214e6d12 Add comments on precise file deletion. #8 2014-04-15 14:28:26 +04:00
Andrey Smirnov 73761c311e Update man. #29 [ci skip] 2014-04-15 13:36:29 +04:00
Andrey Smirnov b85f46547b Allow to customize Origin/Label during publishing. #29 2014-04-15 11:47:21 +04:00
Andrey Smirnov 108dc235a7 Update man page. #27 2014-04-15 11:09:25 +04:00
Andrey Smirnov 90dd21b270 Raw (machine-readable) format for aptly mirror/repo/snapshot list. #27 2014-04-15 10:43:36 +04:00
Andrey Smirnov ce615facf9 Fix gom invocation. #28
Thanks to @erickeller for finding that.
2014-04-13 23:10:53 +04:00
Andrey Smirnov a8cf83774a PublishedRepo remembers RefList is has been published with. #8 2014-04-08 11:58:32 +04:00
Andrey Smirnov 470571c7db Fix variable shadowing. 2014-04-08 11:53:02 +04:00
Andrey Smirnov 55807412a1 Update goleveldb with fix for missing manifest.
See also #25, syndtr/goleveldb#49
2014-04-08 11:47:16 +04:00
Andrey Smirnov c106e66cff Style fixes. 2014-04-08 01:39:41 +04:00
Andrey Smirnov d6fd4e46a0 Style fix. 2014-04-08 01:04:01 +04:00
Andrey Smirnov b34707faed man page built with fixed ronn. #22 2014-04-08 00:28:17 +04:00
Andrey Smirnov e4de1738ce Update to goleveldb with fix for missing CURRENT file in db. #25 2014-04-08 00:26:01 +04:00
Andrey Smirnov ff045f9a48 Fixups after renaming debian -> deb. #21 2014-04-07 21:22:58 +04:00
Andrey Smirnov fd662c9275 Rename debian -> deb. #21 2014-04-07 21:15:13 +04:00
Andrey Smirnov 83b2e0250d Rework fatal mechanism, open config on demand. 2014-04-06 21:05:06 +04:00
Andrey Smirnov 3db7125932 Refactor ppa parsing: take pointer to config. 2014-04-06 19:12:07 +04:00
Andrey Smirnov efcce4ef3c Re-enable system test on db recover with empty DB. #25 2014-04-06 16:10:11 +04:00
Andrey Smirnov d90f8dba7f Update goleveldb with my pull request merged. #25 2014-04-06 16:01:19 +04:00
Andrey Smirnov 173dd775bc Tests for aptly db recover. #25 2014-04-05 17:01:16 +04:00
Andrey Smirnov 4afa3126e4 Fix ugly bug. #25 2014-04-05 16:20:04 +04:00
Andrey Smirnov 5a6ccb7259 Add command aptly db recover. 2014-04-05 16:11:19 +04:00
Andrey Smirnov 2c3553ef0b Major refactoring: access to context happens in methods. #13 2014-04-05 16:10:51 +04:00
Andrey Smirnov 400d0da7d4 Add method to recover LevelDB after crash. 2014-04-05 00:30:39 +04:00
Andrey Smirnov 4caeea49b1 Stop building on 1.2. 2014-04-03 00:57:39 +04:00
Andrey Smirnov f648c9547c Support for switching to smira/commander with free placement of flags. #17 2014-04-03 00:16:18 +04:00
Andrey Smirnov d84226a054 Switch to own fork of commander/flag. 2014-03-28 23:05:54 +04:00
Andrey Smirnov 006d173d4f One more fix for the test. 2014-03-28 21:27:05 +04:00
Andrey Smirnov 6ca62a9d50 Fix test. 2014-03-28 21:04:29 +04:00
Andrey Smirnov c7dcc8ff59 System test for aptly repo edit. #12 2014-03-28 20:49:37 +04:00
Andrey Smirnov 4c237ed1b1 Command aptly repo edit to change repository defaults. #12 2014-03-28 20:41:51 +04:00
Andrey Smirnov ec866eb403 Pool would be modified by repo add, so create copy. 2014-03-26 22:29:43 +04:00
Andrey Smirnov 53e73c52a4 Tests for guessing publishing defaults from local repo params. #12 2014-03-26 21:40:17 +04:00
Andrey Smirnov 0f8f43b9f0 Default distribution/component could be specified when creating repo. #12 2014-03-26 21:24:09 +04:00
Andrey Smirnov 7f2f435e2d Forgotten file. #10 2014-03-26 21:23:49 +04:00
Andrey Smirnov c325119081 Repo can't be dropped if published. #10 2014-03-26 21:10:43 +04:00
Andrey Smirnov 3b16ca156a Pool as a fixture is not required. #10 2014-03-26 19:48:34 +04:00
Andrey Smirnov 22014206d7 System tests for aptly publish repo. #10 2014-03-26 19:44:38 +04:00
Andrey Smirnov 9ff49ff24a Fix words: source might be snapshot or local repo. #10 2014-03-26 19:44:15 +04:00
Andrey Smirnov 37ea845fd9 Command aptly publish repo. #10 2014-03-25 18:42:56 +04:00
Andrey Smirnov 1a88876e63 GPG may suddenly decide to re-validate its trustdb, resulting in any
call to `gpg` resulting in exit code 2.

Don't allow GPG to validate trustdb when invoked in automated fashion.
2014-03-25 18:42:03 +04:00
Andrey Smirnov 1a60ac6aa0 Refactoring: use CollectionFactory instead of manual collection creation. 2014-03-25 14:59:26 +04:00
Andrey Smirnov a0497058ee Don't allow to drop repo if it is published. 2014-03-25 14:29:25 +04:00
Andrey Smirnov 140c925079 Fix shadowed variables. 2014-03-24 18:39:45 +04:00
Andrey Smirnov 5bd5e0a827 Style fix. 2014-03-24 18:23:34 +04:00
Andrey Smirnov 3c32cd3884 Move man page to /usr/share/man/, as it should be. 2014-03-24 18:20:16 +04:00
Andrey Smirnov 32717e92ba First round of support for localRepos as source for publishing. Also more intelligent algo to get publishing defaults. #10 #12 2014-03-19 16:43:42 +04:00
Andrey Smirnov c2fc2f9988 Add placeholders for default local repo publish settings. #12 2014-03-19 16:19:57 +04:00
Andrey Smirnov 1189bca5a4 Add central place to create all collections from. 2014-03-18 18:58:09 +04:00
Andrey Smirnov 2315c00ae1 Test on creating snapshot from empty local repo. 2014-03-18 18:57:16 +04:00
Andrey Smirnov e5de8b9353 Print bytes in human-readable format. #18 2014-03-17 17:05:38 +04:00
Andrey Smirnov 099806aa82 Function HumanBytes for human-readable representation of numbers. #18 2014-03-17 16:47:04 +04:00
Andrey Smirnov 6a42aad322 Compact LevelDB in aptly db cleanup. #19 2014-03-17 16:22:49 +04:00
Andrey Smirnov b13e50a570 Bump version to development 0.5~dev. 2014-03-17 16:06:37 +04:00
Andrey Smirnov dff0ab2fa3 Leave filedescriptors to gpg. 2014-03-17 15:55:35 +04:00
Andrey Smirnov a7f135a441 .deb package building instructions. 2014-03-17 15:55:21 +04:00
Andrey Smirnov c71a57169c Add instruction on installation from repo. 2014-03-13 00:24:46 +04:00
Andrey Smirnov 04588d7566 Fix keyring in system test. 2014-03-12 18:57:21 +04:00
Andrey Smirnov 8153c7e2e9 Version bump to 0.4.1 2014-03-12 18:47:59 +04:00
Andrey Smirnov b3e92717bd Test for https://github.com/smira/aptly/issues/5 2014-03-12 18:46:36 +04:00
Andrey Smirnov d61a77d6e3 With list of components for Release file, do filepath.Base. 2014-03-12 18:37:38 +04:00
Andrey Smirnov 91da3b2046 Don't exit if returnCode is 0: otherwise panic is not reported. 2014-03-12 18:15:18 +04:00
Andrey Smirnov aade09e74e Shutdown progress bar in any case. 2014-03-12 18:13:01 +04:00
Andrey Smirnov 4dcfd74323 Version bump to 0.5~dev 2014-03-11 16:12:51 +04:00
Andrey Smirnov f2a432b96d Aptly version 0.4. 2014-03-11 13:37:21 +04:00
Andrey Smirnov 431a1da0b7 Update build instructions. 2014-03-11 13:34:55 +04:00
Andrey Smirnov 16d5da8889 Allow to override PYTHON. 2014-03-11 12:20:29 +04:00
Andrey Smirnov 0f8d4df344 A bit of simplification for Makefile. 2014-03-11 12:13:46 +04:00
Andrey Smirnov e08e8716ae Build on go1.2.1 as well. 2014-03-11 12:13:22 +04:00
Andrey Smirnov d1d05aaefb Manual page & generator using ronn as intermediate language. 2014-03-10 19:51:14 +04:00
Andrey Smirnov c6bf47d3ea Update system tests after help changes. 2014-03-10 19:50:26 +04:00
Andrey Smirnov 4c81f0f52a Update integrated help. 2014-03-10 19:42:27 +04:00
Andrey Smirnov c28a641293 Don't overwrite entry if there are no changes. 2014-03-10 19:42:08 +04:00
Andrey Smirnov a96ab00afc Verify dependencies with progress. 2014-03-07 18:13:48 +04:00
Andrey Smirnov c55733fc05 Progress during publishing. 2014-03-07 17:24:45 +04:00
Andrey Smirnov 1571a3331d Fix race with context shutdown and error message printing. 2014-03-07 17:23:56 +04:00
Andrey Smirnov 57722597ee Move coveralls build to go tip build, which is allowed to fail.
coveralls.io is really unstable.
2014-03-07 16:37:17 +04:00
Andrey Smirnov 9496c7e9ac All output should go via Progress object. 2014-03-07 16:34:09 +04:00
Andrey Smirnov 74c88f3ef6 Progress when downloading single files and when parsing remote mirrors. 2014-03-07 00:37:06 +04:00
Andrey Smirnov 04d6603f38 aptly snapshot verify now prints: "loading packages"... 2014-03-07 00:36:45 +04:00
Andrey Smirnov 6f86bfec72 Show progress when loading packages from reflist. 2014-03-07 00:04:35 +04:00
Andrey Smirnov 81dd5a398b Uncomment lines. 2014-03-06 23:46:06 +04:00
Andrey Smirnov f143277a8e aptly snapshot diff does colour output via context.progress. 2014-03-06 23:34:46 +04:00
Andrey Smirnov d5f0c576d1 aptly snapshot pull outputs all messages through progress. 2014-03-06 23:03:39 +04:00
Andrey Smirnov b5f35cd540 Test for ppa short URL in aptly mirror create. 2014-03-06 21:18:26 +04:00
Andrey Smirnov d9bd016d1f New config options. 2014-03-06 21:09:40 +04:00
Andrey Smirnov bed0ac475e Short syntax for aptly mirror create to support ppa:user/project mirror URLs. 2014-03-06 21:06:50 +04:00
Andrey Smirnov 92c3bf0220 New flag -no-remove for aptly snapshot pull. 2014-03-05 16:15:31 +04:00
Andrey Smirnov 9f1f5aa92f Make system test ignore debugging features being on/off. 2014-03-05 16:00:00 +04:00
Andrey Smirnov 410caa6141 Fix race in shutdown: context should be shut down in case of error. 2014-03-04 18:39:43 +04:00
Andrey Smirnov 1902f38e6b Fix race in fatal exit & context shutdown. 2014-03-04 18:26:19 +04:00
Andrey Smirnov e59c327a7c One more test for partial loading. 2014-03-04 18:10:27 +04:00
Andrey Smirnov ef9267c722 Update db with new wheezy-backports. 2014-03-04 17:58:16 +04:00
Andrey Smirnov f9cb66a955 Test on reading and converting old pre-0.3 package. 2014-03-04 17:19:52 +04:00
Andrey Smirnov 9c6253d86d Now files are sorted internally. 2014-03-04 17:03:00 +04:00
Andrey Smirnov 3fe8a09928 Major refactoring to lower memory consumption.
Package has been split into multiple structure loaded on demand: files, extra, depends.

They're saved and loaded completely separately.
2014-03-04 16:49:26 +04:00
Andrey Smirnov 32e517b4f2 aptly no longer prints 'Saving packages to database.' 2014-03-04 16:48:37 +04:00
Andrey Smirnov 0894c41636 Use Package.UpdateFiles to touch package.Files 2014-03-04 16:48:23 +04:00
Andrey Smirnov 073374eb78 gnuplot script. 2014-03-03 20:59:37 +04:00
Andrey Smirnov d0c3659679 Gnuplot mem.dat to PNG. 2014-03-03 16:15:50 +04:00
Andrey Smirnov e055581e34 Dump correct memory stats. 2014-03-03 16:15:36 +04:00
Andrey Smirnov 735593fc84 Fix tests. 2014-03-01 23:13:07 +04:00
Andrey Smirnov 7d6387e78e Add debugging facilities. 2014-03-01 21:32:04 +04:00
Andrey Smirnov c554a5c7df Fix command help. 2014-02-28 18:04:52 +04:00
Andrey Smirnov c6e4239a22 Remove files only if whole package has been processed successfully. 2014-02-28 11:24:11 +04:00
Andrey Smirnov e881a6df00 Fix command help. [ci skip] 2014-02-27 23:51:01 +04:00
Andrey Smirnov d251a519b6 Fix race in Progress shutdown where some messages could have been lost. 2014-02-27 22:52:11 +04:00
Andrey Smirnov 268128482c Fix system tests. 2014-02-27 22:01:24 +04:00
Andrey Smirnov 91a45a2fde Display deb-src line if snapshot contains sources. 2014-02-27 21:10:52 +04:00
Andrey Smirnov 9e26207659 System test for publishing local repos. 2014-02-27 21:10:40 +04:00
Andrey Smirnov f1f008f2f5 System tests for aptly repo import. 2014-02-27 20:57:30 +04:00
Andrey Smirnov abe9a37408 System tests for aptly repo move|copy. 2014-02-27 18:06:13 +04:00
Andrey Smirnov a45d2f3ce9 Test on snapshot listing with local repo. 2014-02-27 17:48:32 +04:00
Andrey Smirnov a08cbc2edc Commands aptly repo move, copy, import. 2014-02-27 17:10:28 +04:00
Andrey Smirnov 0549db6833 System tests for aptly repo remove. 2014-02-27 16:22:18 +04:00
Andrey Smirnov 07cf61a641 Command aptly repo remove. 2014-02-27 16:14:54 +04:00
Andrey Smirnov d6dec91c93 List filtering with dependency resolution. 2014-02-27 15:54:17 +04:00
Andrey Smirnov f430e78a2b Fix system tests after Package.String() has been changed. 2014-02-27 13:15:28 +04:00
Andrey Smirnov 774d1d9aab Extract MatchDependency, change Package.String(). 2014-02-27 12:48:13 +04:00
Andrey Smirnov 03100c28ce Fix typo. 2014-02-26 23:51:13 +04:00
Andrey Smirnov 1ae8063873 Cleanup after local repo. 2014-02-26 23:50:49 +04:00
Andrey Smirnov 85f3d15e8c System tests for aptly repo drop. 2014-02-26 22:14:18 +04:00
Andrey Smirnov 565e82bd1a Command aptly repo drop. 2014-02-26 22:07:51 +04:00
Andrey Smirnov 4df80d38cf SnapshotCollection.ByLocalRepoSource method. 2014-02-26 21:30:14 +04:00
Andrey Smirnov eff530a284 aptly db cleanup test with local repos. 2014-02-26 21:21:44 +04:00
Andrey Smirnov df811ff36a Tests for aptly snapshot create from repo 2014-02-26 21:17:10 +04:00
Andrey Smirnov f19a334b2b aptly snapshot create from repo command. 2014-02-26 21:03:04 +04:00
Andrey Smirnov 544724e59e Include local repos into graph. 2014-02-26 21:02:51 +04:00
Andrey Smirnov 059abc465b Creating snapshots from local repos. 2014-02-26 21:02:34 +04:00
Andrey Smirnov 2f30cf0846 Don't draw edges on graph to non-existing entities. 2014-02-26 19:39:19 +04:00
Andrey Smirnov f643d3f04d Use original version of gom as pull requests has been merged. 2014-02-26 19:36:37 +04:00
Andrey Smirnov c7d8772b9b Use my fixed version of gom for now. 2014-02-26 17:06:27 +04:00
Andrey Smirnov 5e97e18d9d Add blank line to Gomfile. 2014-02-26 16:49:04 +04:00
Andrey Smirnov d65ed73a8a Use explicit path to gom for Travis. 2014-02-26 16:29:18 +04:00
Andrey Smirnov 10e1a85fb8 Install gom first. 2014-02-26 16:25:56 +04:00
Andrey Smirnov 7c26c3ac14 Fix all dependencies in Gomfile. 2014-02-26 16:24:54 +04:00
Andrey Smirnov 8ebc3e65a5 Fix test depending on $HOME. 2014-02-26 13:42:36 +04:00
Andrey Smirnov 653a7d8d5a System tests for aptly repo add.. 2014-02-26 13:08:55 +04:00
Andrey Smirnov 3ddf39ee58 Sort files before import, really stop importing on file problem. 2014-02-26 13:08:39 +04:00
Andrey Smirnov e0cb43fbd3 Style fixes. 2014-02-25 14:25:14 +04:00
Andrey Smirnov 70df28bdf4 New command: aptly repo add to add packages to repository. 2014-02-25 14:16:25 +04:00
Andrey Smirnov ee62dd34f7 Refactoring: Filename now contains only base path, any order of files is accepted. 2014-02-25 14:15:16 +04:00
Andrey Smirnov e94bca2733 Use different apt repository as test mirror, ppa.launchpad.net is too unstable. 2014-02-25 14:13:27 +04:00
Andrey Smirnov 943d089e7e Allow fails on go tip. 2014-02-25 13:29:19 +04:00
Andrey Smirnov da550188de Build on Go tip as well. 2014-02-25 13:25:04 +04:00
Andrey Smirnov 901ce3ed00 Add final / to archive URL if missing. 2014-02-25 13:24:51 +04:00
Andrey Smirnov 00a9e25706 Support colored printf (with non-colored output when not on terminal) in Progress. 2014-02-25 13:17:23 +04:00
Andrey Smirnov 98bd76f350 Fix snapshot system tests. 2014-02-25 12:34:01 +04:00
Andrey Smirnov 750a947479 Fix some system tests. 2014-02-25 12:12:49 +04:00
Andrey Smirnov 1400b45d9d Fix PackageCollection.Update to detect conflicts and skip updates when it's not necessary. 2014-02-25 00:40:32 +04:00
Andrey Smirnov e36971fdc3 Fix system tests. 2014-02-25 00:12:01 +04:00
Andrey Smirnov 8328c44c39 Use progress to output message to fix order of messages on screen. 2014-02-25 00:11:44 +04:00
Andrey Smirnov c06e69a485 In db cleanup include local repos. 2014-02-24 23:46:11 +04:00
Andrey Smirnov 1b10c87bad Updating reflist in local repo. 2014-02-24 23:45:47 +04:00
Andrey Smirnov 4277f09e2a Importing files into pool: interface. 2014-02-24 23:45:32 +04:00
Andrey Smirnov 65c790b6cf Importing files to package pool. 2014-02-24 23:45:17 +04:00
Andrey Smirnov 1b64612aef Handle corner-case: null reflist. 2014-02-24 12:17:24 +04:00
Andrey Smirnov 97becf199f Simplify iteration in LevelDB. 2014-02-24 12:05:51 +04:00
Andrey Smirnov 5f40e02a84 No second gpg verification for InRelease. 2014-02-23 13:49:56 +04:00
Andrey Smirnov e2067eab23 Bugfix: in cleanup ignore non-updated mirrors. 2014-02-23 13:44:45 +04:00
Andrey Smirnov 33c9c08632 Fixture example .dsc files. 2014-02-23 13:44:26 +04:00
Andrey Smirnov 1fe8a8b703 Refactor GPG clearsigned verification into extract + verification. 2014-02-23 13:44:03 +04:00
Andrey Smirnov a44742f6b8 Reading control files from .deb & .dsc files. 2014-02-23 13:20:37 +04:00
Andrey Smirnov 8951b4f42a Reading control file from .deb package. 2014-02-21 20:42:25 +04:00
Andrey Smirnov f63b0dd315 Update README. [ci skip] 2014-02-21 15:32:28 +04:00
Andrey Smirnov 196dc56dd9 Command aptly repo list. 2014-02-20 17:04:06 +04:00
Andrey Smirnov 54421a9377 Command aptly repo show. 2014-02-20 16:39:58 +04:00
Andrey Smirnov 63cd4a80bb Command aptly repo create. 2014-02-20 12:01:41 +04:00
Andrey Smirnov 8df4378f4c Don't generate default comment for local repos. 2014-02-20 12:01:22 +04:00
Andrey Smirnov c3819d6724 Fix collection of coverage for all new packages. 2014-02-19 23:34:13 +04:00
Andrey Smirnov 7da203e8d2 Local repo: model + collection. 2014-02-19 23:29:52 +04:00
Andrey Smirnov 5617385c44 Fix comments. 2014-02-19 17:16:45 +04:00
Andrey Smirnov d43a15e658 Refactor snapshot module into subcommands. 2014-02-19 15:27:47 +04:00
Andrey Smirnov e72f178ca9 Split publish into subcommands. 2014-02-19 15:12:16 +04:00
Andrey Smirnov 66cf2fe53e Split mirror subcommands into files. 2014-02-19 15:07:56 +04:00
Andrey Smirnov ccff7935bd Fixes for refactoring to cmd. 2014-02-19 15:07:41 +04:00
Andrey Smirnov eb18b04c40 Refactor commands to subpackage. 2014-02-19 14:59:00 +04:00
Andrey Smirnov 35c2178074 Add docblocks to method description in interfaces. [ci skip] 2014-02-19 13:44:14 +04:00
Andrey Smirnov 2d589bd23d Refactoring: new packages console, http, Progress is interface. 2014-02-19 13:08:55 +04:00
Andrey Smirnov bd119dbfed Style fixes. 2014-02-19 12:54:19 +04:00
Andrey Smirnov d1e16a0ef0 Refactor Repository: split into PackagePool and PublishedStorage. 2014-02-19 12:03:01 +04:00
Andrey Smirnov 7864ce241b Refactoring: replace sort.StringSlice with simply []string 2014-02-18 14:13:18 +04:00
Andrey Smirnov 190a81e141 Use ${HOME} instead of hardcoded home dir. 2014-02-18 01:11:57 +04:00
Andrey Smirnov 19af0547e8 Style fixes [no ci] 2014-02-18 01:00:49 +04:00
Andrey Smirnov e51f226cdc aptly serve with deb-src system test. 2014-02-18 00:57:02 +04:00
Andrey Smirnov dccb6d10a9 aptly db cleanup with source packages 2014-02-18 00:54:46 +04:00
Andrey Smirnov 532c85eaa6 Test for publihsing snapshot with sources. 2014-02-18 00:49:17 +04:00
Andrey Smirnov 3942734eca Update test to treat empty source as source package name-version same as binary. 2014-02-18 00:30:35 +04:00
Andrey Smirnov 3b77d7f3c7 Test for pulling packages with source following. 2014-02-18 00:22:04 +04:00
Andrey Smirnov e9449a9b15 Support different formats of supplying Source: field. 2014-02-18 00:21:31 +04:00
Andrey Smirnov 841771c18e Sort architectures for stability of result, use package architecture for dependency search. 2014-02-18 00:18:55 +04:00
Andrey Smirnov d8fe97e0cb System tests for snapshots verification with sources. 2014-02-17 20:53:22 +04:00
Andrey Smirnov 2d1c6e5cf3 Package might specify its own source pkg version. 2014-02-17 20:53:08 +04:00
Andrey Smirnov 506987d31f Support version numbers like pkg (1.4) 2014-02-17 20:52:37 +04:00
Andrey Smirnov b8fd33a92c Add wheezy snapshots with sources. 2014-02-17 20:52:13 +04:00
Andrey Smirnov 812bc6e1e1 Updating mirrors with sources. 2014-02-17 16:16:55 +04:00
Andrey Smirnov 9b0bb17908 Creating mirror with sources, listing mirrors. 2014-02-17 15:59:14 +04:00
Andrey Smirnov e3ef4038b4 Add gnuplot-maverick with sources. 2014-02-17 15:51:08 +04:00
Andrey Smirnov f32c19047f Publishing source packages index. 2014-02-17 15:41:08 +04:00
Andrey Smirnov a0b1ff8abe Support for dependencies with fixed archs (like as source dependency). 2014-02-17 15:36:16 +04:00
Andrey Smirnov c2f01c8aa1 When requiring source package, specify exact version of binary package. 2014-02-17 15:35:48 +04:00
Andrey Smirnov b19e6cfadd Update system tests for new options. 2014-02-17 15:14:29 +04:00
Andrey Smirnov af2266d572 Enable following to source dependencies. 2014-02-17 15:09:16 +04:00
Andrey Smirnov 21123ac6a4 Add config option to follow dependency to source packages. 2014-02-17 15:08:21 +04:00
Andrey Smirnov c96491e873 Follow build & source dependencies from package. 2014-02-17 15:06:26 +04:00
Andrey Smirnov 7ab456f6ff Make decorator for architecture be in curly braces, so there's no conflict with Debian "only for arch". 2014-02-17 15:05:34 +04:00
Andrey Smirnov 5af0c45e10 Support for parsing dependencies with architecture like [amd64]. 2014-02-17 14:03:41 +04:00
Andrey Smirnov 09a1b60946 Add 'deb-src' line to repos with source packages included. 2014-02-17 13:29:19 +04:00
Andrey Smirnov 54ac38c56b Test for Directory regeneration. 2014-02-17 13:03:50 +04:00
Andrey Smirnov ba178b9863 Regenerate Directory: field for source packages while publishing. 2014-02-17 13:03:27 +04:00
Andrey Smirnov d999258744 "all" architecture doesn't match "source", fix for missing checksums while publishing. 2014-02-17 11:41:33 +04:00
Andrey Smirnov e8de4db522 Free up queue as soon as we don't need it anymore. 2014-02-16 23:18:24 +04:00
Andrey Smirnov 47b3f3ed6a Reset count to zero. 2014-02-16 22:08:01 +04:00
Andrey Smirnov 73e0d8c213 Attempt to lower memory pressure a bit. 2014-02-16 21:59:10 +04:00
Andrey Smirnov a0757aadd6 Fix test. 2014-02-16 21:36:56 +04:00
Andrey Smirnov ce793c6dae List of architectures now includes optionally "source" virtual arch. 2014-02-16 21:36:25 +04:00
Andrey Smirnov 5e8b6da1db Add whitespace back. 2014-02-16 21:21:45 +04:00
Andrey Smirnov 7b41df7049 Fix system test with addition of 'Download Sources' in mirror show. 2014-02-16 02:49:54 +04:00
Andrey Smirnov e4d8ef4744 Showing download sources argument in mirror show and mirror list. 2014-02-15 16:56:10 +04:00
Andrey Smirnov fb9b90e715 Tests for downloading with sources, flat and regular repos. 2014-02-15 16:33:00 +04:00
Andrey Smirnov 5fb512f86e Add ability to expect responses in any order. 2014-02-15 16:32:28 +04:00
Andrey Smirnov b2523b4215 --with-sources, downloading source packages flag. 2014-02-14 23:58:09 +04:00
Andrey Smirnov 519082a61e Remote repos with source packages option. 2014-02-14 23:45:14 +04:00
Andrey Smirnov 36446e46a1 Add config option 'downloadSourcePackages'. 2014-02-14 23:44:46 +04:00
Andrey Smirnov e6a2f27d47 Correctly prepend directory to filenames of source package files. 2014-02-14 23:33:33 +04:00
Andrey Smirnov 2b4dfe257e Fix comments. 2014-02-14 22:05:51 +04:00
Andrey Smirnov 22c427bb96 Parsing & generation of source package stanzas. 2014-02-14 22:04:48 +04:00
Andrey Smirnov 213c1e0b4f Sort HTTP output to fix unstable system test. 2014-02-13 13:01:56 +04:00
Andrey Smirnov ad2680aeba Use batched writes to DB when saving packages from the mirror. 2014-02-13 12:32:12 +04:00
Andrey Smirnov fb6df84ec7 Fix more system tests with GPG publishing. 2014-02-13 01:02:35 +04:00
Andrey Smirnov 796489e88d Don't compain about missing keys when using custom keyring. 2014-02-13 00:05:21 +04:00
Andrey Smirnov fb2e1adb5c Use different keyring in system tests. 2014-02-12 21:25:55 +04:00
Andrey Smirnov 5bf370e18a Add new flags for specifying GPG keyring manually when signing. 2014-02-12 21:25:33 +04:00
Andrey Smirnov ced832b1c0 System test for aptly db cleanup. 2014-02-12 20:33:16 +04:00
Andrey Smirnov d4fb5853cc Add fixture pool creation from GitHub for system tests. 2014-02-12 17:24:35 +04:00
Andrey Smirnov 2557c41d49 Fix repotoken submission for new version of goveralls. 2014-02-12 17:13:02 +04:00
Andrey Smirnov ce3ae80feb Fix system tests. 2014-02-12 16:14:24 +04:00
Andrey Smirnov 7ec8d80053 Add stats on disk space savings. 2014-02-12 16:06:53 +04:00
Andrey Smirnov bd89d7c62e Command aptly db cleanup. 2014-02-12 15:29:18 +04:00
Andrey Smirnov 62ea87dc6c Removing files from package pool. 2014-02-12 15:19:04 +04:00
Andrey Smirnov 130efaa350 Relative pool path and list of all filepaths. 2014-02-12 13:49:01 +04:00
Andrey Smirnov b5da3e9680 Deleting packages & building file list from packages. 2014-02-12 13:12:07 +04:00
Andrey Smirnov 974d30b837 StrSlice substraction. 2014-02-12 12:59:02 +04:00
Andrey Smirnov 8ae1f7aab0 Fix: progress bar sometimes doesn't disappear. 2014-02-12 11:56:42 +04:00
Andrey Smirnov 3ba7bc7943 Simplify test by using WriteFile. 2014-02-12 11:22:27 +04:00
Andrey Smirnov c30862dff9 Build all package refs. 2014-02-11 21:11:36 +04:00
Andrey Smirnov bff299d268 Batch writes/deletes in LevelDB. 2014-02-11 21:11:15 +04:00
Andrey Smirnov 6b02b18c8e Substraction of PackageRefLists. 2014-02-11 18:52:30 +04:00
Andrey Smirnov 7be4404a87 Fix call to Merge with new parameter. 2014-02-11 17:33:39 +04:00
Andrey Smirnov 94616e1b06 Diffing without overrideMatching. 2014-02-11 17:26:18 +04:00
Andrey Smirnov 8d72f1a959 Database: return list of keys by prefix. 2014-02-11 17:21:35 +04:00
Andrey Smirnov aea8ae9a96 Bump version to 0.4~dev. 2014-02-11 11:28:11 +04:00
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
610 changed files with 862301 additions and 3102 deletions
+8
View File
@@ -24,3 +24,11 @@ _testmain.go
coverage.html
coverage*.out
*.pyc
_vendor/
gen
man/aptly.1.html
man/aptly.1.ronn
+7 -1
View File
@@ -2,7 +2,9 @@ language: go
go:
- 1.1
- 1.2
- 1.2.1
- 1.3
- tip
env:
global:
@@ -13,3 +15,7 @@ install:
script: make travis
matrix:
allow_failures:
- go: tip
+6
View File
@@ -0,0 +1,6 @@
List of contributors, in chronological order:
* Andrey Smirnov (https://github.com/smira)
* Sebastien Binet (https://github.com/sbinet)
* Ryan Uber (https://github.com/ryanuber)
* Simon Aquino (https://github.com/simonaquino)
+24
View File
@@ -0,0 +1,24 @@
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
gom 'code.google.com/p/go.crypto/ssh/terminal', :commit => '7aa593ce8cea'
gom 'code.google.com/p/gographviz', :commit => '454bc64fdfa2'
gom 'code.google.com/p/mxk/go1/flowcontrol', :commit => '5ff2502e2556'
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
gom 'github.com/cheggaaa/pb', :commit => '74be7a1388046f374ac36e93d46f5d56e856f827'
gom 'github.com/mitchellh/goamz/s3', :commit => '55f224c07975fddef9d2116600c664e30df3d594'
gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
gom 'github.com/smira/flag', :commit => '0d0aac2addb39050f45e92c5a6252926096dc841'
gom 'github.com/syndtr/goleveldb/leveldb', :commit => '9888007'
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
group :test do
gom 'launchpad.net/gocheck'
end
group :development do
gom 'github.com/golang/lint/golint'
gom 'github.com/mattn/goveralls'
gom 'github.com/axw/gocov/gocov'
gom 'code.google.com/p/go.tools/cmd/cover'
end
+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
+60 -31
View File
@@ -1,56 +1,85 @@
GOVERSION=$(shell go version | awk '{print $$3;}')
PACKAGES=database deb files http query s3 utils
ALL_PACKAGES=aptly cmd console database deb files http query s3 utils
BINPATH=$(abspath ./_vendor/bin)
GOM_ENVIRONMENT=-test
PYTHON?=python
ifeq ($(TRAVIS), true)
GOVERALLS?=$(HOME)/gopath/bin/goveralls
SRCPATH?=$(HOME)/gopath/src
else
GOVERALLS?=goveralls
SRCPATH?=$(GOPATH)/src
endif
ifeq ($(GOVERSION), go1.2)
ifeq ($(GOVERSION), devel)
TRAVIS_TARGET=coveralls
PREPARE_LIST=cover-prepare
GOM_ENVIRONMENT+=-development
else
TRAVIS_TARGET=test
PREPARE_LIST=
endif
all: test check
ifeq ($(TRAVIS), true)
GOM=$(HOME)/gopath/bin/gom
else
GOM=gom
endif
prepare: $(PREPARE_LIST)
go get -d -v ./...
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
all: test check system-test
cover-prepare:
go get github.com/golang/lint/golint
go get github.com/mattn/goveralls
go get github.com/axw/gocov/gocov
go get code.google.com/p/go.tools/cmd/cover
prepare:
go get -u github.com/mattn/gom
$(GOM) $(GOM_ENVIRONMENT) install
coverage.out:
go test -coverprofile=coverage.debian.out -covermode=count ./debian
go test -coverprofile=coverage.utils.out -covermode=count ./utils
go test -coverprofile=coverage.database.out -covermode=count ./database
rm -f coverage.*.out
for i in $(PACKAGES); do $(GOM) test -coverprofile=coverage.$$i.out -covermode=count ./$$i; done
echo "mode: count" > coverage.out
grep -v -h "mode: count" coverage.*.out >> coverage.out
rm -f coverage.*.out
coverage: coverage.out
go tool cover -html=coverage.out
$(GOM) exec go tool cover -html=coverage.out
rm -f coverage.out
check:
go tool vet -all=true .
golint .
$(GOM) exec go tool vet -all=true -shadow=true $(ALL_PACKAGES:%=./%)
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
travis: $(TRAVIS_TARGET)
install:
$(GOM) build -o $(BINPATH)/aptly
system-test: install
ifeq ($(GOVERSION),$(filter $(GOVERSION),go1.2 go1.2.1 go1.3 devel))
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
endif
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
travis: $(TRAVIS_TARGET) system-test
test:
go test -v ./... -gocheck.v=true
$(GOM) test -v ./... -gocheck.v=true
coveralls: coverage.out
@$(GOVERALLS) -service travis-ci.org -coverprofile=coverage.out $(COVERALLS_TOKEN)
$(GOM) exec $(BINPATH)/goveralls -service travis-ci.org -coverprofile=coverage.out -repotoken=$(COVERALLS_TOKEN)
mem.png: mem.dat mem.gp
gnuplot mem.gp
open mem.png
package:
rm -rf root/
mkdir -p root/usr/bin/ root/usr/share/man/man1/ root/etc/bash_completion.d
cp $(BINPATH)/aptly root/usr/bin
cp man/aptly.1 root/usr/share/man/man1
(cd root/etc/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/master/aptly)
gzip root/usr/share/man/man1/aptly.1
fpm -s dir -t deb -n aptly -v $(VERSION) --url=http://www.aptly.info/ --license=MIT --vendor="Andrey Smirnov <me@smira.ru>" \
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" --deb-recommends bzip2 -C root/ .
mv aptly_$(VERSION)_*.deb ~
src-package:
rm -rf aptly-$(VERSION)
mkdir -p aptly-$(VERSION)/src/github.com/smira/aptly/
cd aptly-$(VERSION)/src/github.com/smira/ && git clone https://github.com/smira/aptly && cd aptly && git checkout v$(VERSION)
cd aptly-$(VERSION)/src/github.com/smira/aptly && gom -production install
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . \( -name .git -o -name .bzr -o -name .hg \) -print | xargs rm -rf
rm -rf aptly-$(VERSION)/src/github.com/smira/aptly/_vendor/{pkg,bin}
tar cyf aptly-$(VERSION)-src.tar.bz2 aptly-$(VERSION)
rm -rf aptly-$(VERSION)
.PHONY: coverage.out
+30 -337
View File
@@ -10,366 +10,59 @@ aptly
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
* 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
* merge two or more snapshots into one (+)
* controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies
* merge two or more snapshots into one
* 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 (+)
* mirror repositories "as-is" (without resigning with user's key) (+)
* support for yum repositories (+)
Current limitations:
* source packages, debian-installer and translations not supported yet
* checksums and signature are not verified while downloading
* deleting created items is not implemented
* cleaning up stale files is not implemented
Currently aptly is under heavy development, so please use it with care.
.. contents::
* debian-installer and translations not supported yet
Download
--------
TBD
To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
Configuration
-------------
deb http://repo.aptly.info/ squeeze main
aptly looks for configuration file in ``/etc/aptly.conf`` and ``~/.aptly.conf``, if no config file found,
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.
And import key that is used to sign the release::
Configuration file is stored in JSON format::
$ gpg --keyserver keys.gnupg.net --recv-keys 2A194991
$ gpg -a --export 2A194991 | sudo apt-key add -
{
"rootDir": "/var/aptly",
"downloadConcurrency": 4
}
After that you can install aptly as any other software package::
Options:
$ apt-get update
$ apt-get install aptly
* ``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
Don't worry about squeeze part in repo name: aptly package should work on Debian squeeze+,
Ubuntu 10.0+. Package contains aptly binary, man page and bash completion.
Example
-------
Binary executables (depends almost only on libc) are available for download from `Bintray <http://dl.bintray.com/smira/aptly/>`_.
Create mirror::
If you have Go environment set up, you can build aptly from source by running (go 1.1+ required)::
$ 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...
...
go get -u github.com/mattn/gom
mkdir -p $GOPATH/src/github.com/smira/aptly
git clone https://github.com/smira/aptly $GOPATH/src/github.com/smira/aptly
cd $GOPATH/src/github.com/smira/aptly
gom -production install
gom build -o $GOPATH/bin/aptly
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 is using `gom <https://github.com/mattn/gom>`_ to fix external dependencies, so regular ``go get github.com/smira/aptly``
should work as well, but might fail or produce different result (if external libraries got updated).
Take snapshot::
If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_.
$ 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``.
+95
View File
@@ -0,0 +1,95 @@
// Package aptly provides common infrastructure that doesn't depend directly on
// Debian or CentOS
package aptly
import (
"github.com/smira/aptly/utils"
"io"
)
// PackagePool is asbtraction of package pool storage.
//
// PackagePool stores all the package files, deduplicating them.
type PackagePool interface {
// Path returns full path to package file in pool given any name and hash of file contents
Path(filename string, hashMD5 string) (string, error)
// RelativePath returns path relative to pool's root for package files given MD5 and original filename
RelativePath(filename string, hashMD5 string) (string, error)
// FilepathList returns file paths of all the files in the pool
FilepathList(progress Progress) ([]string, error)
// Remove deletes file in package pool returns its size
Remove(path string) (size int64, err error)
// Import copies file into package pool
Import(path string, hashMD5 string) error
}
// PublishedStorage is abstraction of filesystem storing all published repositories
type PublishedStorage interface {
// MkDir creates directory recursively under public path
MkDir(path string) error
// PutFile puts file into published storage at specified path
PutFile(path string, sourceFilename string) error
// RemoveDirs removes directory structure under public path
RemoveDirs(path string, progress Progress) error
// Remove removes single file under public path
Remove(path string) error
// LinkFromPool links package file from pool to dist's pool location
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath, sourceMD5 string) error
// Filelist returns list of files under prefix
Filelist(prefix string) ([]string, error)
// RenameFile renames (moves) file
RenameFile(oldName, newName string) error
}
// LocalPublishedStorage is published storage on local filesystem
type LocalPublishedStorage interface {
// PublicPath returns root of public part
PublicPath() string
}
// PublishedStorageProvider is a thing that returns PublishedStorage by name
type PublishedStorageProvider interface {
// GetPublishedStorage returns PublishedStorage by name
GetPublishedStorage(name string) PublishedStorage
}
// Progress is a progress displaying entity, it allows progress bars & simple prints
type Progress interface {
// Writer interface to support progress bar ticking
io.Writer
// Start makes progress start its work
Start()
// Shutdown shuts down progress display
Shutdown()
// Flush returns when all queued messages are sent
Flush()
// InitBar starts progressbar for count bytes or count items
InitBar(count int64, isBytes bool)
// ShutdownBar stops progress bar and hides it
ShutdownBar()
// AddBar increments progress for progress bar
AddBar(count int)
// SetBar sets current position for progress bar
SetBar(count int)
// Printf does printf but in safe manner: not overwriting progress bar
Printf(msg string, a ...interface{})
// ColoredPrintf does printf in colored way + newline
ColoredPrintf(msg string, a ...interface{})
}
// Downloader is parallel HTTP fetcher
type Downloader interface {
// Download starts new download task
Download(url string, destination string, result chan<- error)
// DownloadWithChecksum starts new download task with checksum verification
DownloadWithChecksum(url string, destination string, result chan<- error, expected utils.ChecksumInfo, ignoreMismatch bool)
// Pause pauses task processing
Pause()
// Resume resumes task processing
Resume()
// Shutdown stops downloader after current tasks are finished,
// but doesn't process rest of queue
Shutdown()
// GetProgress returns Progress object
GetProgress() Progress
}
+7
View File
@@ -0,0 +1,7 @@
package aptly
// Version of aptly
const Version = "0.7"
// Enable debugging features?
const EnableDebug = false
+81
View File
@@ -0,0 +1,81 @@
// Package cmd implements console commands
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"time"
)
// ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
if reflist == nil {
return
}
err = reflist.ForEach(func(key []byte) error {
p, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
return err2
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
// RootCommand creates root command in command tree
func RootCommand() *commander.Command {
cmd := &commander.Command{
UsageLine: os.Args[0],
Short: "Debian repository management tool",
Long: `
aptly is a tool to create partial and full mirrors of remote
repositories, manage local repositories, filter them, merge,
upgrade individual packages, take snapshots and publish them
back as Debian repositories.
aptly's goal is to establish repeatability and controlled changes
in a package-centric environment. aptly allows to fix a set of packages
in a repository, so that package installation and upgrade becomes
deterministic. At the same time aptly allows to perform controlled,
fine-grained changes in repository contents to transition your
package environment to new version.`,
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
Subcommands: []*commander.Command{
makeCmdDb(),
makeCmdGraph(),
makeCmdMirror(),
makeCmdRepo(),
makeCmdServe(),
makeCmdSnapshot(),
makeCmdPublish(),
makeCmdVersion(),
},
}
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
cmd.Flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
if aptly.EnableDebug {
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
cmd.Flag.String("memprofile", "", "write memory profile to this file")
cmd.Flag.String("memstats", "", "write memory stats periodically to this file")
cmd.Flag.Duration("meminterval", 100*time.Millisecond, "memory stats dump interval")
}
return cmd
}
+321
View File
@@ -0,0 +1,321 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/console"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/files"
"github.com/smira/aptly/http"
"github.com/smira/aptly/s3"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"time"
)
// AptlyContext is a common context shared by all commands
type AptlyContext struct {
flags *flag.FlagSet
configLoaded bool
progress aptly.Progress
downloader aptly.Downloader
database database.Storage
packagePool aptly.PackagePool
publishedStorages map[string]aptly.PublishedStorage
collectionFactory *deb.CollectionFactory
dependencyOptions int
architecturesList []string
// Debug features
fileCPUProfile *os.File
fileMemProfile *os.File
fileMemStats *os.File
}
var context *AptlyContext
// Check interface
var _ aptly.PublishedStorageProvider = &AptlyContext{}
// FatalError is type for panicking to abort execution with non-zero
// exit code and print meaningful explanation
type FatalError struct {
ReturnCode int
Message string
}
// Fatal panics and aborts execution with exit code 1
func Fatal(err error) {
returnCode := 1
if err == commander.ErrFlagError || err == commander.ErrCommandError {
returnCode = 2
}
panic(&FatalError{ReturnCode: returnCode, Message: err.Error()})
}
// Config loads and returns current configuration
func (context *AptlyContext) Config() *utils.ConfigStructure {
if !context.configLoaded {
var err error
configLocation := context.flags.Lookup("config").Value.String()
if configLocation != "" {
err = utils.LoadConfig(configLocation, &utils.Config)
if err != nil {
Fatal(err)
}
} else {
configLocations := []string{
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
"/etc/aptly.conf",
}
for _, configLocation := range configLocations {
err = utils.LoadConfig(configLocation, &utils.Config)
if err == nil {
break
}
if !os.IsNotExist(err) {
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
}
}
if err != nil {
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
utils.SaveConfig(configLocations[0], &utils.Config)
}
}
context.configLoaded = true
}
return &utils.Config
}
// DependencyOptions calculates options related to dependecy handling
func (context *AptlyContext) DependencyOptions() int {
if context.dependencyOptions == -1 {
context.dependencyOptions = 0
if context.Config().DepFollowSuggests || context.flags.Lookup("dep-follow-suggests").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSuggests
}
if context.Config().DepFollowRecommends || context.flags.Lookup("dep-follow-recommends").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowRecommends
}
if context.Config().DepFollowAllVariants || context.flags.Lookup("dep-follow-all-variants").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if context.Config().DepFollowSource || context.flags.Lookup("dep-follow-source").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSource
}
}
return context.dependencyOptions
}
// ArchitecturesList returns list of architectures fixed via command line or config
func (context *AptlyContext) ArchitecturesList() []string {
if context.architecturesList == nil {
context.architecturesList = context.Config().Architectures
optionArchitectures := context.flags.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
}
return context.architecturesList
}
// Progress creates or returns Progress object
func (context *AptlyContext) Progress() aptly.Progress {
if context.progress == nil {
context.progress = console.NewProgress()
context.progress.Start()
}
return context.progress
}
// Downloader returns instance of current downloader
func (context *AptlyContext) Downloader() aptly.Downloader {
if context.downloader == nil {
var downloadLimit int64
limitFlag := context.flags.Lookup("download-limit")
if limitFlag != nil {
downloadLimit = limitFlag.Value.Get().(int64)
}
if downloadLimit == 0 {
downloadLimit = context.Config().DownloadLimit
}
context.downloader = http.NewDownloader(context.Config().DownloadConcurrency,
downloadLimit*1024, context.Progress())
}
return context.downloader
}
// DBPath builds path to database
func (context *AptlyContext) DBPath() string {
return filepath.Join(context.Config().RootDir, "db")
}
// Database opens and returns current instance of database
func (context *AptlyContext) Database() (database.Storage, error) {
if context.database == nil {
var err error
context.database, err = database.OpenDB(context.DBPath())
if err != nil {
return nil, fmt.Errorf("can't open database: %s", err)
}
}
return context.database, nil
}
// CollectionFactory builds factory producing all kinds of collections
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
if context.collectionFactory == nil {
db, err := context.Database()
if err != nil {
Fatal(err)
}
context.collectionFactory = deb.NewCollectionFactory(db)
}
return context.collectionFactory
}
// PackagePool returns instance of PackagePool
func (context *AptlyContext) PackagePool() aptly.PackagePool {
if context.packagePool == nil {
context.packagePool = files.NewPackagePool(context.Config().RootDir)
}
return context.packagePool
}
// PublishedStorage returns instance of PublishedStorage
func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedStorage {
publishedStorage, ok := context.publishedStorages[name]
if !ok {
if name == "" {
publishedStorage = files.NewPublishedStorage(context.Config().RootDir)
} else if strings.HasPrefix(name, "s3:") {
params, ok := context.Config().S3PublishRoots[name[3:]]
if !ok {
Fatal(fmt.Errorf("published S3 storage %v not configured", name[3:]))
}
var err error
publishedStorage, err = s3.NewPublishedStorage(params.AccessKeyID, params.SecretAccessKey,
params.Region, params.Bucket, params.ACL, params.Prefix)
if err != nil {
Fatal(err)
}
} else {
Fatal(fmt.Errorf("unknown published storage format: %v", name))
}
context.publishedStorages[name] = publishedStorage
}
return publishedStorage
}
// ShutdownContext shuts context down
func ShutdownContext() {
if aptly.EnableDebug {
if context.fileMemProfile != nil {
pprof.WriteHeapProfile(context.fileMemProfile)
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
if context.fileCPUProfile != nil {
pprof.StopCPUProfile()
context.fileCPUProfile.Close()
context.fileCPUProfile = nil
}
if context.fileMemProfile != nil {
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
}
if context.database != nil {
context.database.Close()
}
if context.downloader != nil {
context.downloader.Shutdown()
}
if context.progress != nil {
context.progress.Shutdown()
}
}
// InitContext initializes context with default settings
func InitContext(flags *flag.FlagSet) error {
var err error
context = &AptlyContext{
flags: flags,
dependencyOptions: -1,
publishedStorages: map[string]aptly.PublishedStorage{},
}
if aptly.EnableDebug {
cpuprofile := flags.Lookup("cpuprofile").Value.String()
if cpuprofile != "" {
context.fileCPUProfile, err = os.Create(cpuprofile)
if err != nil {
return err
}
pprof.StartCPUProfile(context.fileCPUProfile)
}
memprofile := flags.Lookup("memprofile").Value.String()
if memprofile != "" {
context.fileMemProfile, err = os.Create(memprofile)
if err != nil {
return err
}
}
memstats := flags.Lookup("memstats").Value.String()
if memstats != "" {
interval := flags.Lookup("meminterval").Value.Get().(time.Duration)
context.fileMemStats, err = os.Create(memstats)
if err != nil {
return err
}
context.fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
go func() {
var stats runtime.MemStats
start := time.Now().UnixNano()
for {
runtime.ReadMemStats(&stats)
if context.fileMemStats != nil {
context.fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval)
} else {
break
}
}
}()
}
}
return nil
}
+16
View File
@@ -0,0 +1,16 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdDb() *commander.Command {
return &commander.Command{
UsageLine: "db",
Short: "manage aptly's internal database and package pool",
Subcommands: []*commander.Command{
makeCmdDbCleanup(),
makeCmdDbRecover(),
},
}
}
+167
View File
@@ -0,0 +1,167 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"sort"
)
// aptly db cleanup
func aptlyDbCleanup(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
// collect information about references packages...
existingPackageRefs := deb.NewPackageRefList()
context.Progress().Printf("Loading mirrors, local repos and snapshots...\n")
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
}
return nil
})
if err != nil {
return err
}
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
}
return nil
})
if err != nil {
return err
}
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return err
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
return nil
})
if err != nil {
return err
}
// ... and compare it to the list of all packages
context.Progress().Printf("Loading list of all packages...\n")
allPackageRefs := context.CollectionFactory().PackageCollection().AllPackageRefs()
toDelete := allPackageRefs.Substract(existingPackageRefs)
// delete packages that are no longer referenced
context.Progress().Printf("Deleting unreferenced packages (%d)...\n", toDelete.Len())
// database can't err as collection factory already constructed
db, _ := context.Database()
db.StartBatch()
err = toDelete.ForEach(func(ref []byte) error {
return context.CollectionFactory().PackageCollection().DeleteByKey(ref)
})
if err != nil {
return err
}
err = db.FinishBatch()
if err != nil {
return fmt.Errorf("unable to write to DB: %s", err)
}
// now, build a list of files that should be present in Repository (package pool)
context.Progress().Printf("Building list of files referenced by packages...\n")
referencedFiles := make([]string, 0, existingPackageRefs.Len())
context.Progress().InitBar(int64(existingPackageRefs.Len()), false)
err = existingPackageRefs.ForEach(func(key []byte) error {
pkg, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
return err2
}
paths, err2 := pkg.FilepathList(context.PackagePool())
if err2 != nil {
return err2
}
referencedFiles = append(referencedFiles, paths...)
context.Progress().AddBar(1)
return nil
})
if err != nil {
return err
}
sort.Strings(referencedFiles)
context.Progress().ShutdownBar()
// build a list of files in the package pool
context.Progress().Printf("Building list of files in package pool...\n")
existingFiles, err := context.PackagePool().FilepathList(context.Progress())
if err != nil {
return fmt.Errorf("unable to collect file paths: %s", err)
}
// find files which are in the pool but not referenced by packages
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
// delete files that are no longer referenced
context.Progress().Printf("Deleting unreferenced files (%d)...\n", len(filesToDelete))
if len(filesToDelete) > 0 {
context.Progress().InitBar(int64(len(filesToDelete)), false)
var size, totalSize int64
for _, file := range filesToDelete {
size, err = context.PackagePool().Remove(file)
if err != nil {
return err
}
context.Progress().AddBar(1)
totalSize += size
}
context.Progress().ShutdownBar()
context.Progress().Printf("Disk space freed: %s...\n", utils.HumanBytes(totalSize))
}
context.Progress().Printf("Compacting database...\n")
err = db.CompactDB()
return err
}
func makeCmdDbCleanup() *commander.Command {
cmd := &commander.Command{
Run: aptlyDbCleanup,
UsageLine: "cleanup",
Short: "cleanup DB and package pool",
Long: `
Database cleanup removes information about unreferenced packages and removes
files in the package pool that aren't used by packages anymore
Example:
$ aptly db cleanup
`,
}
return cmd
}
+39
View File
@@ -0,0 +1,39 @@
package cmd
import (
"github.com/smira/aptly/database"
"github.com/smira/commander"
)
// aptly db recover
func aptlyDbRecover(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
context.Progress().Printf("Recovering database...\n")
err = database.RecoverDB(context.DBPath())
return err
}
func makeCmdDbRecover() *commander.Command {
cmd := &commander.Command{
Run: aptlyDbRecover,
UsageLine: "recover",
Short: "recover DB after crash",
Long: `
Database recover does its' best to recover the database after a crash.
It is recommended to backup the DB before running recover.
Example:
$ aptly db recover
`,
}
return cmd
}
+205
View File
@@ -0,0 +1,205 @@
package cmd
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
graph := gographviz.NewEscape()
graph.SetDir(true)
graph.SetName("aptly")
existingNodes := map[string]bool{}
fmt.Printf("Loading mirrors...\n")
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages()),
})
existingNodes[repo.UUID] = true
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading local repos...\n")
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "mediumseagreen",
"label": fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
repo.Name, repo.Comment, repo.NumPackages()),
})
existingNodes[repo.UUID] = true
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
existingNodes[snapshot.UUID] = true
return nil
})
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", snapshot.UUID, map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages()),
})
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
_, exists := existingNodes[uuid]
if exists {
graph.AddEdge(uuid, snapshot.UUID, true, nil)
}
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
graph.AddNode("aptly", repo.UUID, map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution,
strings.Join(repo.Components(), " "), strings.Join(repo.Architectures, ", ")),
})
for _, uuid := range repo.Sources {
_, exists := existingNodes[uuid]
if exists {
graph.AddEdge(uuid, 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: "render graph of relationships",
Long: `
Command graph displays relationship between mirrors, local repositories,
snapshots and published repositories using graphviz package to render
graph as an image.
Example:
$ aptly graph
`,
}
return cmd
}
+61
View File
@@ -0,0 +1,61 @@
package cmd
import (
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
if context.Config().GpgDisableVerify || flags.Lookup("ignore-signatures").Value.Get().(bool) {
return nil, nil
}
keyRings := flags.Lookup("keyring").Value.Get().([]string)
verifier := &utils.GpgVerifier{}
for _, keyRing := range 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, ",")
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
makeCmdMirrorRename(),
makeCmdMirrorEdit(),
},
}
}
+98
View File
@@ -0,0 +1,98 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if !(len(args) == 2 && strings.HasPrefix(args[1], "ppa:") || len(args) >= 3) {
cmd.Usage()
return commander.ErrCommandError
}
downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool)
var (
mirrorName, archiveURL, distribution string
components []string
)
mirrorName = args[0]
if len(args) == 2 {
archiveURL, distribution, components, err = deb.ParsePPA(args[1], context.Config())
if err != nil {
return err
}
} else {
archiveURL, distribution, components = args[1], args[2], args[3:]
}
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(), downloadSources)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
repo.Filter = context.flags.Lookup("filter").Value.String()
repo.FilterWithDeps = context.flags.Lookup("filter-with-deps").Value.Get().(bool)
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
}
verifier, err := getVerifier(context.flags)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.Downloader(), verifier)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror",
Long: `
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
via HTTP. aptly would try download Release file from remote repository and verify its' signature. Command
line format resembles apt utlitily sources.list(5).
PPA urls could specified in short format:
$ aptly mirror create <name> ppa:<user>/<project>
Example:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
+67
View File
@@ -0,0 +1,67 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := context.flags.Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().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 = context.CollectionFactory().RemoteRepoCollection().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 makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete mirror",
Long: `
Drop deletes information about remote repository mirror <name>. Package data is not deleted
(since it could still be used by other mirrors or snapshots). If mirror is used as source
to create a snapshot, aptly would refuse to delete such mirror, use flag -force to override.
Example:
$ 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
}
+67
View File
@@ -0,0 +1,67 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
context.flags.Visit(func(flag *flag.Flag) {
switch flag.Name {
case "filter":
repo.Filter = flag.Value.String()
case "filter-with-deps":
repo.FilterWithDeps = flag.Value.Get().(bool)
}
})
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
fmt.Printf("Mirror %s successfully updated.\n", repo)
return err
}
func makeCmdMirrorEdit() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorEdit,
UsageLine: "edit <name>",
Short: "edit properties of mirorr",
Long: `
Command edit allows to change settings of mirror:
filters.
Example:
$ aptly mirror edit -filter=nginx -filter-with-deps some-mirror
`,
Flag: *flag.NewFlagSet("aptly-mirror-edit", flag.ExitOnError),
}
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
return cmd
}
+69
View File
@@ -0,0 +1,69 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
repos := make([]string, context.CollectionFactory().RemoteRepoCollection().Len())
i := 0
context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
if raw {
repos[i] = repo.Name
} else {
repos[i] = repo.String()
}
i++
return nil
})
sort.Strings(repos)
if raw {
for _, repo := range repos {
fmt.Printf("%s\n", repo)
}
} else {
if len(repos) > 0 {
fmt.Printf("List of mirrors:\n")
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
}
return err
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors",
Long: `
List shows full list of remote repository mirrors.
Example:
$ aptly mirror list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyMirrorRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.RemoteRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().RemoteRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: mirror %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nMirror %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdMirrorRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames mirror",
Long: `
Command changes name of the mirror.Mirror name should be unique.
Example:
$ aptly mirror rename wheezy-min wheezy-main
`,
}
return cmd
}
+90
View File
@@ -0,0 +1,90 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
downloadSources := "no"
if repo.DownloadSources {
downloadSources = "yes"
}
fmt.Printf("Download Sources: %s\n", downloadSources)
if repo.Filter != "" {
fmt.Printf("Filter: %s\n", repo.Filter)
filterWithDeps := "no"
if repo.FilterWithDeps {
filterWithDeps = "yes"
}
fmt.Printf("Filter With Deps: %s\n", filterWithDeps)
}
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show <name>",
Short: "show details about mirror",
Long: `
Shows detailed information about the mirror.
Example:
$ aptly mirror show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show detailed list of packages and versions stored in the mirror")
return cmd
}
+89
View File
@@ -0,0 +1,89 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := context.flags.Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(context.flags)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.Downloader(), verifier)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
var filterQuery deb.PackageQuery
if repo.Filter != "" {
filterQuery, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
}
err = repo.Download(context.Progress(), context.Downloader(), context.CollectionFactory(), context.PackagePool(), ignoreMismatch,
context.DependencyOptions(), filterQuery)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update <name>",
Short: "update mirror",
Long: `
Updates remote mirror (downloads package files and meta information). When mirror is created,
this command should be run for the first time to fetch mirror contents. This command can be
run multiple times to get updated repository contents. If interrupted, command can be safely restarted.
Example:
$ aptly mirror update wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Int64("download-limit", 0, "limit download speed (kbytes/sec)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
+55
View File
@@ -0,0 +1,55 @@
package cmd
import (
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
if flags.Lookup("skip-signing").Value.Get().(bool) || context.Config().GpgDisableSign {
return nil, nil
}
signer := &utils.GpgSigner{}
signer.SetKey(flags.Lookup("gpg-key").Value.String())
signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").Value.String())
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func parsePrefix(param string) (storage, prefix string) {
i := strings.LastIndex(param, ":")
if i != -1 {
storage = param[:i]
prefix = param[i+1:]
if prefix == "" {
prefix = "."
}
} else {
prefix = param
}
return
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishDrop(),
makeCmdPublishList(),
makeCmdPublishRepo(),
makeCmdPublishSnapshot(),
makeCmdPublishSwitch(),
makeCmdPublishUpdate(),
},
}
}
+51
View File
@@ -0,0 +1,51 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
)
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
if len(args) == 2 {
param = args[1]
}
storage, prefix := parsePrefix(param)
err = context.CollectionFactory().PublishedRepoCollection().Remove(context, storage, prefix, distribution,
context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
context.Progress().Printf("\nPublished repository has been removed successfully.\n")
return err
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [[<endpoint>:]<prefix>]",
Short: "remove published repository",
Long: `
Command removes whatever has been published under specified <prefix>,
publishing <endpoint> and <distribution> name.
Example:
$ aptly publish drop wheezy
`,
}
return cmd
}
+78
View File
@@ -0,0 +1,78 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
published := make([]string, 0, context.CollectionFactory().PublishedRepoCollection().Len())
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
}
if raw {
published = append(published, fmt.Sprintf("%s %s", repo.Prefix, repo.Distribution))
} else {
published = append(published, repo.String())
}
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
sort.Strings(published)
if raw {
for _, info := range published {
fmt.Printf("%s\n", info)
}
} else {
if len(published) == 0 {
fmt.Printf("No snapshots/local repos have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
}
return err
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "list of published repositories",
Long: `
Display list of currently published snapshots.
Example:
$ aptly publish list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+45
View File
@@ -0,0 +1,45 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdPublishRepo() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "repo <name> [[<endpoint>:]<prefix>]",
Short: "publish local repository",
Long: `
Command publishes current state of local repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple local
repositories as the arguments:
aptly publish repo -component=main,contrib repo-main repo-contrib
It is not recommended to publish local repositories directly unless the
repository is for testing purposes and changes happen frequently. For
production usage please take snapshot of repository and publish it
using publish snapshot command.
Example:
$ aptly publish repo testing
`,
Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.String("origin", "", "origin name to publish")
cmd.Flag.String("label", "", "label to publish")
return cmd
}
+201
View File
@@ -0,0 +1,201 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
var err error
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
if len(args) < len(components) || len(args) > len(components)+1 {
cmd.Usage()
return commander.ErrCommandError
}
var param string
if len(args) == len(components)+1 {
param = args[len(components)]
args = args[0 : len(args)-1]
} else {
param = ""
}
storage, prefix := parsePrefix(param)
var (
sources = []interface{}{}
message string
)
if cmd.Name() == "snapshot" {
var (
snapshot *deb.Snapshot
emptyWarning = false
parts = []string{}
)
for _, name := range args {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, snapshot)
parts = append(parts, snapshot.Name)
if snapshot.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Snapshot %s has", parts[0])
} else {
message = fmt.Sprintf("Snapshots %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else if cmd.Name() == "repo" {
var (
localRepo *deb.LocalRepo
emptyWarning = false
parts = []string{}
)
for _, name := range args {
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, localRepo)
parts = append(parts, localRepo.Name)
if localRepo.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Local repo %s has", parts[0])
} else {
message = fmt.Sprintf("Local repos %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else {
panic("unknown command")
}
distribution := context.flags.Lookup("distribution").Value.String()
published, err := deb.NewPublishedRepo(storage, prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
published.Origin = cmd.Flag.Lookup("origin").Value.String()
published.Label = cmd.Flag.Lookup("label").Value.String()
duplicate := context.CollectionFactory().PublishedRepoCollection().CheckDuplicate(published)
if duplicate != nil {
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(context.flags)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
var repoComponents string
prefix, repoComponents, distribution = published.Prefix, strings.Join(published.Components(), " "), published.Distribution
if prefix == "." {
prefix = ""
} else if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
context.Progress().Printf("\n%s been successfully published.\n", message)
if localStorage, ok := context.GetPublishedStorage(storage).(aptly.LocalPublishedStorage); ok {
context.Progress().Printf("Please setup your webserver to serve directory '%s' with autoindexing.\n",
localStorage.PublicPath())
}
context.Progress().Printf("Now you can add following line to apt sources:\n")
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
if utils.StrSliceHasItem(published.Architectures, "source") {
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
}
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "snapshot <name> [[<endpoint>:]<prefix>]",
Short: "publish snapshot",
Long: `
Command publishes snapshot as Debian repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple snapshots
as the arguments:
aptly publish snapshot -component=main,contrib snap-main snap-contrib
Example:
$ aptly publish snapshot wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.String("origin", "", "origin name to publish")
cmd.Flag.String("label", "", "label to publish")
return cmd
}
+132
View File
@@ -0,0 +1,132 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
var err error
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
if len(args) < len(components)+1 || len(args) > len(components)+2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
var (
names []string
snapshot *deb.Snapshot
)
if len(args) == len(components)+2 {
param = args[1]
names = args[2:]
} else {
names = args[1:]
}
storage, prefix := parsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "snapshot" {
return fmt.Errorf("unable to update: not a snapshot publish")
}
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
publishedComponents := published.Components()
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
components = publishedComponents
}
if len(names) != len(components) {
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
}
for i, component := range components {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
published.UpdateSnapshot(component, snapshot)
}
signer, err := getSigner(context.flags)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Update(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nPublish for snapshot %s has been successfully switched to new snapshot.\n", published.String())
return err
}
func makeCmdPublishSwitch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSwitch,
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
Short: "update published repository by switching to new snapshot",
Long: `
Command switches in-place published repository with new snapshot contents. All
publishing parameters are preserved (architecture list, distribution,
component).
For multiple component repositories, flag -component should be given with
list of components to update. Corresponding snapshots should be given in the
same order, e.g.:
aptly publish update -component=main,contrib wheezy wh-main wh-contrib
Example:
$ aptly publish update wheezy ppa wheezy-7.5
`,
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.String("component", "", "component names to update (for multi-component publishing, separate components with commas)")
return cmd
}
+98
View File
@@ -0,0 +1,98 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
if len(args) == 2 {
param = args[1]
}
storage, prefix := parsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "local" {
return fmt.Errorf("unable to update: not a local repository publish")
}
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
components := published.Components()
for _, component := range components {
published.UpdateLocalRepo(component)
}
signer, err := getSigner(context.flags)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Update(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nPublish for local repo %s has been successfully updated.\n", published.String())
return err
}
func makeCmdPublishUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishUpdate,
UsageLine: "update <distribution> [[<endpoint>:]<prefix>]",
Short: "update published local repository",
Long: `
Command re-publishes (updates) published local repository. <distribution>
and <prefix> should be occupied with local repository published
using command aptly publish repo. Update happens in-place with
minimum possible downtime for published repository.
For multiple component published repositories, all local repositories
are updated.
Example:
$ aptly publish update wheezy ppa
`,
Flag: *flag.NewFlagSet("aptly-publish-update", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
return cmd
}
+25
View File
@@ -0,0 +1,25 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdRepo() *commander.Command {
return &commander.Command{
UsageLine: "repo",
Short: "manage local package repositories",
Subcommands: []*commander.Command{
makeCmdRepoAdd(),
makeCmdRepoCopy(),
makeCmdRepoCreate(),
makeCmdRepoDrop(),
makeCmdRepoEdit(),
makeCmdRepoImport(),
makeCmdRepoList(),
makeCmdRepoMove(),
makeCmdRepoRemove(),
makeCmdRepoShow(),
makeCmdRepoRename(),
},
}
}
+221
View File
@@ -0,0 +1,221 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"path/filepath"
"sort"
"strings"
)
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
verifier := &utils.GpgVerifier{}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to add: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to add: %s", err)
}
context.Progress().Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
packageFiles := []string{}
failedFiles := []string{}
for _, location := range args[1:] {
info, err2 := os.Stat(location)
if err2 != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2)
failedFiles = append(failedFiles, location)
continue
}
if info.IsDir() {
err2 = filepath.Walk(location, func(path string, info os.FileInfo, err3 error) error {
if err3 != nil {
return err3
}
if info.IsDir() {
return nil
}
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, path)
}
return nil
})
} else {
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, location)
} else {
context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
failedFiles = append(failedFiles, location)
continue
}
}
}
processedFiles := []string{}
sort.Strings(packageFiles)
for _, file := range packageFiles {
var (
stanza deb.Stanza
p *deb.Package
)
candidateProcessedFiles := []string{}
isSourcePackage := strings.HasSuffix(file, ".dsc")
if isSourcePackage {
stanza, err = deb.GetControlFileFromDsc(file, verifier)
if err == nil {
stanza["Package"] = stanza["Source"]
delete(stanza, "Source")
p, err = deb.NewSourcePackageFromControlFile(stanza)
}
} else {
stanza, err = deb.GetControlFileFromDeb(file)
p = deb.NewPackageFromControlFile(stanza)
}
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
failedFiles = append(failedFiles, file)
continue
}
var checksums utils.ChecksumInfo
checksums, err = utils.ChecksumsForFile(file)
if err != nil {
return err
}
if isSourcePackage {
p.UpdateFiles(append(p.Files(), deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}))
} else {
p.UpdateFiles([]deb.PackageFile{deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}})
}
err = context.PackagePool().Import(file, checksums.MD5)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err)
failedFiles = append(failedFiles, file)
continue
}
candidateProcessedFiles = append(candidateProcessedFiles, file)
// go over all files, except for the last one (.dsc/.deb itself)
for _, f := range p.Files() {
if filepath.Base(f.Filename) == filepath.Base(file) {
continue
}
sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(f.Filename))
err = context.PackagePool().Import(sourceFile, f.Checksums.MD5)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err)
failedFiles = append(failedFiles, file)
break
}
candidateProcessedFiles = append(candidateProcessedFiles, sourceFile)
}
if err != nil {
// some files haven't been imported
continue
}
err = context.CollectionFactory().PackageCollection().Update(p)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err)
failedFiles = append(failedFiles, file)
continue
}
err = list.Add(p)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
failedFiles = append(failedFiles, file)
continue
}
context.Progress().ColoredPrintf("@g[+]@| %s added@|", p)
processedFiles = append(processedFiles, candidateProcessedFiles...)
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
if context.flags.Lookup("remove-files").Value.Get().(bool) {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
err := os.Remove(file)
if err != nil {
return fmt.Errorf("unable to remove file: %s", err)
}
}
}
if len(failedFiles) > 0 {
context.Progress().ColoredPrintf("@y[!]@| @!Some files were skipped due to errors:@|")
for _, file := range failedFiles {
context.Progress().ColoredPrintf(" %s", file)
}
return fmt.Errorf("some files failed to be added")
}
return err
}
func makeCmdRepoAdd() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoAdd,
UsageLine: "add <name> <package file.deb>|<directory> ...",
Short: "add packages to local repository",
Long: `
Command adds packages to local repository from .deb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.deb or *.dsc
patterns. Every file discovered would be analyzed to extract metadata, package would then be created and added
to the database. Files would be imported to internal package pool. For source packages, all required files are
added automatically as well. Extra files for source package should be in the same directory as *.dsc file.
Example:
$ aptly repo add testing myapp-0.1.2.deb incoming/
`,
Flag: *flag.NewFlagSet("aptly-repo-add", flag.ExitOnError),
}
cmd.Flag.Bool("remove-files", false, "remove files that have been imported successfully into repository")
return cmd
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoCopy() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "copy <src-name> <dst-name> <package-query> ...",
Short: "copy packages between local repositories",
Long: `
Command copy copies packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
$ aptly repo copy testing stable 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-copy", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't copy, just show what would be copied")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo := deb.NewLocalRepo(args[0], context.flags.Lookup("comment").Value.String())
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
if err != nil {
return fmt.Errorf("unable to add local repo: %s", err)
}
fmt.Printf("\nLocal repo %s successfully added.\nYou can run 'aptly repo add %s ...' to add packages to repository.\n", repo, repo.Name)
return err
}
func makeCmdRepoCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoCreate,
UsageLine: "create <name>",
Short: "create local repository",
Long: `
Create local package repository. Repository would be empty when
created, packages could be added from files, copied or moved from
another local repository or imported from the mirror.
Example:
$ aptly repo create testing
`,
Flag: *flag.NewFlagSet("aptly-repo-create", flag.ExitOnError),
}
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "main", "default component when publishing")
return cmd
}
+80
View File
@@ -0,0 +1,80 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
published := context.CollectionFactory().PublishedRepoCollection().ByLocalRepo(repo)
if len(published) > 0 {
fmt.Printf("Local repo `%s` is published currently:\n", repo.Name)
for _, repo := range published {
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: local repo is published")
}
force := context.flags.Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Local repo `%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 local repo with snapshots, use -force to override")
}
}
err = context.CollectionFactory().LocalRepoCollection().Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Local repo `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdRepoDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoDrop,
UsageLine: "drop <name>",
Short: "delete local repository",
Long: `
Drop information about deletions from local repo. Package data is not deleted
(since it could be still used by other mirrors or snapshots).
Example:
$ aptly repo drop local-repo
`,
Flag: *flag.NewFlagSet("aptly-repo-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force local repo deletion even if used by snapshots")
return cmd
}
+68
View File
@@ -0,0 +1,68 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
if context.flags.Lookup("comment").Value.String() != "" {
repo.Comment = context.flags.Lookup("comment").Value.String()
}
if context.flags.Lookup("distribution").Value.String() != "" {
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
}
if context.flags.Lookup("component").Value.String() != "" {
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
}
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
fmt.Printf("Local repo %s successfully updated.\n", repo)
return err
}
func makeCmdRepoEdit() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoEdit,
UsageLine: "edit <name>",
Short: "edit properties of local repository",
Long: `
Command edit allows to change metadata of local repository:
comment, default distribution and component.
Example:
$ aptly repo edit -distribution=wheezy testing
`,
Flag: *flag.NewFlagSet("aptly-repo-edit", flag.ExitOnError),
}
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "", "default component when publishing")
return cmd
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoImport() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "import <src-mirror> <dst-repo> <package-query> ...",
Short: "import packages from mirror to local repository",
Long: `
Command import looks up packages matching <package-query> in mirror <src-mirror>
and copies them to local repo <dst-repo>.
Example:
$ aptly repo import wheezy-main testing nginx
`,
Flag: *flag.NewFlagSet("aptly-repo-import", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't import, just show what would be imported")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+75
View File
@@ -0,0 +1,75 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyRepoList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
repos := make([]string, context.CollectionFactory().LocalRepoCollection().Len())
i := 0
context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
if raw {
repos[i] = repo.Name
} else {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
}
i++
return nil
})
sort.Strings(repos)
if raw {
for _, repo := range repos {
fmt.Printf("%s\n", repo)
}
} else {
if len(repos) > 0 {
fmt.Printf("List of local repos:\n")
for _, repo := range repos {
fmt.Println(repo)
}
fmt.Printf("\nTo get more information about local repository, run `aptly repo show <name>`.\n")
} else {
fmt.Printf("No local repositories found, create one with `aptly repo create ...`.\n")
}
}
return err
}
func makeCmdRepoList() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoList,
UsageLine: "list",
Short: "list local repositories",
Long: `
List command shows full list of local package repositories.
Example:
$ aptly repo list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+191
View File
@@ -0,0 +1,191 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
)
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return commander.ErrCommandError
}
command := cmd.Name()
dstRepo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(dstRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
var (
srcRefList *deb.PackageRefList
srcRepo *deb.LocalRepo
)
if command == "copy" || command == "move" {
srcRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if srcRepo.UUID == dstRepo.UUID {
return fmt.Errorf("unable to %s: source and destination are the same", command)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(srcRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
srcRefList = srcRepo.RefList()
} else if command == "import" {
var srcRemoteRepo *deb.RemoteRepo
srcRemoteRepo, err = context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(srcRemoteRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if srcRemoteRepo.RefList() == nil {
return fmt.Errorf("unable to %s: mirror not updated", command)
}
srcRefList = srcRemoteRepo.RefList()
} else {
panic("unexpected command")
}
context.Progress().Printf("Loading packages...\n")
dstList, err := deb.NewPackageListFromRefList(dstRepo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
srcList, err := deb.NewPackageListFromRefList(srcRefList, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
srcList.PrepareIndex()
var architecturesList []string
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
if withDeps {
dstList.PrepareIndex()
// Calculate architectures
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = dstList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
}
queries := make([]deb.PackageQuery, len(args)-2)
for i := 0; i < len(args)-2; i++ {
queries[i], err = query.Parse(args[i+2])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
}
toProcess, err := srcList.Filter(queries, withDeps, dstList, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
var verb string
if command == "move" {
verb = "moved"
} else if command == "copy" {
verb = "copied"
} else if command == "import" {
verb = "imported"
}
err = toProcess.ForEach(func(p *deb.Package) error {
err = dstList.Add(p)
if err != nil {
return err
}
if command == "move" {
srcList.Remove(p)
}
context.Progress().ColoredPrintf("@g[o]@| %s %s", p, verb)
return nil
})
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if context.flags.Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
dstRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(dstList))
err = context.CollectionFactory().LocalRepoCollection().Update(dstRepo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
if command == "move" {
srcRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(srcList))
err = context.CollectionFactory().LocalRepoCollection().Update(srcRepo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
}
}
return err
}
func makeCmdRepoMove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "move <src-name> <dst-name> <package-query> ...",
Short: "move packages between local repositories",
Long: `
Command move moves packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
$ aptly repo move testing stable 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-move", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't move, just show what would be moved")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+92
View File
@@ -0,0 +1,92 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoRemove(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
context.Progress().Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
queries := make([]deb.PackageQuery, len(args)-1)
for i := 0; i < len(args)-1; i++ {
queries[i], err = query.Parse(args[i+1])
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
}
list.PrepareIndex()
toRemove, err := list.Filter(queries, false, nil, 0, nil)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
toRemove.ForEach(func(p *deb.Package) error {
list.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
return nil
})
if context.flags.Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
}
return err
}
func makeCmdRepoRemove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRemove,
UsageLine: "remove <name> <package-query> ...",
Short: "remove packages from local repository",
Long: `
Commands removes packages matching <package-query> from local repository
<name>. If removed packages are not referenced by other repos or
snapshots, they can be removed completely (including files) by running
'aptly db cleanup'.
Example:
$ aptly repo remove testing 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-add", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't remove, just show what would be removed")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyRepoRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.LocalRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().LocalRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: local repo %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nLocal repo %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdRepoRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames local repository",
Long: `
Command changes name of the local repo. Local repo name should be unique.
Example:
$ aptly repo rename wheezy-min wheezy-main
`,
}
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Comment: %s\n", repo.Comment)
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(repo.RefList())
}
return err
}
func makeCmdRepoShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoShow,
UsageLine: "show <name>",
Short: "show details about local repository",
Long: `
Show command shows full information about local package repository.
ex:
$ aptly repo show testing
`,
Flag: *flag.NewFlagSet("aptly-repo-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
+118
View File
@@ -0,0 +1,118 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"net"
"net/http"
"os"
"sort"
"strings"
)
func aptlyServe(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := context.flags.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, context.CollectionFactory().PublishedRepoCollection().Len())
published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
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, strings.Join(repo.Components(), " "))
if utils.StrSliceHasItem(repo.Architectures, "source") {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
}
}
publicPath := context.GetPublishedStorage("").(aptly.LocalPublishedStorage).PublicPath()
ShutdownContext()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
err = http.ListenAndServe(listen, http.FileServer(http.Dir(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: "HTTP 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.
Example:
$ aptly serve -listen=:8080
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
}
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
return cmd
}
+23
View File
@@ -0,0 +1,23 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
makeCmdSnapshotRename(),
},
}
}
+103
View File
@@ -0,0 +1,103 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var (
err error
snapshot *deb.Snapshot
)
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
// aptly snapshot create snap from mirror mirror
var repo *deb.RemoteRepo
repoName, snapshotName := args[3], args[0]
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = deb.NewSnapshotFromRepository(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 4 && args[1] == "from" && args[2] == "repo" {
// aptly snapshot create snap from repo repo
var repo *deb.LocalRepo
localRepoName, snapshotName := args[3], args[0]
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(localRepoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = deb.NewSnapshotFromLocalRepo(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := deb.NewPackageList()
snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return commander.ErrCommandError
}
err = context.CollectionFactory().SnapshotCollection().Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create <name> from mirror <mirror-name> | from repo <repo-name> | empty",
Short: "creates snapshot of mirror (local repository) contents",
Long: `
Command create <name> from mirror makes persistent immutable snapshot of remote
repository mirror. Snapshot could be published or further modified using
merge, pull and other aptly features.
Command create <name> from repo makes persistent immutable snapshot of local
repository. Snapshot could be processed as mirror snapshots, and mixed with
snapshots of remote mirrors.
Command create <name> empty creates empty snapshot that could be used as a
basis for snapshot pull operations, for example. As snapshots are immutable,
creating one empty snapshot should be enough.
Example:
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`,
}
return cmd
}
+111
View File
@@ -0,0 +1,111 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool)
// Load <name-a> snapshot
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := context.CollectionFactory().SnapshotCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = context.CollectionFactory().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(), context.CollectionFactory().PackageCollection())
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
context.Progress().Printf("Snapshots are identical.\n")
} else {
context.Progress().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!@|"
}
}
context.Progress().ColoredPrintf(code+" %-6s | %-40s | %-40s | %-40s", arch, pkg, verA, verB)
}
}
return err
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "difference between two snapshots",
Long: `
Displays difference in packages between two snapshots. Snapshot is a list
of packages, so difference between snapshots is a difference between package
lists. Package could be either completely missing in one snapshot, or package
is present in both snapshots with different versions.
Example:
$ 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
}
+80
View File
@@ -0,0 +1,80 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
published := context.CollectionFactory().PublishedRepoCollection().BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
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 := context.flags.Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().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 = context.CollectionFactory().SnapshotCollection().Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about a snapshot. If snapshot is published,
it can't be dropped.
Example:
$ 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
}
+116
View File
@@ -0,0 +1,116 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
// Snapshot sorting methods
const (
SortName = iota
SortTime
)
type snapshotListToSort struct {
list []*deb.Snapshot
sortMethod int
}
func parseSortMethod(sortMethod string) (int, error) {
switch sortMethod {
case "time", "Time":
return SortTime, nil
case "name", "Name":
return SortName, nil
}
return -1, fmt.Errorf("sorting method \"%s\" unknown", sortMethod)
}
func (s snapshotListToSort) Swap(i, j int) {
s.list[i], s.list[j] = s.list[j], s.list[i]
}
func (s snapshotListToSort) Less(i, j int) bool {
switch s.sortMethod {
case SortName:
return s.list[i].Name < s.list[j].Name
case SortTime:
return s.list[i].CreatedAt.Before(s.list[j].CreatedAt)
}
panic("unknown sort method")
}
func (s snapshotListToSort) Len() int {
return len(s.list)
}
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
snapshotsToSort := &snapshotListToSort{}
snapshotsToSort.list = make([]*deb.Snapshot, context.CollectionFactory().SnapshotCollection().Len())
snapshotsToSort.sortMethod, err = parseSortMethod(sortMethodString)
if err != nil {
return err
}
i := 0
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
snapshotsToSort.list[i] = snapshot
i++
return nil
})
sort.Sort(snapshotsToSort)
if raw {
for _, snapshot := range snapshotsToSort.list {
fmt.Printf("%s\n", snapshot.Name)
}
} else {
if len(snapshotsToSort.list) > 0 {
fmt.Printf("List of snapshots:\n")
for _, snapshot := range snapshotsToSort.list {
fmt.Printf(" * %s\n", snapshot.String())
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
}
return err
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "list snapshots",
Long: `
Command list shows full list of snapshots created.
Example:
$ aptly snapshot list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
cmd.Flag.String("sort", "name", "display list in 'name' or creation 'time' order")
return cmd
}
+90
View File
@@ -0,0 +1,90 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"strings"
)
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
sources := make([]*deb.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = context.CollectionFactory().SnapshotCollection().ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
latest := context.flags.Lookup("latest").Value.Get().(bool)
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
if noRemove && latest {
return fmt.Errorf("-no-remove and -latest can't be specified together")
}
overrideMatching := !latest && !noRemove
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList(), overrideMatching)
}
if latest {
deb.FilterLatestRefs(result)
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := deb.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = context.CollectionFactory().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 makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots",
Long: `
Merge command merges several <source> snapshots into one <destination> snapshot.
Merge happens from left to right. By default, 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>.
Example:
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
}
cmd.Flag.Bool("latest", false, "use only the latest version of each package")
cmd.Flag.Bool("no-remove", false, "don't remove duplicate arch/name packages")
return cmd
}
+174
View File
@@ -0,0 +1,174 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
"strings"
)
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return commander.ErrCommandError
}
noDeps := context.flags.Lookup("no-deps").Value.Get().(bool)
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
allMatches := context.flags.Lookup("all-matches").Value.Get().(bool)
// Load <name> snapshot
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := context.CollectionFactory().SnapshotCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
context.Progress().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
context.Progress().Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := deb.NewPackageListFromRefList(snapshot.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := deb.NewPackageListFromRefList(source.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
context.Progress().Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
var archQuery deb.PackageQuery = &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: ""}
for _, arch := range architecturesList {
archQuery = &deb.OrQuery{L: &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: arch}, R: archQuery}
}
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-3)
for i, arg := range args[3:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
// Add architecture filter
queries[i] = &deb.AndQuery{queries[i], archQuery}
}
// Filter with dependencies as requested
result, err := sourcePackageList.Filter(queries, !noDeps, packageList, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
result.PrepareIndex()
alreadySeen := map[string]bool{}
result.ForEachIndexed(func(pkg *deb.Package) error {
key := pkg.Architecture + "_" + pkg.Name
_, seen := alreadySeen[key]
// If we haven't seen such name-architecture pair and were instructed to remove, remove it
if !noRemove && !seen {
// Remove all packages with the same name and architecture
pS := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}, true)
for _, p := range pS {
packageList.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
}
}
// If !allMatches, add only first matching name-arch package
if !seen || allMatches {
packageList.Add(pkg)
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
}
alreadySeen[key] = true
return nil
})
alreadySeen = nil
if context.flags.Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := deb.NewSnapshotFromPackageList(args[2], []*deb.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 = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
context.Progress().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 makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-query> ...",
Short: "pull packages from another snapshot",
Long: `
Command pull pulls new packages along with its' dependencies to snapshot <name>
from snapshot <source>. Pull can upgrade package version in <name> with
versions from <source> following dependencies. New snapshot <destination>
is created as a result of this process. Packages could be specified simply
as 'package-name' or as package queries.
Example:
$ 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")
cmd.Flag.Bool("no-remove", false, "don't remove other package versions when pulling package")
cmd.Flag.Bool("all-matches", false, "pull all the packages that satisfy the dependency version requirements")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotRename(cmd *commander.Command, args []string) error {
var (
err error
snapshot *deb.Snapshot
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().SnapshotCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: snapshot %s already exists", newName)
}
snapshot.Name = newName
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nSnapshot %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdSnapshotRename() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames snapshot",
Long: `
Command changes name of the snapshot. Snapshot name should be unique.
Example:
$ aptly snapshot rename wheezy-min wheezy-main
`,
}
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
return err
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show <name>",
Short: "shows details about snapshot",
Long: `
Command show displays full information about a snapshot.
Example:
$ aptly snapshot show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
+115
View File
@@ -0,0 +1,115 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return commander.ErrCommandError
}
snapshots := make([]*deb.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = context.CollectionFactory().SnapshotCollection().ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
context.Progress().Printf("Loading packages...\n")
packageList, err := deb.NewPackageListFromRefList(snapshots[0].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := deb.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
var pL *deb.PackageList
for i := 1; i < len(snapshots); i++ {
pL, err = deb.NewPackageListFromRefList(snapshots[i].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(true)
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
context.Progress().Printf("Verifying...\n")
missing, err := packageList.VerifyDependencies(context.DependencyOptions(), architecturesList, sourcePackageList, context.Progress())
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
context.Progress().Printf("All dependencies are satisfied.\n")
} else {
context.Progress().Printf("Missing dependencies (%d):\n", len(missing))
deps := make([]string, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
context.Progress().Printf(" %s\n", dep)
}
}
return err
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verify dependencies in snapshot",
Long: `
Verify does dependency resolution in snapshot <name>, possibly using additional
snapshots <source> as dependency sources. All unsatisfied dependencies are
printed.
Example:
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
}
return cmd
}
+9 -5
View File
@@ -1,13 +1,18 @@
package main
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/aptly"
"github.com/smira/commander"
)
func aptlyVersion(cmd *commander.Command, args []string) error {
fmt.Printf("aptly version: %s\n", Version)
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("aptly version: %s\n", aptly.Version)
return nil
}
@@ -22,6 +27,5 @@ Shows aptly version.
ex:
$ aptly version
`,
Flag: *flag.NewFlagSet("aptly-version", flag.ExitOnError),
}
}
-228
View File
@@ -1,228 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"strings"
)
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
fmt.Printf("List of mirrors:\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
fmt.Printf(" * %s\n", repo)
return nil
})
fmt.Printf("\nTo get more information about repository, run `aptly mirror show <name>`.\n")
return err
}
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
}
var architectures []string
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 {
return fmt.Errorf("unable to create mirror: %s", err)
}
err = repo.Fetch(context.downloader)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for name, value := range repo.Meta {
fmt.Printf("%s: %s\n", name, value)
}
return err
}
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repo.Fetch(context.downloader)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = repo.Download(context.downloader, packageCollection, context.packageRepository)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
fmt.Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create",
Short: "create new mirror of Debian repository",
Long: `
create only stores metadata about new mirror, and fetches Release files (it doesn't download packages)
ex:
$ aptly mirror create <name> <archive url> <distribution> [<component1> ...]
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.String("architecture", "", "limit architectures to download, comma-delimited list")
return cmd
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors of remote repositories",
Long: `
list shows full list of remote repositories.
ex:
$ aptly mirror list
`,
Flag: *flag.NewFlagSet("aptly-mirror-list", flag.ExitOnError),
}
cmd.Flag.Bool("v", false, "enable verbose output")
return cmd
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show",
Short: "show details about remote repository mirror",
Long: `
show shows full information about mirror.
ex:
$ aptly mirror show <name>
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
return cmd
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update",
Short: "update packages from remote mirror",
Long: `
Update downloads list of packages and packages themselves.
ex:
$ aptly mirror update <name>
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
return cmd
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
//makeCmdMirrorDestroy(),
makeCmdMirrorUpdate(),
},
Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError),
}
}
-125
View File
@@ -1,125 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"strings"
)
func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
} else {
prefix = ""
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
var sourceRepo *debian.RemoteRepo
if snapshot.SourceKind == "repo" && len(snapshot.SourceIDs) == 1 {
repoCollection := debian.NewRemoteRepoCollection(context.database)
sourceRepo, _ = repoCollection.ByUUID(snapshot.SourceIDs[0])
}
component := cmd.Flag.Lookup("component").Value.String()
if component == "" {
if sourceRepo != nil && len(sourceRepo.Components) == 1 {
component = sourceRepo.Components[0]
} else {
component = "main"
}
}
distribution := cmd.Flag.Lookup("distribution").Value.String()
if distribution == "" {
if sourceRepo != nil {
distribution = sourceRepo.Distribution
} else {
return fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
var architecturesList []string
architectures := cmd.Flag.Lookup("architectures").Value.String()
if architectures != "" {
architecturesList = strings.Split(architectures, ",")
}
signer := &utils.GpgSigner{}
signer.SetKey(cmd.Flag.Lookup("gpg-key").Value.String())
published := debian.NewPublishedRepo(prefix, distribution, component, architecturesList, snapshot)
packageCollection := debian.NewPackageCollection(context.database)
err = published.Publish(context.packageRepository, packageCollection, signer)
if err != nil {
return err
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
fmt.Printf("\nSnapshot %s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
snapshot.Name, context.packageRepository.PublicPath())
fmt.Printf("Now you can add following line to apt sources:\n")
fmt.Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component)
fmt.Printf("Don't forget to add your GPG key to apt with apt-key.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshot,
UsageLine: "snapshot",
Short: "makes Debian repository out of snapshot",
Long: `
Publishes snapshot as Debian repository ready to be used by apt tools.
ex:
$ aptly publish snapshot <name> [<prefix>]
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("architectures", "", "list of architectures to publish (comma-separated)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
return cmd
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishSnapshot(),
},
Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError),
}
}
-174
View File
@@ -1,174 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 || args[1] != "from" || args[2] != "mirror" {
cmd.Usage()
return err
}
repoName, mirrorName := args[3], args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err := debian.NewSnapshotFromRepository(mirrorName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
fmt.Printf("List of snapshots:\n")
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
fmt.Printf(" * %s\n", snapshot)
return nil
})
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
return err
}
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database)
err = snapshot.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 err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create",
Short: "creates snapshot out of any mirror",
Long: `
Create makes persistent immutable snapshot of repository mirror state in givent moment of time.
ex:
$ aptly snapshot create <name> from mirror <mirror-name>
`,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "lists snapshots",
Long: `
list shows full list of snapshots created.
ex:
$ aptly snapshot list
`,
Flag: *flag.NewFlagSet("aptly-snapshot-list", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show",
Short: "shows details about snapshot",
Long: `
shows shows full information about snapshot.
ex:
$ aptly snapshot show <name>
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
//makeCmdSnapshotDestroy(),
},
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
}
}
+180
View File
@@ -0,0 +1,180 @@
package console
import (
"fmt"
"github.com/cheggaaa/pb"
"github.com/smira/aptly/aptly"
"github.com/wsxiaoys/terminal/color"
"strings"
)
const (
codePrint = iota
codeProgress
codeHideProgress
codeStop
codeFlush
)
type printTask struct {
code int
message string
reply chan bool
}
// Progress is a progress displaying subroutine, it allows to show download and other operations progress
// mixed with progress bar
type Progress struct {
stop chan bool
stopped chan bool
queue chan printTask
bar *pb.ProgressBar
barShown bool
}
// Check interface
var (
_ aptly.Progress = (*Progress)(nil)
)
// NewProgress creates new progress instance
func NewProgress() *Progress {
return &Progress{
stopped: make(chan bool),
queue: make(chan printTask, 100),
}
}
// Start makes progress start its work
func (p *Progress) Start() {
go p.worker()
}
// Shutdown shuts down progress display
func (p *Progress) Shutdown() {
p.ShutdownBar()
p.queue <- printTask{code: codeStop}
<-p.stopped
}
// Flush waits for all queued messages to be displayed
func (p *Progress) Flush() {
ch := make(chan bool)
p.queue <- printTask{code: codeFlush, reply: ch}
<-ch
}
// InitBar starts progressbar for count bytes or count items
func (p *Progress) InitBar(count int64, isBytes bool) {
if p.bar != nil {
panic("bar already initialized")
}
if RunningOnTerminal() {
p.bar = pb.New(0)
p.bar.Total = count
p.bar.NotPrint = true
p.bar.Callback = func(out string) {
p.queue <- printTask{code: codeProgress, message: out}
}
if isBytes {
p.bar.SetUnits(pb.U_BYTES)
p.bar.ShowSpeed = true
}
p.bar.Start()
}
}
// ShutdownBar stops progress bar and hides it
func (p *Progress) ShutdownBar() {
if p.bar == nil {
return
}
p.bar.Finish()
p.bar = nil
p.queue <- printTask{code: codeHideProgress}
}
// Write is implementation of io.Writer to support updating of progress bar
func (p *Progress) Write(s []byte) (int, error) {
if p.bar != nil {
p.bar.Add(len(s))
}
return len(s), nil
}
// AddBar increments progress for progress bar
func (p *Progress) AddBar(count int) {
if p.bar != nil {
p.bar.Add(count)
}
}
// SetBar sets current position for progress bar
func (p *Progress) SetBar(count int) {
if p.bar != nil {
p.bar.Set(count)
}
}
// Printf does printf but in safe manner: not overwriting progress bar
func (p *Progress) Printf(msg string, a ...interface{}) {
p.queue <- printTask{code: codePrint, message: fmt.Sprintf(msg, a...)}
}
// ColoredPrintf does printf in colored way + newline
func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
if RunningOnTerminal() {
p.queue <- printTask{code: codePrint, message: color.Sprintf(msg, a...) + "\n"}
} else {
// stip color marks
var prev rune
msg = strings.Map(func(r rune) rune {
if prev == '@' {
prev = 0
if r == '@' {
return r
}
return -1
}
prev = r
if r == '@' {
return -1
}
return r
}, msg)
p.Printf(msg+"\n", a...)
}
}
func (p *Progress) worker() {
for {
task := <-p.queue
switch task.code {
case codePrint:
if p.barShown {
fmt.Print("\r\033[2K")
p.barShown = false
}
fmt.Print(task.message)
case codeProgress:
if p.bar != nil {
fmt.Print("\r" + task.message)
p.barShown = true
}
case codeHideProgress:
if p.barShown {
fmt.Print("\r\033[2K")
p.barShown = false
}
case codeFlush:
task.reply <- true
case codeStop:
p.stopped <- true
return
}
}
}
+13
View File
@@ -0,0 +1,13 @@
// +build !freebsd
package console
import (
"code.google.com/p/go.crypto/ssh/terminal"
"syscall"
)
// RunningOnTerminal checks whether stdout is terminal
func RunningOnTerminal() bool {
return terminal.IsTerminal(syscall.Stdout)
}
+10
View File
@@ -0,0 +1,10 @@
// +build freebsd
package console
// RunningOnTerminal checks whether stdout is terminal
//
// Stub for FreeBSD, until in go1.3 terminal.IsTerminal would start working for FreeBSD
func RunningOnTerminal() bool {
return false
}
+105 -13
View File
@@ -7,6 +7,8 @@ import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
)
// Errors for Storage
@@ -18,12 +20,18 @@ var (
type Storage interface {
Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error
Delete(key []byte) error
KeysByPrefix(prefix []byte) [][]byte
FetchByPrefix(prefix []byte) [][]byte
Close() error
StartBatch()
FinishBatch() error
CompactDB() error
}
type levelDB struct {
db *leveldb.DB
db *leveldb.DB
batch *leveldb.Batch
}
// Check interface
@@ -44,6 +52,25 @@ func OpenDB(path string) (Storage, error) {
return &levelDB{db: db}, nil
}
// RecoverDB recovers LevelDB database from corruption
func RecoverDB(path string) error {
stor, err := storage.OpenFile(path)
if err != nil {
return err
}
db, err := leveldb.Recover(stor, nil)
if err != nil {
return err
}
db.Close()
stor.Close()
return nil
}
// Get key value from database
func (l *levelDB) Get(key []byte) ([]byte, error) {
value, err := l.db.Get(key, nil)
if err != nil {
@@ -56,29 +83,94 @@ func (l *levelDB) Get(key []byte) ([]byte, error) {
return value, nil
}
// Put saves key to database, if key has the same value in DB already, it is not saved
func (l *levelDB) Put(key []byte, value []byte) error {
if l.batch != nil {
l.batch.Put(key, value)
return nil
}
old, err := l.db.Get(key, nil)
if err != nil {
if err != leveldb.ErrNotFound {
return err
}
} else {
if bytes.Compare(old, value) == 0 {
return nil
}
}
return l.db.Put(key, value, nil)
}
func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
// Delete removes key from DB
func (l *levelDB) Delete(key []byte) error {
if l.batch != nil {
l.batch.Delete(key)
return nil
}
return l.db.Delete(key, nil)
}
// KeysByPrefix returns all keys that start with prefix
func (l *levelDB) KeysByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0, 20)
iterator := l.db.NewIterator(nil)
if iterator.Seek(prefix) {
for bytes.HasPrefix(iterator.Key(), prefix) {
val := iterator.Value()
valc := make([]byte, len(val))
copy(valc, val)
result = append(result, valc)
if !iterator.Next() {
break
}
}
iterator := l.db.NewIterator(nil, nil)
defer iterator.Release()
for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
key := iterator.Key()
keyc := make([]byte, len(key))
copy(keyc, key)
result = append(result, keyc)
}
return result
}
// FetchByPrefix returns all values with keys that start with prefix
func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0, 20)
iterator := l.db.NewIterator(nil, nil)
defer iterator.Release()
for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
val := iterator.Value()
valc := make([]byte, len(val))
copy(valc, val)
result = append(result, valc)
}
return result
}
// Close finishes DB work
func (l *levelDB) Close() error {
return l.db.Close()
}
// StartBatch starts batch processing of keys
//
// All subsequent Get, Put and Delete would work on batch
func (l *levelDB) StartBatch() {
if l.batch != nil {
panic("batch already started")
}
l.batch = new(leveldb.Batch)
}
// FinishBatch finalizes the batch, saving operations
func (l *levelDB) FinishBatch() error {
if l.batch == nil {
panic("no batch")
}
err := l.db.Write(l.batch, nil)
l.batch = nil
return err
}
// CompactDB compacts database by merging layers
func (l *levelDB) CompactDB() error {
return l.db.CompactRange(util.Range{})
}
+97 -3
View File
@@ -11,7 +11,8 @@ func Test(t *testing.T) {
}
type LevelDBSuite struct {
db Storage
path string
db Storage
}
var _ = Suite(&LevelDBSuite{})
@@ -19,7 +20,8 @@ var _ = Suite(&LevelDBSuite{})
func (s *LevelDBSuite) SetUpTest(c *C) {
var err error
s.db, err = OpenDB(c.MkDir())
s.path = c.MkDir()
s.db, err = OpenDB(s.path)
c.Assert(err, IsNil)
}
@@ -28,6 +30,29 @@ func (s *LevelDBSuite) TearDownTest(c *C) {
c.Assert(err, IsNil)
}
func (s *LevelDBSuite) TestRecoverDB(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Check(err, IsNil)
err = s.db.Close()
c.Check(err, IsNil)
err = RecoverDB(s.path)
c.Check(err, IsNil)
s.db, err = OpenDB(s.path)
c.Check(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
}
func (s *LevelDBSuite) TestGetPut(c *C) {
var (
key = []byte("key")
@@ -45,19 +70,88 @@ func (s *LevelDBSuite) TestGetPut(c *C) {
c.Assert(result, DeepEquals, value)
}
func (s *LevelDBSuite) TestFetchByPrefix(c *C) {
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) TestByPrefix(c *C) {
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{})
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
s.db.Put([]byte{0x80, 0x03}, []byte{0x03})
s.db.Put([]byte{0x80, 0x02}, []byte{0x02})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
s.db.Put([]byte{0x90, 0x01}, []byte{0x04})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
s.db.Put([]byte{0x00, 0x01}, []byte{0x05})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
c.Check(s.db.KeysByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
}
func (s *LevelDBSuite) TestBatch(c *C) {
var (
key = []byte("key")
key2 = []byte("key2")
value = []byte("value")
value2 = []byte("value2")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
s.db.StartBatch()
s.db.Put(key2, value2)
s.db.Delete(key)
v, err := s.db.Get(key)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value)
_, err = s.db.Get(key2)
c.Check(err, ErrorMatches, "key not found")
err = s.db.FinishBatch()
c.Check(err, IsNil)
v2, err := s.db.Get(key2)
c.Check(err, IsNil)
c.Check(v2, DeepEquals, value2)
_, err = s.db.Get(key)
c.Check(err, ErrorMatches, "key not found")
c.Check(func() { s.db.FinishBatch() }, Panics, "no batch")
s.db.StartBatch()
c.Check(func() { s.db.StartBatch() }, Panics, "batch already started")
}
func (s *LevelDBSuite) TestCompactDB(c *C) {
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
s.db.Put([]byte{0x80, 0x03}, []byte{0x03})
s.db.Put([]byte{0x80, 0x02}, []byte{0x02})
c.Check(s.db.CompactDB(), IsNil)
}
+65
View File
@@ -0,0 +1,65 @@
package deb
import (
"github.com/smira/aptly/database"
)
// CollectionFactory is a single place to generate all desired collections
type CollectionFactory struct {
db database.Storage
packages *PackageCollection
remoteRepos *RemoteRepoCollection
snapshots *SnapshotCollection
localRepos *LocalRepoCollection
publishedRepos *PublishedRepoCollection
}
// NewCollectionFactory creates new factory
func NewCollectionFactory(db database.Storage) *CollectionFactory {
return &CollectionFactory{db: db}
}
// PackageCollection returns (or creates) new PackageCollection
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
if factory.packages == nil {
factory.packages = NewPackageCollection(factory.db)
}
return factory.packages
}
// RemoteRepoCollection returns (or creates) new RemoteRepoCollection
func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
if factory.remoteRepos == nil {
factory.remoteRepos = NewRemoteRepoCollection(factory.db)
}
return factory.remoteRepos
}
// SnapshotCollection returns (or creates) new SnapshotCollection
func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
if factory.snapshots == nil {
factory.snapshots = NewSnapshotCollection(factory.db)
}
return factory.snapshots
}
// LocalRepoCollection returns (or creates) new LocalRepoCollection
func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
if factory.localRepos == nil {
factory.localRepos = NewLocalRepoCollection(factory.db)
}
return factory.localRepos
}
// PublishedRepoCollection returns (or creates) new PublishedRepoCollection
func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollection {
if factory.publishedRepos == nil {
factory.publishedRepos = NewPublishedRepoCollection(factory.db)
}
return factory.publishedRepos
}
+99
View File
@@ -0,0 +1,99 @@
package deb
import (
"archive/tar"
"bufio"
"compress/gzip"
"fmt"
"github.com/mkrautz/goar"
"github.com/smira/aptly/utils"
"io"
"os"
"strings"
)
// GetControlFileFromDeb reads control file from deb package
func GetControlFileFromDeb(packageFile string) (Stanza, error) {
file, err := os.Open(packageFile)
if err != nil {
return nil, err
}
defer file.Close()
library := ar.NewReader(file)
for {
header, err := library.Next()
if err == io.EOF {
return nil, fmt.Errorf("unable to find control.tar.gz part")
}
if err != nil {
return nil, fmt.Errorf("unable to read .deb archive: %s", err)
}
if header.Name == "control.tar.gz" {
ungzip, err := gzip.NewReader(library)
if err != nil {
return nil, fmt.Errorf("unable to ungzip: %s", err)
}
defer ungzip.Close()
untar := tar.NewReader(ungzip)
for {
tarHeader, err := untar.Next()
if err == io.EOF {
return nil, fmt.Errorf("unable to find control file")
}
if err != nil {
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
}
if tarHeader.Name == "./control" {
reader := NewControlFileReader(untar)
stanza, err := reader.ReadStanza()
if err != nil {
return nil, err
}
return stanza, nil
}
}
}
}
}
// GetControlFileFromDsc reads control file from dsc package
func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, error) {
file, err := os.Open(dscFile)
if err != nil {
return nil, err
}
defer file.Close()
line, err := bufio.NewReader(file).ReadString('\n')
if err != nil {
return nil, err
}
file.Seek(0, 0)
var text *os.File
if strings.Index(line, "BEGIN PGP SIGN") != -1 {
text, err = verifier.ExtractClearsigned(file)
if err != nil {
return nil, err
}
defer text.Close()
} else {
text = file
}
reader := NewControlFileReader(text)
stanza, err := reader.ReadStanza()
if err != nil {
return nil, err
}
return stanza, nil
}
+56
View File
@@ -0,0 +1,56 @@
package deb
import (
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"path/filepath"
"runtime"
)
type DebSuite struct {
debFile, dscFile, dscFileNoSign string
}
var _ = Suite(&DebSuite{})
func (s *DebSuite) SetUpSuite(c *C) {
_, _File, _, _ := runtime.Caller(0)
s.debFile = filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
s.dscFile = filepath.Join(filepath.Dir(_File), "../system/files/pyspi_0.6.1-1.3.dsc")
s.dscFileNoSign = filepath.Join(filepath.Dir(_File), "../system/files/pyspi-0.6.1-1.3.stripped.dsc")
}
func (s *DebSuite) TestGetControlFileFromDeb(c *C) {
_, err := GetControlFileFromDeb("/no/such/file")
c.Check(err, ErrorMatches, ".*no such file or directory")
_, _File, _, _ := runtime.Caller(0)
_, err = GetControlFileFromDeb(_File)
c.Check(err, ErrorMatches, "unable to read .deb archive: ar: missing global header")
st, err := GetControlFileFromDeb(s.debFile)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "1.49.0.1")
c.Check(st["Package"], Equals, "libboost-program-options-dev")
}
func (s *DebSuite) TestGetControlFileFromDsc(c *C) {
verifier := &utils.GpgVerifier{}
_, err := GetControlFileFromDsc("/no/such/file", verifier)
c.Check(err, ErrorMatches, ".*no such file or directory")
_, _File, _, _ := runtime.Caller(0)
_, err = GetControlFileFromDsc(_File, verifier)
c.Check(err, ErrorMatches, "malformed stanza syntax")
st, err := GetControlFileFromDsc(s.dscFile, verifier)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "0.6.1-1.3")
c.Check(st["Source"], Equals, "pyspi")
st, err = GetControlFileFromDsc(s.dscFileNoSign, verifier)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "0.6.1-1.4")
c.Check(st["Source"], Equals, "pyspi")
}
+2
View File
@@ -0,0 +1,2 @@
// Package deb implements Debian-specific repository handling
package deb
+11
View File
@@ -0,0 +1,11 @@
package deb
import (
. "launchpad.net/gocheck"
"testing"
)
// Launch gocheck tests
func Test(t *testing.T) {
TestingT(t)
}
+4 -2
View File
@@ -1,4 +1,4 @@
package debian
package deb
import (
"bufio"
@@ -11,7 +11,9 @@ import (
type Stanza map[string]string
// Canonical order of fields in stanza
var canocialOrder = []string{"Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer", "Architecture"}
var canocialOrder = []string{"Package", "Origin", "Label", "Suite", "Version", "Installed-Size", "Priority", "Section", "Maintainer",
"Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256",
"Archive", "Component"}
// Copy returns copy of Stanza
func (s Stanza) Copy() (result Stanza) {
+1 -1
View File
@@ -1,4 +1,4 @@
package debian
package deb
import (
"bufio"
+450
View File
@@ -0,0 +1,450 @@
package deb
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"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
// DepFollowBuild pulls build dependencies
DepFollowBuild
)
// PackageList is list of unique (by key) packages
//
// It could be seen as repo snapshot, repo contents, result of filtering,
// merge, etc.
//
// If indexed, PackageList starts supporting searching
type PackageList struct {
// Straight list of packages as map
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
func NewPackageList() *PackageList {
return &PackageList{packages: make(map[string]*Package, 1000)}
}
// NewPackageListFromRefList loads packages list from PackageRefList
func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageCollection, progress aptly.Progress) (*PackageList, error) {
// empty reflist
if reflist == nil {
return NewPackageList(), nil
}
result := &PackageList{packages: make(map[string]*Package, reflist.Len())}
if progress != nil {
progress.InitBar(int64(reflist.Len()), false)
}
err := reflist.ForEach(func(key []byte) error {
p, err2 := collection.ByKey(key)
if err2 != nil {
return fmt.Errorf("unable to load package with key %s: %s", key, err2)
}
if progress != nil {
progress.AddBar(1)
}
return result.Add(p)
})
if progress != nil {
progress.ShutdownBar()
}
if err != nil {
return nil, err
}
return result, nil
}
// Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error {
key := string(p.ShortKey(""))
existing, ok := l.packages[key]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s", p)
}
return nil
}
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.lessPackages(p, l.packagesIndex[j]) })
// 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
}
// ForEach calls handler for each package in list
func (l *PackageList) ForEach(handler func(*Package) error) error {
var err error
for _, p := range l.packages {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// ForEachIndexed calls handler for each package in list in indexed order
func (l *PackageList) ForEachIndexed(handler func(*Package) error) error {
if !l.indexed {
panic("list not indexed, can't iterate")
}
var err error
for _, p := range l.packagesIndex {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// Len returns number of packages in the list
func (l *PackageList) Len() int {
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", 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.ShortKey("")))
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 and flag if source packages are present.
//
// If includeSource is true, meta-architecture "source" would be present in the list
func (l *PackageList) Architectures(includeSource bool) (result []string) {
result = make([]string, 0, 10)
for _, pkg := range l.packages {
if pkg.Architecture != "all" && (pkg.Architecture != "source" || includeSource) && !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, progress aptly.Progress) ([]Dependency, error) {
missing := make([]Dependency, 0, 128)
if progress != nil {
progress.InitBar(int64(l.Len())*int64(len(architectures)), false)
}
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packages {
if progress != nil {
progress.AddBar(1)
}
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 {
if dep.Architecture == "" {
dep.Architecture = arch
}
hash := dep.Hash()
r, ok := cache[hash]
if ok {
if !r {
missingCount++
}
continue
}
if sources.Search(dep, false) == 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
}
}
}
}
}
}
if progress != nil {
progress.ShutdownBar()
}
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]
}
func (l *PackageList) lessPackages(iPkg, jPkg *Package) bool {
if iPkg.Name == jPkg.Name {
cmp := CompareVersions(iPkg.Version, jPkg.Version)
if cmp == 0 {
return iPkg.Architecture < jPkg.Architecture
}
return cmp == 1
}
return iPkg.Name < jPkg.Name
}
// Less compares two packages by name (lexographical) and version (latest to oldest)
func (l *PackageList) Less(i, j int) bool {
return l.lessPackages(l.packagesIndex[i], l.packagesIndex[j])
}
// 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
}
// Scan searches package index using full scan
func (l *PackageList) Scan(q PackageQuery) (result *PackageList) {
result = NewPackageList()
for _, pkg := range l.packages {
if q.Matches(pkg) {
result.Add(pkg)
}
}
return
}
// Search searches package index for specified package(s) using optimized queries
func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
if !l.indexed {
panic("list not indexed, can't search")
}
if dep.Relation == VersionDontCare {
for _, p := range l.providesIndex[dep.Pkg] {
if dep.Architecture == "" || p.MatchesArchitecture(dep.Architecture) {
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
}
}
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.MatchesDependency(dep) {
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
i++
}
return
}
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
if !l.indexed {
panic("list not indexed, can't filter")
}
result := NewPackageList()
for _, query := range queries {
result.Append(query.Query(l))
}
if withDependencies {
added := result.Len()
dependencySource := NewPackageList()
if source != nil {
dependencySource.Append(source)
}
dependencySource.Append(result)
dependencySource.PrepareIndex()
// while some new dependencies were discovered
for added > 0 {
added = 0
// find missing dependencies
missing, err := result.VerifyDependencies(dependencyOptions, architecturesList, dependencySource, nil)
if err != nil {
return nil, err
}
// try to satisfy dependencies
for _, dep := range missing {
searchResults := l.Search(dep, false)
if searchResults != nil {
for _, p := range searchResults {
result.Add(p)
dependencySource.Add(p)
added++
}
}
}
}
}
return result, nil
}
+425
View File
@@ -0,0 +1,425 @@
package deb
import (
"errors"
. "launchpad.net/gocheck"
"regexp"
"sort"
"strings"
)
type containsChecker struct {
*CheckerInfo
}
func (c *containsChecker) Check(params []interface{}, names []string) (result bool, error string) {
var (
pkgSlice1 []*Package
pkgSlice2 []*Package
ok bool
)
pkgMap := make(map[*Package]bool)
pkgSlice1, ok = params[0].([]*Package)
if !ok {
return false, "The first parameter is not a Package slice"
}
pkgSlice2, ok = params[1].([]*Package)
if !ok {
return false, "The second parameter is not a Package slice"
}
for _, pkg := range pkgSlice2 {
pkgMap[pkg] = true
}
for _, pkg := range pkgSlice1 {
if _, ok := pkgMap[pkg]; !ok {
return false, ""
}
}
return true, ""
}
var Contains = &containsChecker{&CheckerInfo{Name: "Contains", Params: []string{"Container", "Expected to contain"}}}
type PackageListSuite struct {
// Simple list with "real" packages from stanzas
list *PackageList
p1, p2, p3, p4, p5, p6 *Package
// Mocked packages in list
packages []*Package
packages2 []*Package
sourcePackages []*Package
il *PackageList
il2 *PackageList
}
var _ = Suite(&PackageListSuite{})
func (s *PackageListSuite) SetUpTest(c *C) {
s.list = NewPackageList()
s.p1 = NewPackageFromControlFile(packageStanza.Copy())
s.p2 = NewPackageFromControlFile(packageStanza.Copy())
stanza := packageStanza.Copy()
stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers"
s.p5 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Version"] = "99.1"
s.p6 = NewPackageFromControlFile(stanza)
s.il = NewPackageList()
s.packages = []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386", Source: "lib (0.9)", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}}},
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all", Source: "app", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
&Package{Name: "mailer", Version: "3.5.8", Architecture: "i386", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "arm", deps: &PackageDependencies{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", deps: &PackageDependencies{PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
&Package{Name: "aa", Version: "2.0-1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
&Package{Name: "libx", Version: "1.5", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "arm", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
&Package{Name: "dpkg", Version: "1.7", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
}
for _, p := range s.packages {
s.il.Add(p)
}
s.il.PrepareIndex()
s.il2 = NewPackageList()
s.packages2 = []*Package{
&Package{Name: "mailer", Version: "3.5.8", Architecture: "amd64", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
&Package{Name: "sendmail", Version: "1.0", Architecture: "amd64", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
&Package{Name: "app", Version: "1.1-bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
&Package{Name: "app", Version: "1.1-bp2", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
&Package{Name: "app", Version: "1.2", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9) | libx (>= 1.5)", "data (>= 1.0) | mail-agent"}}},
&Package{Name: "app", Version: "3.0", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
}
for _, p := range s.packages2 {
s.il2.Add(p)
}
s.il2.PrepareIndex()
s.sourcePackages = []*Package{
&Package{Name: "postfix", Version: "1.3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
&Package{Name: "aa", Version: "2.0-1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
&Package{Name: "lib", Version: "0.9", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
}
}
func (s *PackageListSuite) TestAddLen(c *C) {
c.Check(s.list.Len(), Equals, 0)
c.Check(s.list.Add(s.p1), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p2), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p3), IsNil)
c.Check(s.list.Len(), Equals, 2)
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", "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", "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", "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", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["package-installer"], DeepEquals, []*Package{s.packages[11]})
}
func (s *PackageListSuite) TestForeach(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
Len := 0
err := s.list.ForEach(func(*Package) error {
Len++
return nil
})
c.Check(Len, Equals, 2)
c.Check(err, IsNil)
e := errors.New("a")
err = s.list.ForEach(func(*Package) error {
return e
})
c.Check(err, Equals, e)
}
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, 16)
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) {
//allMatches = False
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}, false) }, Panics, "list not indexed, can't search")
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}, false), DeepEquals, []*Package{s.packages[4]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}, false), IsNil)
// search w/o version should return package with latest version
c.Check(s.il.Search(Dependency{Architecture: "source", Pkg: "dpkg"}, false), DeepEquals, []*Package{s.packages[13]})
// allMatches = True
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "mail-agent"}, true), Contains, []*Package{s.packages2[0], s.packages2[1]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "puppy"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "3"}, true), Contains, []*Package{s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLess, Version: "1.2"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLess, Version: "1.1~"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.2"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1-bp1"}, true), Contains, []*Package{s.packages2[2]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.0"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreater, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreater, Version: "5.0"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}, true), Contains, []*Package{s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "5.0"}, true), IsNil)
}
func (s *PackageListSuite) TestFilter(c *C) {
c.Check(func() { s.list.Filter([]PackageQuery{&PkgQuery{"abcd", "0.3", "i386"}}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
plString := func(l *PackageList) string {
list := make([]string, 0, l.Len())
for _, p := range l.packages {
list = append(list, p.String())
}
sort.Strings(list)
return strings.Join(list, " ")
}
result, err := s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
result, err = s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}, &PkgQuery{"dpkg", "1.7", "source"},
&PkgQuery{"dpkg", "1.8", "amd64"}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{
&DependencyQuery{Dep: Dependency{Pkg: "app"}},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}},
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}},
&DependencyQuery{Dep: Dependency{Pkg: "xyz"}},
&DependencyQuery{Dep: Dependency{Pkg: "aa", Relation: VersionGreater, Version: "3.0"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{&DependencyQuery{Dep: Dependency{Pkg: "app", Architecture: "i386"}}}, true, NewPackageList(), 0, []string{"i386"})
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
result, err = s.il.Filter([]PackageQuery{
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "0.9"}},
&DependencyQuery{Dep: Dependency{Pkg: "lib"}},
&DependencyQuery{Dep: Dependency{Pkg: "data"}}}, true, NewPackageList(), 0, []string{"i386", "amd64"})
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
result, err = s.il.Filter([]PackageQuery{&OrQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{&AndQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "")
result, err = s.il.Filter([]PackageQuery{&OrQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_i386 data_1.1~bp1_all")
result, err = s.il.Filter([]PackageQuery{&AndQuery{&FieldQuery{Field: "Version", Relation: VersionGreaterOrEqual, Value: "1.0"},
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 data_1.1~bp1_all")
result, err = s.il.Filter([]PackageQuery{&AndQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
result, err = s.il.Filter([]PackageQuery{&NotQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.6.1-3_arm dpkg_1.6.1-3_source dpkg_1.7_source libx_1.5_arm")
result, err = s.il.Filter([]PackageQuery{&AndQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionRegexp, Value: "i.*6", Regexp: regexp.MustCompile("i.*6")}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
}
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
missing, err := s.il.VerifyDependencies(0, []string{"i386"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(0, []string{"i386", "amd64"}, s.il, nil)
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, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(DepFollowAllVariants, []string{"arm"}, s.il, nil)
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"}})
for _, p := range s.sourcePackages {
s.il.Add(p)
}
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"i386", "amd64"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"arm"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "libx", Relation: VersionEqual, Version: "1.5", Architecture: "source"}})
_, err = s.il.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.il, nil)
c.Check(err, ErrorMatches, "unable to process package app_1.0_s390:.*")
}
func (s *PackageListSuite) TestArchitectures(c *C) {
archs := s.il.Architectures(true)
sort.Strings(archs)
c.Check(archs, DeepEquals, []string{"amd64", "arm", "i386", "s390", "source"})
archs = s.il.Architectures(false)
sort.Strings(archs)
c.Check(archs, DeepEquals, []string{"amd64", "arm", "i386", "s390"})
}
+223
View File
@@ -0,0 +1,223 @@
package deb
import (
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt"
"github.com/smira/aptly/database"
"github.com/ugorji/go/codec"
"log"
)
// LocalRepo is a collection of packages created locally
type LocalRepo struct {
// Permanent internal ID
UUID string
// User-assigned name
Name string
// Comment
Comment string
// DefaultDistribution
DefaultDistribution string `codec:",omitempty"`
// DefaultComponent
DefaultComponent string `codec:",omitempty"`
// "Snapshot" of current list of packages
packageRefs *PackageRefList
}
// NewLocalRepo creates new instance of Debian local repository
func NewLocalRepo(name string, comment string) *LocalRepo {
return &LocalRepo{
UUID: uuid.New(),
Name: name,
Comment: comment,
}
}
// String interface
func (repo *LocalRepo) String() string {
if repo.Comment != "" {
return fmt.Sprintf("[%s]: %s", repo.Name, repo.Comment)
}
return fmt.Sprintf("[%s]", repo.Name)
}
// NumPackages return number of packages in local repo
func (repo *LocalRepo) NumPackages() int {
if repo.packageRefs == nil {
return 0
}
return repo.packageRefs.Len()
}
// RefList returns package list for repo
func (repo *LocalRepo) RefList() *PackageRefList {
return repo.packageRefs
}
// UpdateRefList changes package list for local repo
func (repo *LocalRepo) UpdateRefList(reflist *PackageRefList) {
repo.packageRefs = reflist
}
// Encode does msgpack encoding of LocalRepo
func (repo *LocalRepo) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(repo)
return buf.Bytes()
}
// Decode decodes msgpack representation into LocalRepo
func (repo *LocalRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(repo)
}
// Key is a unique id in DB
func (repo *LocalRepo) Key() []byte {
return []byte("L" + repo.UUID)
}
// RefKey is a unique id for package reference list
func (repo *LocalRepo) RefKey() []byte {
return []byte("E" + repo.UUID)
}
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
type LocalRepoCollection struct {
db database.Storage
list []*LocalRepo
}
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
result := &LocalRepoCollection{
db: db,
}
blobs := db.FetchByPrefix([]byte("L"))
result.list = make([]*LocalRepo, 0, len(blobs))
for _, blob := range blobs {
r := &LocalRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding mirror: %s\n", err)
} else {
result.list = append(result.list, r)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *LocalRepoCollection) Add(repo *LocalRepo) error {
for _, r := range collection.list {
if r.Name == repo.Name {
return fmt.Errorf("local repo with name %s already exists", repo.Name)
}
}
err := collection.Update(repo)
if err != nil {
return err
}
collection.list = append(collection.list, repo)
return nil
}
// Update stores updated information about repo in DB
func (collection *LocalRepoCollection) Update(repo *LocalRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
if repo.packageRefs != nil {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
if err != nil {
return err
}
}
return nil
}
// LoadComplete loads additional information for local repo
func (collection *LocalRepoCollection) LoadComplete(repo *LocalRepo) error {
encoded, err := collection.db.Get(repo.RefKey())
if err == database.ErrNotFound {
return nil
}
if err != nil {
return err
}
repo.packageRefs = &PackageRefList{}
return repo.packageRefs.Decode(encoded)
}
// ByName looks up repository by name
func (collection *LocalRepoCollection) ByName(name string) (*LocalRepo, error) {
for _, r := range collection.list {
if r.Name == name {
return r, nil
}
}
return nil, fmt.Errorf("local repo with name %s not found", name)
}
// ByUUID looks up repository by uuid
func (collection *LocalRepoCollection) ByUUID(uuid string) (*LocalRepo, error) {
for _, r := range collection.list {
if r.UUID == uuid {
return r, nil
}
}
return nil, fmt.Errorf("local repo with uuid %s not found", uuid)
}
// ForEach runs method for each repository
func (collection *LocalRepoCollection) ForEach(handler func(*LocalRepo) 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 *LocalRepoCollection) Len() int {
return len(collection.list)
}
// Drop removes remote repo from collection
func (collection *LocalRepoCollection) Drop(repo *LocalRepo) error {
repoPosition := -1
for i, r := range collection.list {
if r == repo {
repoPosition = i
break
}
}
if repoPosition == -1 {
panic("local 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())
}
+195
View File
@@ -0,0 +1,195 @@
package deb
import (
"errors"
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
)
type LocalRepoSuite struct {
db database.Storage
list *PackageList
reflist *PackageRefList
repo *LocalRepo
}
var _ = Suite(&LocalRepoSuite{})
func (s *LocalRepoSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.list = NewPackageList()
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
s.reflist = NewPackageRefListFromPackageList(s.list)
s.repo = NewLocalRepo("lrepo", "Super repo")
s.repo.packageRefs = s.reflist
}
func (s *LocalRepoSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *LocalRepoSuite) TestString(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").String(), Equals, "[lrepo]: My first repo")
c.Check(NewLocalRepo("lrepo2", "").String(), Equals, "[lrepo2]")
}
func (s *LocalRepoSuite) TestNumPackages(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").NumPackages(), Equals, 0)
c.Check(s.repo.NumPackages(), Equals, 2)
}
func (s *LocalRepoSuite) TestRefList(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").RefList(), IsNil)
c.Check(s.repo.RefList(), Equals, s.reflist)
}
func (s *LocalRepoSuite) TestUpdateRefList(c *C) {
s.repo.UpdateRefList(nil)
c.Check(s.repo.RefList(), IsNil)
}
func (s *LocalRepoSuite) TestEncodeDecode(c *C) {
repo := &LocalRepo{}
err := repo.Decode(s.repo.Encode())
c.Assert(err, IsNil)
c.Check(repo.Name, Equals, s.repo.Name)
c.Check(repo.Comment, Equals, s.repo.Comment)
}
func (s *LocalRepoSuite) TestKey(c *C) {
c.Assert(len(s.repo.Key()), Equals, 37)
c.Assert(s.repo.Key()[0], Equals, byte('L'))
}
func (s *LocalRepoSuite) TestRefKey(c *C) {
c.Assert(len(s.repo.RefKey()), Equals, 37)
c.Assert(s.repo.RefKey()[0], Equals, byte('E'))
c.Assert(s.repo.RefKey()[1:], DeepEquals, s.repo.Key()[1:])
}
type LocalRepoCollectionSuite struct {
db database.Storage
collection *LocalRepoCollection
list *PackageList
reflist *PackageRefList
}
var _ = Suite(&LocalRepoCollectionSuite{})
func (s *LocalRepoCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewLocalRepoCollection(s.db)
s.list = NewPackageList()
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
s.reflist = NewPackageRefListFromPackageList(s.list)
}
func (s *LocalRepoCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *LocalRepoCollectionSuite) TestAddByName(c *C) {
r, err := s.collection.ByName("local1")
c.Assert(err, ErrorMatches, "*.not found")
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Add(repo), IsNil)
c.Assert(s.collection.Add(repo), ErrorMatches, ".*already exists")
r, err = s.collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
collection := NewLocalRepoCollection(s.db)
r, err = collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
}
func (s *LocalRepoCollectionSuite) TestByUUID(c *C) {
r, err := s.collection.ByUUID("some-uuid")
c.Assert(err, ErrorMatches, "*.not found")
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Add(repo), IsNil)
r, err = s.collection.ByUUID(repo.UUID)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
}
func (s *LocalRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Update(repo), IsNil)
collection := NewLocalRepoCollection(s.db)
r, err := collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.packageRefs, IsNil)
repo.packageRefs = s.reflist
c.Assert(s.collection.Update(repo), IsNil)
collection = NewLocalRepoCollection(s.db)
r, err = collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.packageRefs, IsNil)
c.Assert(r.NumPackages(), Equals, 0)
c.Assert(s.collection.LoadComplete(r), IsNil)
c.Assert(r.NumPackages(), Equals, 2)
}
func (s *LocalRepoCollectionSuite) TestForEachAndLen(c *C) {
repo := NewLocalRepo("local1", "Comment 1")
s.collection.Add(repo)
count := 0
err := s.collection.ForEach(func(*LocalRepo) 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(*LocalRepo) error {
return e
})
c.Assert(err, Equals, e)
}
func (s *LocalRepoCollectionSuite) TestDrop(c *C) {
repo1 := NewLocalRepo("local1", "Comment 1")
s.collection.Add(repo1)
repo2 := NewLocalRepo("local2", "Comment 2")
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, "local repo .* not found")
collection := NewLocalRepoCollection(s.db)
_, err = collection.ByName("local1")
c.Check(err, ErrorMatches, "local repo .* not found")
r2, _ := collection.ByName("local2")
c.Check(r2.String(), Equals, repo2.String())
c.Check(func() { s.collection.Drop(repo1) }, Panics, "local repo not found!")
}
+577
View File
@@ -0,0 +1,577 @@
package deb
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"path/filepath"
"strconv"
"strings"
)
// Package is single instance of Debian package
type Package struct {
// Basic package properties
Name string
Version string
Architecture string
// If this source package, this field holds "real" architecture value,
// while Architecture would be equal to "source"
SourceArchitecture string
// For binary package, name of source package
Source string
// List of virtual packages this package provides
Provides []string
// Is this source package
IsSource bool
// Hash of files section
FilesHash uint64
// Is this >= 0.6 package?
V06Plus bool
// Offload fields
deps *PackageDependencies
extra *Stanza
files *PackageFiles
// Mother collection
collection *PackageCollection
}
// NewPackageFromControlFile creates Package from parsed Debian control file
func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{
Name: input["Package"],
Version: input["Version"],
Architecture: input["Architecture"],
Source: input["Source"],
V06Plus: true,
}
delete(input, "Package")
delete(input, "Version")
delete(input, "Architecture")
delete(input, "Source")
filesize, _ := strconv.ParseInt(input["Size"], 10, 64)
result.UpdateFiles(PackageFiles{PackageFile{
Filename: filepath.Base(input["Filename"]),
downloadPath: filepath.Dir(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, "SHA1")
delete(input, "SHA256")
delete(input, "Size")
depends := &PackageDependencies{}
depends.Depends = parseDependencies(input, "Depends")
depends.PreDepends = parseDependencies(input, "Pre-Depends")
depends.Suggests = parseDependencies(input, "Suggests")
depends.Recommends = parseDependencies(input, "Recommends")
result.deps = depends
result.Provides = parseDependencies(input, "Provides")
result.extra = &input
return result
}
// NewSourcePackageFromControlFile creates Package from parsed Debian control file for source package
func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
result := &Package{
IsSource: true,
Name: input["Package"],
Version: input["Version"],
Architecture: "source",
SourceArchitecture: input["Architecture"],
V06Plus: true,
}
delete(input, "Package")
delete(input, "Version")
delete(input, "Architecture")
files := make(PackageFiles, 0, 3)
parseSums := func(field string, setter func(sum *utils.ChecksumInfo, data string)) error {
for _, line := range strings.Split(input[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)
}
filename := filepath.Base(parts[2])
found := false
pos := 0
for i, file := range files {
if file.Filename == filename {
found = true
pos = i
break
}
}
if !found {
files = append(files, PackageFile{Filename: filename, downloadPath: input["Directory"]})
pos = len(files) - 1
}
files[pos].Checksums.Size = size
setter(&files[pos].Checksums, parts[0])
}
delete(input, field)
return nil
}
err := parseSums("Files", func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data })
if err != nil {
return nil, err
}
err = parseSums("Checksums-Sha1", func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data })
if err != nil {
return nil, err
}
err = parseSums("Checksums-Sha256", func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data })
if err != nil {
return nil, err
}
result.UpdateFiles(files)
depends := &PackageDependencies{}
depends.BuildDepends = parseDependencies(input, "Build-Depends")
depends.BuildDependsInDep = parseDependencies(input, "Build-Depends-Indep")
result.deps = depends
result.extra = &input
return result, nil
}
// Key returns unique key identifying package
func (p *Package) Key(prefix string) []byte {
if p.V06Plus {
return []byte(fmt.Sprintf("%sP%s %s %s %08x", prefix, p.Architecture, p.Name, p.Version, p.FilesHash))
}
return p.ShortKey(prefix)
}
// ShortKey returns key for the package that should be unique in one list
func (p *Package) ShortKey(prefix string) []byte {
return []byte(fmt.Sprintf("%sP%s %s %s", prefix, p.Architecture, p.Name, p.Version))
}
// String creates readable representation
func (p *Package) String() string {
return fmt.Sprintf("%s_%s_%s", p.Name, p.Version, p.Architecture)
}
// GetField returns fields from package
func (p *Package) GetField(name string) string {
switch name {
// $Version is handled in FieldQuery
case "$Source":
if p.IsSource {
return ""
}
source := p.Source
if source == "" {
return p.Name
} else if pos := strings.Index(source, "("); pos != -1 {
return strings.TrimSpace(source[:pos])
}
return source
case "$SourceVersion":
if p.IsSource {
return ""
}
source := p.Source
if pos := strings.Index(source, "("); pos != -1 {
if pos2 := strings.LastIndex(source, ")"); pos2 != -1 && pos2 > pos {
return strings.TrimSpace(source[pos+1 : pos2])
}
}
return p.Version
case "$Architecture":
return p.Architecture
case "$PackageType":
if p.IsSource {
return "source"
}
return "deb"
case "Name":
return p.Name
case "Version":
return p.Version
case "Architecture":
if p.IsSource {
return p.SourceArchitecture
}
return p.Architecture
case "Source":
return p.Source
case "Depends":
return strings.Join(p.Deps().Depends, ", ")
case "Pre-Depends":
return strings.Join(p.Deps().PreDepends, ", ")
case "Suggests":
return strings.Join(p.Deps().Suggests, ", ")
case "Recommends":
return strings.Join(p.Deps().Recommends, ", ")
case "Provides":
return strings.Join(p.Provides, ", ")
case "Build-Depends":
return strings.Join(p.Deps().BuildDepends, ", ")
case "Build-Depends-Indep":
return strings.Join(p.Deps().BuildDependsInDep, ", ")
default:
return p.Extra()[name]
}
return ""
}
// MatchesArchitecture checks whether packages matches specified architecture
func (p *Package) MatchesArchitecture(arch string) bool {
if p.Architecture == "all" && arch != "source" {
return true
}
return p.Architecture == arch
}
// MatchesDependency checks whether package matches specified dependency
func (p *Package) MatchesDependency(dep Dependency) bool {
if dep.Architecture != "" && !p.MatchesArchitecture(dep.Architecture) {
return false
}
if dep.Relation == VersionDontCare {
if utils.StrSliceHasItem(p.Provides, dep.Pkg) {
return true
}
return dep.Pkg == p.Name
}
if dep.Pkg != p.Name {
return false
}
r := CompareVersions(p.Version, dep.Version)
switch dep.Relation {
case VersionEqual:
return r == 0
case VersionLess:
return r < 0
case VersionGreater:
return r > 0
case VersionLessOrEqual:
return r <= 0
case VersionGreaterOrEqual:
return r >= 0
case VersionPatternMatch:
matched, err := filepath.Match(dep.Version, p.Version)
return err == nil && matched
case VersionRegexp:
return dep.Regexp.FindStringIndex(p.Version) != nil
}
panic("unknown relation")
}
// GetDependencies compiles list of dependenices by flags from options
func (p *Package) GetDependencies(options int) (dependencies []string) {
deps := p.Deps()
dependencies = make([]string, 0, 30)
dependencies = append(dependencies, deps.Depends...)
dependencies = append(dependencies, deps.PreDepends...)
if options&DepFollowRecommends == DepFollowRecommends {
dependencies = append(dependencies, deps.Recommends...)
}
if options&DepFollowSuggests == DepFollowSuggests {
dependencies = append(dependencies, deps.Suggests...)
}
if options&DepFollowBuild == DepFollowBuild {
dependencies = append(dependencies, deps.BuildDepends...)
dependencies = append(dependencies, deps.BuildDependsInDep...)
}
if options&DepFollowSource == DepFollowSource {
source := p.Source
if source == "" {
source = p.Name
}
if strings.Index(source, ")") != -1 {
dependencies = append(dependencies, fmt.Sprintf("%s {source}", source))
} else {
dependencies = append(dependencies, fmt.Sprintf("%s (= %s) {source}", source, p.Version))
}
}
return
}
// Extra returns Stanza of extra fields (it may load it from collection)
func (p *Package) Extra() Stanza {
if p.extra == nil {
if p.collection == nil {
panic("extra == nil && collection == nil")
}
p.extra = p.collection.loadExtra(p)
}
return *p.extra
}
// Deps returns parsed package dependencies (it may load it from collection)
func (p *Package) Deps() *PackageDependencies {
if p.deps == nil {
if p.collection == nil {
panic("deps == nil && collection == nil")
}
p.deps = p.collection.loadDependencies(p)
}
return p.deps
}
// Files returns parsed files records (it may load it from collection)
func (p *Package) Files() PackageFiles {
if p.files == nil {
if p.collection == nil {
panic("files == nil && collection == nil")
}
p.files = p.collection.loadFiles(p)
}
return *p.files
}
// UpdateFiles saves new state of files
func (p *Package) UpdateFiles(files PackageFiles) {
p.files = &files
p.FilesHash = files.Hash()
}
// Stanza creates original stanza from package
func (p *Package) Stanza() (result Stanza) {
result = p.Extra().Copy()
result["Package"] = p.Name
result["Version"] = p.Version
if p.IsSource {
result["Architecture"] = p.SourceArchitecture
} else {
result["Architecture"] = p.Architecture
result["Source"] = p.Source
}
if p.IsSource {
md5, sha1, sha256 := make([]string, 0), make([]string, 0), make([]string, 0)
for _, f := range p.Files() {
if f.Checksums.MD5 != "" {
md5 = append(md5, fmt.Sprintf(" %s %d %s\n", f.Checksums.MD5, f.Checksums.Size, f.Filename))
}
if f.Checksums.SHA1 != "" {
sha1 = append(sha1, fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA1, f.Checksums.Size, f.Filename))
}
if f.Checksums.SHA256 != "" {
sha256 = append(sha256, fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA256, f.Checksums.Size, f.Filename))
}
}
result["Files"] = strings.Join(md5, "")
result["Checksums-Sha1"] = strings.Join(sha1, "")
result["Checksums-Sha256"] = strings.Join(sha256, "")
} else {
f := p.Files()[0]
result["Filename"] = f.DownloadURL()
if f.Checksums.MD5 != "" {
result["MD5sum"] = f.Checksums.MD5
}
if f.Checksums.SHA1 != "" {
result["SHA1"] = " " + f.Checksums.SHA1
}
if f.Checksums.SHA256 != "" {
result["SHA256"] = " " + f.Checksums.SHA256
}
result["Size"] = fmt.Sprintf("%d", f.Checksums.Size)
}
deps := p.Deps()
if deps.Depends != nil {
result["Depends"] = strings.Join(deps.Depends, ", ")
}
if deps.PreDepends != nil {
result["Pre-Depends"] = strings.Join(deps.PreDepends, ", ")
}
if deps.Suggests != nil {
result["Suggests"] = strings.Join(deps.Suggests, ", ")
}
if deps.Recommends != nil {
result["Recommends"] = strings.Join(deps.Recommends, ", ")
}
if p.Provides != nil {
result["Provides"] = strings.Join(p.Provides, ", ")
}
if deps.BuildDepends != nil {
result["Build-Depends"] = strings.Join(deps.BuildDepends, ", ")
}
if deps.BuildDependsInDep != nil {
result["Build-Depends-Indep"] = strings.Join(deps.BuildDependsInDep, ", ")
}
return
}
// Equals compares two packages to be identical
func (p *Package) Equals(p2 *Package) bool {
return p.Name == p2.Name && p.Version == p2.Version && p.SourceArchitecture == p2.SourceArchitecture &&
p.Architecture == p2.Architecture && p.Source == p2.Source && p.IsSource == p2.IsSource &&
p.FilesHash == p2.FilesHash
}
// LinkFromPool links package file from pool to dist's pool location
func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packagePool aptly.PackagePool, prefix string, component string) error {
poolDir, err := p.PoolDirectory()
if err != nil {
return err
}
for i, f := range p.Files() {
sourcePath, err := packagePool.Path(f.Filename, f.Checksums.MD5)
if err != nil {
return err
}
relPath := filepath.Join("pool", component, poolDir)
publishedDirectory := filepath.Join(prefix, relPath)
err = publishedStorage.LinkFromPool(publishedDirectory, packagePool, sourcePath, f.Checksums.MD5)
if err != nil {
return err
}
if p.IsSource {
p.Extra()["Directory"] = relPath
} else {
p.Files()[i].downloadPath = relPath
}
}
return nil
}
// PoolDirectory returns directory in package pool of published repository for this package files
func (p *Package) PoolDirectory() (string, error) {
source := p.Source
if source == "" {
source = p.Name
} else if pos := strings.Index(source, "("); pos != -1 {
source = strings.TrimSpace(source[:pos])
}
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(packagePool aptly.PackagePool) (result []PackageDownloadTask, err error) {
result = make([]PackageDownloadTask, 0, 1)
for _, f := range p.Files() {
poolPath, err := packagePool.Path(f.Filename, f.Checksums.MD5)
if err != nil {
return nil, err
}
verified, err := f.Verify(packagePool)
if err != nil {
return nil, err
}
if !verified {
result = append(result, PackageDownloadTask{RepoURI: f.DownloadURL(), DestinationPath: poolPath, Checksums: f.Checksums})
}
}
return result, nil
}
// VerifyFiles verifies that all package files have neen correctly downloaded
func (p *Package) VerifyFiles(packagePool aptly.PackagePool) (result bool, err error) {
result = true
for _, f := range p.Files() {
result, err = f.Verify(packagePool)
if err != nil || !result {
return
}
}
return
}
// FilepathList returns list of paths to files in package repository
func (p *Package) FilepathList(packagePool aptly.PackagePool) ([]string, error) {
var err error
result := make([]string, len(p.Files()))
for i, f := range p.Files() {
result[i], err = packagePool.RelativePath(f.Filename, f.Checksums.MD5)
if err != nil {
return nil, err
}
}
return result, nil
}
+239
View File
@@ -0,0 +1,239 @@
package deb
import (
"bytes"
"fmt"
"github.com/smira/aptly/database"
"github.com/ugorji/go/codec"
"path/filepath"
)
// PackageCollection does management of packages in DB
type PackageCollection struct {
db database.Storage
encodeBuffer bytes.Buffer
codecHandle *codec.MsgpackHandle
}
// NewPackageCollection creates new PackageCollection and binds it to database
func NewPackageCollection(db database.Storage) *PackageCollection {
return &PackageCollection{
db: db,
codecHandle: &codec.MsgpackHandle{},
}
}
// oldPackage is Package struct for aptly < 0.4 with all fields in one struct
// It is used to decode old aptly DBs
type oldPackage struct {
IsSource bool
Name string
Version string
Architecture string
SourceArchitecture string
Source string
Provides []string
Depends []string
BuildDepends []string
BuildDependsInDep []string
PreDepends []string
Suggests []string
Recommends []string
Files []PackageFile
Extra Stanza
}
// ByKey find package in DB by its key
func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
encoded, err := collection.db.Get(key)
if err != nil {
return nil, err
}
p := &Package{}
if len(encoded) > 2 && (encoded[0] != 0xc1 || encoded[1] != 0x1) {
oldp := &oldPackage{}
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(oldp)
if err != nil {
return nil, err
}
p.Name = oldp.Name
p.Version = oldp.Version
p.Architecture = oldp.Architecture
p.IsSource = oldp.IsSource
p.SourceArchitecture = oldp.SourceArchitecture
p.Source = oldp.Source
p.Provides = oldp.Provides
p.deps = &PackageDependencies{
Depends: oldp.Depends,
BuildDepends: oldp.BuildDepends,
BuildDependsInDep: oldp.BuildDependsInDep,
PreDepends: oldp.PreDepends,
Suggests: oldp.Suggests,
Recommends: oldp.Recommends,
}
p.extra = &oldp.Extra
for i := range oldp.Files {
oldp.Files[i].Filename = filepath.Base(oldp.Files[i].Filename)
}
p.UpdateFiles(PackageFiles(oldp.Files))
// Save in new format
err = collection.Update(p)
if err != nil {
return nil, err
}
} else {
decoder := codec.NewDecoderBytes(encoded[2:], collection.codecHandle)
err = decoder.Decode(p)
if err != nil {
return nil, err
}
}
p.collection = collection
return p, nil
}
// loadExtra loads Stanza with all the xtra information about the package
func (collection *PackageCollection) loadExtra(p *Package) *Stanza {
encoded, err := collection.db.Get(p.Key("xE"))
if err != nil {
panic("unable to load extra")
}
stanza := &Stanza{}
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(stanza)
if err != nil {
panic("unable to decode extra")
}
return stanza
}
// loadDependencies loads dependencies for the package
func (collection *PackageCollection) loadDependencies(p *Package) *PackageDependencies {
encoded, err := collection.db.Get(p.Key("xD"))
if err != nil {
panic(fmt.Sprintf("unable to load deps: %s, %s", p, err))
}
deps := &PackageDependencies{}
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(deps)
if err != nil {
panic("unable to decode deps")
}
return deps
}
// loadFiles loads additional PackageFiles record
func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
encoded, err := collection.db.Get(p.Key("xF"))
if err != nil {
panic("unable to load files")
}
files := &PackageFiles{}
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(files)
if err != nil {
panic("unable to decode files")
}
return files
}
// Update adds or updates information about package in DB checking for conficts first
func (collection *PackageCollection) Update(p *Package) error {
encoder := codec.NewEncoder(&collection.encodeBuffer, collection.codecHandle)
collection.encodeBuffer.Reset()
collection.encodeBuffer.WriteByte(0xc1)
collection.encodeBuffer.WriteByte(0x1)
err := encoder.Encode(p)
if err != nil {
return err
}
err = collection.db.Put(p.Key(""), collection.encodeBuffer.Bytes())
if err != nil {
return err
}
// Encode offloaded fields one by one
if p.files != nil {
collection.encodeBuffer.Reset()
err = encoder.Encode(*p.files)
if err != nil {
return err
}
err = collection.db.Put(p.Key("xF"), collection.encodeBuffer.Bytes())
if err != nil {
return err
}
}
if p.deps != nil {
collection.encodeBuffer.Reset()
err = encoder.Encode(*p.deps)
if err != nil {
return err
}
err = collection.db.Put(p.Key("xD"), collection.encodeBuffer.Bytes())
if err != nil {
return err
}
p.deps = nil
}
if p.extra != nil {
collection.encodeBuffer.Reset()
err = encoder.Encode(*p.extra)
if err != nil {
return err
}
err = collection.db.Put(p.Key("xE"), collection.encodeBuffer.Bytes())
if err != nil {
return err
}
p.extra = nil
}
p.collection = collection
return nil
}
// AllPackageRefs returns list of all packages as PackageRefList
func (collection *PackageCollection) AllPackageRefs() *PackageRefList {
return &PackageRefList{Refs: collection.db.KeysByPrefix([]byte("P"))}
}
// DeleteByKey deletes package in DB by key
func (collection *PackageCollection) DeleteByKey(key []byte) error {
for _, key := range [][]byte{key, append([]byte("xF"), key...), append([]byte("xD"), key...), append([]byte("xE"), key...)} {
err := collection.db.Delete(key)
if err != nil {
return err
}
}
return nil
}
+191
View File
@@ -0,0 +1,191 @@
package deb
import (
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
)
type PackageCollectionSuite struct {
collection *PackageCollection
p *Package
db database.Storage
}
var _ = Suite(&PackageCollectionSuite{})
func (s *PackageCollectionSuite) SetUpTest(c *C) {
s.p = NewPackageFromControlFile(packageStanza.Copy())
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewPackageCollection(s.db)
}
func (s *PackageCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PackageCollectionSuite) TestUpdate(c *C) {
// package doesn't exist, update ok
err := s.collection.Update(s.p)
c.Assert(err, IsNil)
res, err := s.collection.ByKey(s.p.Key(""))
c.Assert(err, IsNil)
c.Assert(res.Equals(s.p), Equals, true)
// same package, ok
p2 := NewPackageFromControlFile(packageStanza.Copy())
err = s.collection.Update(p2)
c.Assert(err, IsNil)
res, err = s.collection.ByKey(p2.Key(""))
c.Assert(err, IsNil)
c.Assert(res.Equals(s.p), Equals, true)
// change some metadata
p2.Source = "lala"
err = s.collection.Update(p2)
c.Assert(err, IsNil)
res, err = s.collection.ByKey(p2.Key(""))
c.Assert(err, IsNil)
c.Assert(res.Equals(s.p), Equals, false)
c.Assert(res.Equals(p2), Equals, true)
}
func (s *PackageCollectionSuite) TestByKey(c *C) {
err := s.collection.Update(s.p)
c.Assert(err, IsNil)
p2, err := s.collection.ByKey(s.p.Key(""))
c.Assert(err, IsNil)
c.Assert(p2.Equals(s.p), Equals, true)
c.Check(p2.GetDependencies(0), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)"})
c.Check(p2.Extra()["Priority"], Equals, "extra")
c.Check(p2.Files()[0].Filename, Equals, "alien-arena-common_7.40-2_i386.deb")
}
func (s *PackageCollectionSuite) TestByKeyOld_0_3(c *C) {
key := []byte("Pi386 vmware-view-open-client 4.5.0-297975+dfsg-4+b1")
s.db.Put(key, old_0_3_Package)
p, err := s.collection.ByKey(key)
c.Check(err, IsNil)
c.Check(p.Name, Equals, "vmware-view-open-client")
c.Check(p.Version, Equals, "4.5.0-297975+dfsg-4+b1")
c.Check(p.Architecture, Equals, "i386")
c.Check(p.Files(), DeepEquals, PackageFiles{
PackageFile{Filename: "vmware-view-open-client_4.5.0-297975+dfsg-4+b1_i386.deb",
Checksums: utils.ChecksumInfo{
Size: 520080,
MD5: "9c61b54e2638a18f955a695b9162d6af",
SHA1: "5b7c99e64a70f4f509bfa3a674088ff9cef68163",
SHA256: "4a9e4b2d9b3db13f9a29e522f3ffbb34eee96fc6f34a0647042ab1b5b0f2e04d"}}})
c.Check(p.GetDependencies(0), DeepEquals, []string{"libatk1.0-0 (>= 1.12.4)", "libboost-signals1.49.0 (>= 1.49.0-1)",
"libc6 (>= 2.3.6-6~)", "libcairo2 (>= 1.2.4)", "libcurl3 (>= 7.18.0)", "libfontconfig1 (>= 2.8.0)", "libfreetype6 (>= 2.2.1)",
"libgcc1 (>= 1:4.1.1)", "libgdk-pixbuf2.0-0 (>= 2.22.0)", "libglib2.0-0 (>= 2.24.0)", "libgtk2.0-0 (>= 2.24.0)",
"libicu48 (>= 4.8-1)", "libpango1.0-0 (>= 1.14.0)", "libssl1.0.0 (>= 1.0.0)", "libstdc++6 (>= 4.6)", "libx11-6",
"libxml2 (>= 2.7.4)", "rdesktop"})
c.Check(p.Extra()["Priority"], Equals, "optional")
}
func (s *PackageCollectionSuite) TestAllPackageRefs(c *C) {
err := s.collection.Update(s.p)
c.Assert(err, IsNil)
refs := s.collection.AllPackageRefs()
c.Check(refs.Len(), Equals, 1)
c.Check(refs.Refs[0], DeepEquals, s.p.Key(""))
}
func (s *PackageCollectionSuite) TestDeleteByKey(c *C) {
err := s.collection.Update(s.p)
c.Assert(err, IsNil)
_, err = s.db.Get(s.p.Key(""))
c.Check(err, IsNil)
_, err = s.db.Get(s.p.Key("xD"))
c.Check(err, IsNil)
_, err = s.db.Get(s.p.Key("xE"))
c.Check(err, IsNil)
_, err = s.db.Get(s.p.Key("xF"))
c.Check(err, IsNil)
err = s.collection.DeleteByKey(s.p.Key(""))
c.Check(err, IsNil)
_, err = s.collection.ByKey(s.p.Key(""))
c.Check(err, ErrorMatches, "key not found")
_, err = s.db.Get(s.p.Key(""))
c.Check(err, ErrorMatches, "key not found")
_, err = s.db.Get(s.p.Key("xD"))
c.Check(err, ErrorMatches, "key not found")
_, err = s.db.Get(s.p.Key("xE"))
c.Check(err, ErrorMatches, "key not found")
_, err = s.db.Get(s.p.Key("xF"))
c.Check(err, ErrorMatches, "key not found")
}
// This is old package (pre-0.4) that would habe to be converted
var old_0_3_Package = []byte{0x8f, 0xac, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0xa4, 0x69, 0x33, 0x38, 0x36,
0xac, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0xc0, 0xb1, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65,
0x70, 0x65, 0x6e, 0x64, 0x73, 0x49, 0x6e, 0x44, 0x65, 0x70, 0xc0, 0xa7, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0xdc, 0x0, 0x12,
0xb7, 0x6c, 0x69, 0x62, 0x61, 0x74, 0x6b, 0x31, 0x2e, 0x30, 0x2d, 0x30, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x2e, 0x31, 0x32, 0x2e,
0x34, 0x29, 0xda, 0x0, 0x24, 0x6c, 0x69, 0x62, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x31,
0x2e, 0x34, 0x39, 0x2e, 0x30, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x2e, 0x34, 0x39, 0x2e, 0x30, 0x2d, 0x31, 0x29, 0xb3, 0x6c, 0x69,
0x62, 0x63, 0x36, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x33, 0x2e, 0x36, 0x2d, 0x36, 0x7e, 0x29, 0xb4, 0x6c, 0x69, 0x62, 0x63,
0x61, 0x69, 0x72, 0x6f, 0x32, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x2e, 0x32, 0x2e, 0x34, 0x29, 0xb4, 0x6c, 0x69, 0x62, 0x63, 0x75,
0x72, 0x6c, 0x33, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x37, 0x2e, 0x31, 0x38, 0x2e, 0x30, 0x29, 0xb9, 0x6c, 0x69, 0x62, 0x66, 0x6f, 0x6e,
0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x31, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x38, 0x2e, 0x30, 0x29, 0xb7, 0x6c, 0x69,
0x62, 0x66, 0x72, 0x65, 0x65, 0x74, 0x79, 0x70, 0x65, 0x36, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x32, 0x2e, 0x31, 0x29, 0xb4,
0x6c, 0x69, 0x62, 0x67, 0x63, 0x63, 0x31, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x3a, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x29, 0xbe, 0x6c,
0x69, 0x62, 0x67, 0x64, 0x6b, 0x2d, 0x70, 0x69, 0x78, 0x62, 0x75, 0x66, 0x32, 0x2e, 0x30, 0x2d, 0x30, 0x20, 0x28, 0x3e, 0x3d, 0x20,
0x32, 0x2e, 0x32, 0x32, 0x2e, 0x30, 0x29, 0xb8, 0x6c, 0x69, 0x62, 0x67, 0x6c, 0x69, 0x62, 0x32, 0x2e, 0x30, 0x2d, 0x30, 0x20, 0x28,
0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x32, 0x34, 0x2e, 0x30, 0x29, 0xb7, 0x6c, 0x69, 0x62, 0x67, 0x74, 0x6b, 0x32, 0x2e, 0x30, 0x2d, 0x30,
0x20, 0x28, 0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x32, 0x34, 0x2e, 0x30, 0x29, 0xb3, 0x6c, 0x69, 0x62, 0x69, 0x63, 0x75, 0x34, 0x38, 0x20,
0x28, 0x3e, 0x3d, 0x20, 0x34, 0x2e, 0x38, 0x2d, 0x31, 0x29, 0xb9, 0x6c, 0x69, 0x62, 0x70, 0x61, 0x6e, 0x67, 0x6f, 0x31, 0x2e, 0x30,
0x2d, 0x30, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x2e, 0x31, 0x34, 0x2e, 0x30, 0x29, 0xb6, 0x6c, 0x69, 0x62, 0x73, 0x73, 0x6c, 0x31,
0x2e, 0x30, 0x2e, 0x30, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x29, 0xb3, 0x6c, 0x69, 0x62, 0x73, 0x74, 0x64,
0x63, 0x2b, 0x2b, 0x36, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x34, 0x2e, 0x36, 0x29, 0xa8, 0x6c, 0x69, 0x62, 0x78, 0x31, 0x31, 0x2d, 0x36,
0xb2, 0x6c, 0x69, 0x62, 0x78, 0x6d, 0x6c, 0x32, 0x20, 0x28, 0x3e, 0x3d, 0x20, 0x32, 0x2e, 0x37, 0x2e, 0x34, 0x29, 0xa8, 0x72, 0x64,
0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0xa5, 0x45, 0x78, 0x74, 0x72, 0x61, 0x88, 0xa3, 0x54, 0x61, 0x67, 0xbd, 0x72, 0x6f, 0x6c, 0x65,
0x3a, 0x3a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2c, 0x20, 0x75, 0x69, 0x74, 0x6f, 0x6f, 0x6c, 0x6b, 0x69, 0x74, 0x3a, 0x3a,
0x67, 0x74, 0x6b, 0xa8, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0xa8, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0xaa,
0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0xda, 0x0, 0x28, 0x44, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x20, 0x51, 0x41,
0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x3c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x40, 0x71, 0x61, 0x2e, 0x64, 0x65,
0x62, 0x69, 0x61, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x3e, 0xa8, 0x48, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0xda, 0x0, 0x30, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x70, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0xaf, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x64, 0x35, 0xda, 0x0, 0x20,
0x62, 0x34, 0x34, 0x64, 0x34, 0x39, 0x62, 0x34, 0x37, 0x61, 0x65, 0x30, 0x35, 0x35, 0x32, 0x63, 0x62, 0x66, 0x61, 0x64, 0x32, 0x31,
0x30, 0x64, 0x65, 0x32, 0x31, 0x63, 0x64, 0x65, 0x31, 0x39, 0xa7, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0xab, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x69, 0x62, 0x2f, 0x78, 0x31, 0x31, 0xae, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x2d, 0x53, 0x69, 0x7a,
0x65, 0xa4, 0x31, 0x34, 0x35, 0x39, 0xab, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0xb9, 0x20, 0x56, 0x4d,
0x77, 0x61, 0x72, 0x65, 0x20, 0x56, 0x69, 0x65, 0x77, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0xa,
0xa5, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x91, 0x82, 0xa9, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x73, 0x84, 0xa3, 0x4d, 0x44,
0x35, 0xda, 0x0, 0x20, 0x39, 0x63, 0x36, 0x31, 0x62, 0x35, 0x34, 0x65, 0x32, 0x36, 0x33, 0x38, 0x61, 0x31, 0x38, 0x66, 0x39, 0x35,
0x35, 0x61, 0x36, 0x39, 0x35, 0x62, 0x39, 0x31, 0x36, 0x32, 0x64, 0x36, 0x61, 0x66, 0xa4, 0x53, 0x48, 0x41, 0x31, 0xda, 0x0, 0x28,
0x35, 0x62, 0x37, 0x63, 0x39, 0x39, 0x65, 0x36, 0x34, 0x61, 0x37, 0x30, 0x66, 0x34, 0x66, 0x35, 0x30, 0x39, 0x62, 0x66, 0x61, 0x33,
0x61, 0x36, 0x37, 0x34, 0x30, 0x38, 0x38, 0x66, 0x66, 0x39, 0x63, 0x65, 0x66, 0x36, 0x38, 0x31, 0x36, 0x33, 0xa6, 0x53, 0x48, 0x41,
0x32, 0x35, 0x36, 0xda, 0x0, 0x40, 0x34, 0x61, 0x39, 0x65, 0x34, 0x62, 0x32, 0x64, 0x39, 0x62, 0x33, 0x64, 0x62, 0x31, 0x33, 0x66,
0x39, 0x61, 0x32, 0x39, 0x65, 0x35, 0x32, 0x32, 0x66, 0x33, 0x66, 0x66, 0x62, 0x62, 0x33, 0x34, 0x65, 0x65, 0x65, 0x39, 0x36, 0x66,
0x63, 0x36, 0x66, 0x33, 0x34, 0x61, 0x30, 0x36, 0x34, 0x37, 0x30, 0x34, 0x32, 0x61, 0x62, 0x31, 0x62, 0x35, 0x62, 0x30, 0x66, 0x32,
0x65, 0x30, 0x34, 0x64, 0xa4, 0x53, 0x69, 0x7a, 0x65, 0xce, 0x0, 0x7, 0xef, 0x90, 0xa8, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
0x65, 0xda, 0x0, 0x5e, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x2f, 0x76, 0x2f, 0x76, 0x6d, 0x77,
0x61, 0x72, 0x65, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76,
0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x5f, 0x34, 0x2e, 0x35, 0x2e, 0x30, 0x2d, 0x32, 0x39, 0x37, 0x39, 0x37, 0x35, 0x2b, 0x64, 0x66, 0x73, 0x67, 0x2d, 0x34, 0x2b, 0x62,
0x31, 0x5f, 0x69, 0x33, 0x38, 0x36, 0x2e, 0x64, 0x65, 0x62, 0xa8, 0x49, 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0xc2, 0xa4, 0x4e,
0x61, 0x6d, 0x65, 0xb7, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0xaa, 0x50, 0x72, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0xc0, 0xa8, 0x50, 0x72, 0x6f, 0x76,
0x69, 0x64, 0x65, 0x73, 0xc0, 0xaa, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x73, 0xc0, 0xa6, 0x53, 0x6f, 0x75, 0x72,
0x63, 0x65, 0xda, 0x0, 0x2d, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x2d,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x34, 0x2e, 0x35, 0x2e, 0x30, 0x2d, 0x32, 0x39, 0x37, 0x39, 0x37, 0x35, 0x2b, 0x64,
0x66, 0x73, 0x67, 0x2d, 0x34, 0x29, 0xb2, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74,
0x75, 0x72, 0x65, 0xa0, 0xa8, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x73, 0xc0, 0xa7, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0xb6, 0x34, 0x2e, 0x35, 0x2e, 0x30, 0x2d, 0x32, 0x39, 0x37, 0x39, 0x37, 0x35, 0x2b, 0x64, 0x66, 0x73, 0x67, 0x2d, 0x34, 0x2b, 0x62, 0x31}
+30
View File
@@ -0,0 +1,30 @@
package deb
import (
"strings"
)
// PackageDependencies are various parsed dependencies
type PackageDependencies struct {
Depends []string
BuildDepends []string
BuildDependsInDep []string
PreDepends []string
Suggests []string
Recommends []string
}
func parseDependencies(input Stanza, key string) []string {
value, ok := input[key]
if !ok {
return nil
}
delete(input, key)
result := strings.Split(value, ",")
for i := range result {
result[i] = strings.TrimSpace(result[i])
}
return result
}
+78
View File
@@ -0,0 +1,78 @@
package deb
import (
"encoding/binary"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"hash/fnv"
"os"
"path/filepath"
"sort"
)
// PackageFile is a single file entry in package
type PackageFile struct {
// Filename is name of file for the package (without directory)
Filename string
// Hashes for the file
Checksums utils.ChecksumInfo
// Temporary field used while downloading, stored relative path on the mirror
downloadPath string
}
// Verify that package file is present and correct
func (f *PackageFile) Verify(packagePool aptly.PackagePool) (bool, error) {
poolPath, err := packagePool.Path(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
}
// DownloadURL return relative URL to package download location
func (f *PackageFile) DownloadURL() string {
return filepath.Join(f.downloadPath, f.Filename)
}
// PackageFiles is collection of package files
type PackageFiles []PackageFile
// Hash compute hash of all file items, sorting them first
func (files PackageFiles) Hash() uint64 {
sort.Sort(files)
h := fnv.New64a()
for _, f := range files {
h.Write([]byte(f.Filename))
binary.Write(h, binary.BigEndian, f.Checksums.Size)
h.Write([]byte(f.Checksums.MD5))
h.Write([]byte(f.Checksums.SHA1))
h.Write([]byte(f.Checksums.SHA256))
}
return h.Sum64()
}
// Len returns number of files
func (files PackageFiles) Len() int {
return len(files)
}
// Swap swaps elements
func (files PackageFiles) Swap(i, j int) {
files[i], files[j] = files[j], files[i]
}
// Less compares by filename
func (files PackageFiles) Less(i, j int) bool {
return files[i].Filename < files[j].Filename
}
+61
View File
@@ -0,0 +1,61 @@
package deb
import (
"github.com/smira/aptly/files"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"os"
"path/filepath"
)
type PackageFilesSuite struct {
files PackageFiles
}
var _ = Suite(&PackageFilesSuite{})
func (s *PackageFilesSuite) SetUpTest(c *C) {
s.files = PackageFiles{PackageFile{
Filename: "alien-arena-common_7.40-2_i386.deb",
downloadPath: "pool/contrib/a/alien-arena",
Checksums: utils.ChecksumInfo{
Size: 187518,
MD5: "1e8cba92c41420aa7baa8a5718d67122",
SHA1: "46955e48cad27410a83740a21d766ce362364024",
SHA256: "eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5",
}}}
}
func (s *PackageFilesSuite) TestVerify(c *C) {
packagePool := files.NewPackagePool(c.MkDir())
poolPath, _ := packagePool.Path(s.files[0].Filename, s.files[0].Checksums.MD5)
result, err := s.files[0].Verify(packagePool)
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 = s.files[0].Verify(packagePool)
c.Check(err, IsNil)
c.Check(result, Equals, false)
s.files[0].Checksums.Size = 5
result, err = s.files[0].Verify(packagePool)
c.Check(err, IsNil)
c.Check(result, Equals, true)
}
func (s *PackageFilesSuite) TestDownloadURL(c *C) {
c.Check(s.files[0].DownloadURL(), Equals, "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb")
}
func (s *PackageFilesSuite) TestHash(c *C) {
c.Check(s.files.Hash(), Equals, uint64(0xc8901eedd79ac51b))
}
+457
View File
@@ -0,0 +1,457 @@
package deb
import (
"bytes"
"github.com/smira/aptly/files"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"os"
"path/filepath"
"regexp"
)
type PackageSuite struct {
stanza Stanza
sourceStanza Stanza
}
var _ = Suite(&PackageSuite{})
func (s *PackageSuite) SetUpTest(c *C) {
s.stanza = packageStanza.Copy()
buf := bytes.NewBufferString(sourcePackageMeta)
s.sourceStanza, _ = NewControlFileReader(buf).ReadStanza()
}
func (s *PackageSuite) TestNewFromPara(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.IsSource, Equals, false)
c.Check(p.Name, Equals, "alien-arena-common")
c.Check(p.Version, Equals, "7.40-2")
c.Check(p.Architecture, Equals, "i386")
c.Check(p.Provides, DeepEquals, []string(nil))
c.Check(p.Files(), HasLen, 1)
c.Check(p.Files()[0].Filename, Equals, "alien-arena-common_7.40-2_i386.deb")
c.Check(p.Files()[0].downloadPath, Equals, "pool/contrib/a/alien-arena")
c.Check(p.Files()[0].Checksums.Size, Equals, int64(187518))
c.Check(p.Files()[0].Checksums.MD5, Equals, "1e8cba92c41420aa7baa8a5718d67122")
c.Check(p.deps.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"})
}
func (s *PackageSuite) TestNewSourceFromPara(c *C) {
p, err := NewSourcePackageFromControlFile(s.sourceStanza)
c.Check(err, IsNil)
c.Check(p.IsSource, Equals, true)
c.Check(p.Name, Equals, "access-modifier-checker")
c.Check(p.Version, Equals, "1.0-4")
c.Check(p.Architecture, Equals, "source")
c.Check(p.SourceArchitecture, Equals, "all")
c.Check(p.Provides, IsNil)
c.Check(p.deps.BuildDepends, DeepEquals, []string{"cdbs", "debhelper (>= 7)", "default-jdk", "maven-debian-helper"})
c.Check(p.deps.BuildDependsInDep, DeepEquals, []string{"default-jdk-doc", "junit (>= 3.8.1)", "libannotation-indexer-java (>= 1.3)", "libannotation-indexer-java-doc", "libasm3-java", "libmaven-install-plugin-java", "libmaven-javadoc-plugin-java", "libmaven-scm-java", "libmaven2-core-java", "libmaven2-core-java-doc", "libmetainf-services-java", "libmetainf-services-java-doc", "libmaven-plugin-tools-java (>= 2.8)"})
c.Check(p.Files(), HasLen, 3)
c.Check(p.Files()[0].Filename, Equals, "access-modifier-checker_1.0-4.debian.tar.gz")
c.Check(p.Files()[0].downloadPath, Equals, "pool/main/a/access-modifier-checker")
c.Check(p.Files()[1].Filename, Equals, "access-modifier-checker_1.0-4.dsc")
c.Check(p.Files()[1].downloadPath, Equals, "pool/main/a/access-modifier-checker")
c.Check(p.Files()[1].Checksums.Size, Equals, int64(3))
c.Check(p.Files()[1].Checksums.MD5, Equals, "900150983cd24fb0d6963f7d28e17f72")
c.Check(p.Files()[1].Checksums.SHA1, Equals, "a9993e364706816aba3e25717850c26c9cd0d89d")
c.Check(p.Files()[1].Checksums.SHA256, Equals, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
c.Check(p.Files()[2].Filename, Equals, "access-modifier-checker_1.0.orig.tar.gz")
c.Check(p.Files()[2].downloadPath, Equals, "pool/main/a/access-modifier-checker")
c.Check(p.Files()[2].Checksums.Size, Equals, int64(4))
c.Check(p.Files()[2].Checksums.MD5, Equals, "e2fc714c4727ee9395f324cd2e7f331f")
c.Check(p.Files()[2].Checksums.SHA1, Equals, "81fe8bfe87576c3ecb22426f8e57847382917acf")
c.Check(p.Files()[2].Checksums.SHA256, Equals, "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589")
c.Check(p.deps.Depends, IsNil)
}
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) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
p.V06Plus = false
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
}
func (s *PackageSuite) TestShortKey(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.ShortKey(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
c.Check(p.ShortKey("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
}
func (s *PackageSuite) TestStanza(c *C) {
p := NewPackageFromControlFile(s.stanza.Copy())
stanza := p.Stanza()
c.Assert(stanza, DeepEquals, s.stanza)
p, _ = NewSourcePackageFromControlFile(s.sourceStanza.Copy())
stanza = p.Stanza()
c.Assert(stanza, DeepEquals, s.sourceStanza)
}
func (s *PackageSuite) TestString(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Assert(p.String(), Equals, "alien-arena-common_7.40-2_i386")
}
func (s *PackageSuite) TestGetField(c *C) {
p := NewPackageFromControlFile(s.stanza.Copy())
stanza2 := s.stanza.Copy()
delete(stanza2, "Source")
stanza2["Provides"] = "app, game"
p2 := NewPackageFromControlFile(stanza2)
stanza3 := s.stanza.Copy()
stanza3["Source"] = "alien-arena (3.5)"
p3 := NewPackageFromControlFile(stanza3)
p4, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy())
c.Check(p.GetField("$Source"), Equals, "alien-arena")
c.Check(p2.GetField("$Source"), Equals, "alien-arena-common")
c.Check(p3.GetField("$Source"), Equals, "alien-arena")
c.Check(p4.GetField("$Source"), Equals, "")
c.Check(p.GetField("$SourceVersion"), Equals, "7.40-2")
c.Check(p2.GetField("$SourceVersion"), Equals, "7.40-2")
c.Check(p3.GetField("$SourceVersion"), Equals, "3.5")
c.Check(p4.GetField("$SourceVersion"), Equals, "")
c.Check(p.GetField("$Architecture"), Equals, "i386")
c.Check(p4.GetField("$Architecture"), Equals, "source")
c.Check(p.GetField("$PackageType"), Equals, "deb")
c.Check(p4.GetField("$PackageType"), Equals, "source")
c.Check(p.GetField("Name"), Equals, "alien-arena-common")
c.Check(p4.GetField("Name"), Equals, "access-modifier-checker")
c.Check(p.GetField("Architecture"), Equals, "i386")
c.Check(p4.GetField("Architecture"), Equals, "all")
c.Check(p.GetField("Version"), Equals, "7.40-2")
c.Check(p.GetField("Source"), Equals, "alien-arena")
c.Check(p2.GetField("Source"), Equals, "")
c.Check(p3.GetField("Source"), Equals, "alien-arena (3.5)")
c.Check(p4.GetField("Source"), Equals, "")
c.Check(p.GetField("Depends"), Equals, "libc6 (>= 2.7), alien-arena-data (>= 7.40)")
c.Check(p.GetField("Provides"), Equals, "")
c.Check(p2.GetField("Provides"), Equals, "app, game")
c.Check(p.GetField("Section"), Equals, "contrib/games")
c.Check(p.GetField("Priority"), Equals, "extra")
}
func (s *PackageSuite) TestEquals(c *C) {
p := NewPackageFromControlFile(s.stanza)
p2 := NewPackageFromControlFile(packageStanza.Copy())
c.Check(p.Equals(p2), Equals, true)
p2.deps.Depends = []string{"package1"}
c.Check(p.Equals(p2), Equals, true) // strange, but Equals doesn't check deep
p2 = NewPackageFromControlFile(packageStanza.Copy())
files := p2.Files()
files[0].Checksums.MD5 = "abcdefabcdef"
p2.UpdateFiles(files)
c.Check(p.Equals(p2), Equals, false)
so, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy())
so2, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy())
c.Check(so.Equals(so2), Equals, true)
files = so2.Files()
files[2].Checksums.MD5 = "abcde"
so2.UpdateFiles(files)
c.Check(so.Equals(so2), Equals, false)
so2, _ = NewSourcePackageFromControlFile(s.sourceStanza.Copy())
files = so2.Files()
files[1].Filename = "other.deb"
so2.UpdateFiles(files)
c.Check(so.Equals(so2), 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)
c.Check(p.MatchesArchitecture("source"), Equals, false)
p, _ = NewSourcePackageFromControlFile(s.sourceStanza)
c.Check(p.MatchesArchitecture("source"), Equals, true)
c.Check(p.MatchesArchitecture("amd64"), Equals, false)
}
func (s *PackageSuite) TestMatchesDependency(c *C) {
p := NewPackageFromControlFile(s.stanza)
// exact match
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, true)
// exact match, same version, no revision specified
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40"}), Equals, false)
// different name
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, false)
// different version
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40-3"}), Equals, false)
// different arch
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "amd64", Relation: VersionEqual, Version: "7.40-2"}), Equals, false)
// empty arch
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "", Relation: VersionEqual, Version: "7.40-2"}), Equals, true)
// version don't care
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionDontCare, Version: ""}), Equals, true)
// >
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionGreater, Version: "7.40-2"}), Equals, false)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionGreater, Version: "7.40-1"}), Equals, true)
// <
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLess, Version: "7.40-2"}), Equals, false)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLess, Version: "7.40-3"}), Equals, true)
// >=
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionGreaterOrEqual, Version: "7.40-2"}), Equals, true)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionGreaterOrEqual, Version: "7.40-3"}), Equals, false)
// <=
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-2"}), Equals, true)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-1"}), Equals, false)
// %
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-*"}), Equals, true)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2]"}), Equals, true)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2"}), Equals, false)
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[34]"}), Equals, false)
// ~
c.Check(
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
Regexp: regexp.MustCompile("7\\.40-.*")}), Equals, true)
c.Check(
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
Regexp: regexp.MustCompile("40")}), Equals, true)
c.Check(
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
Regexp: regexp.MustCompile("39-.*")}), Equals, false)
// Provides
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, false)
p.Provides = []string{"fun", "game"}
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, true)
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Architecture: "amd64", Relation: VersionDontCare}), Equals, false)
}
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"})
c.Check(p.GetDependencies(DepFollowSource), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)", "alien-arena (= 7.40-2) {source}"})
p.Source = "alien-arena (7.40-3)"
c.Check(p.GetDependencies(DepFollowSource), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)", "alien-arena (7.40-3) {source}"})
p.Source = ""
c.Check(p.GetDependencies(DepFollowSource), DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)", "dpkg (>= 1.6)", "alien-arena-common (= 7.40-2) {source}"})
p, _ = NewSourcePackageFromControlFile(s.sourceStanza)
c.Check(p.GetDependencies(0), DeepEquals, []string{})
c.Check(p.GetDependencies(DepFollowBuild), DeepEquals, []string{"cdbs", "debhelper (>= 7)", "default-jdk", "maven-debian-helper", "default-jdk-doc", "junit (>= 3.8.1)", "libannotation-indexer-java (>= 1.3)", "libannotation-indexer-java-doc", "libasm3-java", "libmaven-install-plugin-java", "libmaven-javadoc-plugin-java", "libmaven-scm-java", "libmaven2-core-java", "libmaven2-core-java-doc", "libmetainf-services-java", "libmetainf-services-java-doc", "libmaven-plugin-tools-java (>= 2.8)"})
}
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 = "gcc-defaults (1.77)"
dir, err = p.PoolDirectory()
c.Check(err, IsNil)
c.Check(dir, Equals, "g/gcc-defaults")
p = NewPackageFromControlFile(packageStanza.Copy())
p.Source = "l"
_, err = p.PoolDirectory()
c.Check(err, ErrorMatches, ".* too short")
}
func (s *PackageSuite) TestLinkFromPool(c *C) {
packagePool := files.NewPackagePool(c.MkDir())
publishedStorage := files.NewPublishedStorage(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
poolPath, _ := packagePool.Path(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(publishedStorage, packagePool, "", "non-free")
c.Check(err, IsNil)
c.Check(p.Files()[0].Filename, Equals, "alien-arena-common_7.40-2_i386.deb")
c.Check(p.Files()[0].downloadPath, Equals, "pool/non-free/a/alien-arena")
p.IsSource = true
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free")
c.Check(err, IsNil)
c.Check(p.Extra()["Directory"], Equals, "pool/non-free/a/alien-arena")
}
func (s *PackageSuite) TestFilepathList(c *C) {
packagePool := files.NewPackagePool(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
list, err := p.FilepathList(packagePool)
c.Check(err, IsNil)
c.Check(list, DeepEquals, []string{"1e/8c/alien-arena-common_7.40-2_i386.deb"})
}
func (s *PackageSuite) TestDownloadList(c *C) {
packagePool := files.NewPackagePool(c.MkDir())
p := NewPackageFromControlFile(s.stanza)
p.Files()[0].Checksums.Size = 5
poolPath, _ := packagePool.Path(p.Files()[0].Filename, p.Files()[0].Checksums.MD5)
list, err := p.DownloadList(packagePool)
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(packagePool)
c.Check(err, IsNil)
c.Check(list, DeepEquals, []PackageDownloadTask{})
}
func (s *PackageSuite) TestVerifyFiles(c *C) {
p := NewPackageFromControlFile(s.stanza)
packagePool := files.NewPackagePool(c.MkDir())
poolPath, _ := packagePool.Path(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.WriteString("abcde")
file.Close()
result, err := p.VerifyFiles(packagePool)
c.Check(err, IsNil)
c.Check(result, Equals, false)
p.Files()[0].Checksums.Size = 5
result, err = p.VerifyFiles(packagePool)
c.Check(err, IsNil)
c.Check(result, Equals, true)
}
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"}
const sourcePackageMeta = `Package: access-modifier-checker
Binary: libaccess-modifier-checker-java, libaccess-modifier-checker-java-doc
Version: 1.0-4
Maintainer: Debian Java Maintainers <pkg-java-maintainers@lists.alioth.debian.org>
Uploaders: James Page <james.page@ubuntu.com>
Build-Depends: cdbs, debhelper (>= 7), default-jdk, maven-debian-helper
Build-Depends-Indep: default-jdk-doc, junit (>= 3.8.1), libannotation-indexer-java (>= 1.3), libannotation-indexer-java-doc, libasm3-java, libmaven-install-plugin-java, libmaven-javadoc-plugin-java, libmaven-scm-java, libmaven2-core-java, libmaven2-core-java-doc, libmetainf-services-java, libmetainf-services-java-doc, libmaven-plugin-tools-java (>= 2.8)
Architecture: all
Standards-Version: 3.9.3
Format: 3.0 (quilt)
Files:
ab56b4d92b40713acc5af89985d4b786 5 access-modifier-checker_1.0-4.debian.tar.gz
900150983cd24fb0d6963f7d28e17f72 3 access-modifier-checker_1.0-4.dsc
e2fc714c4727ee9395f324cd2e7f331f 4 access-modifier-checker_1.0.orig.tar.gz
Dm-Upload-Allowed: yes
Vcs-Browser: http://git.debian.org/?p=pkg-java/access-modifier-checker.git
Vcs-Git: git://git.debian.org/git/pkg-java/access-modifier-checker.git
Checksums-Sha1:
03de6c570bfe24bfc328ccd7ca46b76eadaf4334 5 access-modifier-checker_1.0-4.debian.tar.gz
a9993e364706816aba3e25717850c26c9cd0d89d 3 access-modifier-checker_1.0-4.dsc
81fe8bfe87576c3ecb22426f8e57847382917acf 4 access-modifier-checker_1.0.orig.tar.gz
Checksums-Sha256:
36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c 5 access-modifier-checker_1.0-4.debian.tar.gz
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad 3 access-modifier-checker_1.0-4.dsc
88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589 4 access-modifier-checker_1.0.orig.tar.gz
Homepage: https://github.com/kohsuke/access-modifier
Package-List:
libaccess-modifier-checker-java deb java optional
libaccess-modifier-checker-java-doc deb doc optional
Directory: pool/main/a/access-modifier-checker
Priority: source
Section: java
`
+54
View File
@@ -0,0 +1,54 @@
package deb
import (
"fmt"
"github.com/smira/aptly/utils"
"os/exec"
"regexp"
"strings"
)
var ppaRegexp = regexp.MustCompile("^ppa:([^/]+)/(.+)$")
// ParsePPA converts ppa URL like ppa:user/ppa-name to full HTTP url
func ParsePPA(ppaURL string, config *utils.ConfigStructure) (url string, distribution string, components []string, err error) {
matches := ppaRegexp.FindStringSubmatch(ppaURL)
if matches == nil {
err = fmt.Errorf("unable to parse ppa URL: %v", ppaURL)
return
}
distributorID := config.PpaDistributorID
if distributorID == "" {
distributorID, err = getDistributorID()
if err != nil {
err = fmt.Errorf("unable to figure out Distributor ID: %s, please set config option ppaDistributorID", err)
return
}
}
codename := config.PpaCodename
if codename == "" {
codename, err = getCodename()
if err != nil {
err = fmt.Errorf("unable to figure out Codename: %s, please set config option ppaCodename", err)
return
}
}
distribution = codename
components = []string{"main"}
url = fmt.Sprintf("http://ppa.launchpad.net/%s/%s/%s", matches[1], matches[2], distributorID)
return
}
func getCodename() (string, error) {
out, err := exec.Command("lsb_release", "-sc").Output()
return strings.TrimSpace(string(out)), err
}
func getDistributorID() (string, error) {
out, err := exec.Command("lsb_release", "-si").Output()
return strings.ToLower(strings.TrimSpace(string(out))), err
}
+26
View File
@@ -0,0 +1,26 @@
package deb
import (
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
)
type PpaSuite struct {
config utils.ConfigStructure
}
var _ = Suite(&PpaSuite{})
func (s *PpaSuite) TestParsePPA(c *C) {
_, _, _, err := ParsePPA("ppa:dedeed", &s.config)
c.Check(err, ErrorMatches, "unable to parse ppa URL.*")
s.config.PpaDistributorID = "debian"
s.config.PpaCodename = "wheezy"
url, distribution, components, err := ParsePPA("ppa:user/project", &s.config)
c.Check(err, IsNil)
c.Check(url, Equals, "http://ppa.launchpad.net/user/project/debian")
c.Check(distribution, Equals, "wheezy")
c.Check(components, DeepEquals, []string{"main"})
}
+1089
View File
File diff suppressed because it is too large Load Diff
+805
View File
@@ -0,0 +1,805 @@
package deb
import (
"bytes"
"errors"
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/database"
"github.com/smira/aptly/files"
"github.com/ugorji/go/codec"
"io/ioutil"
. "launchpad.net/gocheck"
"os"
"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{}
func (n *NullSigner) Init() error {
return nil
}
func (n *NullSigner) SetKey(keyRef string) {
}
func (n *NullSigner) SetKeyRing(keyring, secretKeyring string) {
}
func (n *NullSigner) DetachedSign(source string, destination string) error {
return ioutil.WriteFile(destination, []byte{}, 0644)
}
func (n *NullSigner) ClearSign(source string, destination string) error {
return ioutil.WriteFile(destination, []byte{}, 0644)
}
type FakeStorageProvider struct {
storages map[string]aptly.PublishedStorage
}
func (p *FakeStorageProvider) GetPublishedStorage(name string) aptly.PublishedStorage {
storage, ok := p.storages[name]
if !ok {
panic(fmt.Sprintf("unknown storage: %#v", name))
}
return storage
}
type PublishedRepoSuite struct {
PackageListMixinSuite
repo, repo2, repo3, repo4, repo5 *PublishedRepo
root, root2 string
provider *FakeStorageProvider
publishedStorage, publishedStorage2 *files.PublishedStorage
packagePool aptly.PackagePool
localRepo *LocalRepo
snapshot, snapshot2 *Snapshot
db database.Storage
factory *CollectionFactory
packageCollection *PackageCollection
}
var _ = Suite(&PublishedRepoSuite{})
func (s *PublishedRepoSuite) SetUpTest(c *C) {
s.SetUpPackages()
s.db, _ = database.OpenDB(c.MkDir())
s.factory = NewCollectionFactory(s.db)
s.root = c.MkDir()
s.publishedStorage = files.NewPublishedStorage(s.root)
s.root2 = c.MkDir()
s.publishedStorage2 = files.NewPublishedStorage(s.root2)
s.provider = &FakeStorageProvider{map[string]aptly.PublishedStorage{
"": s.publishedStorage,
"files:other": s.publishedStorage2}}
s.packagePool = files.NewPackagePool(s.root)
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
repo.packageRefs = s.reflist
s.factory.RemoteRepoCollection().Add(repo)
s.localRepo = NewLocalRepo("local1", "comment1")
s.localRepo.packageRefs = s.reflist
s.factory.LocalRepoCollection().Add(s.localRepo)
s.snapshot, _ = NewSnapshotFromRepository("snap", repo)
s.factory.SnapshotCollection().Add(s.snapshot)
s.snapshot2, _ = NewSnapshotFromRepository("snap", repo)
s.factory.SnapshotCollection().Add(s.snapshot2)
s.packageCollection = s.factory.PackageCollection()
s.packageCollection.Update(s.p1)
s.packageCollection.Update(s.p2)
s.packageCollection.Update(s.p3)
s.repo, _ = NewPublishedRepo("", "ppa", "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
s.repo2, _ = NewPublishedRepo("", "ppa", "maverick", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.repo3, _ = NewPublishedRepo("", "linux", "natty", nil, []string{"main", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
s.repo4, _ = NewPublishedRepo("", "ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
poolPath, _ := s.packagePool.Path(s.p1.Files()[0].Filename, s.p1.Files()[0].Checksums.MD5)
err := os.MkdirAll(filepath.Dir(poolPath), 0755)
f, err := os.Create(poolPath)
c.Assert(err, IsNil)
f.Close()
}
func (s *PublishedRepoSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoSuite) TestNewPublishedRepo(c *C) {
c.Check(s.repo.sourceItems["main"].snapshot, Equals, s.snapshot)
c.Check(s.repo.SourceKind, Equals, "snapshot")
c.Check(s.repo.Sources["main"], Equals, s.snapshot.UUID)
c.Check(s.repo.Components(), DeepEquals, []string{"main"})
c.Check(s.repo2.sourceItems["main"].localRepo, Equals, s.localRepo)
c.Check(s.repo2.SourceKind, Equals, "local")
c.Check(s.repo2.Sources["main"], Equals, s.localRepo.UUID)
c.Check(s.repo2.sourceItems["main"].packageRefs.Len(), Equals, 3)
c.Check(s.repo2.Components(), DeepEquals, []string{"main"})
c.Check(s.repo.RefList("main").Len(), Equals, 3)
c.Check(s.repo2.RefList("main").Len(), Equals, 3)
c.Check(s.repo3.Sources, DeepEquals, map[string]string{"main": s.snapshot.UUID, "contrib": s.snapshot2.UUID})
c.Check(s.repo3.SourceKind, Equals, "snapshot")
c.Check(s.repo3.sourceItems["main"].snapshot, Equals, s.snapshot)
c.Check(s.repo3.sourceItems["contrib"].snapshot, Equals, s.snapshot2)
c.Check(s.repo3.Components(), DeepEquals, []string{"contrib", "main"})
c.Check(s.repo3.RefList("main").Len(), Equals, 3)
c.Check(s.repo3.RefList("contrib").Len(), Equals, 3)
c.Check(func() { NewPublishedRepo("", ".", "a", nil, nil, nil, s.factory) }, PanicMatches, "publish with empty sources")
c.Check(func() {
NewPublishedRepo("", ".", "a", nil, []string{"main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
}, PanicMatches, "sources and components should be equal in size")
c.Check(func() {
NewPublishedRepo("", ".", "a", nil, []string{"main", "contrib"}, []interface{}{s.localRepo, s.snapshot2}, s.factory)
}, PanicMatches, "interface conversion:.*")
_, err := NewPublishedRepo("", ".", "a", nil, []string{"main", "main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, ErrorMatches, "duplicate component name: main")
}
func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
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", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
if t.errorExpected != "" {
c.Check(err, ErrorMatches, t.errorExpected)
} else {
c.Check(repo.Prefix, Equals, t.expected)
}
}
}
func (s *PublishedRepoSuite) TestDistributionComponentGuessing(c *C) {
repo, err := NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("", "ppa", "wheezy", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "wheezy")
c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"non-free"}, []interface{}{s.snapshot}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Components(), DeepEquals, []string{"non-free"})
repo, err = NewPublishedRepo("", "ppa", "squeeze", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Components(), DeepEquals, []string{"main"})
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
c.Check(err, ErrorMatches, "unable to guess distribution name, please specify explicitly")
s.localRepo.DefaultDistribution = "precise"
s.localRepo.DefaultComponent = "contrib"
s.factory.LocalRepoCollection().Update(s.localRepo)
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "precise")
c.Check(repo.Components(), DeepEquals, []string{"contrib"})
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "squeeze")
c.Check(repo.Components(), DeepEquals, []string{"contrib", "main"})
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"", ""}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
c.Check(err, ErrorMatches, "duplicate component name: main")
}
func (s *PublishedRepoSuite) TestPublish(c *C) {
err := s.repo.Publish(s.packagePool, s.provider, s.factory, &NullSigner{}, nil)
c.Assert(err, IsNil)
c.Check(s.repo.Architectures, DeepEquals, []string{"i386"})
rf, err := os.Open(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"))
c.Assert(err, IsNil)
cfr := NewControlFileReader(rf)
st, err := cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Origin"], Equals, "ppa squeeze")
c.Check(st["Components"], Equals, "main")
c.Check(st["Architectures"], Equals, "i386")
pf, err := os.Open(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/main/binary-i386/Packages"))
c.Assert(err, IsNil)
cfr = NewControlFileReader(pf)
for i := 0; i < 3; i++ {
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Filename"], Equals, "pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb")
}
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Assert(st, IsNil)
drf, err := os.Open(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/main/binary-i386/Release"))
c.Assert(err, IsNil)
cfr = NewControlFileReader(drf)
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Archive"], Equals, "squeeze")
c.Check(st["Architecture"], Equals, "i386")
_, err = os.Stat(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb"))
c.Assert(err, IsNil)
}
func (s *PublishedRepoSuite) TestPublishNoSigner(c *C) {
err := s.repo.Publish(s.packagePool, s.provider, s.factory, nil, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/main/binary-i386/Release"), PathExists)
}
func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
err := s.repo2.Publish(s.packagePool, s.provider, s.factory, nil, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/binary-i386/Release"), PathExists)
}
func (s *PublishedRepoSuite) TestPublishLocalSourceRepo(c *C) {
err := s.repo4.Publish(s.packagePool, s.provider, s.factory, nil, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/source/Release"), PathExists)
}
func (s *PublishedRepoSuite) TestPublishOtherStorage(c *C) {
err := s.repo5.Publish(s.packagePool, s.provider, s.factory, nil, nil)
c.Assert(err, IsNil)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), Not(PathExists))
}
func (s *PublishedRepoSuite) TestString(c *C) {
c.Check(s.repo.String(), Equals,
"ppa/squeeze [] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
c.Check(s.repo2.String(), Equals,
"ppa/maverick [] publishes {main: [local1]: comment1}")
repo, _ := NewPublishedRepo("", "", "squeeze", []string{"s390"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
c.Check(repo.String(), Equals,
"./squeeze [s390] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo, _ = NewPublishedRepo("", "", "squeeze", []string{"i386", "amd64"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
c.Check(repo.String(), Equals,
"./squeeze [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo.Origin = "myorigin"
c.Check(repo.String(), Equals,
"./squeeze (origin: myorigin) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
repo.Label = "mylabel"
c.Check(repo.String(), Equals,
"./squeeze (origin: myorigin, label: mylabel) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
c.Check(s.repo3.String(), Equals,
"linux/natty [] publishes {contrib: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}, {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
c.Check(s.repo5.String(), Equals,
"files:other:ppa/maverick [source] publishes {main: [local1]: comment1}")
}
func (s *PublishedRepoSuite) TestKey(c *C) {
c.Check(s.repo.Key(), DeepEquals, []byte("Uppa>>squeeze"))
c.Check(s.repo5.Key(), DeepEquals, []byte("Ufiles:other:ppa>>maverick"))
}
func (s *PublishedRepoSuite) TestRefKey(c *C) {
c.Check(s.repo.RefKey(""), DeepEquals, []byte("E"+s.repo.UUID))
c.Check(s.repo.RefKey("main"), DeepEquals, []byte("E"+s.repo.UUID+"main"))
}
func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
encoded := s.repo.Encode()
repo := &PublishedRepo{}
err := repo.Decode(encoded)
s.repo.sourceItems = nil
c.Assert(err, IsNil)
c.Assert(repo, DeepEquals, s.repo)
encoded2 := s.repo2.Encode()
repo2 := &PublishedRepo{}
err = repo2.Decode(encoded2)
s.repo2.sourceItems = nil
c.Assert(err, IsNil)
c.Assert(repo2, DeepEquals, s.repo2)
}
type PublishedRepoCollectionSuite struct {
PackageListMixinSuite
db database.Storage
factory *CollectionFactory
snapshotCollection *SnapshotCollection
collection *PublishedRepoCollection
snap1, snap2 *Snapshot
localRepo *LocalRepo
repo1, repo2, repo3, repo4, repo5 *PublishedRepo
}
var _ = Suite(&PublishedRepoCollectionSuite{})
func (s *PublishedRepoCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.factory = NewCollectionFactory(s.db)
s.snapshotCollection = s.factory.SnapshotCollection()
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.localRepo = NewLocalRepo("local1", "comment1")
s.factory.LocalRepoCollection().Add(s.localRepo)
s.repo1, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo2, _ = NewPublishedRepo("", "", "anaconda", []string{}, []string{"main", "contrib"}, []interface{}{s.snap2, s.snap1}, s.factory)
s.repo3, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap2}, s.factory)
s.repo4, _ = NewPublishedRepo("", "ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
s.collection = s.factory.PublishedRepoCollection()
}
func (s *PublishedRepoCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoCollectionSuite) TestAddByStoragePrefixDistribution(c *C) {
r, err := s.collection.ByStoragePrefixDistribution("", "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)
c.Assert(s.collection.Add(s.repo4), IsNil)
c.Assert(s.collection.Add(s.repo5), IsNil)
r, err = s.collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Assert(err, IsNil)
err = s.collection.LoadComplete(r, s.factory)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, s.repo1.String())
collection := NewPublishedRepoCollection(s.db)
r, err = collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Assert(err, IsNil)
err = s.collection.LoadComplete(r, s.factory)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, s.repo1.String())
r, err = s.collection.ByStoragePrefixDistribution("files:other", "ppa", "precise")
c.Check(r.String(), Equals, s.repo5.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.factory)
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)
c.Assert(s.collection.Update(s.repo4), IsNil)
collection := NewPublishedRepoCollection(s.db)
r, err := collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Assert(err, IsNil)
c.Assert(r.sourceItems["main"].snapshot, IsNil)
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
c.Assert(r.Sources["main"], Equals, s.repo1.sourceItems["main"].snapshot.UUID)
c.Assert(r.RefList("main").Len(), Equals, 0)
r, err = collection.ByStoragePrefixDistribution("", "ppa", "precise")
c.Assert(err, IsNil)
c.Assert(r.sourceItems["main"].localRepo, IsNil)
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
c.Assert(r.sourceItems["main"].localRepo.UUID, Equals, s.repo4.sourceItems["main"].localRepo.UUID)
c.Assert(r.sourceItems["main"].packageRefs.Len(), Equals, 0)
c.Assert(r.RefList("main").Len(), Equals, 0)
}
func (s *PublishedRepoCollectionSuite) TestLoadPre0_6(c *C) {
type oldPublishedRepo struct {
UUID string
Prefix string
Distribution string
Origin string
Label string
Architectures []string
SourceKind string
Component string
SourceUUID string `codec:"SnapshotUUID"`
}
old := oldPublishedRepo{
UUID: s.repo1.UUID,
Prefix: "ppa",
Distribution: "anaconda",
Architectures: []string{"i386"},
SourceKind: "local",
Component: "contrib",
SourceUUID: s.localRepo.UUID,
}
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(&old)
c.Assert(s.db.Put(s.repo1.Key(), buf.Bytes()), IsNil)
c.Assert(s.db.Put(s.repo1.RefKey(""), s.localRepo.RefList().Encode()), IsNil)
collection := NewPublishedRepoCollection(s.db)
repo, err := collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Check(err, IsNil)
c.Check(repo.Component, Equals, "")
c.Check(repo.SourceUUID, Equals, "")
c.Check(repo.Sources, DeepEquals, map[string]string{"contrib": s.localRepo.UUID})
c.Check(collection.LoadComplete(repo, s.factory), IsNil)
c.Check(repo.sourceItems["contrib"].localRepo.UUID, Equals, s.localRepo.UUID)
c.Check(repo.RefList("contrib").Len(), Equals, 0)
}
func (s *PublishedRepoCollectionSuite) TestForEachAndLen(c *C) {
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, s.repo2})
c.Check(s.collection.BySnapshot(s.snap2), DeepEquals, []*PublishedRepo{s.repo2})
}
func (s *PublishedRepoCollectionSuite) TestByLocalRepo(c *C) {
c.Check(s.collection.Add(s.repo1), IsNil)
c.Check(s.collection.Add(s.repo4), IsNil)
c.Check(s.collection.Add(s.repo5), IsNil)
c.Check(s.collection.ByLocalRepo(s.localRepo), DeepEquals, []*PublishedRepo{s.repo4, s.repo5})
}
type PublishedRepoRemoveSuite struct {
PackageListMixinSuite
db database.Storage
factory *CollectionFactory
snapshotCollection *SnapshotCollection
collection *PublishedRepoCollection
root, root2 string
provider *FakeStorageProvider
publishedStorage, publishedStorage2 *files.PublishedStorage
snap1 *Snapshot
repo1, repo2, repo3, repo4, repo5 *PublishedRepo
}
var _ = Suite(&PublishedRepoRemoveSuite{})
func (s *PublishedRepoRemoveSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.factory = NewCollectionFactory(s.db)
s.snapshotCollection = s.factory.SnapshotCollection()
s.snap1 = NewSnapshotFromPackageList("snap1", []*Snapshot{}, NewPackageList(), "desc1")
s.snapshotCollection.Add(s.snap1)
s.repo1, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo2, _ = NewPublishedRepo("", "", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo3, _ = NewPublishedRepo("", "ppa", "meduza", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
s.repo4, _ = NewPublishedRepo("", "ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
s.collection = s.factory.PublishedRepoCollection()
s.collection.Add(s.repo1)
s.collection.Add(s.repo2)
s.collection.Add(s.repo3)
s.collection.Add(s.repo4)
s.collection.Add(s.repo5)
s.root = c.MkDir()
s.publishedStorage = files.NewPublishedStorage(s.root)
s.publishedStorage.MkDir("ppa/dists/anaconda")
s.publishedStorage.MkDir("ppa/dists/meduza")
s.publishedStorage.MkDir("ppa/dists/osminog")
s.publishedStorage.MkDir("ppa/pool/main")
s.publishedStorage.MkDir("ppa/pool/contrib")
s.publishedStorage.MkDir("dists/anaconda")
s.publishedStorage.MkDir("pool/main")
s.root2 = c.MkDir()
s.publishedStorage2 = files.NewPublishedStorage(s.root2)
s.publishedStorage2.MkDir("ppa/dists/osminog")
s.publishedStorage2.MkDir("ppa/pool/contrib")
s.provider = &FakeStorageProvider{map[string]aptly.PublishedStorage{
"": s.publishedStorage,
"files:other": s.publishedStorage2}}
}
func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
s.repo1.RemoveFiles(s.provider, false, []string{}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
s.repo1.RemoveFiles(s.provider, false, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithTwoPools(c *C) {
s.repo1.RemoveFiles(s.provider, false, []string{"main", "contrib"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
s.repo1.RemoveFiles(s.provider, true, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
s.repo2.RemoveFiles(s.provider, true, []string{"main"}, nil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
err := s.collection.Remove(s.provider, "", "ppa", "anaconda", s.factory, nil)
c.Check(err, IsNil)
_, err = s.collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
collection := NewPublishedRepoCollection(s.db)
_, err = collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
err = s.collection.Remove(s.provider, "", "ppa", "anaconda", s.factory, nil)
c.Check(err, ErrorMatches, ".*not found")
err = s.collection.Remove(s.provider, "", "ppa", "meduza", s.factory, nil)
c.Check(err, IsNil)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveRepo3(c *C) {
err := s.collection.Remove(s.provider, "", ".", "anaconda", s.factory, nil)
c.Check(err, IsNil)
_, err = s.collection.ByStoragePrefixDistribution("", ".", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
collection := NewPublishedRepoCollection(s.db)
_, err = collection.ByStoragePrefixDistribution("", ".", "anaconda")
c.Check(err, ErrorMatches, ".*not found")
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
}
func (s *PublishedRepoRemoveSuite) TestRemoveRepo5(c *C) {
err := s.collection.Remove(s.provider, "files:other", "ppa", "osminog", s.factory, nil)
c.Check(err, IsNil)
_, err = s.collection.ByStoragePrefixDistribution("files:other", "ppa", "osminog")
c.Check(err, ErrorMatches, ".*not found")
collection := NewPublishedRepoCollection(s.db)
_, err = collection.ByStoragePrefixDistribution("files:other", "ppa", "osminog")
c.Check(err, ErrorMatches, ".*not found")
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/"), PathExists)
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/"), PathExists)
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), Not(PathExists))
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
}
+262
View File
@@ -0,0 +1,262 @@
package deb
import (
"fmt"
"path/filepath"
"regexp"
"strings"
)
// PackageQuery is interface of predicate on Package
type PackageQuery interface {
// Matches calculates match of condition against package
Matches(pkg *Package) bool
// Fast returns if search strategy is possible for this query
Fast() bool
// Query performs search on package list
Query(list *PackageList) *PackageList
// String interface
String() string
}
// OrQuery is L | R
type OrQuery struct {
L, R PackageQuery
}
// AndQuery is L , R
type AndQuery struct {
L, R PackageQuery
}
// NotQuery is ! Q
type NotQuery struct {
Q PackageQuery
}
// FieldQuery is generic request against field
type FieldQuery struct {
Field string
Relation int
Value string
Regexp *regexp.Regexp `codec:"-"`
}
// PkgQuery is search request against specific package
type PkgQuery struct {
Pkg string
Version string
Arch string
}
// DependencyQuery is generic Debian-dependency like query
type DependencyQuery struct {
Dep Dependency
}
// Matches if any of L, R matches
func (q *OrQuery) Matches(pkg *Package) bool {
return q.L.Matches(pkg) || q.R.Matches(pkg)
}
// Fast is true only if both parts are fast
func (q *OrQuery) Fast() bool {
return q.L.Fast() && q.R.Fast()
}
// Query strategy depends on nodes
func (q *OrQuery) Query(list *PackageList) (result *PackageList) {
if q.Fast() {
result = q.L.Query(list)
result.Append(q.R.Query(list))
} else {
result = list.Scan(q)
}
return
}
// String interface
func (q *OrQuery) String() string {
return fmt.Sprintf("(%s) | (%s)", q.L, q.R)
}
// Matches if both of L, R matches
func (q *AndQuery) Matches(pkg *Package) bool {
return q.L.Matches(pkg) && q.R.Matches(pkg)
}
// Fast is true if any of the parts are fast
func (q *AndQuery) Fast() bool {
return q.L.Fast() || q.R.Fast()
}
// Query strategy depends on nodes
func (q *AndQuery) Query(list *PackageList) (result *PackageList) {
if !q.Fast() {
result = list.Scan(q)
} else {
if q.L.Fast() {
result = q.L.Query(list)
result = result.Scan(q.R)
} else {
result = q.R.Query(list)
result = result.Scan(q.L)
}
}
return
}
// String interface
func (q *AndQuery) String() string {
return fmt.Sprintf("(%s), (%s)", q.L, q.R)
}
// Matches if not matches
func (q *NotQuery) Matches(pkg *Package) bool {
return !q.Q.Matches(pkg)
}
// Fast is false
func (q *NotQuery) Fast() bool {
return false
}
// Query strategy is scan always
func (q *NotQuery) Query(list *PackageList) (result *PackageList) {
result = list.Scan(q)
return
}
// String interface
func (q *NotQuery) String() string {
return fmt.Sprintf("!(%s)", q.Q)
}
// Matches on generic field
func (q *FieldQuery) Matches(pkg *Package) bool {
if q.Field == "$Version" {
return pkg.MatchesDependency(Dependency{Pkg: pkg.Name, Relation: q.Relation, Version: q.Value, Regexp: q.Regexp})
}
if q.Field == "$Architecture" && q.Relation == VersionEqual {
return pkg.MatchesArchitecture(q.Value)
}
field := pkg.GetField(q.Field)
switch q.Relation {
case VersionDontCare:
return field != ""
case VersionEqual:
return field == q.Value
case VersionGreater:
return field > q.Value
case VersionGreaterOrEqual:
return field >= q.Value
case VersionLess:
return field < q.Value
case VersionLessOrEqual:
return field <= q.Value
case VersionPatternMatch:
matched, err := filepath.Match(q.Value, field)
return err == nil && matched
case VersionRegexp:
if q.Regexp == nil {
q.Regexp = regexp.MustCompile(q.Value)
}
return q.Regexp.FindStringIndex(field) != nil
}
panic("unknown relation")
}
// Query runs iteration through list
func (q *FieldQuery) Query(list *PackageList) (result *PackageList) {
result = list.Scan(q)
return
}
// Fast depends on the query
func (q *FieldQuery) Fast() bool {
return false
}
// String interface
func (q *FieldQuery) String() string {
escape := func(val string) string {
if strings.IndexAny(val, "()|,!{} \t\n") != -1 {
return "'" + strings.Replace(strings.Replace(val, "\\", "\\\\", -1), "'", "\\'", -1) + "'"
}
return val
}
var op string
switch q.Relation {
case VersionEqual:
op = "="
case VersionGreater:
op = ">>"
case VersionLess:
op = "<<"
case VersionRegexp:
op = "~"
case VersionPatternMatch:
op = "%"
case VersionGreaterOrEqual:
op = ">="
case VersionLessOrEqual:
op = "<="
}
return fmt.Sprintf("%s (%s %s)", escape(q.Field), op, escape(q.Value))
}
// Matches on dependency condition
func (q *DependencyQuery) Matches(pkg *Package) bool {
return pkg.MatchesDependency(q.Dep)
}
// Fast is always true for dependency query
func (q *DependencyQuery) Fast() bool {
return true
}
// Query runs PackageList.Search
func (q *DependencyQuery) Query(list *PackageList) (result *PackageList) {
result = NewPackageList()
for _, pkg := range list.Search(q.Dep, true) {
result.Add(pkg)
}
return
}
// String interface
func (q *DependencyQuery) String() string {
return q.Dep.String()
}
// Matches on specific properties
func (q *PkgQuery) Matches(pkg *Package) bool {
return pkg.Name == q.Pkg && pkg.Version == q.Version && pkg.Architecture == q.Arch
}
// Fast is always true for package query
func (q *PkgQuery) Fast() bool {
return true
}
// Query looks up specific package
func (q *PkgQuery) Query(list *PackageList) (result *PackageList) {
result = NewPackageList()
pkg := list.packages["P"+q.Arch+" "+q.Pkg+" "+q.Version]
if pkg != nil {
result.Add(pkg)
}
return
}
// String interface
func (q *PkgQuery) String() string {
return fmt.Sprintf("%s_%s_%s", q.Pkg, q.Version, q.Arch)
}
+342
View File
@@ -0,0 +1,342 @@
package deb
import (
"bytes"
"github.com/ugorji/go/codec"
"sort"
)
// PackageRefList is a list of keys of packages, this is basis for snapshot
// and similar stuff
//
// Refs are sorted in lexicographical order
type PackageRefList struct {
// List of package keys
Refs [][]byte
}
// Verify interface
var (
_ sort.Interface = &PackageRefList{}
)
// NewPackageRefList creates empty PackageRefList
func NewPackageRefList() *PackageRefList {
return &PackageRefList{}
}
// NewPackageRefListFromPackageList creates PackageRefList from PackageList
func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList {
reflist := &PackageRefList{}
reflist.Refs = make([][]byte, list.Len())
i := 0
for _, p := range list.packages {
reflist.Refs[i] = p.Key("")
i++
}
sort.Sort(reflist)
return reflist
}
// Len returns number of refs
func (l *PackageRefList) Len() int {
return len(l.Refs)
}
// Swap swaps two refs
func (l *PackageRefList) Swap(i, j int) {
l.Refs[i], l.Refs[j] = l.Refs[j], l.Refs[i]
}
// Compare compares two refs in lexographical order
func (l *PackageRefList) Less(i, j int) bool {
return bytes.Compare(l.Refs[i], l.Refs[j]) < 0
}
// Encode does msgpack encoding of PackageRefList
func (l *PackageRefList) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(l)
return buf.Bytes()
}
// Decode decodes msgpack representation into PackageRefLit
func (l *PackageRefList) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(l)
}
// ForEach calls handler for each package ref in list
func (l *PackageRefList) ForEach(handler func([]byte) error) error {
var err error
for _, p := range l.Refs {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// Substract returns all packages in l that are not in r
func (l *PackageRefList) Substract(r *PackageRefList) *PackageRefList {
result := &PackageRefList{Refs: make([][]byte, 0, 128)}
// pointer to left and right reflists
il, ir := 0, 0
// length of reflists
ll, lr := l.Len(), r.Len()
for il < ll || ir < lr {
if il == ll {
// left list exhausted, we got the result
break
}
if ir == lr {
// right list exhausted, append what is left to result
result.Refs = append(result.Refs, l.Refs[il:]...)
break
}
rel := bytes.Compare(l.Refs[il], r.Refs[ir])
if rel == 0 {
// r contains entry from l, so we skip it
il++
ir++
} else if rel < 0 {
// item il is not in r, append
result.Refs = append(result.Refs, l.Refs[il])
il++
} else {
// skip over to next item in r
ir++
}
}
return result
}
// 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
}
}
// otherwise pl or pr is missing on one of the sides
if rel < 0 {
// compaction: +(,A) -(B,) --> !(A,B)
if len(result) > 0 && result[len(result)-1].Left == nil && result[len(result)-1].Right.Name == pl.Name &&
result[len(result)-1].Right.Architecture == pl.Architecture {
result[len(result)-1] = PackageDiff{Left: pl, Right: result[len(result)-1].Right}
} else {
result = append(result, PackageDiff{Left: pl, Right: nil})
}
il++
pl = nil
} else {
// compaction: -(A,) +(,B) --> !(A,B)
if len(result) > 0 && result[len(result)-1].Right == nil && result[len(result)-1].Left.Name == pr.Name &&
result[len(result)-1].Left.Architecture == pr.Architecture {
result[len(result)-1] = PackageDiff{Left: result[len(result)-1].Left, Right: pr}
} else {
result = append(result, PackageDiff{Left: nil, Right: pr})
}
ir++
pr = nil
}
}
}
return
}
// Merge merges reflist r into current reflist. If overrideMatching, merge
// replaces matching packages (by architecture/name) with reference from r.
// Otherwise, all packages are saved.
func (l *PackageRefList) Merge(r *PackageRefList, overrideMatching bool) (result *PackageRefList) {
var overriddenArch, overridenName []byte
// 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++
overridenName = nil
overriddenArch = nil
} else {
if overrideMatching {
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.Equal(archL, overriddenArch) && bytes.Equal(nameL, overridenName) {
// this package has already been overriden on the right
il++
continue
}
if bytes.Equal(archL, archR) && bytes.Equal(nameL, nameR) {
// override with package from the right
result.Refs = append(result.Refs, r.Refs[ir])
il++
ir++
overriddenArch = archL
overridenName = nameL
continue
}
}
// 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++
overridenName = nil
overriddenArch = nil
}
}
}
return
}
// FilterLatestRefs takes in a reflist with potentially multiples of the same
// packages and reduces it to only the latest of each package. The operations
// are done in-place. This implements a "latest wins" approach which can be used
// while merging two or more snapshots together.
func FilterLatestRefs(r *PackageRefList) {
var (
lastArch, lastName, lastVer []byte
arch, name, ver []byte
parts [][]byte
)
for i := 0; i < len(r.Refs); i++ {
parts = bytes.Split(r.Refs[i][1:], []byte(" "))
arch, name, ver = parts[0], parts[1], parts[2]
if bytes.Equal(arch, lastArch) && bytes.Equal(name, lastName) {
// Two packages are identical, check version and only one wins
vres := CompareVersions(string(ver), string(lastVer))
// Remove the older refs from the result
if vres > 0 {
// ver[i] > ver[i-1], remove element i-1
r.Refs = append(r.Refs[:i-1], r.Refs[i:]...)
} else {
// ver[i] < ver[i-1], remove element i
r.Refs = append(r.Refs[:i], r.Refs[i+1:]...)
arch, name, ver = lastArch, lastName, lastVer
}
// Compensate for the reduced set
i -= 1
}
lastArch, lastName, lastVer = arch, name, ver
}
return
}
+304
View File
@@ -0,0 +1,304 @@
package deb
import (
"errors"
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
)
type PackageRefListSuite struct {
// Simple list with "real" packages from stanzas
list *PackageList
p1, p2, p3, p4, p5, p6 *Package
}
var _ = Suite(&PackageRefListSuite{})
func toStrSlice(reflist *PackageRefList) (result []string) {
result = make([]string, reflist.Len())
for i, r := range reflist.Refs {
result[i] = string(r)
}
return
}
func (s *PackageRefListSuite) SetUpTest(c *C) {
s.list = NewPackageList()
s.p1 = NewPackageFromControlFile(packageStanza.Copy())
s.p2 = NewPackageFromControlFile(packageStanza.Copy())
stanza := packageStanza.Copy()
stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers"
s.p5 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Version"] = "99.1"
s.p6 = NewPackageFromControlFile(stanza)
}
func (s *PackageRefListSuite) 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, nil)
c.Assert(err, ErrorMatches, "unable to load package with key.*")
coll.Update(s.p5)
coll.Update(s.p6)
list, err := NewPackageListFromRefList(reflist, coll, nil)
c.Assert(err, IsNil)
c.Check(list.Len(), Equals, 4)
c.Check(list.Add(s.p4), ErrorMatches, "conflict in package.*")
list, err = NewPackageListFromRefList(nil, coll, nil)
c.Assert(err, IsNil)
c.Check(list.Len(), Equals, 0)
}
func (s *PackageRefListSuite) TestNewPackageRefList(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
c.Assert(reflist.Len(), Equals, 4)
c.Check(reflist.Refs[0], DeepEquals, []byte(s.p1.Key("")))
c.Check(reflist.Refs[1], DeepEquals, []byte(s.p6.Key("")))
c.Check(reflist.Refs[2], DeepEquals, []byte(s.p5.Key("")))
c.Check(reflist.Refs[3], DeepEquals, []byte(s.p3.Key("")))
reflist = NewPackageRefList()
c.Check(reflist.Len(), Equals, 0)
}
func (s *PackageRefListSuite) TestPackageRefListEncodeDecode(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
reflist2 := &PackageRefList{}
err := reflist2.Decode(reflist.Encode())
c.Assert(err, IsNil)
c.Check(reflist2.Len(), Equals, reflist.Len())
c.Check(reflist2.Refs, DeepEquals, reflist.Refs)
}
func (s *PackageRefListSuite) TestPackageRefListForeach(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
Len := 0
err := reflist.ForEach(func([]byte) error {
Len++
return nil
})
c.Check(Len, Equals, 4)
c.Check(err, IsNil)
e := errors.New("b")
err = reflist.ForEach(func([]byte) error {
return e
})
c.Check(err, Equals, e)
}
func (s *PackageRefListSuite) TestSubstract(c *C) {
r1 := []byte("r1")
r2 := []byte("r2")
r3 := []byte("r3")
r4 := []byte("r4")
r5 := []byte("r5")
empty := &PackageRefList{Refs: [][]byte{}}
l1 := &PackageRefList{Refs: [][]byte{r1, r2, r3, r4}}
l2 := &PackageRefList{Refs: [][]byte{r1, r3}}
l3 := &PackageRefList{Refs: [][]byte{r2, r4}}
l4 := &PackageRefList{Refs: [][]byte{r4, r5}}
l5 := &PackageRefList{Refs: [][]byte{r1, r2, r3}}
c.Check(l1.Substract(empty), DeepEquals, l1)
c.Check(l1.Substract(l2), DeepEquals, l3)
c.Check(l1.Substract(l3), DeepEquals, l2)
c.Check(l1.Substract(l4), DeepEquals, l5)
c.Check(empty.Substract(l1), DeepEquals, empty)
c.Check(l2.Substract(l3), DeepEquals, l2)
}
func (s *PackageRefListSuite) 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 *PackageRefListSuite) 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, true)
mergeBA := reflistB.Merge(reflistA, true)
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"})
mergeABall := reflistA.Merge(reflistB, false)
mergeBAall := reflistB.Merge(reflistA, false)
c.Check(mergeABall, DeepEquals, mergeBAall)
c.Check(toStrSlice(mergeBAall), DeepEquals,
[]string{"Pall data 1.1~bp1", "Pamd64 app 1.1~bp2", "Pi386 app 1.1~bp1", "Pi386 app 1.1~bp2", "Pi386 dpkg 1.0", "Pi386 dpkg 1.7", "Pi386 lib 1.0", "Psparc xyz 1.0"})
}
func (s *PackageRefListSuite) TestFilterLatestRefs(c *C) {
packages := []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386"},
&Package{Name: "lib", Version: "1.2~bp1", Architecture: "i386"},
&Package{Name: "lib", Version: "1.2", Architecture: "i386"},
&Package{Name: "dpkg", Version: "1.2", Architecture: "i386"},
&Package{Name: "dpkg", Version: "1.3", Architecture: "i386"},
&Package{Name: "dpkg", Version: "1.3~bp2", Architecture: "i386"},
&Package{Name: "dpkg", Version: "1.5", Architecture: "i386"},
&Package{Name: "dpkg", Version: "1.6", Architecture: "i386"},
}
rl := NewPackageList()
rl.Add(packages[0])
rl.Add(packages[1])
rl.Add(packages[2])
rl.Add(packages[3])
rl.Add(packages[4])
rl.Add(packages[5])
rl.Add(packages[6])
rl.Add(packages[7])
result := NewPackageRefListFromPackageList(rl)
FilterLatestRefs(result)
c.Check(toStrSlice(result), DeepEquals,
[]string{"Pi386 dpkg 1.6", "Pi386 lib 1.2"})
}
+654
View File
@@ -0,0 +1,654 @@
package deb
import (
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/database"
"github.com/smira/aptly/http"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"log"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
)
// RemoteRepo represents remote (fetchable) Debian repository.
//
// Repostitory could be filtered when fetching by components, architectures
type RemoteRepo struct {
// Permanent internal ID
UUID string
// User-assigned name
Name string
// Root of Debian archive, URL
ArchiveRoot string
// Distribution name, e.g. squeeze
Distribution string
// List of components to fetch, if empty, then fetch all components
Components []string
// List of architectures to fetch, if empty, then fetch all architectures
Architectures []string
// Should we download sources?
DownloadSources bool
// Meta-information about repository
Meta Stanza
// Last update date
LastDownloadDate time.Time
// Checksums for release files
ReleaseFiles map[string]utils.ChecksumInfo
// Filter for packages
Filter string
// FilterWithDeps to include dependencies from filter query
FilterWithDeps bool
// "Snapshot" of current list of packages
packageRefs *PackageRefList
// Parsed archived root
archiveRootURL *url.URL
}
// NewRemoteRepo creates new instance of Debian remote repository with specified params
func NewRemoteRepo(name string, archiveRoot string, distribution string, components []string,
architectures []string, downloadSources bool) (*RemoteRepo, error) {
result := &RemoteRepo{
UUID: uuid.New(),
Name: name,
ArchiveRoot: archiveRoot,
Distribution: distribution,
Components: components,
Architectures: architectures,
DownloadSources: downloadSources,
}
err := result.prepare()
if err != nil {
return nil, err
}
if strings.HasSuffix(result.Distribution, "/") || strings.HasPrefix(result.Distribution, ".") {
// flat repo
if !strings.HasPrefix(result.Distribution, ".") {
result.Distribution = "./" + 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
}
func (repo *RemoteRepo) prepare() error {
var err error
// Add final / to URL
if !strings.HasSuffix(repo.ArchiveRoot, "/") {
repo.ArchiveRoot = repo.ArchiveRoot + "/"
}
repo.archiveRootURL, err = url.Parse(repo.ArchiveRoot)
return err
}
// String interface
func (repo *RemoteRepo) String() string {
srcFlag := ""
if repo.DownloadSources {
srcFlag = " [src]"
}
distribution := repo.Distribution
if distribution == "" {
distribution = "./"
}
return fmt.Sprintf("[%s]: %s %s%s", repo.Name, repo.ArchiveRoot, distribution, srcFlag)
}
// IsFlat determines if repository is flat
func (repo *RemoteRepo) IsFlat() bool {
// aptly < 0.5.1 had Distribution = "" for flat repos
// aptly >= 0.5.1 had Distribution = "./[path]/" for flat repos
return repo.Distribution == "" || (strings.HasPrefix(repo.Distribution, ".") && strings.HasSuffix(repo.Distribution, "/"))
}
// NumPackages return number of packages retrived from remote repo
func (repo *RemoteRepo) NumPackages() int {
if repo.packageRefs == nil {
return 0
}
return repo.packageRefs.Len()
}
// RefList returns package list for repo
func (repo *RemoteRepo) RefList() *PackageRefList {
return repo.packageRefs
}
// 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: filepath.Join(repo.Distribution, name)}
}
return repo.archiveRootURL.ResolveReference(path)
}
// FlatBinaryURL returns URL to Packages files for flat repo
func (repo *RemoteRepo) FlatBinaryURL() *url.URL {
path := &url.URL{Path: filepath.Join(repo.Distribution, "Packages")}
return repo.archiveRootURL.ResolveReference(path)
}
// FlatSourcesURL returns URL to Sources files for flat repo
func (repo *RemoteRepo) FlatSourcesURL() *url.URL {
path := &url.URL{Path: filepath.Join(repo.Distribution, "Sources")}
return repo.archiveRootURL.ResolveReference(path)
}
// BinaryURL returns URL of Packages files for given component and
// architecture
func (repo *RemoteRepo) BinaryURL(component string, architecture string) *url.URL {
path := &url.URL{Path: fmt.Sprintf("dists/%s/%s/binary-%s/Packages", repo.Distribution, component, architecture)}
return repo.archiveRootURL.ResolveReference(path)
}
// SourcesURL returns URL of Sources files for given component
func (repo *RemoteRepo) SourcesURL(component string) *url.URL {
path := &url.URL{Path: fmt.Sprintf("dists/%s/%s/source/Sources", repo.Distribution, component)}
return repo.archiveRootURL.ResolveReference(path)
}
// PackageURL returns URL of package file relative to repository root
// architecture
func (repo *RemoteRepo) PackageURL(filename string) *url.URL {
path := &url.URL{Path: filename}
return repo.archiveRootURL.ResolveReference(path)
}
// Fetch updates information about repository
func (repo *RemoteRepo) Fetch(d aptly.Downloader, verifier utils.Verifier) error {
var (
release, inrelease, releasesig *os.File
err error
)
if verifier == nil {
// 0. Just download release file to temporary URL
release, err = http.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil {
return err
}
} else {
// 1. try InRelease file
inrelease, err = http.DownloadTemp(d, repo.ReleaseURL("InRelease").String())
if err != nil {
goto splitsignature
}
defer inrelease.Close()
err = verifier.VerifyClearsigned(inrelease)
if err != nil {
goto splitsignature
}
inrelease.Seek(0, 0)
release, err = verifier.ExtractClearsigned(inrelease)
if err != nil {
goto splitsignature
}
goto ok
splitsignature:
// 2. try Release + Release.gpg
release, err = http.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil {
return err
}
releasesig, err = http.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()
sreader := NewControlFileReader(release)
stanza, err := sreader.ReadStanza()
if err != nil {
return err
}
if !repo.IsFlat() {
architectures := strings.Split(stanza["Architectures"], " ")
if len(repo.Architectures) == 0 {
repo.Architectures = architectures
} else {
err = utils.StringsIsSubset(repo.Architectures, architectures,
fmt.Sprintf("architecture %%s not available in repo %s", repo))
if err != nil {
return err
}
}
components := strings.Split(stanza["Components"], " ")
for i := range components {
components[i] = path.Base(components[i])
}
if len(repo.Components) == 0 {
repo.Components = components
} else {
err = utils.StringsIsSubset(repo.Components, components,
fmt.Sprintf("component %%s not available in repo %s", repo))
if err != nil {
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)
}
var size int64
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
}
repo.Meta = stanza
return nil
}
// Download downloads all repo files
func (repo *RemoteRepo) Download(progress aptly.Progress, d aptly.Downloader, collectionFactory *CollectionFactory,
packagePool aptly.PackagePool, ignoreMismatch bool, dependencyOptions int, filterQuery PackageQuery) error {
list := NewPackageList()
progress.Printf("Downloading & parsing package files...\n")
// Download and parse all Packages & Source files
packagesURLs := [][]string{}
if repo.IsFlat() {
packagesURLs = append(packagesURLs, []string{repo.FlatBinaryURL().String(), "binary"})
if repo.DownloadSources {
packagesURLs = append(packagesURLs, []string{repo.FlatSourcesURL().String(), "source"})
}
} else {
for _, component := range repo.Components {
for _, architecture := range repo.Architectures {
packagesURLs = append(packagesURLs, []string{repo.BinaryURL(component, architecture).String(), "binary"})
}
if repo.DownloadSources {
packagesURLs = append(packagesURLs, []string{repo.SourcesURL(component).String(), "source"})
}
}
}
for _, info := range packagesURLs {
url, kind := info[0], info[1]
packagesReader, packagesFile, err := http.DownloadTryCompression(d, url, repo.ReleaseFiles, ignoreMismatch)
if err != nil {
return err
}
defer packagesFile.Close()
stat, _ := packagesFile.Stat()
progress.InitBar(stat.Size(), true)
sreader := NewControlFileReader(packagesReader)
for {
stanza, err := sreader.ReadStanza()
if err != nil {
return err
}
if stanza == nil {
break
}
off, _ := packagesFile.Seek(0, 1)
progress.SetBar(int(off))
var p *Package
if kind == "binary" {
p = NewPackageFromControlFile(stanza)
} else if kind == "source" {
p, err = NewSourcePackageFromControlFile(stanza)
if err != nil {
return err
}
}
err = list.Add(p)
if err != nil {
return err
}
err = collectionFactory.PackageCollection().Update(p)
if err != nil {
return err
}
}
progress.ShutdownBar()
}
var err error
if repo.Filter != "" {
progress.Printf("Applying filter...\n")
list.PrepareIndex()
emptyList := NewPackageList()
emptyList.PrepareIndex()
origPackages := list.Len()
list, err = list.Filter([]PackageQuery{filterQuery}, repo.FilterWithDeps, emptyList, dependencyOptions, repo.Architectures)
if err != nil {
return err
}
progress.Printf("Packages filtered: %d -> %d.\n", origPackages, list.Len())
}
progress.Printf("Building download queue...\n")
// Build download queue
queued := make(map[string]PackageDownloadTask, list.Len())
count := 0
downloadSize := int64(0)
err = list.ForEach(func(p *Package) error {
list, err2 := p.DownloadList(packagePool)
if err2 != nil {
return err2
}
p.files = nil
for _, task := range list {
key := task.RepoURI + "-" + task.DestinationPath
_, found := queued[key]
if !found {
count++
downloadSize += task.Checksums.Size
queued[key] = task
}
}
return nil
})
if err != nil {
return fmt.Errorf("unable to build download queue: %s", err)
}
repo.packageRefs = NewPackageRefListFromPackageList(list)
// free up package list, we don't need it after this point
list = nil
progress.Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
progress.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)
}
// We don't need queued after this point
queued = nil
// Wait for all downloads to finish
errors := make([]string, 0)
for count > 0 {
err = <-ch
if err != nil {
errors = append(errors, err.Error())
}
count--
}
progress.ShutdownBar()
if len(errors) > 0 {
return fmt.Errorf("download errors:\n %s\n", strings.Join(errors, "\n "))
}
repo.LastDownloadDate = time.Now()
return nil
}
// Encode does msgpack encoding of RemoteRepo
func (repo *RemoteRepo) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(repo)
return buf.Bytes()
}
// Decode decodes msgpack representation into RemoteRepo
func (repo *RemoteRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
err := decoder.Decode(repo)
if err != nil {
return err
}
return repo.prepare()
}
// Key is a unique id in DB
func (repo *RemoteRepo) Key() []byte {
return []byte("R" + repo.UUID)
}
// RefKey is a unique id for package reference list
func (repo *RemoteRepo) RefKey() []byte {
return []byte("E" + repo.UUID)
}
// RemoteRepoCollection does listing, updating/adding/deleting of RemoteRepos
type RemoteRepoCollection struct {
db database.Storage
list []*RemoteRepo
}
// NewRemoteRepoCollection loads RemoteRepos from DB and makes up collection
func NewRemoteRepoCollection(db database.Storage) *RemoteRepoCollection {
result := &RemoteRepoCollection{
db: db,
}
blobs := db.FetchByPrefix([]byte("R"))
result.list = make([]*RemoteRepo, 0, len(blobs))
for _, blob := range blobs {
r := &RemoteRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding mirror: %s\n", err)
} else {
result.list = append(result.list, r)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *RemoteRepoCollection) Add(repo *RemoteRepo) error {
for _, r := range collection.list {
if r.Name == repo.Name {
return fmt.Errorf("mirror with name %s already exists", repo.Name)
}
}
err := collection.Update(repo)
if err != nil {
return err
}
collection.list = append(collection.list, repo)
return nil
}
// Update stores updated information about repo in DB
func (collection *RemoteRepoCollection) Update(repo *RemoteRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
if repo.packageRefs != nil {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
if err != nil {
return err
}
}
return nil
}
// LoadComplete loads additional information for remote repo
func (collection *RemoteRepoCollection) LoadComplete(repo *RemoteRepo) error {
encoded, err := collection.db.Get(repo.RefKey())
if err == database.ErrNotFound {
return nil
}
if err != nil {
return err
}
repo.packageRefs = &PackageRefList{}
return repo.packageRefs.Decode(encoded)
}
// ByName looks up repository by name
func (collection *RemoteRepoCollection) ByName(name string) (*RemoteRepo, error) {
for _, r := range collection.list {
if r.Name == name {
return r, nil
}
}
return nil, fmt.Errorf("mirror with name %s not found", name)
}
// ByUUID looks up repository by uuid
func (collection *RemoteRepoCollection) ByUUID(uuid string) (*RemoteRepo, error) {
for _, r := range collection.list {
if r.UUID == uuid {
return r, nil
}
}
return nil, fmt.Errorf("mirror with uuid %s not found", uuid)
}
// ForEach runs method for each repository
func (collection *RemoteRepoCollection) ForEach(handler func(*RemoteRepo) 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 *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())
}
+295 -38
View File
@@ -1,16 +1,44 @@
package debian
package deb
import (
"errors"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/console"
"github.com/smira/aptly/database"
"github.com/smira/aptly/files"
"github.com/smira/aptly/http"
"github.com/smira/aptly/utils"
"io"
"io/ioutil"
. "launchpad.net/gocheck"
"testing"
"os"
)
// Launch gocheck tests
func Test(t *testing.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) error {
return nil
}
func (n *NullVerifier) ExtractClearsigned(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 {
@@ -40,68 +68,155 @@ func (s *PackageListMixinSuite) SetUpPackages() {
type RemoteRepoSuite struct {
PackageListMixinSuite
repo *RemoteRepo
downloader *utils.FakeDownloader
flat *RemoteRepo
downloader *http.FakeDownloader
progress aptly.Progress
db database.Storage
packageCollection *PackageCollection
packageRepo *Repository
collectionFactory *CollectionFactory
packagePool aptly.PackagePool
}
var _ = Suite(&RemoteRepoSuite{})
func (s *RemoteRepoSuite) SetUpTest(c *C) {
s.repo, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
s.downloader = utils.NewFakeDownloader().ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release", exampleReleaseFile)
s.repo, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian", "squeeze", []string{"main"}, []string{}, false)
s.flat, _ = NewRemoteRepo("exp42", "http://repos.express42.com/virool/precise/", "./", []string{}, []string{}, false)
s.downloader = http.NewFakeDownloader().ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/Release", exampleReleaseFile)
s.progress = console.NewProgress()
s.db, _ = database.OpenDB(c.MkDir())
s.packageCollection = NewPackageCollection(s.db)
s.packageRepo = NewRepository(c.MkDir())
s.collectionFactory = NewCollectionFactory(s.db)
s.packagePool = files.NewPackagePool(c.MkDir())
s.SetUpPackages()
s.progress.Start()
}
func (s *RemoteRepoSuite) TearDownTest(c *C) {
s.progress.Shutdown()
s.db.Close()
}
func (s *RemoteRepoSuite) TestInvalidURL(c *C) {
_, err := NewRemoteRepo("s", "http://lolo%2", "squeeze", []string{"main"}, []string{})
_, err := NewRemoteRepo("s", "http://lolo%2", "squeeze", []string{"main"}, []string{}, false)
c.Assert(err, ErrorMatches, ".*hexadecimal escape in host.*")
}
func (s *RemoteRepoSuite) TestFlatCreation(c *C) {
c.Check(s.flat.IsFlat(), Equals, true)
c.Check(s.flat.Distribution, Equals, "./")
c.Check(s.flat.Architectures, IsNil)
c.Check(s.flat.Components, IsNil)
flat2, _ := NewRemoteRepo("flat2", "http://pkg.jenkins-ci.org/debian-stable", "binary/", []string{}, []string{}, false)
c.Check(flat2.IsFlat(), Equals, true)
c.Check(flat2.Distribution, Equals, "./binary/")
_, err := NewRemoteRepo("fl", "http://some.repo/", "./", []string{"main"}, []string{}, false)
c.Check(err, ErrorMatches, "components aren't supported for flat repos")
}
func (s *RemoteRepoSuite) TestString(c *C) {
c.Check(s.repo.String(), Equals, "[yandex]: http://mirror.yandex.ru/debian/ squeeze")
c.Check(s.flat.String(), Equals, "[exp42]: http://repos.express42.com/virool/precise/ ./")
s.repo.DownloadSources = true
s.flat.DownloadSources = true
c.Check(s.repo.String(), Equals, "[yandex]: http://mirror.yandex.ru/debian/ squeeze [src]")
c.Check(s.flat.String(), Equals, "[exp42]: http://repos.express42.com/virool/precise/ ./ [src]")
}
func (s *RemoteRepoSuite) TestNumPackages(c *C) {
c.Check(s.repo.NumPackages(), Equals, 0)
s.repo.packageRefs = s.reflist
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) {
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) {
c.Assert(s.repo.BinaryURL("main", "amd64").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/main/binary-amd64/Packages")
}
func (s *RemoteRepoSuite) TestSourcesURL(c *C) {
c.Assert(s.repo.SourcesURL("main").String(), Equals, "http://mirror.yandex.ru/debian/dists/squeeze/main/source/Sources")
}
func (s *RemoteRepoSuite) TestFlatBinaryURL(c *C) {
c.Assert(s.flat.FlatBinaryURL().String(), Equals, "http://repos.express42.com/virool/precise/Packages")
}
func (s *RemoteRepoSuite) TestFlatSourcesURL(c *C) {
c.Assert(s.flat.FlatSourcesURL().String(), Equals, "http://repos.express42.com/virool/precise/Sources")
}
func (s *RemoteRepoSuite) TestPackageURL(c *C) {
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")
}
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(s.repo.Architectures, DeepEquals, []string{"amd64", "armel", "armhf", "i386", "powerpc"})
c.Assert(s.repo.Components, DeepEquals, []string{"main"})
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 := http.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 := http.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) {
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{"xyz"})
err := s.repo.Fetch(s.downloader)
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{"xyz"}, false)
err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, ErrorMatches, "architecture xyz not available in repo.*")
}
func (s *RemoteRepoSuite) TestFetchWrongComponent(c *C) {
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"xyz"}, []string{"i386"})
err := s.repo.Fetch(s.downloader)
s.repo, _ = NewRemoteRepo("s", "http://mirror.yandex.ru/debian/", "squeeze", []string{"xyz"}, []string{"i386"}, false)
err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, ErrorMatches, "component xyz not available in repo.*")
}
@@ -128,7 +243,7 @@ func (s *RemoteRepoSuite) TestRefKey(c *C) {
func (s *RemoteRepoSuite) TestDownload(c *C) {
s.repo.Architectures = []string{"i386"}
err := s.repo.Fetch(s.downloader)
err := s.repo.Fetch(s.downloader, nil)
c.Assert(err, IsNil)
s.downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages.bz2", errors.New("HTTP 404"))
@@ -136,20 +251,132 @@ 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/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.progress, s.downloader, s.collectionFactory, s.packagePool, false, 0, nil)
c.Assert(err, IsNil)
c.Assert(s.downloader.Empty(), Equals, true)
c.Assert(s.repo.packageRefs, NotNil)
pkg, err := s.packageCollection.ByKey(s.repo.packageRefs.Refs[0])
pkg, err := s.collectionFactory.PackageCollection().ByKey(s.repo.packageRefs.Refs[0])
c.Assert(err, IsNil)
poolPath, _ := s.packageRepo.PoolPath(pkg.Filename, pkg.HashMD5)
c.Check(pkg.VerifyFile(poolPath), Equals, true)
result, err := pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "amanda-client")
}
func (s *RemoteRepoSuite) TestDownloadWithSources(c *C) {
s.repo.Architectures = []string{"i386"}
s.repo.DownloadSources = true
err := s.repo.Fetch(s.downloader, nil)
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.gz", errors.New("HTTP 404"))
s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/main/binary-i386/Packages", examplePackagesFile)
s.downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/main/source/Sources.bz2", errors.New("HTTP 404"))
s.downloader.ExpectError("http://mirror.yandex.ru/debian/dists/squeeze/main/source/Sources.gz", errors.New("HTTP 404"))
s.downloader.ExpectResponse("http://mirror.yandex.ru/debian/dists/squeeze/main/source/Sources", exampleSourcesFile)
s.downloader.AnyExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb", "xyz")
s.downloader.AnyExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/access-modifier-checker/access-modifier-checker_1.0-4.dsc", "abc")
s.downloader.AnyExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/access-modifier-checker/access-modifier-checker_1.0.orig.tar.gz", "abcd")
s.downloader.AnyExpectResponse("http://mirror.yandex.ru/debian/pool/main/a/access-modifier-checker/access-modifier-checker_1.0-4.debian.tar.gz", "abcde")
err = s.repo.Download(s.progress, s.downloader, s.collectionFactory, s.packagePool, false, 0, nil)
c.Assert(err, IsNil)
c.Assert(s.downloader.Empty(), Equals, true)
c.Assert(s.repo.packageRefs, NotNil)
pkg, err := s.collectionFactory.PackageCollection().ByKey(s.repo.packageRefs.Refs[0])
c.Assert(err, IsNil)
result, err := pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "amanda-client")
pkg, err = s.collectionFactory.PackageCollection().ByKey(s.repo.packageRefs.Refs[1])
c.Assert(err, IsNil)
result, err = pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "access-modifier-checker")
}
func (s *RemoteRepoSuite) TestDownloadFlat(c *C) {
downloader := http.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(s.progress, downloader, s.collectionFactory, s.packagePool, false, 0, nil)
c.Assert(err, IsNil)
c.Assert(downloader.Empty(), Equals, true)
c.Assert(s.flat.packageRefs, NotNil)
pkg, err := s.collectionFactory.PackageCollection().ByKey(s.flat.packageRefs.Refs[0])
c.Assert(err, IsNil)
result, err := pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "amanda-client")
}
func (s *RemoteRepoSuite) TestDownloadWithSourcesFlat(c *C) {
s.flat.DownloadSources = true
downloader := http.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.ExpectError("http://repos.express42.com/virool/precise/Sources.bz2", errors.New("HTTP 404"))
downloader.ExpectError("http://repos.express42.com/virool/precise/Sources.gz", errors.New("HTTP 404"))
downloader.ExpectResponse("http://repos.express42.com/virool/precise/Sources", exampleSourcesFile)
downloader.AnyExpectResponse("http://repos.express42.com/virool/precise/pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb", "xyz")
downloader.AnyExpectResponse("http://repos.express42.com/virool/precise/pool/main/a/access-modifier-checker/access-modifier-checker_1.0-4.dsc", "abc")
downloader.AnyExpectResponse("http://repos.express42.com/virool/precise/pool/main/a/access-modifier-checker/access-modifier-checker_1.0.orig.tar.gz", "abcd")
downloader.AnyExpectResponse("http://repos.express42.com/virool/precise/pool/main/a/access-modifier-checker/access-modifier-checker_1.0-4.debian.tar.gz", "abcde")
err := s.flat.Fetch(downloader, nil)
c.Assert(err, IsNil)
err = s.flat.Download(s.progress, downloader, s.collectionFactory, s.packagePool, false, 0, nil)
c.Assert(err, IsNil)
c.Assert(downloader.Empty(), Equals, true)
c.Assert(s.flat.packageRefs, NotNil)
pkg, err := s.collectionFactory.PackageCollection().ByKey(s.flat.packageRefs.Refs[0])
c.Assert(err, IsNil)
result, err := pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "amanda-client")
pkg, err = s.collectionFactory.PackageCollection().ByKey(s.flat.packageRefs.Refs[1])
c.Assert(err, IsNil)
result, err = pkg.VerifyFiles(s.packagePool)
c.Check(result, Equals, true)
c.Check(err, IsNil)
c.Check(pkg.Name, Equals, "access-modifier-checker")
}
type RemoteRepoCollectionSuite struct {
PackageListMixinSuite
db database.Storage
@@ -172,7 +399,7 @@ func (s *RemoteRepoCollectionSuite) TestAddByName(c *C) {
r, err := s.collection.ByName("yandex")
c.Assert(err, ErrorMatches, "*.not found")
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
c.Assert(s.collection.Add(repo), IsNil)
c.Assert(s.collection.Add(repo), ErrorMatches, ".*already exists")
@@ -190,7 +417,7 @@ func (s *RemoteRepoCollectionSuite) TestByUUID(c *C) {
r, err := s.collection.ByUUID("some-uuid")
c.Assert(err, ErrorMatches, "*.not found")
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
c.Assert(s.collection.Add(repo), IsNil)
r, err = s.collection.ByUUID(repo.UUID)
@@ -199,7 +426,7 @@ func (s *RemoteRepoCollectionSuite) TestByUUID(c *C) {
}
func (s *RemoteRepoCollectionSuite) TestUpdateLoadComplete(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{}, false)
c.Assert(s.collection.Update(repo), IsNil)
collection := NewRemoteRepoCollection(s.db)
@@ -219,8 +446,8 @@ func (s *RemoteRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
c.Assert(r.NumPackages(), Equals, 3)
}
func (s *RemoteRepoCollectionSuite) TestForEach(c *C) {
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
func (s *RemoteRepoCollectionSuite) TestForEachAndLen(c *C) {
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
s.collection.Add(repo)
count := 0
@@ -231,6 +458,8 @@ func (s *RemoteRepoCollectionSuite) TestForEach(c *C) {
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(*RemoteRepo) error {
@@ -239,6 +468,32 @@ func (s *RemoteRepoCollectionSuite) TestForEach(c *C) {
c.Assert(err, Equals, e)
}
func (s *RemoteRepoCollectionSuite) TestDrop(c *C) {
repo1, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
s.collection.Add(repo1)
repo2, _ := NewRemoteRepo("tyndex", "http://mirror.yandex.ru/debian/", "wheezy", []string{"main"}, []string{}, false)
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
Label: Agenda Daily Builds
Suite: precise
@@ -261,7 +516,7 @@ MD5Sum:
c63d31e8e3a5650c29a7124e541d6c23 134 main/binary-armhf/Release
4059d198768f9f8dc9372dc1c54bc3c3 14 main/binary-armhf/Packages.bz2
d41d8cd98f00b204e9800998ecf8427e 0 main/binary-armhf/Packages
708fc548e709eea0dfd2d7edb6098829 1344 main/binary-i386/Packages
c8d336856df67d509032bb54145c2f89 826 main/binary-i386/Packages
92262f0668b265401291f0467bc93763 133 main/binary-i386/Release
7954ed80936429687122b554620c1b5b 734 main/binary-i386/Packages.bz2
e2eef4fe7d285b12c511adfa3a39069e 641 main/binary-i386/Packages.gz
@@ -285,7 +540,7 @@ MD5Sum:
4059d198768f9f8dc9372dc1c54bc3c3 14 main/debian-installer/binary-powerpc/Packages.bz2
9d10bb61e59bd799891ae4fbcf447ec9 29 main/debian-installer/binary-powerpc/Packages.gz
3481d65651306df1596dca9078c2506a 135 main/source/Release
0531474bd4630bfcfd39048be830483d 1119 main/source/Sources
0459b7e4512db5479cb982bac6e2f9a1 2003 main/source/Sources
3d83a489f1bd3c04226aa6520b8a6d07 656 main/source/Sources.bz2
b062b5b77094aeeb05ca8dbb1ecf68a9 592 main/source/Sources.gz
SHA1:
@@ -301,7 +556,7 @@ SHA1:
585a452e27c2e7e047c49d4b0a7459d8c627aa08 134 main/binary-armhf/Release
64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 main/binary-armhf/Packages.bz2
da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-armhf/Packages
2bfad956c2d2437924a8527970858c59823451b7 1344 main/binary-i386/Packages
1d2f0cd7a3c9e687b853eb277e241cd712b6e3b1 826 main/binary-i386/Packages
16020809662f9bda36eb516d0995658dd94d1ad5 133 main/binary-i386/Release
95a463a0739bf9ff622c8d68f6e4598d400f5248 734 main/binary-i386/Packages.bz2
bf8c0dec9665ba78311c97cae1755d4b2e60af76 641 main/binary-i386/Packages.gz
@@ -325,7 +580,7 @@ SHA1:
64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 main/debian-installer/binary-powerpc/Packages.bz2
3df6ca52b6e8ecfb4a8fac6b8e02c777e3c7960d 29 main/debian-installer/binary-powerpc/Packages.gz
49cfec0c9b1df3a25e983a3ddf29d15b0e376e02 135 main/source/Release
4987db83999b0a8bbbbeeb183f066cadb87a5fa5 1119 main/source/Sources
6b92e0fc84307226172696fde59ca5f33f380b57 2003 main/source/Sources
ecb8afea11030a5df46941cb8ec297ca24c85736 656 main/source/Sources.bz2
923e71383969c91146f12fa8cd121397f2467a2e 592 main/source/Sources.gz
SHA256:
@@ -341,7 +596,7 @@ SHA256:
d25382b633c4a1621f8df6ce86e5c63da2e506a377e05ae9453238bb18191540 134 main/binary-armhf/Release
d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 main/binary-armhf/Packages.bz2
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-armhf/Packages
9cd4bad3462e795bad509a44bae48622f2e9c9e56aafc999419cc5221f087dc8 1344 main/binary-i386/Packages
b1bb341bb613363ca29440c2eb9c08a9289de5458209990ec502ed27711a83a2 826 main/binary-i386/Packages
e5aaceaac5ecb59143a4b4ed2bf700fe85d6cf08addd10cf2058bde697b7b219 133 main/binary-i386/Release
377890a26f99db55e117dfc691972dcbbb7d8be1630c8fc8297530c205377f2b 734 main/binary-i386/Packages.bz2
6361e8efc67d2e7c1a8db45388aec0311007c0a1bd96698623ddeb5ed0bdc914 641 main/binary-i386/Packages.gz
@@ -365,7 +620,7 @@ SHA256:
d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 main/debian-installer/binary-powerpc/Packages.bz2
825d493158fe0f50ca1acd70367aefa391170563af2e4ee9cedbcbe6796c8384 29 main/debian-installer/binary-powerpc/Packages.gz
d683102993b6f11067ce86d73111f067e36a199e9dc1f4295c8b19c274dc9ef8 135 main/source/Release
a8707486566f1623f0e50c0f8f61d93a93d79fb3043b6e1c407fc9f2afb002ce 1119 main/source/Sources
45f868fd5d9efe611d67572ffcf96a00a5b9ec38ea5102753290c38c36b8c282 2003 main/source/Sources
d178f1e310218d9f0f16c37d0780637f1cf3640a94a7fb0e24dc940c51b1e115 656 main/source/Sources.bz2
080228b550da407fb8ac73fb30b37323468fd2b2de98dd56a324ee7d701f6103 592 main/source/Sources.gz`
@@ -385,7 +640,9 @@ Section: utils
Priority: optional
Filename: pool/main/a/amanda/amanda-client_3.3.1-3~bpo60+1_amd64.deb
Size: 3
MD5sum: cdc997dc06126e18ea9ba843efed9811
SHA1: 049ba341d520c447fa2e6a1f8c871b3dbbe00106
SHA256: 4487115ca47fe9acd95355b9278f30e18c53f33c385252252d3d7948d650d1d0
MD5sum: d16fb36f0911f878998c136191af705e
SHA1: 66b27417d37e024c46526c2f6d358a754fc552f3
SHA256: 3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
`
const exampleSourcesFile = sourcePackageMeta
+124 -2
View File
@@ -1,4 +1,4 @@
package debian
package deb
import (
"bytes"
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"log"
"time"
@@ -46,6 +47,46 @@ func NewSnapshotFromRepository(name string, repo *RemoteRepo) (*Snapshot, error)
}, nil
}
// NewSnapshotFromLocalRepo creates snapshot from current state of local repository
func NewSnapshotFromLocalRepo(name string, repo *LocalRepo) (*Snapshot, error) {
if repo.packageRefs == nil {
return nil, errors.New("local repo doesn't have packages")
}
return &Snapshot{
UUID: uuid.New(),
Name: name,
CreatedAt: time.Now(),
SourceKind: "local",
SourceIDs: []string{repo.UUID},
Description: fmt.Sprintf("Snapshot from local repo %s", repo),
packageRefs: repo.packageRefs,
}, 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
func (s *Snapshot) String() string {
return fmt.Sprintf("[%s]: %s", s.Name, s.Description)
@@ -137,7 +178,10 @@ func (collection *SnapshotCollection) Update(snapshot *Snapshot) error {
if err != nil {
return err
}
return collection.db.Put(snapshot.RefKey(), snapshot.packageRefs.Encode())
if snapshot.packageRefs != nil {
return collection.db.Put(snapshot.RefKey(), snapshot.packageRefs.Encode())
}
return nil
}
// LoadComplete loads additional information about snapshot
@@ -161,6 +205,52 @@ func (collection *SnapshotCollection) ByName(name string) (*Snapshot, error) {
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 RemoteRepo 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
}
// ByLocalRepoSource looks up snapshots that have specified LocalRepo as a source
func (collection *SnapshotCollection) ByLocalRepoSource(repo *LocalRepo) []*Snapshot {
result := make([]*Snapshot, 0)
for _, s := range collection.list {
if s.SourceKind == "local" && 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
func (collection *SnapshotCollection) ForEach(handler func(*Snapshot) error) error {
var err error
@@ -172,3 +262,35 @@ func (collection *SnapshotCollection) ForEach(handler func(*Snapshot) error) 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())
}
+249
View File
@@ -0,0 +1,249 @@
package deb
import (
"errors"
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
)
type SnapshotSuite struct {
PackageListMixinSuite
repo *RemoteRepo
}
var _ = Suite(&SnapshotSuite{})
func (s *SnapshotSuite) SetUpTest(c *C) {
s.SetUpPackages()
s.repo, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
s.repo.packageRefs = s.reflist
}
func (s *SnapshotSuite) TestNewSnapshotFromRepository(c *C) {
snapshot, _ := NewSnapshotFromRepository("snap1", s.repo)
c.Check(snapshot.Name, Equals, "snap1")
c.Check(snapshot.NumPackages(), 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
_, err := NewSnapshotFromRepository("snap2", s.repo)
c.Check(err, ErrorMatches, ".*not updated")
}
func (s *SnapshotSuite) TestNewSnapshotFromLocalRepo(c *C) {
localRepo := NewLocalRepo("lala", "hoorah!")
_, err := NewSnapshotFromLocalRepo("snap2", localRepo)
c.Check(err, ErrorMatches, "local repo doesn't have packages")
localRepo.UpdateRefList(s.reflist)
snapshot, _ := NewSnapshotFromLocalRepo("snap1", localRepo)
c.Check(snapshot.Name, Equals, "snap1")
c.Check(snapshot.NumPackages(), Equals, 3)
c.Check(snapshot.RefList().Len(), Equals, 3)
c.Check(snapshot.SourceKind, Equals, "local")
c.Check(snapshot.SourceIDs, DeepEquals, []string{localRepo.UUID})
}
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) {
snapshot, _ := NewSnapshotFromRepository("snap1", s.repo)
c.Assert(len(snapshot.Key()), Equals, 37)
c.Assert(snapshot.Key()[0], Equals, byte('S'))
}
func (s *SnapshotSuite) TestRefKey(c *C) {
snapshot, _ := NewSnapshotFromRepository("snap1", s.repo)
c.Assert(len(snapshot.RefKey()), Equals, 37)
c.Assert(snapshot.RefKey()[0], Equals, byte('E'))
c.Assert(snapshot.RefKey()[1:], DeepEquals, snapshot.Key()[1:])
}
func (s *SnapshotSuite) TestEncodeDecode(c *C) {
snapshot, _ := NewSnapshotFromRepository("snap1", s.repo)
s.repo.packageRefs = s.reflist
snapshot2 := &Snapshot{}
c.Assert(snapshot2.Decode(snapshot.Encode()), IsNil)
c.Assert(snapshot2.Name, Equals, snapshot.Name)
c.Assert(snapshot2.packageRefs, IsNil)
}
type SnapshotCollectionSuite struct {
PackageListMixinSuite
db database.Storage
repo1, repo2 *RemoteRepo
lrepo1, lrepo2 *LocalRepo
snapshot1, snapshot2 *Snapshot
snapshot3, snapshot4 *Snapshot
collection *SnapshotCollection
}
var _ = Suite(&SnapshotCollectionSuite{})
func (s *SnapshotCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewSnapshotCollection(s.db)
s.SetUpPackages()
s.repo1, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
s.repo1.packageRefs = s.reflist
s.snapshot1, _ = NewSnapshotFromRepository("snap1", s.repo1)
s.repo2, _ = NewRemoteRepo("android", "http://mirror.yandex.ru/debian/", "lenny", []string{"main"}, []string{}, false)
s.repo2.packageRefs = s.reflist
s.snapshot2, _ = NewSnapshotFromRepository("snap2", s.repo2)
s.lrepo1 = NewLocalRepo("local1", "")
s.lrepo1.packageRefs = s.reflist
s.snapshot3, _ = NewSnapshotFromLocalRepo("snap3", s.lrepo1)
s.lrepo2 = NewLocalRepo("local2", "")
s.lrepo2.packageRefs = s.reflist
s.snapshot4, _ = NewSnapshotFromLocalRepo("snap4", s.lrepo2)
}
func (s *SnapshotCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *SnapshotCollectionSuite) TestAddByNameByUUID(c *C) {
snapshot, err := s.collection.ByName("snap1")
c.Assert(err, ErrorMatches, "*.not found")
c.Assert(s.collection.Add(s.snapshot1), IsNil)
c.Assert(s.collection.Add(s.snapshot1), ErrorMatches, ".*already exists")
c.Assert(s.collection.Add(s.snapshot2), IsNil)
snapshot, err = s.collection.ByName("snap1")
c.Assert(err, IsNil)
c.Assert(snapshot.String(), Equals, s.snapshot1.String())
collection := NewSnapshotCollection(s.db)
snapshot, err = collection.ByName("snap1")
c.Assert(err, IsNil)
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) {
c.Assert(s.collection.Update(s.snapshot1), IsNil)
collection := NewSnapshotCollection(s.db)
snapshot, err := collection.ByName("snap1")
c.Assert(err, IsNil)
c.Assert(snapshot.packageRefs, IsNil)
c.Assert(s.collection.LoadComplete(snapshot), IsNil)
c.Assert(snapshot.NumPackages(), Equals, 3)
}
func (s *SnapshotCollectionSuite) TestForEachAndLen(c *C) {
s.collection.Add(s.snapshot1)
s.collection.Add(s.snapshot2)
count := 0
err := s.collection.ForEach(func(*Snapshot) error {
count++
return nil
})
c.Assert(count, Equals, 2)
c.Assert(err, IsNil)
c.Check(s.collection.Len(), Equals, 2)
e := errors.New("d")
err = s.collection.ForEach(func(*Snapshot) error {
return 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{}, false)
c.Check(s.collection.ByRemoteRepoSource(repo3), DeepEquals, []*Snapshot{})
}
func (s *SnapshotCollectionSuite) TestFindByLocalRepoSource(c *C) {
c.Assert(s.collection.Add(s.snapshot1), IsNil)
c.Assert(s.collection.Add(s.snapshot2), IsNil)
c.Assert(s.collection.Add(s.snapshot3), IsNil)
c.Assert(s.collection.Add(s.snapshot4), IsNil)
c.Check(s.collection.ByLocalRepoSource(s.lrepo1), DeepEquals, []*Snapshot{s.snapshot3})
c.Check(s.collection.ByLocalRepoSource(s.lrepo2), DeepEquals, []*Snapshot{s.snapshot4})
lrepo3 := NewLocalRepo("other", "")
c.Check(s.collection.ByLocalRepoSource(lrepo3), 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!")
}
+295
View File
@@ -0,0 +1,295 @@
package deb
import (
"fmt"
"regexp"
"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
VersionPatternMatch
VersionRegexp
)
// Dependency is a parsed version of Debian dependency to package
type Dependency struct {
Pkg string
Relation int
Version string
Architecture string
Regexp *regexp.Regexp
}
// 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 VersionPatternMatch:
rel = "%"
case VersionRegexp:
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) [arch]" into parts
func ParseDependency(dep string) (d Dependency, err error) {
if strings.HasSuffix(dep, "}") {
i := strings.LastIndex(dep, "{")
if i == -1 {
err = fmt.Errorf("unable to parse dependency: %s", dep)
return
}
d.Architecture = dep[i+1 : len(dep)-1]
dep = strings.TrimSpace(dep[:i])
}
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 := ""
if dep[i+1] == '>' || dep[i+1] == '<' || dep[i+1] == '=' {
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])
}
} else {
d.Version = strings.TrimSpace(dep[i+1 : 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 %#v in dependency %s", rel, dep)
}
return
}
+219
View File
@@ -0,0 +1,219 @@
package deb
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")
c.Check(d.Architecture, Equals, "")
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, 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, 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, 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 (>>1.6) {i386}")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionGreater)
c.Check(d.Version, Equals, "1.6")
c.Check(d.Architecture, Equals, "i386")
d, e = ParseDependency("dpkg{i386}")
c.Check(e, IsNil)
c.Check(d.Pkg, Equals, "dpkg")
c.Check(d.Relation, Equals, VersionDontCare)
c.Check(d.Version, Equals, "")
c.Check(d.Architecture, Equals, "i386")
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.*")
d, e = ParseDependency("dpkg i386}")
c.Check(e, ErrorMatches, "unable to parse.*")
d, e = ParseDependency("dpkg ) {i386}")
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]")
}
-120
View File
@@ -1,120 +0,0 @@
package debian
import (
"bytes"
"fmt"
"github.com/ugorji/go/codec"
"sort"
)
// PackageList is list of unique (by key) packages
//
// It could be seen as repo snapshot, repo contents, result of filtering,
// merge, etc.
type PackageList struct {
packages map[string]*Package
}
// NewPackageList creates empty package list
func NewPackageList() *PackageList {
return &PackageList{packages: make(map[string]*Package, 1000)}
}
// Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error {
key := string(p.Key())
existing, ok := l.packages[key]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
}
return nil
}
l.packages[key] = p
return nil
}
// ForEach calls handler for each package in list
func (l *PackageList) ForEach(handler func(*Package) error) error {
var err error
for _, p := range l.packages {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// Len returns number of packages in the list
func (l *PackageList) Len() int {
return len(l.packages)
}
// PackageRefList is a list of keys of packages, this is basis for snapshot
// and similar stuff
//
// Refs are sorted in lexographical order
type PackageRefList struct {
// List of package keys
Refs [][]byte
}
// NewPackageRefListFromPackageList creates PackageRefList from PackageList
func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList {
reflist := &PackageRefList{}
reflist.Refs = make([][]byte, list.Len())
i := 0
for _, p := range list.packages {
reflist.Refs[i] = p.Key()
i++
}
sort.Sort(reflist)
return reflist
}
// Len returns number of refs
func (l *PackageRefList) Len() int {
return len(l.Refs)
}
// Swap swaps two refs
func (l *PackageRefList) Swap(i, j int) {
l.Refs[i], l.Refs[j] = l.Refs[j], l.Refs[i]
}
// Compare compares two refs in lexographical order
func (l *PackageRefList) Less(i, j int) bool {
return bytes.Compare(l.Refs[i], l.Refs[j]) < 0
}
// Encode does msgpack encoding of PackageRefList
func (l *PackageRefList) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(l)
return buf.Bytes()
}
// Decode decodes msgpack representation into PackageRefLit
func (l *PackageRefList) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(l)
}
// ForEach calls handler for each package ref in list
func (l *PackageRefList) ForEach(handler func([]byte) error) error {
var err error
for _, p := range l.Refs {
err = handler(p)
if err != nil {
return err
}
}
return err
}
-121
View File
@@ -1,121 +0,0 @@
package debian
import (
"errors"
. "launchpad.net/gocheck"
)
type PackageListSuite struct {
list *PackageList
p1, p2, p3, p4, p5, p6 *Package
}
var _ = Suite(&PackageListSuite{})
func (s *PackageListSuite) SetUpTest(c *C) {
s.list = NewPackageList()
s.p1 = NewPackageFromControlFile(packageStanza.Copy())
s.p2 = NewPackageFromControlFile(packageStanza.Copy())
stanza := packageStanza.Copy()
stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Size"] = "42"
s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers"
s.p5 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Version"] = "99.1"
s.p6 = NewPackageFromControlFile(stanza)
}
func (s *PackageListSuite) TestAddLen(c *C) {
c.Check(s.list.Len(), Equals, 0)
c.Check(s.list.Add(s.p1), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p2), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p3), IsNil)
c.Check(s.list.Len(), Equals, 2)
c.Check(s.list.Add(s.p4), ErrorMatches, "conflict in package.*")
}
func (s *PackageListSuite) TestForeach(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
Len := 0
err := s.list.ForEach(func(*Package) error {
Len++
return nil
})
c.Check(Len, Equals, 2)
c.Check(err, IsNil)
e := errors.New("a")
err = s.list.ForEach(func(*Package) error {
return e
})
c.Check(err, Equals, e)
}
func (s *PackageListSuite) TestNewPackageRefList(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
c.Assert(reflist.Len(), Equals, 4)
c.Check(reflist.Refs[0], DeepEquals, []byte(s.p1.Key()))
c.Check(reflist.Refs[1], DeepEquals, []byte(s.p6.Key()))
c.Check(reflist.Refs[2], DeepEquals, []byte(s.p5.Key()))
c.Check(reflist.Refs[3], DeepEquals, []byte(s.p3.Key()))
}
func (s *PackageListSuite) TestPackageRefListEncodeDecode(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
reflist2 := &PackageRefList{}
err := reflist2.Decode(reflist.Encode())
c.Assert(err, IsNil)
c.Check(reflist2.Len(), Equals, reflist.Len())
c.Check(reflist2.Refs, DeepEquals, reflist.Refs)
}
func (s *PackageListSuite) TestPackageRefListForeach(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
s.list.Add(s.p6)
reflist := NewPackageRefListFromPackageList(s.list)
Len := 0
err := reflist.ForEach(func([]byte) error {
Len++
return nil
})
c.Check(Len, Equals, 4)
c.Check(err, IsNil)
e := errors.New("b")
err = reflist.ForEach(func([]byte) error {
return e
})
c.Check(err, Equals, e)
}
-195
View File
@@ -1,195 +0,0 @@
package debian
import (
"bytes"
"fmt"
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"os"
"strconv"
"strings"
)
// Package is single instance of Debian package
//
// TODO: support source & binary
type Package struct {
Name string
Version string
Filename string
Filesize int64
Architecture string
Source string
// Various dependencies
Depends []string
PreDepends []string
Suggests []string
Recommends []string
// Hashsums of package contents
HashMD5 string
HashSHA1 string
HashSHA256 string
// Extra information from stanza
Extra Stanza
}
func parseDependencies(input Stanza, key string) []string {
value, ok := input[key]
if !ok {
return nil
}
delete(input, key)
return strings.Split(value, ", ")
}
// NewPackageFromControlFile creates Package from parsed Debian control file
func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{
Name: input["Package"],
Version: input["Version"],
Filename: input["Filename"],
Architecture: input["Architecture"],
Source: input["Source"],
HashMD5: input["MD5sum"],
HashSHA1: input["SHA1"],
HashSHA256: input["SHA256"],
}
delete(input, "Package")
delete(input, "Version")
delete(input, "Filename")
delete(input, "Architecture")
delete(input, "Source")
delete(input, "MD5sum")
delete(input, "SHA1")
delete(input, "SHA256")
result.Filesize, _ = strconv.ParseInt(input["Size"], 10, 64)
delete(input, "Size")
result.Depends = parseDependencies(input, "Depends")
result.PreDepends = parseDependencies(input, "Pre-Depends")
result.Suggests = parseDependencies(input, "Suggests")
result.Recommends = parseDependencies(input, "Recommends")
result.Extra = input
return result
}
// Key returns unique key identifying package
func (p *Package) Key() []byte {
return []byte("P" + p.Name + " " + p.Version + " " + p.Architecture)
}
// Encode does msgpack encoding of Package
func (p *Package) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(p)
return buf.Bytes()
}
// Decode decodes msgpack representation into Package
func (p *Package) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(p)
}
// String creates readable representation
func (p *Package) String() string {
return fmt.Sprintf("%s-%s_%s", p.Name, p.Version, p.Architecture)
}
// Stanza creates original stanza from package
func (p *Package) Stanza() (result Stanza) {
result = p.Extra.Copy()
result["Package"] = p.Name
result["Version"] = p.Version
result["Filename"] = p.Filename
result["Architecture"] = p.Architecture
result["Source"] = p.Source
if p.HashMD5 != "" {
result["MD5sum"] = p.HashMD5
}
if p.HashSHA1 != "" {
result["SHA1"] = p.HashSHA1
}
if p.HashSHA256 != "" {
result["SHA256"] = p.HashSHA256
}
if p.Depends != nil {
result["Depends"] = strings.Join(p.Depends, ", ")
}
if p.PreDepends != nil {
result["Pre-Depends"] = strings.Join(p.PreDepends, ", ")
}
if p.Suggests != nil {
result["Suggests"] = strings.Join(p.Suggests, ", ")
}
if p.Recommends != nil {
result["Recommends"] = strings.Join(p.Recommends, ", ")
}
result["Size"] = fmt.Sprintf("%d", p.Filesize)
return
}
// Equals compares two packages to be identical
func (p *Package) Equals(p2 *Package) bool {
return p.Name == p2.Name && p.Version == p2.Version && p.Filename == p2.Filename &&
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.Recommends, p2.Recommends) && utils.StrMapsEqual(p.Extra, p2.Extra) &&
p.Filesize == p2.Filesize && p.HashMD5 == p2.HashMD5 && p.HashSHA1 == p2.HashSHA1 &&
p.HashSHA256 == p2.HashSHA256 && p.Source == p2.Source
}
// VerifyFile verifies integrity and existence of local files for the package
func (p *Package) VerifyFile(filepath string) bool {
st, err := os.Stat(filepath)
if err != nil {
return false
}
return st.Size() == p.Filesize
}
// PackageCollection does management of packages in DB
type PackageCollection struct {
db database.Storage
}
// NewPackageCollection creates new PackageCollection and binds it to database
func NewPackageCollection(db database.Storage) *PackageCollection {
return &PackageCollection{
db: db,
}
}
// ByKey find package in DB by its key
func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
encoded, err := collection.db.Get(key)
if err != nil {
return nil, err
}
p := &Package{}
err = p.Decode(encoded)
if err != nil {
return nil, err
}
return p, nil
}
// Update adds or updates information about package in DB
func (collection *PackageCollection) Update(p *Package) error {
return collection.db.Put(p.Key(), p.Encode())
}
-96
View File
@@ -1,96 +0,0 @@
package debian
import (
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
)
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"}
type PackageSuite struct {
stanza Stanza
}
var _ = Suite(&PackageSuite{})
func (s *PackageSuite) SetUpTest(c *C) {
s.stanza = packageStanza.Copy()
}
func (s *PackageSuite) TestNewFromPara(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.Name, Equals, "alien-arena-common")
c.Check(p.Version, Equals, "7.40-2")
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.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) TestKey(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Check(p.Key(), DeepEquals, []byte("Palien-arena-common 7.40-2 i386"))
}
func (s *PackageSuite) TestEncodeDecode(c *C) {
p := NewPackageFromControlFile(s.stanza)
encoded := p.Encode()
p2 := &Package{}
err := p2.Decode(encoded)
c.Assert(err, IsNil)
c.Assert(p2, DeepEquals, p)
}
func (s *PackageSuite) TestStanza(c *C) {
p := NewPackageFromControlFile(s.stanza.Copy())
stanza := p.Stanza()
c.Assert(stanza, DeepEquals, s.stanza)
}
func (s *PackageSuite) TestString(c *C) {
p := NewPackageFromControlFile(s.stanza)
c.Assert(p.String(), Equals, "alien-arena-common-7.40-2_i386")
}
func (s *PackageSuite) TestEquals(c *C) {
p := NewPackageFromControlFile(s.stanza)
stanza2 := packageStanza.Copy()
p2 := NewPackageFromControlFile(stanza2)
c.Check(p.Equals(p2), Equals, true)
p2.Depends = []string{"package1"}
c.Check(p.Equals(p2), Equals, false)
}
type PackageCollectionSuite struct {
collection *PackageCollection
p *Package
db database.Storage
}
var _ = Suite(&PackageCollectionSuite{})
func (s *PackageCollectionSuite) SetUpTest(c *C) {
s.p = NewPackageFromControlFile(packageStanza.Copy())
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewPackageCollection(s.db)
}
func (s *PackageCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *PackageCollectionSuite) TestUpdateByKey(c *C) {
err := s.collection.Update(s.p)
c.Assert(err, IsNil)
p2, err := s.collection.ByKey(s.p.Key())
c.Assert(err, IsNil)
c.Assert(p2.Equals(s.p), Equals, true)
}
-212
View File
@@ -1,212 +0,0 @@
package debian
import (
"bufio"
"fmt"
"github.com/smira/aptly/utils"
"path/filepath"
"strings"
"time"
)
// PublishedRepo is a published for http/ftp representation of snapshot as Debian repository
type PublishedRepo struct {
// Prefix & distribution should be unique across all published repositories
Prefix string
Distribution string
Component string
// Architectures is a list of all architectures published
Architectures []string
// Snapshot as a source of publishing
SnapshotUUID string
snapshot *Snapshot
}
// NewPublishedRepo creates new published repository
func NewPublishedRepo(prefix string, distribution string, component string, architectures []string, snapshot *Snapshot) *PublishedRepo {
return &PublishedRepo{
Prefix: prefix,
Distribution: distribution,
Component: component,
Architectures: architectures,
SnapshotUUID: snapshot.UUID,
snapshot: snapshot,
}
}
// Publish publishes snapshot (repository) contents, links package files, generates Packages & Release files, signs them
func (p *PublishedRepo) Publish(repo *Repository, packageCollection *PackageCollection, signer utils.Signer) error {
err := repo.MkDir(filepath.Join(p.Prefix, "pool"))
if err != nil {
return err
}
basePath := filepath.Join(p.Prefix, "dists", p.Distribution)
err = repo.MkDir(basePath)
if err != nil {
return err
}
// Load all packages
list := NewPackageList()
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 {
return fmt.Errorf("unable to load packages: %s", err)
}
if list.Len() == 0 {
return fmt.Errorf("repository is empty, can't publish")
}
if p.Architectures == nil {
p.Architectures = make([]string, 0, 10)
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 {
return fmt.Errorf("unable to figure out list of architectures, please supply explicit list")
}
generatedFiles := map[string]*utils.ChecksumInfo{}
// For all architectures, generate release file
for _, arch := range p.Architectures {
relativePath := filepath.Join(p.Component, fmt.Sprintf("binary-%s", arch), "Packages")
err = repo.MkDir(filepath.Dir(filepath.Join(basePath, relativePath)))
if err != nil {
return err
}
packagesFile, err := repo.CreateFile(filepath.Join(basePath, relativePath))
if err != nil {
return fmt.Errorf("unable to creates Packages file: %s", err)
}
bufWriter := bufio.NewWriter(packagesFile)
err = list.ForEach(func(pkg *Package) error {
if pkg.Architecture == arch || pkg.Architecture == "all" {
source := pkg.Source
if source == "" {
source = pkg.Name
}
path, err := repo.LinkFromPool(p.Prefix, p.Component, pkg.Filename, pkg.HashMD5, source)
if err != nil {
return err
}
pkg.Filename = path
err = pkg.Stanza().WriteTo(bufWriter)
if err != nil {
return err
}
err = bufWriter.WriteByte('\n')
if err != nil {
return err
}
}
return nil
})
if err != nil {
return fmt.Errorf("unable to creates process packages: %s", err)
}
err = bufWriter.Flush()
if err != nil {
return fmt.Errorf("unable to write Packages file: %s", err)
}
err = utils.CompressFile(packagesFile)
if err != nil {
return fmt.Errorf("unable to compress Packages files: %s", err)
}
packagesFile.Close()
checksumInfo, err := repo.ChecksumsForFile(filepath.Join(basePath, relativePath))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath] = checksumInfo
checksumInfo, err = repo.ChecksumsForFile(filepath.Join(basePath, relativePath+".gz"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".gz"] = checksumInfo
checksumInfo, err = repo.ChecksumsForFile(filepath.Join(basePath, relativePath+".bz2"))
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
generatedFiles[relativePath+".bz2"] = checksumInfo
}
release := make(Stanza)
release["Origin"] = p.Prefix + " " + p.Distribution
release["Label"] = p.Prefix + " " + p.Distribution
release["Codename"] = p.Distribution
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
release["Components"] = p.Component
release["Architectures"] = strings.Join(p.Architectures, " ")
release["Description"] = "Generated by aptly\n"
release["MD5Sum"] = "\n"
release["SHA1"] = "\n"
release["SHA256"] = "\n"
for path, info := range generatedFiles {
release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path)
release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path)
release["SHA256"] += fmt.Sprintf(" %s %8d %s\n", info.SHA256, info.Size, path)
}
releaseFile, err := repo.CreateFile(filepath.Join(basePath, "Release"))
if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
}
bufWriter := bufio.NewWriter(releaseFile)
err = release.WriteTo(bufWriter)
if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
}
err = bufWriter.Flush()
if err != nil {
return fmt.Errorf("unable to create Release file: %s", err)
}
releaseFilename := releaseFile.Name()
releaseFile.Close()
err = signer.DetachedSign(releaseFilename, releaseFilename+".gpg")
if err != nil {
return fmt.Errorf("unable to sign Release file: %s", err)
}
err = signer.ClearSign(releaseFilename, filepath.Join(filepath.Dir(releaseFilename), "InRelease"))
if err != nil {
return fmt.Errorf("unable to sign Release file: %s", err)
}
return nil
}
-95
View File
@@ -1,95 +0,0 @@
package debian
import (
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
"os"
"path/filepath"
)
type NullSigner struct{}
func (n *NullSigner) SetKey(keyRef string) {
}
func (n *NullSigner) DetachedSign(source string, destination string) error {
return nil
}
func (n *NullSigner) ClearSign(source string, destination string) error {
return nil
}
type PublishedRepoSuite struct {
PackageListMixinSuite
repo *PublishedRepo
packageRepo *Repository
db database.Storage
packageCollection *PackageCollection
}
var _ = Suite(&PublishedRepoSuite{})
func (s *PublishedRepoSuite) SetUpTest(c *C) {
s.SetUpPackages()
s.db, _ = database.OpenDB(c.MkDir())
s.packageRepo = NewRepository(c.MkDir())
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
repo.packageRefs = s.reflist
snapshot, _ := NewSnapshotFromRepository("snap", repo)
s.repo = NewPublishedRepo("ppa", "squeeze", "main", nil, snapshot)
s.packageCollection = NewPackageCollection(s.db)
s.packageCollection.Update(s.p1)
s.packageCollection.Update(s.p2)
s.packageCollection.Update(s.p3)
poolPath, _ := s.packageRepo.PoolPath(s.p1.Filename, s.p1.HashMD5)
err := os.MkdirAll(filepath.Dir(poolPath), 0755)
f, err := os.Create(poolPath)
c.Assert(err, IsNil)
f.Close()
}
func (s *PublishedRepoSuite) TestPublish(c *C) {
err := s.repo.Publish(s.packageRepo, s.packageCollection, &NullSigner{})
c.Assert(err, IsNil)
c.Check(s.repo.Architectures, DeepEquals, []string{"i386"})
rf, err := os.Open(filepath.Join(s.packageRepo.RootPath, "public/ppa/dists/squeeze/Release"))
c.Assert(err, IsNil)
cfr := NewControlFileReader(rf)
st, err := cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Origin"], Equals, "ppa squeeze")
c.Check(st["Components"], Equals, "main")
c.Check(st["Architectures"], Equals, "i386")
pf, err := os.Open(filepath.Join(s.packageRepo.RootPath, "public/ppa/dists/squeeze/main/binary-i386/Packages"))
c.Assert(err, IsNil)
cfr = NewControlFileReader(pf)
for i := 0; i < 3; i++ {
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Check(st["Filename"], Equals, "pool/main/a/alien-arena/alien-arena-common_7.40-2_i386.deb")
}
st, err = cfr.ReadStanza()
c.Assert(err, IsNil)
c.Assert(st, IsNil)
_, 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)
}
-362
View File
@@ -1,362 +0,0 @@
// Package debian implements Debian-specific repository handling
package debian
import (
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt"
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"log"
"net/url"
"strings"
"time"
)
// RemoteRepo represents remote (fetchable) Debian repository.
//
// Repostitory could be filtered when fetching by components, architectures
// TODO: support flat format
type RemoteRepo struct {
// Permanent internal ID
UUID string
// User-assigned name
Name string
// Root of Debian archive, URL
ArchiveRoot string
// Distribution name, e.g. squeeze
Distribution string
// List of components to fetch, if empty, then fetch all components
Components []string
// List of architectures to fetch, if empty, then fetch all architectures
Architectures []string
// Meta-information about repository
Meta Stanza
// Last update date
LastDownloadDate time.Time
// "Snapshot" of current list of packages
packageRefs *PackageRefList
// Parsed archived root
archiveRootURL *url.URL
}
// NewRemoteRepo creates new instance of Debian remote repository with specified params
func NewRemoteRepo(name string, archiveRoot string, distribution string, components []string, architectures []string) (*RemoteRepo, error) {
result := &RemoteRepo{
UUID: uuid.New(),
Name: name,
ArchiveRoot: archiveRoot,
Distribution: distribution,
Components: components,
Architectures: architectures,
}
err := result.prepare()
if err != nil {
return nil, err
}
return result, nil
}
func (repo *RemoteRepo) prepare() error {
var err error
repo.archiveRootURL, err = url.Parse(repo.ArchiveRoot)
return err
}
// String interface
func (repo *RemoteRepo) String() string {
return fmt.Sprintf("[%s]: %s %s", repo.Name, repo.ArchiveRoot, repo.Distribution)
}
// NumPackages return number of packages retrived from remore repo
func (repo *RemoteRepo) NumPackages() int {
if repo.packageRefs == nil {
return 0
}
return repo.packageRefs.Len()
}
// ReleaseURL returns URL to Release file in repo root
// TODO: InRelease, Release.gz, Release.bz2 handling
func (repo *RemoteRepo) ReleaseURL() *url.URL {
path := &url.URL{Path: fmt.Sprintf("dists/%s/Release", repo.Distribution)}
return repo.archiveRootURL.ResolveReference(path)
}
// BinaryURL returns URL of Packages file for given component and
// architecture
func (repo *RemoteRepo) BinaryURL(component string, architecture string) *url.URL {
path := &url.URL{Path: fmt.Sprintf("dists/%s/%s/binary-%s/Packages", repo.Distribution, component, architecture)}
return repo.archiveRootURL.ResolveReference(path)
}
// PackageURL returns URL of package file relative to repository root
// architecture
func (repo *RemoteRepo) PackageURL(filename string) *url.URL {
path := &url.URL{Path: filename}
return repo.archiveRootURL.ResolveReference(path)
}
// Fetch updates information about repository
func (repo *RemoteRepo) Fetch(d utils.Downloader) error {
// Download release file to temporary URL
release, err := utils.DownloadTemp(d, repo.ReleaseURL().String())
if err != nil {
return err
}
defer release.Close()
sreader := NewControlFileReader(release)
stanza, err := sreader.ReadStanza()
if err != nil {
return err
}
architectures := strings.Split(stanza["Architectures"], " ")
if len(repo.Architectures) == 0 {
repo.Architectures = architectures
} else {
err = utils.StringsIsSubset(repo.Architectures, architectures,
fmt.Sprintf("architecture %%s not available in repo %s", repo))
if err != nil {
return err
}
}
components := strings.Split(stanza["Components"], " ")
if len(repo.Components) == 0 {
repo.Components = components
} else {
err = utils.StringsIsSubset(repo.Components, components,
fmt.Sprintf("component %%s not available in repo %s", repo))
if err != nil {
return err
}
}
delete(stanza, "MD5Sum")
delete(stanza, "SHA1")
delete(stanza, "SHA256")
repo.Meta = stanza
return nil
}
// Download downloads all repo files
func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository) error {
list := NewPackageList()
// Download and parse all Release files
for _, component := range repo.Components {
for _, architecture := range repo.Architectures {
packagesReader, packagesFile, err := utils.DownloadTryCompression(d, repo.BinaryURL(component, architecture).String())
if err != nil {
return err
}
defer packagesFile.Close()
sreader := NewControlFileReader(packagesReader)
for {
stanza, err := sreader.ReadStanza()
if err != nil {
return err
}
if stanza == nil {
break
}
p := NewPackageFromControlFile(stanza)
list.Add(p)
}
}
}
// Save package meta information to DB
err := list.ForEach(func(p *Package) error {
return packageCollection.Update(p)
})
if err != nil {
return fmt.Errorf("unable to save packages to db: %s", err)
}
// Download all package files
ch := make(chan error, list.Len())
count := 0
err = list.ForEach(func(p *Package) error {
poolPath, err := packageRepo.PoolPath(p.Filename, p.HashMD5)
if err != nil {
return err
}
if !p.VerifyFile(poolPath) {
d.Download(repo.PackageURL(p.Filename).String(), poolPath, ch)
count++
}
return nil
})
if err != nil {
return fmt.Errorf("unable to download packages: %s", err)
}
errors := make([]string, 0)
// Wait for all downloads to finish
for count > 0 {
err = <-ch
if err != nil {
errors = append(errors, err.Error())
}
count--
}
if len(errors) > 0 {
return fmt.Errorf("download errors: %s", strings.Join(errors, ", "))
}
repo.LastDownloadDate = time.Now()
repo.packageRefs = NewPackageRefListFromPackageList(list)
return nil
}
// Encode does msgpack encoding of RemoteRepo
func (repo *RemoteRepo) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(repo)
return buf.Bytes()
}
// Decode decodes msgpack representation into RemoteRepo
func (repo *RemoteRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
err := decoder.Decode(repo)
if err != nil {
return err
}
return repo.prepare()
}
// Key is a unique id in DB
func (repo *RemoteRepo) Key() []byte {
return []byte("R" + repo.UUID)
}
// RefKey is a unique id for package reference list
func (repo *RemoteRepo) RefKey() []byte {
return []byte("E" + repo.UUID)
}
// RemoteRepoCollection does listing, updating/adding/deleting of RemoteRepos
type RemoteRepoCollection struct {
db database.Storage
list []*RemoteRepo
}
// NewRemoteRepoCollection loads RemoteRepos from DB and makes up collection
func NewRemoteRepoCollection(db database.Storage) *RemoteRepoCollection {
result := &RemoteRepoCollection{
db: db,
}
blobs := db.FetchByPrefix([]byte("R"))
result.list = make([]*RemoteRepo, 0, len(blobs))
for _, blob := range blobs {
r := &RemoteRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding mirror: %s\n", err)
} else {
result.list = append(result.list, r)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *RemoteRepoCollection) Add(repo *RemoteRepo) error {
for _, r := range collection.list {
if r.Name == repo.Name {
return fmt.Errorf("mirror with name %s already exists", repo.Name)
}
}
err := collection.Update(repo)
if err != nil {
return err
}
collection.list = append(collection.list, repo)
return nil
}
// Update stores updated information about repo in DB
func (collection *RemoteRepoCollection) Update(repo *RemoteRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
if repo.packageRefs != nil {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
if err != nil {
return err
}
}
return nil
}
// LoadComplete loads additional information for remote repo
func (collection *RemoteRepoCollection) LoadComplete(repo *RemoteRepo) error {
encoded, err := collection.db.Get(repo.RefKey())
if err == database.ErrNotFound {
return nil
}
if err != nil {
return err
}
repo.packageRefs = &PackageRefList{}
return repo.packageRefs.Decode(encoded)
}
// ByName looks up repository by name
func (collection *RemoteRepoCollection) ByName(name string) (*RemoteRepo, error) {
for _, r := range collection.list {
if r.Name == name {
return r, nil
}
}
return nil, fmt.Errorf("mirror with name %s not found", name)
}
// ByUUID looks up repository by uuid
func (collection *RemoteRepoCollection) ByUUID(uuid string) (*RemoteRepo, error) {
for _, r := range collection.list {
if r.UUID == uuid {
return r, nil
}
}
return nil, fmt.Errorf("mirror with uuid %s not found", uuid)
}
// ForEach runs method for each repository
func (collection *RemoteRepoCollection) ForEach(handler func(*RemoteRepo) error) error {
var err error
for _, r := range collection.list {
err = handler(r)
if err != nil {
return err
}
}
return err
}

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