Compare commits

..

627 Commits

Author SHA1 Message Date
Andrey Smirnov 14bd443d4d Disable support for aptly task for now. #96 2014-10-03 12:52:46 +04:00
Andrey Smirnov 9109c60c43 Version bump to 0.8. 2014-10-03 12:20:21 +04:00
Andrey Smirnov 445ecbe8f3 Update man page. #45 #114 2014-10-03 11:06:14 +04:00
Andrey Smirnov 27de979733 More comments. #45 #114 2014-10-03 11:02:31 +04:00
Andrey Smirnov ad11053412 Support for locking, unlocking, interruption, cleanup. #45 #114 2014-10-03 01:34:22 +04:00
Andrey Smirnov a356f3dff9 Marking RemoteRepo as being updated, with worker PID, checking for locks. #45 #114 2014-10-03 01:32:19 +04:00
Andrey Smirnov 1042894123 Abort downloader on shutdown, don't wait for downloads to finish. #45 #114 2014-10-03 01:31:38 +04:00
Andrey Smirnov 43eb993160 Don't panic on double re-open/close, ignore it. #45 #114 2014-10-03 01:20:26 +04:00
Andrey Smirnov d190ffd39a Update goleveldb to the latest version. 2014-10-03 01:19:36 +04:00
Andrey Smirnov 93c1c7aaab Support for closing and re-opening database. #45 #114 2014-10-02 21:41:58 +04:00
Andrey Smirnov 3e5ba27cb7 Ability to re-open db after close. #45 #114 2014-10-02 21:13:56 +04:00
Andrey Smirnov cd3b24799a Use less files for the download test. 2014-10-02 21:00:00 +04:00
Andrey Smirnov a0870f6726 Refactor mirror download code, split it into separate methods. #45 #114 2014-10-02 19:30:37 +04:00
Andrey Smirnov d45b456334 Update test. #26 2014-10-02 14:32:52 +04:00
Andrey Smirnov 91c753ad2f Add URL to all download errrors, so that they're easier to understand. #26 2014-10-02 14:12:43 +04:00
Andrey Smirnov 40509f73b3 Update system test. 2014-10-02 10:55:57 +04:00
Andrey Smirnov 1daa076d65 Don't allow '/' in distribution name, auto-replace '/' with '-' while guessing. #110 2014-10-01 22:59:05 +04:00
Andrey Smirnov aeae6009c4 Introduce plusWorkaround: generate copy of file with '+' -> ' ' to workaround S3/apt bug. #98 2014-10-01 21:32:56 +04:00
Andrey Smirnov 8049d69793 Update goamz to version with fix for multidel, re-enable S3 delete test. 2014-10-01 19:41:32 +04:00
Andrey Smirnov 8aa1954ba7 Support for custom storage class and encryption method. #105 2014-10-01 19:16:15 +04:00
Andrey Smirnov a02a90a3d8 Remove validate argument, not supported by Travi CI boto version. 2014-10-01 18:39:35 +04:00
Andrey Smirnov f303aabf26 Another way to install boto. 2014-10-01 18:17:55 +04:00
Andrey Smirnov 735cbac60d Install boto library for system tests. 2014-10-01 18:09:51 +04:00
Andrey Smirnov 5d69871ca4 Tests for publishing to Amazon S3. 2014-10-01 17:48:51 +04:00
Andrey Smirnov 1afbae8f7c Add AWS credentials. 2014-10-01 17:29:40 +04:00
Andrey Smirnov 1ed647e1b0 List storage & prefix in publish list. #113 2014-10-01 00:44:01 +04:00
Andrey Smirnov 01b8e9eda5 Fix system tests. #108 2014-10-01 00:31:10 +04:00
Andrey Smirnov f43d514804 Merge branch '108-udebs' 2014-09-30 23:29:27 +04:00
Andrey Smirnov 7e8f692b2c Use better words. #108 2014-09-30 23:24:51 +04:00
Andrey Smirnov 4b50f817d7 Fix system tests. #108 2014-09-30 23:09:57 +04:00
Andrey Smirnov e123e4dfac .udebs are supported now. #108 2014-09-30 21:53:19 +04:00
Andrey Smirnov 4fb09d9e85 Update man page. #108 2014-09-30 21:52:25 +04:00
Andrey Smirnov d9b23167bc Test on publishing repo with .udebs. #108 2014-09-30 21:51:38 +04:00
Andrey Smirnov 2c84faaf8d System test for repo adding .udebs. #108 2014-09-30 21:26:28 +04:00
Andrey Smirnov 6514b87e3e Add keyring for publish. #108 2014-09-30 21:25:52 +04:00
Andrey Smirnov bd34ba4088 Pregenerate all udebs indexes if at least one udeb has been discovered. #108 2014-09-30 21:11:01 +04:00
Andrey Smirnov fae6e977c3 System tests for publishing snapshot from mirror with .udebs. #108 2014-09-30 19:40:16 +04:00
Andrey Smirnov 2ae34cd873 Use Package.MatchesArchitecture instead of homegrown function. #108 2014-09-30 19:39:31 +04:00
Andrey Smirnov b365e5e0b2 System test: regular publish doesn't generate debian-installer files. #108 2014-09-27 02:15:08 +04:00
Andrey Smirnov e171f90fd5 Restore ${HOME} links. #108 2014-09-27 01:56:35 +04:00
Andrey Smirnov db499f872d Major refactoring of the publishing method, now uses helper indexFile(s). #108 2014-09-27 01:39:02 +04:00
Andrey Smirnov 8f9944117c Update tests on show mirror format change. #108 2014-09-25 23:38:45 +04:00
Andrey Smirnov ea399a335a Update tests on show mirror format change. #108 2014-09-25 23:37:11 +04:00
Andrey Smirnov 976ddb5ff9 Fix tests on arguments help. #108 2014-09-25 23:34:53 +04:00
Andrey Smirnov 7d8600b840 Add support for mirroring, showing, and editing remote repos with .udebs. #108 2014-09-25 22:12:59 +04:00
Andrey Smirnov 7ad1bb387b Support for .udeb downloads from remote mirrors. #108 2014-09-25 19:34:16 +04:00
Andrey Smirnov 2fbf465fbf Support for .udeb in deb.Package. #108 2014-09-25 19:31:21 +04:00
Andrey Smirnov fa786332de Allow changing "download sources" option for the mirror. #109 2014-09-22 19:36:48 +04:00
Andrey Smirnov 5e1bd0ff0e Correctly parse boolean flags in combination with config options. #104
Config option should act as default, while flag should override it only if set.
2014-09-22 13:41:26 +04:00
Andrey Smirnov 9c92b81706 Remove -dry-run flag for aptly snapshot filter, as it is useless. #82 2014-09-22 01:54:35 +04:00
Andrey Smirnov 144ccbf809 Make order of configuration file loading clear. 2014-09-21 00:55:23 +04:00
Andrey Smirnov a11805efb4 Update to goleveldb with misspellings fixed. 2014-09-20 21:46:45 +04:00
Andrey Smirnov 5b6cea2d62 Fix system test after spelling fixes. 2014-09-20 21:39:08 +04:00
Andrey Smirnov d4699a3b24 Fix system test. 2014-09-20 18:21:32 +04:00
Andrey Smirnov 09a695a128 Fix spelling mistakes. 2014-09-20 18:10:13 +04:00
Andrey Smirnov ec4d2bcefe Fix spelling mistakes found by lintian. 2014-09-20 18:09:47 +04:00
Andrey Smirnov 3040aceb7f Update goleveldb to the version which reduces memory usage significantly. 2014-09-20 18:03:24 +04:00
Andrey Smirnov 61d8639a8a System tests for aptly snapshot filter. #82 2014-09-01 22:25:17 +04:00
Andrey Smirnov b47754a106 Update man page. #82 2014-09-01 22:11:07 +04:00
Andrey Smirnov 1b08b7311f Implementation of command aptly snapshot filter. #82 2014-09-01 22:09:58 +04:00
Andrey Smirnov 0130fc0392 Add -force-replace flag to repo aptly add to replace conflicting packages. #83 2014-09-01 17:59:29 +04:00
Andrey Smirnov de32595d29 Fix test. #94 2014-09-01 16:03:35 +04:00
Andrey Smirnov 95e5fdd34a Update README. [ci skip] 2014-09-01 15:30:31 +04:00
Andrey Smirnov a05f00d9f1 Regenerate man page. #94 2014-09-01 15:13:54 +04:00
Andrey Smirnov 97158ef37b Support for --passphrase & --passphrase-file arguments on publishing. #94 2014-09-01 15:13:07 +04:00
Andrey Smirnov f01ac06d97 Remove extra whitespace [ci skip] 2014-08-30 01:23:48 +04:00
Andrey Smirnov a549778754 Fix system tests. #48 2014-08-30 01:16:42 +04:00
Andrey Smirnov 47d952f712 System test for ftp:// download. #48 2014-08-29 19:46:58 +04:00
Andrey Smirnov 166f31c34d Regenerate man page. #48 2014-08-29 19:39:17 +04:00
Andrey Smirnov 4940fdc951 Note support of FTP. #48 2014-08-29 19:38:25 +04:00
Andrey Smirnov 7ae785f5a3 Implementation of ftp:// support for downloading. #48 2014-08-29 19:37:10 +04:00
Andrey Smirnov 09c8421648 Update man page. 2014-08-29 00:58:47 +04:00
Andrey Smirnov 6a2059150f Add Vincent Batoufflet to list of authors. 2014-08-29 00:53:56 +04:00
Andrey Smirnov 9b3dfe920d Merge branch 'vbatoufflet-mirror-edit-arch' 2014-08-29 00:53:15 +04:00
Andrey Smirnov 72f8e4ab61 Check architectures before applying arch change. #99 2014-08-29 00:52:47 +04:00
Andrey Smirnov 755944652f System tests for mirror edit with architectures. #99 2014-08-29 00:52:03 +04:00
Andrey Smirnov b29d42d023 Fix system test, use ${HOME}. #80 2014-08-29 00:46:59 +04:00
Andrey Smirnov f19ece776d Merge branch 'mirror-edit-arch' of https://github.com/vbatoufflet/aptly into vbatoufflet-mirror-edit-arch 2014-08-28 22:49:19 +04:00
Andrey Smirnov 839763c0b9 Command package show with tests. #80 2014-08-28 22:47:41 +04:00
Andrey Smirnov c56ecab06f Method PackageRefList.Has(). #80 2014-08-28 22:25:19 +04:00
Andrey Smirnov 02d86422a8 Fix tests by introducing stable sort. #80 2014-08-28 22:03:06 +04:00
Andrey Smirnov 65efe0cd2a System tests for aptly package search. #80 2014-08-28 21:44:41 +04:00
Andrey Smirnov 468b1f11b9 New command: package search to search whole package DB for matching packages. #80 2014-08-28 19:42:47 +04:00
Andrey Smirnov 608870265c New common interface for PackgeCollection & PackageList: PackageCatalog. #80 2014-08-28 19:41:30 +04:00
Andrey Smirnov ed03a7c69e New algorithm for dependency resolution, tests. #100 #81 2014-08-28 19:07:39 +04:00
Andrey Smirnov 5a42c60af4 Simplify and make more deterministic algorithm for dependency pulling. #100 2014-08-28 19:05:32 +04:00
Vincent Batoufflet f66302ef31 Add ability to edit mirror architectures 2014-08-26 23:22:51 +02:00
Andrey Smirnov 346a7bcce9 System tests for mirror, snapshot, repo search. #81 2014-08-27 00:04:01 +04:00
Andrey Smirnov 9bee7cdd08 Simplify dependency verification code. #81 2014-08-27 00:03:46 +04:00
Andrey Smirnov 74eee3496c Capture test results in prepared format. #81 2014-08-26 23:58:25 +04:00
Andrey Smirnov 3ef5429212 System tests for aptly mirror search. #81 2014-08-26 19:38:27 +04:00
Andrey Smirnov 3030e66d4c Fix -with-deps searching. #81 2014-08-26 19:25:02 +04:00
Andrey Smirnov 9ae5a5ffb2 Update system tests. #96 2014-08-26 02:03:58 +04:00
Andrey Smirnov 833d37d22c Update system tests. #81 2014-08-26 02:03:06 +04:00
Andrey Smirnov 03ec1f97a7 Fix after style fix. #96 2014-08-26 02:02:11 +04:00
Andrey Smirnov a2df51b40e Commands mirror/repo/snapshot search. #81 2014-08-26 02:01:11 +04:00
Andrey Smirnov ae906f525e Style fixes. #96 2014-08-26 01:09:49 +04:00
Andrey Smirnov b4a5a55cac Style fixes. #96 2014-08-26 01:06:33 +04:00
Andrey Smirnov 6003764ff5 Add more system tests. #96 2014-08-25 22:15:21 +04:00
Andrey Smirnov 099a82c816 Style fixes. #96 2014-08-25 22:06:25 +04:00
Andrey Smirnov ef992e2b44 Merge branch 'queeno-script_run_command' 2014-08-25 22:05:34 +04:00
Andrey Smirnov 68e600974d Refactoring: remove context switching, another way to catch panics, colored output. #96 2014-08-25 22:05:02 +04:00
Andrey Smirnov 39a1f0ec2d Use go1.3.1. 2014-08-25 21:22:55 +04:00
Andrey Smirnov 318fc5b7f4 Merge branch 'script_run_command' of https://github.com/queeno/aptly into queeno-script_run_command 2014-08-23 22:04:50 +04:00
Simon Aquino 72e54aa3d1 Fixed a bug with the context switching
The context switching wasn't really happening. Now the issue is fixed.
2014-08-16 23:32:38 +00:00
Simon Aquino 91ff904ac4 Adding filename flag to specify task run filename.
Just realised commands can not have any subcommands and therefore
consist of single words (for example serve or version). Hence I can't
assume that if len(args)==1 then the user has entered the filename.
I have created the filename flag so the user is forced to specify it
when they wish to run aptly tasks from files.
2014-08-16 22:13:24 +00:00
Simon Aquino b59471ad35 Added RunTask acceptance tests
Added a basic RunTask test to test the functionality of the new aptly
task run command. More to come...
2014-08-16 15:11:14 +00:00
Simon Aquino 6ff601f4a2 Making sure context is initialised before using it
Now checkong context is not nil before setting panicked = true
2014-08-16 14:33:36 +00:00
Simon Aquino 0c09bdedaa Fixed t03_help:MainTest failing due to new cmd 2014-08-16 14:22:04 +00:00
Simon Aquino dfc1f27d4c Better wording for task run message. 2014-08-16 14:18:35 +00:00
Simon Aquino 005cee572e Aptly script has now become aptly task
It makes more sense. Multiple lines of aptly commands can now be called
'aptly tasks' which could potentially be automated, in the future?
2014-08-16 14:14:56 +00:00
Simon Aquino 18e3ed5d64 aptly script run implementation
This new aptly command will allow to run multiple commands within a single
aptly command, running in a single thread. The commands can be included
in a text file or added to the aptly script run command in one line,
separated by comas ','.
If one command returns an error, then all the subsequent commands will
be skipped.
Each command output will appear on the console within coloured strings,
clearly stating which command is running and the start and the end of
the received output.
2014-08-16 13:55:13 +00:00
Simon Aquino 3c7696ef7e Refactored main.go
The main function - whuch runs aptly commands - has been taken out from
main.go and included to the cmd package. This is useful for the aptly
script run command, which should use that behaviour.
2014-08-16 13:55:13 +00:00
Simon Aquino b2779d7a88 Go-shellwords added to Gomfile
This packages allows us to parse shell commands. Useful for
script_run.go (later added)
2014-08-16 13:55:13 +00:00
Simon Aquino cdd34b4759 Added panicked attribute to context.go
This attribute is set to false during initalisation, and it's set to
true when error arises.
2014-08-16 13:55:13 +00:00
Simon Aquino 1f2ddca32b Add switchContext function to context.go 2014-08-16 13:55:13 +00:00
Simon Aquino df06dc356b Added script cmd in cmd.go 2014-08-16 13:54:46 +00:00
Simon Aquino b6c82f073f Added new script command 2014-08-16 10:17:44 +00:00
Andrey Smirnov 9a03b5f696 Update leveldb to the latest version. 2014-08-15 21:50:41 +04:00
Andrey Smirnov 047270540a Version bump: 0.8~dev 2014-08-06 23:34:51 +04:00
Andrey Smirnov eff3823edf Upload src-package to bintray. 2014-08-06 13:40:13 +04:00
Andrey Smirnov 9d02f057c6 Version bump: 0.7.1. 2014-08-06 02:24:43 +04:00
Andrey Smirnov 8387586cc8 Man page update: -force-overwrite flag. #90 2014-08-06 02:04:25 +04:00
Andrey Smirnov b433e7dad5 Workaround for broken time.Time encoding in msgpack with go < 1.2. #89
Decoding looses value of time.Time field, but that is not critical.
2014-08-06 01:56:13 +04:00
Andrey Smirnov dec4bdee71 Merge branch 'patch-1' of https://github.com/guilhem/aptly 2014-08-05 17:01:55 +04:00
Andrey Smirnov bb6593d21e Add -force-overwrite flag to publish update, switch, snapshot and repo commands. #90
Includes new and updated system tests.
2014-08-05 17:01:18 +04:00
Andrey Smirnov fe879acf9c Remove Makefile part specific for go1.1 2014-08-05 15:59:32 +04:00
Andrey Smirnov 5b8390c644 Uncomment and fix publish updat tests. 2014-08-05 15:58:47 +04:00
Andrey Smirnov d558791070 Add -force-overwrite command flag. #90 2014-08-05 15:47:38 +04:00
Andrey Smirnov 38ea595c9a Add forceOverwrite on the path to LinkFromPool. #90 2014-08-05 15:47:23 +04:00
Andrey Smirnov c03b7929d4 Fix line ends: system tests. 2014-08-05 15:44:12 +04:00
Andrey Smirnov d122ab6013 Fix system tests. 2014-08-05 15:27:39 +04:00
Andrey Smirnov a7b594d076 Drop support for go1.1 2014-08-05 15:26:41 +04:00
Andrey Smirnov e07bcf8e51 Fix style and add comments. #90 2014-08-05 14:50:15 +04:00
Andrey Smirnov da6d5b7cf8 Add 'force' to LinkFromPool method: overwrite file even if exists and different content. #90 2014-08-05 14:50:06 +04:00
Guilhem Lettron 15ef5c63c5 Add gobuild.io badge 2014-07-31 14:52:22 +02:00
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
664 changed files with 870921 additions and 460737 deletions
+6
View File
@@ -26,3 +26,9 @@ coverage.html
coverage*.out
*.pyc
_vendor/
gen
man/aptly.1.html
man/aptly.1.ronn
+12 -4
View File
@@ -1,15 +1,23 @@
language: go
go:
- 1.1
- 1.2
- 1.2.1
- 1.3.1
- tip
env:
global:
- secure: "YSwtFrMqh4oUvdSQTXBXMHHLWeQgyNEL23ChIZwU0nuDGIcQZ65kipu0PzefedtUbK4ieC065YCUi4UDDh6gPotB/Wu1pnYg3dyQ7rFvhaVYAAUEpajAdXZhlx+7+J8a4FZMeC/kqiahxoRgLbthF9019ouIqhGB9zHKI6/yZwc="
- secure: "V7OjWrfQ8UbktgT036jYQPb/7GJT3Ol9LObDr8FYlzsQ+F1uj2wLac6ePuxcOS4FwWOJinWGM1h+JiFkbxbyFqfRNJ0jj0O2p93QyDojxFVOn1mXqqvV66KFqAWR2Vzkny/gDvj8LTvdB1cgAIm2FNOkQc6E1BFnyWS2sN9ea5E="
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y python-boto
install:
- make prepare
script: make travis
script: make travis
matrix:
allow_failures:
- go: tip
+7
View File
@@ -0,0 +1,7 @@
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)
* Vincent Batoufflet (https://github.com/vbatoufflet)
+27
View File
@@ -0,0 +1,27 @@
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/vaughan0/go-ini', :commit => 'a98ad7ee00ec53921f08832bc06ecf7fd600e6a1'
gom 'github.com/mattn/go-shellwords', :commit => 'c7ca6f94add751566a61cf2199e1de78d4c3eee4'
gom 'github.com/mitchellh/goamz/s3', :commit => 'e7664b32019f31fd1bdf33f9e85f28722f700405'
gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
gom 'github.com/smira/flag', :commit => '357ed3e599ffcbd4aeaa828e1d10da2df3ea5107'
gom 'github.com/smira/go-ftp-protocol/protocol', :commit => '066b75c2b70dca7ae10b1b88b47534a3c31ccfaa'
gom 'github.com/syndtr/goleveldb/leveldb', :commit => 'e2fa4e6ac1cc41a73bc9fd467878ecbf65df5cc3'
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
+57 -36
View File
@@ -1,63 +1,84 @@
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
BINPATH=$(HOME)/gopath/bin
else
GOVERALLS?=goveralls
SRCPATH?=$(GOPATH)/src
BINPATH?=$(GOPATH)/bin
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
ifeq ($(TRAVIS), true)
GOM=$(HOME)/gopath/bin/gom
else
GOM=gom
endif
all: test check system-test
prepare: $(PREPARE_LIST)
go get -d -v ./...
go get launchpad.net/gocheck
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:%=./%)
system-test:
ifeq ($(GOVERSION), go1.2)
install:
$(GOM) build -o $(BINPATH)/aptly
system-test: install
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
endif
go install
PATH=$(BINPATH):$(PATH) python system/run.py --long
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)
.PHONY: coverage.out
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)
curl -T aptly-$(VERSION)-src.tar.bz2 -usmira:$(BINTRAY_KEY) https://api.bintray.com/content/smira/aptly/aptly/$(VERSION)/$(VERSION)/aptly-$(VERSION)-src.tar.bz2
.PHONY: coverage.out
+41 -10
View File
@@ -8,8 +8,14 @@ aptly
.. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD
:target: https://coveralls.io/r/smira/aptly?branch=HEAD
.. image:: http://gobuild.io/badge/github.com/smira/aptly/download.png
:target: http://gobuild.io/github.com/smira/aptly
Aptly is a swiss army knife for Debian repository management.
.. image:: http://www.aptly.info/img/aptly_logo.png
:target: http://www.aptly.info/
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>`_.
@@ -19,25 +25,50 @@ Aptly features: ("+" means planned features)
* 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
* 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 (+)
* publish self-made packages as Debian repositories (+)
* merge two or more snapshots into one
* filter repository by search query, pulling dependencies when required
* 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
* signature is not verified while downloading
* cleaning up stale files is not implemented
Currently aptly is under heavy development, so please use it with care.
* translations are not supported yet
Download
--------
To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
deb http://repo.aptly.info/ squeeze main
And import key that is used to sign the release::
$ gpg --keyserver keys.gnupg.net --recv-keys 2A194991
$ gpg -a --export 2A194991 | sudo apt-key add -
After that you can install aptly as any other software package::
$ apt-get update
$ apt-get install aptly
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.
Binary executables (depends almost only on libc) are available for download from `Bintray <http://dl.bintray.com/smira/aptly/>`_.
If you have Go environment set up, you can build aptly from source by running (go 1.1+ required)::
If you have Go environment set up, you can build aptly from source by running (go 1.2+ required)::
go get github.com/smira/aptly
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
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).
If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_.
+97
View File
@@ -0,0 +1,97 @@
// 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, force bool) 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()
// Abort stops downloader without waiting for shutdown
Abort()
// GetProgress returns Progress object
GetProgress() Progress
}
+7
View File
@@ -0,0 +1,7 @@
package aptly
// Version of aptly
const Version = "0.8"
// Enable debugging features?
const EnableDebug = false
+96
View File
@@ -0,0 +1,96 @@
// 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
}
// LookupOption checks boolean flag with default (usually config) and command-line
// setting
func LookupOption(defaultValue bool, flags *flag.FlagSet, name string) (result bool) {
result = defaultValue
if flags.IsSet(name) {
result = flags.Lookup(name).Value.Get().(bool)
}
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 one to fix a set of packages
in a repository, so that package installation and upgrade becomes
deterministic. At the same time aptly allows one 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(),
// Disabled on no docs
//makeCmdTask(),
makeCmdPublish(),
makeCmdVersion(),
makeCmdPackage(),
},
}
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
}
+377
View File
@@ -0,0 +1,377 @@
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, globalFlags *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.globalFlags.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 LookupOption(context.Config().DepFollowSuggests, context.globalFlags, "dep-follow-suggests") {
context.dependencyOptions |= deb.DepFollowSuggests
}
if LookupOption(context.Config().DepFollowRecommends, context.globalFlags, "dep-follow-recommends") {
context.dependencyOptions |= deb.DepFollowRecommends
}
if LookupOption(context.Config().DepFollowAllVariants, context.globalFlags, "dep-follow-all-variants") {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if LookupOption(context.Config().DepFollowSource, context.globalFlags, "dep-follow-source") {
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.globalFlags.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
}
// CloseDatabase closes the db temporarily
func (context *AptlyContext) CloseDatabase() error {
if context.database == nil {
return nil
}
return context.database.Close()
}
// ReOpenDatabase reopens the db after close
func (context *AptlyContext) ReOpenDatabase() error {
if context.database == nil {
return nil
}
const MaxTries = 10
const Delay = 10 * time.Second
for try := 0; try < MaxTries; try++ {
err := context.database.ReOpen()
if err == nil || strings.Index(err.Error(), "resource temporarily unavailable") == -1 {
return err
}
context.Progress().Printf("Unable to reopen database, sleeping %s\n", Delay)
<-time.After(Delay)
}
return fmt.Errorf("unable to reopen the DB, maximum number of retries reached")
}
// 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
}
// GetPublishedStorage 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, params.StorageClass,
params.EncryptionMethod, params.PlusWorkaround)
if err != nil {
Fatal(err)
}
} else {
Fatal(fmt.Errorf("unknown published storage format: %v", name))
}
context.publishedStorages[name] = publishedStorage
}
return publishedStorage
}
// UpdateFlags sets internal copy of flags in the context
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
context.flags = flags
}
// 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()
context.database = nil
}
if context.downloader != nil {
context.downloader.Abort()
context.downloader = nil
}
if context.progress != nil {
context.progress.Shutdown()
context.progress = nil
}
}
// CleanupContext does partial shutdown of context
func CleanupContext() {
if context.downloader != nil {
context.downloader.Shutdown()
context.downloader = nil
}
if context.progress != nil {
context.progress.Shutdown()
context.progress = nil
}
}
// InitContext initializes context with default settings
func InitContext(flags *flag.FlagSet) error {
var err error
if context != nil {
panic("context already initialized")
}
context = &AptlyContext{
flags: flags,
globalFlags: 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
}
+62
View File
@@ -0,0 +1,62 @@
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 LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
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(),
makeCmdMirrorSearch(),
},
}
}
+101
View File
@@ -0,0 +1,101 @@
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 := LookupOption(context.Config().DownloadSourcePackages, context.flags, "with-sources")
downloadUdebs := context.flags.Lookup("with-udebs").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, downloadUdebs)
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 and FTP. 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.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
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
}
+72
View File
@@ -0,0 +1,72 @@
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)
}
err = repo.CheckLock()
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
}
+91
View File
@@ -0,0 +1,91 @@
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)
}
err = repo.CheckLock()
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)
case "with-sources":
repo.DownloadSources = flag.Value.Get().(bool)
case "with-udebs":
repo.DownloadUdebs = flag.Value.Get().(bool)
}
})
if repo.IsFlat() && repo.DownloadUdebs {
return fmt.Errorf("unable to edit: flat mirrors don't support udebs")
}
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
if context.globalFlags.Lookup("architectures").Value.String() != "" {
repo.Architectures = context.ArchitecturesList()
err = repo.Fetch(context.Downloader(), nil)
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 mirror settings",
Long: `
Command edit allows one to change settings of mirror:
filters, list of architectures.
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")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
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
}
+64
View File
@@ -0,0 +1,64 @@
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 = repo.CheckLock()
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
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdMirrorSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search mirror for packages matching query",
Long: `
Command search displays list of packages in mirror that match package query
Example:
$ aptly mirror search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
return cmd
}
+99
View File
@@ -0,0 +1,99 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"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)
if repo.Status == deb.MirrorUpdating {
fmt.Printf("Status: In Update (PID %d)\n", repo.WorkerPID)
}
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)
downloadUdebs := "no"
if repo.DownloadUdebs {
downloadUdebs = "yes"
}
fmt.Printf("Download .udebs: %s\n", downloadUdebs)
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
}
+193
View File
@@ -0,0 +1,193 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"os/signal"
"strings"
)
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)
}
force := context.flags.Lookup("force").Value.Get().(bool)
if !force {
err = repo.CheckLock()
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)
}
context.Progress().Printf("Downloading & parsing package files...\n")
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if repo.Filter != "" {
context.Progress().Printf("Applying filter...\n")
var filterQuery deb.PackageQuery
filterQuery, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
var oldLen, newLen int
oldLen, newLen, err = repo.ApplyFilter(context.DependencyOptions(), filterQuery)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("Packages filtered: %d -> %d.\n", oldLen, newLen)
}
var (
downloadSize int64
queue []deb.PackageDownloadTask
)
context.Progress().Printf("Building download queue...\n")
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
defer func() {
// on any interruption, unlock the mirror
err := context.ReOpenDatabase()
if err == nil {
repo.MarkAsIdle()
context.CollectionFactory().RemoteRepoCollection().Update(repo)
}
}()
repo.MarkAsUpdating()
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CloseDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
// Catch ^C
sigch := make(chan os.Signal)
signal.Notify(sigch, os.Interrupt)
count := len(queue)
context.Progress().Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
// Download from the queue
context.Progress().InitBar(downloadSize, true)
// Download all package files
ch := make(chan error, count)
// In separate goroutine (to avoid blocking main), push queue to downloader
go func() {
for _, task := range queue {
context.Downloader().DownloadWithChecksum(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch, task.Checksums, ignoreMismatch)
}
// We don't need queue after this point
queue = nil
}()
// Wait for all downloads to finish
errors := make([]string, 0)
for count > 0 {
select {
case <-sigch:
signal.Stop(sigch)
return fmt.Errorf("unable to update: interrupted")
case err = <-ch:
if err != nil {
errors = append(errors, err.Error())
}
count--
}
}
context.Progress().ShutdownBar()
signal.Stop(sigch)
if len(errors) > 0 {
return fmt.Errorf("unable to update: download errors:\n %s\n", strings.Join(errors, "\n "))
}
err = context.ReOpenDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
repo.FinalizeDownload()
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("force", false, "force update mirror even if it is locked by another process")
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
}
+16
View File
@@ -0,0 +1,16 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdPackage() *commander.Command {
return &commander.Command{
UsageLine: "package",
Short: "operations on packages",
Subcommands: []*commander.Command{
makeCmdPackageSearch(),
makeCmdPackageShow(),
},
}
}
+48
View File
@@ -0,0 +1,48 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyPackageSearch(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
q, err := query.Parse(args[0])
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
result := q.Query(context.CollectionFactory().PackageCollection())
result.ForEach(func(p *deb.Package) error {
context.Progress().Printf("%s\n", p)
return nil
})
return err
}
func makeCmdPackageSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPackageSearch,
UsageLine: "search <package-query>",
Short: "search for packages matching query",
Long: `
Command search displays list of packages in whole DB that match package query
Example:
$ aptly package search '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-package-search", flag.ExitOnError),
}
return cmd
}
+137
View File
@@ -0,0 +1,137 @@
package cmd
import (
"bufio"
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
)
func printReferencesTo(p *deb.Package) (err error) {
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 {
if repo.RefList().Has(p) {
fmt.Printf(" mirror %s\n", repo)
}
}
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 {
if repo.RefList().Has(p) {
fmt.Printf(" local repo %s\n", repo)
}
}
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
}
if snapshot.RefList().Has(p) {
fmt.Printf(" snapshot %s\n", snapshot)
}
return nil
})
if err != nil {
return err
}
return nil
}
func aptlyPackageShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
q, err := query.Parse(args[0])
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
withFiles := context.flags.Lookup("with-files").Value.Get().(bool)
withReferences := context.flags.Lookup("with-references").Value.Get().(bool)
w := bufio.NewWriter(os.Stdout)
result := q.Query(context.CollectionFactory().PackageCollection())
err = result.ForEach(func(p *deb.Package) error {
p.Stanza().WriteTo(w)
w.Flush()
fmt.Printf("\n")
if withFiles {
fmt.Printf("Files in the pool:\n")
for _, f := range p.Files() {
path, err := context.PackagePool().Path(f.Filename, f.Checksums.MD5)
if err != nil {
return err
}
fmt.Printf(" %s\n", path)
}
fmt.Printf("\n")
}
if withReferences {
fmt.Printf("References to package:\n")
printReferencesTo(p)
fmt.Printf("\n")
}
return nil
})
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
return err
}
func makeCmdPackageShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyPackageShow,
UsageLine: "show <package-query>",
Short: "show details about packages matcing query",
Long: `
Command shows displays detailed meta-information about packages
matching query. Information from Debian control file is displayed.
Optionally information about package files and
inclusion into mirrors/snapshots/local repos is shown.
Example:
$ aptly package show nginx-light_1.2.1-2.2+wheezy2_i386'
`,
Flag: *flag.NewFlagSet("aptly-package-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-files", false, "display information about files from package pool")
cmd.Flag.Bool("with-references", false, "display information about mirrors, snapshots and local repos referencing this package")
return cmd
}
+56
View File
@@ -0,0 +1,56 @@
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 LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
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())
signer.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").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.StoragePrefix(), 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
}
+48
View File
@@ -0,0 +1,48 @@
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.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
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")
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+210
View File
@@ -0,0 +1,210 @@
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)
}
forceOverwrite := context.flags.Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
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.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
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")
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+141
View File
@@ -0,0 +1,141 @@
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)
}
forceOverwrite := context.flags.Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
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.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
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)")
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+107
View File
@@ -0,0 +1,107 @@
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)
}
forceOverwrite := context.flags.Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
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.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+26
View File
@@ -0,0 +1,26 @@
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(),
makeCmdRepoSearch(),
},
}
}
+243
View File
@@ -0,0 +1,243 @@
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)
}
forceReplace := context.flags.Lookup("force-replace").Value.Get().(bool)
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(), ".udeb") ||
strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, path)
}
return nil
})
} else {
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
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)
if forceReplace {
list.PrepareIndex()
}
for _, file := range packageFiles {
var (
stanza deb.Stanza
p *deb.Package
)
candidateProcessedFiles := []string{}
isSourcePackage := strings.HasSuffix(file, ".dsc")
isUdebPackage := strings.HasSuffix(file, ".udeb")
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)
if isUdebPackage {
p = deb.NewUdebPackageFromControlFile(stanza)
} else {
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
}
if forceReplace {
conflictingPackages := list.Search(deb.Dependency{Pkg: p.Name, Version: p.Version, Architecture: p.Architecture}, true)
for _, cp := range conflictingPackages {
context.Progress().ColoredPrintf("@r[-]@| %s removed due to conflict with package being added", cp)
list.Remove(cp)
}
}
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, .udeb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.[u]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")
cmd.Flag.Bool("force-replace", false, "when adding package that conflicts with existing package, remove existing package")
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 one 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
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search repo for packages matching query",
Long: `
Command search displays list of packages in local repository that match package query
Example:
$ aptly repo search my-software '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-repo-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
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
}
+44
View File
@@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
)
// Run runs single command starting from root cmd with args, optionally initializing context
func Run(cmd *commander.Command, cmdArgs []string, initContext bool) (returnCode int) {
defer func() {
if r := recover(); r != nil {
fatal, ok := r.(*FatalError)
if !ok {
panic(r)
}
fmt.Println("ERROR:", fatal.Message)
returnCode = fatal.ReturnCode
}
}()
returnCode = 0
flags, args, err := cmd.ParseFlags(cmdArgs)
if err != nil {
Fatal(err)
}
if initContext {
err = InitContext(flags)
if err != nil {
Fatal(err)
}
defer ShutdownContext()
}
context.UpdateFlags(flags)
err = cmd.Dispatch(args)
if err != nil {
Fatal(err)
}
return
}
+29 -17
View File
@@ -1,28 +1,33 @@
package main
package cmd
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"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
publishedCollection := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
if publishedCollection.Len() == 0 {
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := cmd.Flag.Lookup("listen").Value.String()
listen := context.flags.Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen)
@@ -39,11 +44,11 @@ func aptlyServe(cmd *commander.Command, args []string) error {
fmt.Printf("Serving published repositories, recommended apt sources list:\n\n")
sources := make(sort.StringSlice, 0, publishedCollection.Len())
published := make(map[string]*debian.PublishedRepo, publishedCollection.Len())
sources := make(sort.StringSlice, 0, context.CollectionFactory().PublishedRepoCollection().Len())
published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
err = publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollection.LoadComplete(repo, snapshotCollection)
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
}
@@ -71,14 +76,20 @@ func aptlyServe(cmd *commander.Command, args []string) error {
}
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component)
repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
if utils.StrSliceHasItem(repo.Architectures, "source") {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
}
}
context.database.Close()
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(context.packageRepository.PublicPath())))
err = http.ListenAndServe(listen, http.FileServer(http.Dir(publicPath)))
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
@@ -89,12 +100,13 @@ func makeCmdServe() *commander.Command {
cmd := &commander.Command{
Run: aptlyServe,
UsageLine: "serve",
Short: "start embedded HTTP server to serve published repositories",
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.
ex:
Example:
$ aptly serve -listen=:8080
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
+25
View File
@@ -0,0 +1,25 @@
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(),
makeCmdSnapshotSearch(),
makeCmdSnapshotFilter(),
},
}
}
+108
View File
@@ -0,0 +1,108 @@
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 = repo.CheckLock()
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
}
+107
View File
@@ -0,0 +1,107 @@
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 aptlySnapshotFilter(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return commander.ErrCommandError
}
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
// Load <source> snapshot
source, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Convert snapshot to package list
context.Progress().Printf("Loading packages (%d)...\n", source.RefList().Len())
packageList, 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()
// 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 && withDeps {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-2)
for i, arg := range args[2:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
}
// Filter with dependencies as requested
result, err := packageList.Filter(queries, withDeps, nil, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Create <destination> snapshot
destination := deb.NewSnapshotFromPackageList(args[1], []*deb.Snapshot{source}, result,
fmt.Sprintf("Filtered '%s', query was: '%s'", source.Name, strings.Join(args[2:], " ")))
err = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
context.Progress().Printf("\nSnapshot %s successfully filtered.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func makeCmdSnapshotFilter() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotFilter,
UsageLine: "filter <source> <destination> <package-query> ...",
Short: "filter packages in snapshot producing another snapshot",
Long: `
Command filter does filtering in snapshot <source>, producing another
snapshot <destination>. Packages could be specified simply
as 'package-name' or as package queries.
Example:
$ aptly snapshot filter wheezy-main wheezy-required 'Priorioty (required)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-filter", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependent packages as well")
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
}
+125
View File
@@ -0,0 +1,125 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
)
func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
command := cmd.Parent.Name()
var reflist *deb.PackageRefList
if command == "snapshot" {
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = snapshot.RefList()
} else if command == "mirror" {
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = repo.RefList()
} else if command == "repo" {
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = repo.RefList()
} else {
panic("unknown command")
}
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
list.PrepareIndex()
q, err := query.Parse(args[1])
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
architecturesList := []string{}
if withDeps {
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = list.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
}
result, err := list.Filter([]deb.PackageQuery{q}, withDeps,
nil, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
result.ForEach(func(p *deb.Package) error {
context.Progress().Printf("%s\n", p)
return nil
})
return err
}
func makeCmdSnapshotSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search snapshot for packages matching query",
Long: `
Command search displays list of packages in snapshot that match package query
Example:
$ aptly snapshot search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-search", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
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
}
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdTask() *commander.Command {
return &commander.Command{
UsageLine: "task",
Short: "manage aptly tasks",
Subcommands: []*commander.Command{
makeCmdTaskRun(),
},
}
}
+148
View File
@@ -0,0 +1,148 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/mattn/go-shellwords"
"github.com/smira/commander"
)
func aptlyTaskRun(cmd *commander.Command, args []string) error {
var err error
var cmdList [][]string
if filename := cmd.Flag.Lookup("filename").Value.Get().(string); filename != "" {
var text string
cmdArgs := []string{}
if finfo, err := os.Stat(filename); os.IsNotExist(err) || finfo.IsDir() {
return fmt.Errorf("no such file, %s\n", filename)
}
fmt.Println("Reading file...\n")
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
text = strings.TrimSpace(scanner.Text()) + ","
parsedArgs, _ := shellwords.Parse(text)
cmdArgs = append(cmdArgs, parsedArgs...)
}
if err = scanner.Err(); err != nil {
return err
}
if len(cmdArgs) == 0 {
return fmt.Errorf("the file is empty")
}
cmdList = formatCommands(cmdArgs)
} else if len(args) == 0 {
var text string
cmdArgs := []string{}
fmt.Println("Please enter one command per line and leave one blank when finished.")
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("> ")
text, _ = reader.ReadString('\n')
if text == "\n" {
break
} else {
text = strings.TrimSpace(text) + ","
parsedArgs, _ := shellwords.Parse(text)
cmdArgs = append(cmdArgs, parsedArgs...)
}
}
if len(cmdArgs) == 0 {
return fmt.Errorf("nothing entered")
}
cmdList = formatCommands(cmdArgs)
} else {
cmdList = formatCommands(args)
}
commandErrored := false
for i, command := range cmdList {
if !commandErrored {
context.Progress().ColoredPrintf("@g%d) [Running]: %s@!", (i + 1), strings.Join(command, " "))
context.Progress().ColoredPrintf("\n@yBegin command output: ----------------------------@!")
context.Progress().Flush()
returnCode := Run(RootCommand(), command, false)
if returnCode != 0 {
commandErrored = true
}
context.Progress().ColoredPrintf("\n@yEnd command output: ------------------------------@!")
CleanupContext()
} else {
context.Progress().ColoredPrintf("@r%d) [Skipping]: %s@!", (i + 1), strings.Join(command, " "))
}
}
if commandErrored {
err = fmt.Errorf("at least one command has reported an error")
}
return err
}
func formatCommands(args []string) [][]string {
var cmd []string
var cmdArray [][]string
for _, s := range args {
if sTrimmed := strings.TrimRight(s, ","); sTrimmed != s {
cmd = append(cmd, sTrimmed)
cmdArray = append(cmdArray, cmd)
cmd = []string{}
} else {
cmd = append(cmd, s)
}
}
if len(cmd) > 0 {
cmdArray = append(cmdArray, cmd)
}
return cmdArray
}
func makeCmdTaskRun() *commander.Command {
cmd := &commander.Command{
Run: aptlyTaskRun,
UsageLine: "run -filename=<filename> | <command1>, <command2>, ...",
Short: "run aptly tasks",
Long: `
Command helps origanise multiple aptly commands in one single aptly task, running as single thread.
Example:
$ aptly task run
> repo create local
> repo add local pkg1
> publish repo local
> serve
>
`,
}
cmd.Flag.String("filename", "", "specifies the filename that contains the commands to run")
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),
}
}
-174
View File
@@ -1,174 +0,0 @@
package main
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
graph := gographviz.NewGraph()
graph.SetDir(true)
graph.SetName("aptly")
fmt.Printf("Loading mirrors...\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
err := repoCollection.LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())),
})
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
err := snapshotCollection.LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
})
if snapshot.SourceKind == "repo" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
} else if snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
publishedCollection := debian.NewPublishedRepoCollection(context.database)
publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
})
graph.AddEdge(graphvizEscape(repo.SnapshotUUID), "", graphvizEscape(repo.UUID), "", true, nil)
return nil
})
fmt.Printf("Generating graph...\n")
buf := bytes.NewBufferString(graph.String())
tempfile, err := ioutil.TempFile("", "aptly-graph")
if err != nil {
return err
}
tempfile.Close()
os.Remove(tempfile.Name())
tempfilename := tempfile.Name() + ".png"
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
}
_, err = io.Copy(stdin, buf)
if err != nil {
return err
}
err = stdin.Close()
if err != nil {
return err
}
err = command.Wait()
if err != nil {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
}
return err
}
func makeCmdGraph() *commander.Command {
cmd := &commander.Command{
Run: aptlyGraph,
UsageLine: "graph",
Short: "display graph of dependencies between aptly objects (requires graphviz)",
Long: `
Command graph displays relationship between mirrors, snapshots and published repositories using
graphviz package to render graph as image.
ex:
$ aptly graph
`,
Flag: *flag.NewFlagSet("aptly-graph", flag.ExitOnError),
}
return cmd
}
-362
View File
@@ -1,362 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getVerifier(cmd *commander.Command) (utils.Verifier, error) {
if utils.Config.GpgDisableVerify || cmd.Flag.Lookup("ignore-signatures").Value.Get().(bool) {
return nil, nil
}
verifier := &utils.GpgVerifier{}
for _, keyRing := range keyRings.keyRings {
verifier.AddKeyring(keyRing)
}
err := verifier.InitKeyring()
if err != nil {
return nil, err
}
return verifier, nil
}
type keyRingsFlag struct {
keyRings []string
}
func (k *keyRingsFlag) Set(value string) error {
k.keyRings = append(k.keyRings, value)
return nil
}
func (k *keyRingsFlag) Get() interface{} {
return k.keyRings
}
func (k *keyRingsFlag) String() string {
return strings.Join(k.keyRings, ",")
}
var keyRings = keyRingsFlag{}
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
if repoCollection.Len() > 0 {
fmt.Printf("List of mirrors:\n")
repos := make(sort.StringSlice, repoCollection.Len())
i := 0
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
repos[i] = repo.String()
i++
return nil
})
sort.Strings(repos)
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
return err
}
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
}
repo, err := debian.NewRemoteRepo(args[0], args[1], args[2], args[3:], context.architecturesList)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err
}
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := cmd.Flag.Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = repo.Download(context.downloader, packageCollection, context.packageRepository, ignoreMismatch)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
fmt.Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshots := snapshotCollection.ByRemoteRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Mirror `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete mirror with snapshots, use -force to override")
}
}
err = repoCollection.Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Mirror `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror of Debian repository",
Long: `
Create records information about new mirror and fetches Release file (it doesn't download packages).
ex:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors of remote repositories",
Long: `
List shows full list of remote repositories.
ex:
$ aptly mirror list
`,
Flag: *flag.NewFlagSet("aptly-mirror-list", flag.ExitOnError),
}
cmd.Flag.Bool("v", false, "enable verbose output")
return cmd
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show <name>",
Short: "show details about remote repository mirror",
Long: `
Show shows full information about mirror.
ex:
$ aptly mirror show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update <name>",
Short: "update packages from remote mirror",
Long: `
Update downloads list of packages and package files.
ex:
$ aptly mirror update wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete remote repository mirror",
Long: `
Drop deletes information about remote repository mirror. Package data is not deleted
(it could be still used by other mirrors or snapshots).
ex:
$ aptly mirror drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force mirror deletion even if used by snapshots")
return cmd
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
},
Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError),
}
}
-266
View File
@@ -1,266 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getSigner(cmd *commander.Command) (utils.Signer, error) {
if cmd.Flag.Lookup("skip-signing").Value.Get().(bool) || utils.Config.GpgDisableSign {
return nil, nil
}
signer := &utils.GpgSigner{}
key := cmd.Flag.Lookup("gpg-key").Value.String()
if key != "" {
signer.SetKey(key)
}
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
} else {
prefix = ""
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
var sourceRepo *debian.RemoteRepo
if snapshot.SourceKind == "repo" && len(snapshot.SourceIDs) == 1 {
repoCollection := debian.NewRemoteRepoCollection(context.database)
sourceRepo, _ = repoCollection.ByUUID(snapshot.SourceIDs[0])
}
component := cmd.Flag.Lookup("component").Value.String()
if component == "" {
if sourceRepo != nil && len(sourceRepo.Components) == 1 {
component = sourceRepo.Components[0]
} else {
component = "main"
}
}
distribution := cmd.Flag.Lookup("distribution").Value.String()
if distribution == "" {
if sourceRepo != nil {
distribution = sourceRepo.Distribution
}
if distribution == "" {
return fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
published, err := debian.NewPublishedRepo(prefix, distribution, component, context.architecturesList, snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
duplicate := publishedCollecton.CheckDuplicate(published)
if duplicate != nil {
publishedCollecton.LoadComplete(duplicate, snapshotCollection)
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = published.Publish(context.packageRepository, packageCollection, signer)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = publishedCollecton.Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
fmt.Printf("\nSnapshot %s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
snapshot.Name, context.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")
fmt.Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollecton.Len() == 0 {
fmt.Printf("No snapshots have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
published := make(sort.StringSlice, 0, publishedCollecton.Len())
err = publishedCollecton.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollecton.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
published = append(published, repo.String())
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
sort.Strings(published)
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
return err
}
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
distribution := args[0]
prefix := "."
if len(args) == 2 {
prefix = args[1]
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
err = publishedCollecton.Remove(context.packageRepository, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
fmt.Printf("\nPublished repositroy has been removed successfully.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshot,
UsageLine: "snapshot <name> [<prefix>]",
Short: "makes Debian repository out of snapshot",
Long: `
Command publish oublishes snapshot as Debian repository ready to be used by apt tools.
ex.
$ aptly publish snapshot wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
return cmd
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
Short: "removes files of published repository",
Long: `
Command removes whatever has been published under specified prefix and distribution name.
ex.
$ aptly publish drop wheezy
`,
Flag: *flag.NewFlagSet("aptly-publish-drop", flag.ExitOnError),
}
return cmd
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "displays list of published repositories",
Long: `
Display command displays list of currently published snapshots with information about published root.
ex.
$ aptly publish list
`,
Flag: *flag.NewFlagSet("aptly-publish-list", flag.ExitOnError),
}
return cmd
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishSnapshot(),
makeCmdPublishList(),
makeCmdPublishDrop(),
},
Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError),
}
}
-730
View File
@@ -1,730 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/wsxiaoys/terminal/color"
"sort"
"strings"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var (
err error
snapshot *debian.Snapshot
)
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
// aptly snapshot create snap from mirror mirror
repoName, snapshotName := args[3], args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = debian.NewSnapshotFromRepository(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := debian.NewPackageList()
snapshot = debian.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
if snapshotCollection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
snapshots := make(sort.StringSlice, snapshotCollection.Len())
i := 0
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
snapshots[i] = snapshot.String()
i++
return nil
})
sort.Strings(snapshots)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
return err
}
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
return err
}
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
snapshots := make([]*debian.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = snapshotCollection.ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = snapshotCollection.LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
packageList, err := debian.NewPackageListFromRefList(snapshots[0].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := debian.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
for i := 1; i < len(snapshots); i++ {
pL, err := debian.NewPackageListFromRefList(snapshots[i].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
missing, err := packageList.VerifyDependencies(context.dependencyOptions, architecturesList, sourcePackageList)
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
fmt.Printf("All dependencies are satisfied.\n")
} else {
fmt.Printf("Missing dependencies (%d):\n", len(missing))
deps := make(sort.StringSlice, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
fmt.Printf(" %s\n", dep)
}
}
return err
}
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
}
noDeps := cmd.Flag.Lookup("no-deps").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name> snapshot
snapshot, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
fmt.Printf("Dependencies would be pulled into snapshot:\n %s\nfrom snapshot:\n %s\nand result would be saved as new snapshot %s.\n",
snapshot, source, args[2])
// Convert snapshot to package list
fmt.Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := debian.NewPackageListFromRefList(snapshot.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := debian.NewPackageListFromRefList(source.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
fmt.Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]debian.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = debian.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]debian.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
}
// Go over list of initial dependencies + list of dependencies found
for i := 0; i < len(dependencies); i++ {
dep := dependencies[i]
// Search for package that can satisfy dependencies
pkg := sourcePackageList.Search(dep)
if pkg == nil {
color.Printf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
fmt.Printf("\n")
continue
}
// Remove all packages with the same name and architecture
for p := packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
color.Printf("@r[-]@| %s removed", p)
fmt.Printf("\n")
p = packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name})
}
// Add new discovered package
packageList.Add(pkg)
color.Printf("@g[+]@| %s added", pkg)
fmt.Printf("\n")
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := debian.NewPackageList()
pL.Add(pkg)
missing, err := pL.VerifyDependencies(context.dependencyOptions, []string{arch}, packageList)
if err != nil {
color.Printf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
fmt.Printf("\n")
}
// Append missing dependencies to the list of dependencies to satisfy
for _, misDep := range missing {
found := false
for _, d := range dependencies {
if d == misDep {
found = true
break
}
}
if !found {
dependencies = append(dependencies, misDep)
}
}
}
}
if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
fmt.Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := debian.NewSnapshotFromPackageList(args[2], []*debian.Snapshot{snapshot, source}, packageList,
fmt.Sprintf("Pulled into '%s' with '%s' as source, pull request was: '%s'", snapshot.Name, source.Name, strings.Join(args[3:], " ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
}
return err
}
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
}
onlyMatching := cmd.Flag.Lookup("only-matching").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name-a> snapshot
snapshotA, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotB)
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
fmt.Printf("Snapshots are identical.\n")
} else {
fmt.Printf(" Arch | Package | Version in A | Version in B\n")
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
var verA, verB, pkg, arch, code string
if pdiff.Left == nil {
verA = "-"
verB = pdiff.Right.Version
pkg = pdiff.Right.Name
arch = pdiff.Right.Architecture
} else {
pkg = pdiff.Left.Name
arch = pdiff.Left.Architecture
verA = pdiff.Left.Version
if pdiff.Right == nil {
verB = "-"
} else {
verB = pdiff.Right.Version
}
}
if pdiff.Left == nil {
code = "@g+@|"
} else {
if pdiff.Right == nil {
code = "@r-@|"
} else {
code = "@y!@|"
}
}
color.Printf(code+" %-6s | %-40s | %-40s | %-40s\n", arch, pkg, verA, verB)
}
}
return err
}
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
sources := make([]*debian.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = snapshotCollection.ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList())
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := debian.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
publishedRepoCollection := debian.NewPublishedRepoCollection(context.database)
published := publishedRepoCollection.BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = publishedRepoCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: snapshot is published")
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
fmt.Printf("Snapshot `%s` was used as a source in following snapshots:\n", snapshot.Name)
for _, snap := range snapshots {
fmt.Printf(" * %s\n", snap)
}
return fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use -force to override")
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create <name> from mirror <mirror-name> | create <name> empty",
Short: "creates immutable snapshot of mirror contents",
Long: `
Command create .. from mirror makes persistent immutable snapshot of remote repository mirror. Snapshot could be
published or further modified using merge, pull and other aptly features.
Command create .. empty creates empty snapshot that could be used as a basis for snapshot pull operations, for example.
As snapshots are immutable, creating one empty snapshot should be enough.
ex.
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "lists snapshots",
Long: `
Command list shows full list of snapshots created.
ex:
$ aptly snapshot list
`,
Flag: *flag.NewFlagSet("aptly-snapshot-list", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show <name>",
Short: "shows details about snapshot",
Long: `
Command show displays full information about snapshot.
ex.
$ aptly snapshot show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verifies that dependencies are satisfied in snapshot",
Long: `
Verify does depenency resolution in snapshot, possibly using additional snapshots as dependency sources.
All unsatisfied dependencies are returned.
ex.
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
Flag: *flag.NewFlagSet("aptly-snapshot-verify", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
Short: "performs partial upgrades (pulls new packages) from another snapshot",
Long: `
Command pull pulls new packages along with its dependencies in <name> snapshot
from <source> snapshot. Also can upgrade package version from one snapshot into
another, once again along with dependencies. New snapshot <destination> is created as result of this
process. Packages could be specified simply as 'package-name' or as dependency 'package-name (>= version)'.
ex.
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
return cmd
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "calculates difference in packages between two snapshots",
Long: `
Command diff shows list of missing and new packages, difference in package versions between two snapshots.
ex.
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots into one, replacing matching packages",
Long: `
Merge merges several snapshots into one. Merge happens from left to right. Packages with the same
name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
If run with only one source snapshot, merge copies source into destination.
ex.
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-merge", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about snapshot. If snapshot is published,
it can't be dropped.
ex.
$ aptly snapshot drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "remove snapshot even if it was used as source for other snapshots")
return cmd
}
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
},
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
}
}
-27
View File
@@ -1,27 +0,0 @@
package main
import (
"fmt"
"github.com/smira/aptly/debian"
)
//ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *debian.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database)
err = reflist.ForEach(func(key []byte) error {
p, err := packageCollection.ByKey(key)
if err != nil {
return err
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
+74 -21
View File
@@ -1,19 +1,25 @@
package utils
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
@@ -26,10 +32,14 @@ type Progress struct {
barShown bool
}
// Check interface
var (
_ aptly.Progress = (*Progress)(nil)
)
// NewProgress creates new progress instance
func NewProgress() *Progress {
return &Progress{
stop: make(chan bool),
stopped: make(chan bool),
queue: make(chan printTask, 100),
}
@@ -43,11 +53,18 @@ func (p *Progress) Start() {
// Shutdown shuts down progress display
func (p *Progress) Shutdown() {
p.ShutdownBar()
p.stop <- true
p.queue <- printTask{code: codeStop}
<-p.stopped
}
// InitBar creates progressbar for count bytes
// 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")
@@ -64,7 +81,6 @@ func (p *Progress) InitBar(count int64, isBytes bool) {
p.bar.SetUnits(pb.U_BYTES)
p.bar.ShowSpeed = true
}
p.barShown = false
p.bar.Start()
}
}
@@ -94,32 +110,69 @@ func (p *Progress) AddBar(count int) {
}
}
// 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 {
select {
case 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:
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 <-p.stop:
case codeHideProgress:
if p.barShown {
fmt.Print("\r\033[2K")
p.barShown = false
}
case codeFlush:
task.reply <- true
case codeStop:
p.stopped <- true
return
}
+2 -1
View File
@@ -1,12 +1,13 @@
// +build !freebsd
package utils
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
}
+128 -19
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
@@ -19,12 +21,19 @@ 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
ReOpen() error
StartBatch()
FinishBatch() error
CompactDB() error
}
type levelDB struct {
db *leveldb.DB
path string
db *leveldb.DB
batch *leveldb.Batch
}
// Check interface
@@ -32,19 +41,42 @@ var (
_ Storage = &levelDB{}
)
// OpenDB opens (creates) LevelDB database
func OpenDB(path string) (Storage, error) {
func internalOpen(path string) (*leveldb.DB, error) {
o := &opt.Options{
Filter: filter.NewBloomFilter(10),
}
db, err := leveldb.OpenFile(path, o)
return leveldb.OpenFile(path, o)
}
// OpenDB opens (creates) LevelDB database
func OpenDB(path string) (Storage, error) {
db, err := internalOpen(path)
if err != nil {
return nil, err
}
return &levelDB{db: db}, nil
return &levelDB{db: db, path: path}, 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 {
@@ -57,33 +89,110 @@ 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)
}
// 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)
}
func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
// 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
}
func (l *levelDB) Close() error {
return l.db.Close()
// 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 {
if l.db == nil {
return nil
}
err := l.db.Close()
l.db = nil
return err
}
// Reopen tries to re-open the database
func (l *levelDB) ReOpen() error {
if l.db != nil {
return nil
}
var err error
l.db, err = internalOpen(l.path)
return err
}
// 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{})
}
+98 -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")
@@ -64,19 +89,89 @@ func (s *LevelDBSuite) TestDelete(c *C) {
c.Assert(err, IsNil)
}
func (s *LevelDBSuite) TestFetchByPrefix(c *C) {
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)
}
func (s *LevelDBSuite) TestReOpen(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
err = s.db.Close()
c.Assert(err, IsNil)
err = s.db.ReOpen()
c.Assert(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
}
+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 -3
View File
@@ -1,4 +1,4 @@
package debian
package deb
import (
"bufio"
@@ -11,8 +11,9 @@ import (
type Stanza map[string]string
// Canonical order of fields in stanza
var canocialOrder = []string{"Origin", "Label", "Suite", "Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer",
"Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256"}
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"
+254
View File
@@ -0,0 +1,254 @@
package deb
import (
"bufio"
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"os"
"path/filepath"
"strings"
)
type indexFiles struct {
publishedStorage aptly.PublishedStorage
basePath string
renameMap map[string]string
generatedFiles map[string]utils.ChecksumInfo
tempDir string
suffix string
indexes map[string]*indexFile
}
type indexFile struct {
parent *indexFiles
discardable bool
compressable bool
signable bool
relativePath string
tempFilename string
tempFile *os.File
w *bufio.Writer
}
func (file *indexFile) BufWriter() (*bufio.Writer, error) {
if file.w == nil {
var err error
file.tempFilename = filepath.Join(file.parent.tempDir, strings.Replace(file.relativePath, "/", "_", -1))
file.tempFile, err = os.Create(file.tempFilename)
if err != nil {
return nil, fmt.Errorf("unable to create temporary index file: %s", err)
}
file.w = bufio.NewWriter(file.tempFile)
}
return file.w, nil
}
func (file *indexFile) Finalize(signer utils.Signer) error {
if file.w == nil {
if file.discardable {
return nil
}
file.BufWriter()
}
err := file.w.Flush()
if err != nil {
file.tempFile.Close()
return fmt.Errorf("unable to write to index file: %s", err)
}
if file.compressable {
err = utils.CompressFile(file.tempFile)
if err != nil {
file.tempFile.Close()
return fmt.Errorf("unable to compress index file: %s", err)
}
}
file.tempFile.Close()
exts := []string{""}
if file.compressable {
exts = append(exts, ".gz", ".bz2")
}
for _, ext := range exts {
var checksumInfo utils.ChecksumInfo
checksumInfo, err = utils.ChecksumsForFile(file.tempFilename + ext)
if err != nil {
return fmt.Errorf("unable to collect checksums: %s", err)
}
file.parent.generatedFiles[file.relativePath+ext] = checksumInfo
}
err = file.parent.publishedStorage.MkDir(filepath.Dir(filepath.Join(file.parent.basePath, file.relativePath)))
if err != nil {
return fmt.Errorf("unable to create dir: %s", err)
}
for _, ext := range exts {
err = file.parent.publishedStorage.PutFile(filepath.Join(file.parent.basePath, file.relativePath+file.parent.suffix+ext),
file.tempFilename+ext)
if err != nil {
return fmt.Errorf("unable to publish file: %s", err)
}
if file.parent.suffix != "" {
file.parent.renameMap[filepath.Join(file.parent.basePath, file.relativePath+file.parent.suffix+ext)] =
filepath.Join(file.parent.basePath, file.relativePath+ext)
}
}
if file.signable && signer != nil {
err = signer.DetachedSign(file.tempFilename, file.tempFilename+".gpg")
if err != nil {
return fmt.Errorf("unable to detached sign file: %s", err)
}
err = signer.ClearSign(file.tempFilename, filepath.Join(filepath.Dir(file.tempFilename), "In"+filepath.Base(file.tempFilename)))
if err != nil {
return fmt.Errorf("unable to clearsign file: %s", err)
}
if file.parent.suffix != "" {
file.parent.renameMap[filepath.Join(file.parent.basePath, file.relativePath+file.parent.suffix+".gpg")] =
filepath.Join(file.parent.basePath, file.relativePath+".gpg")
file.parent.renameMap[filepath.Join(file.parent.basePath, "In"+file.relativePath+file.parent.suffix)] =
filepath.Join(file.parent.basePath, "In"+file.relativePath)
}
err = file.parent.publishedStorage.PutFile(filepath.Join(file.parent.basePath, file.relativePath+file.parent.suffix+".gpg"),
file.tempFilename+".gpg")
if err != nil {
return fmt.Errorf("unable to publish file: %s", err)
}
err = file.parent.publishedStorage.PutFile(filepath.Join(file.parent.basePath, "In"+file.relativePath+file.parent.suffix),
filepath.Join(filepath.Dir(file.tempFilename), "In"+filepath.Base(file.tempFilename)))
if err != nil {
return fmt.Errorf("unable to publish file: %s", err)
}
}
return nil
}
func newIndexFiles(publishedStorage aptly.PublishedStorage, basePath, tempDir, suffix string) *indexFiles {
return &indexFiles{
publishedStorage: publishedStorage,
basePath: basePath,
renameMap: make(map[string]string),
generatedFiles: make(map[string]utils.ChecksumInfo),
tempDir: tempDir,
suffix: suffix,
indexes: make(map[string]*indexFile),
}
}
func (files *indexFiles) PackageIndex(component, arch string, udeb bool) *indexFile {
key := fmt.Sprintf("pi-%s-%s-%s", component, arch, udeb)
file, ok := files.indexes[key]
if !ok {
var relativePath string
if arch == "source" {
relativePath = filepath.Join(component, "source", "Sources")
} else {
if udeb {
relativePath = filepath.Join(component, "debian-installer", fmt.Sprintf("binary-%s", arch), "Packages")
} else {
relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Packages")
}
}
file = &indexFile{
parent: files,
discardable: false,
compressable: true,
signable: false,
relativePath: relativePath,
}
files.indexes[key] = file
}
return file
}
func (files *indexFiles) ReleaseIndex(component, arch string, udeb bool) *indexFile {
key := fmt.Sprintf("ri-%s-%s-%s", component, arch, udeb)
file, ok := files.indexes[key]
if !ok {
var relativePath string
if arch == "source" {
relativePath = filepath.Join(component, "source", "Release")
} else {
if udeb {
relativePath = filepath.Join(component, "debian-installer", fmt.Sprintf("binary-%s", arch), "Release")
} else {
relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Release")
}
}
file = &indexFile{
parent: files,
discardable: udeb,
compressable: false,
signable: false,
relativePath: relativePath,
}
files.indexes[key] = file
}
return file
}
func (files *indexFiles) ReleaseFile() *indexFile {
return &indexFile{
parent: files,
discardable: false,
compressable: false,
signable: true,
relativePath: "Release",
}
}
func (files *indexFiles) FinalizeAll(progress aptly.Progress) (err error) {
if progress != nil {
progress.InitBar(int64(len(files.indexes)), false)
defer progress.ShutdownBar()
}
for _, file := range files.indexes {
err = file.Finalize(nil)
if err != nil {
return
}
if progress != nil {
progress.AddBar(1)
}
}
files.indexes = make(map[string]*indexFile)
return
}
func (files *indexFiles) RenameFiles() error {
var err error
for oldName, newName := range files.renameMap {
err = files.publishedStorage.RenameFile(oldName, newName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
}
return nil
}
+470
View File
@@ -0,0 +1,470 @@
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{}
_ PackageCatalog = &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) {
l.PrepareIndex()
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.packagesIndex {
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))
for _, dep := range variants {
if dep.Architecture == "" {
dep.Architecture = arch
}
hash := dep.Hash()
satisfied, ok := cache[hash]
if !ok {
satisfied = sources.Search(dep, false) != nil
cache[hash] = satisfied
}
if !satisfied && !ok {
variantsMissing = append(variantsMissing, dep)
}
if satisfied && options&DepFollowAllVariants == 0 {
variantsMissing = nil
break
}
}
missing = append(missing, variantsMissing...)
}
}
}
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() {
if l.indexed {
return
}
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
}
// SearchSupported returns true for PackageList
func (l *PackageList) SearchSupported() bool {
return true
}
// SearchByKey looks up package by exact key reference
func (l *PackageList) SearchByKey(arch, name, version string) (result *PackageList) {
result = NewPackageList()
pkg := l.packages["P"+arch+" "+name+" "+version]
if pkg != nil {
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()
result.PrepareIndex()
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 {
// dependency might have already been satisfied
// with packages already been added
if result.Search(dep, false) != nil {
continue
}
searchResults := l.Search(dep, false)
if searchResults != nil {
for _, p := range searchResults {
result.Add(p)
dependencySource.Add(p)
added++
if dependencyOptions&DepFollowAllVariants == 0 {
break
}
}
}
}
}
}
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!")
}
+591
View File
@@ -0,0 +1,591 @@
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
// Is this udeb package
IsUdeb 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
}
// NewUdebPackageFromControlFile creates .udeb Package from parsed Debian control file
func NewUdebPackageFromControlFile(input Stanza) *Package {
p := NewPackageFromControlFile(input)
p.IsUdeb = true
return p
}
// 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"
}
if p.IsUdeb {
return "udeb"
}
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, component string, force bool) 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, force)
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
}
+290
View File
@@ -0,0 +1,290 @@
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
}
// Verify interface
var (
_ PackageCatalog = &PackageCollection{}
)
// 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
}
// Scan does full scan on all the packages
func (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) {
result = NewPackageList()
for _, key := range collection.db.KeysByPrefix([]byte("P")) {
pkg, err := collection.ByKey(key)
if err != nil {
panic(fmt.Sprintf("unable to load package: %s", err))
}
if q.Matches(pkg) {
result.Add(pkg)
}
}
return
}
// Search is not implemented
func (collection *PackageCollection) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
panic("Not implemented")
}
// SearchSupported returns false
func (collection *PackageCollection) SearchSupported() bool {
return false
}
// SearchByKey finds package by exact key
func (collection *PackageCollection) SearchByKey(arch, name, version string) (result *PackageList) {
result = NewPackageList()
for _, key := range collection.db.KeysByPrefix([]byte(fmt.Sprintf("P%s %s %s", arch, name, version))) {
pkg, err := collection.ByKey(key)
if err != nil {
panic(fmt.Sprintf("unable to load package: %s", err))
}
if pkg.Architecture == arch && pkg.Name == name && pkg.Version == version {
result.Add(pkg)
}
}
return
}
+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))
}
+498
View File
@@ -0,0 +1,498 @@
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.IsUdeb, 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) TestNewUdebFromPara(c *C) {
stanza, _ := NewControlFileReader(bytes.NewBufferString(udebPackageMeta)).ReadStanza()
p := NewUdebPackageFromControlFile(stanza)
c.Check(p.IsSource, Equals, false)
c.Check(p.IsUdeb, Equals, true)
c.Check(p.Name, Equals, "dmidecode-udeb")
c.Check(p.Version, Equals, "2.11-9")
c.Check(p.Architecture, Equals, "amd64")
c.Check(p.Provides, DeepEquals, []string(nil))
c.Check(p.Files(), HasLen, 1)
c.Check(p.Files()[0].Filename, Equals, "dmidecode-udeb_2.11-9_amd64.udeb")
c.Check(p.deps.Depends, DeepEquals, []string{"libc6-udeb (>= 2.13)"})
}
func (s *PackageSuite) TestNewSourceFromPara(c *C) {
p, err := NewSourcePackageFromControlFile(s.sourceStanza)
c.Check(err, IsNil)
c.Check(p.IsSource, Equals, true)
c.Check(p.IsUdeb, Equals, false)
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())
stanza5, _ := NewControlFileReader(bytes.NewBufferString(udebPackageMeta)).ReadStanza()
p5 := NewUdebPackageFromControlFile(stanza5)
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(p5.GetField("$Source"), Equals, "dmidecode")
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(p5.GetField("$SourceVersion"), Equals, "2.11-9")
c.Check(p.GetField("$Architecture"), Equals, "i386")
c.Check(p4.GetField("$Architecture"), Equals, "source")
c.Check(p5.GetField("$Architecture"), Equals, "amd64")
c.Check(p.GetField("$PackageType"), Equals, "deb")
c.Check(p4.GetField("$PackageType"), Equals, "source")
c.Check(p5.GetField("$PackageType"), Equals, "udeb")
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", false)
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", false)
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
`
const udebPackageMeta = `Package: dmidecode-udeb
Source: dmidecode
Version: 2.11-9
Installed-Size: 115
Maintainer: Daniel Baumann <daniel.baumann@progress-technologies.net>
Architecture: amd64
Depends: libc6-udeb (>= 2.13)
Description: SMBIOS/DMI table decoder (udeb)
Description-md5: bdfb786c6a57097be8c8600b800e749f
Section: debian-installer
Priority: optional
Filename: pool/main/d/dmidecode/dmidecode-udeb_2.11-9_amd64.udeb
Size: 29188
MD5sum: ae70341c4d96dcded89fa670bcfea31e
SHA1: 9532ae4226a85805189a671ee0283f719d48a5ba
SHA256: bbb3a2cb07f741c3995b6d4bb08d772d83582b93a0236d4ea7736bc0370fc320`
+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"})
}
+1001
View File
File diff suppressed because it is too large Load Diff
+818
View File
@@ -0,0 +1,818 @@
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) SetPassphrase(passphrase, passphraseFile 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, 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")
_, err = NewPublishedRepo("", ".", "wheezy/updates", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
c.Check(err, ErrorMatches, "invalid distribution wheezy/updates, '/' is not allowed")
}
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"})
s.localRepo.DefaultDistribution = "precise/updates"
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
c.Check(err, IsNil)
c.Check(repo.Distribution, Equals, "precise-updates")
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, false)
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, false)
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, false)
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, false)
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, false)
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))
}
+267
View File
@@ -0,0 +1,267 @@
package deb
import (
"fmt"
"path/filepath"
"regexp"
"strings"
)
// PackageCatalog is abstraction on top of PackageCollection and PackageList
type PackageCatalog interface {
Scan(q PackageQuery) (result *PackageList)
Search(dep Dependency, allMatches bool) (searchResults []*Package)
SearchSupported() bool
SearchByKey(arch, name, version string) (result *PackageList)
}
// 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(list PackageCatalog) bool
// Query performs search on package list
Query(list PackageCatalog) *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(list PackageCatalog) bool {
return q.L.Fast(list) && q.R.Fast(list)
}
// Query strategy depends on nodes
func (q *OrQuery) Query(list PackageCatalog) (result *PackageList) {
if q.Fast(list) {
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(list PackageCatalog) bool {
return q.L.Fast(list) || q.R.Fast(list)
}
// Query strategy depends on nodes
func (q *AndQuery) Query(list PackageCatalog) (result *PackageList) {
if !q.Fast(list) {
result = list.Scan(q)
} else {
if q.L.Fast(list) {
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(list PackageCatalog) bool {
return false
}
// Query strategy is scan always
func (q *NotQuery) Query(list PackageCatalog) (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 PackageCatalog) (result *PackageList) {
result = list.Scan(q)
return
}
// Fast depends on the query
func (q *FieldQuery) Fast(list PackageCatalog) 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(list PackageCatalog) bool {
return list.SearchSupported()
}
// Query runs PackageList.Search
func (q *DependencyQuery) Query(list PackageCatalog) (result *PackageList) {
if q.Fast(list) {
result = NewPackageList()
for _, pkg := range list.Search(q.Dep, true) {
result.Add(pkg)
}
} else {
result = list.Scan(q)
}
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(list PackageCatalog) bool {
return true
}
// Query looks up specific package
func (q *PkgQuery) Query(list PackageCatalog) (result *PackageList) {
return list.SearchByKey(q.Arch, q.Pkg, q.Version)
}
// String interface
func (q *PkgQuery) String() string {
return fmt.Sprintf("%s_%s_%s", q.Pkg, q.Version, q.Arch)
}
+350
View File
@@ -0,0 +1,350 @@
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
}
// Has checks whether package is part of reflist
func (l *PackageRefList) Has(p *Package) bool {
key := p.Key("")
i := sort.Search(len(l.Refs), func(j int) bool { return bytes.Compare(l.Refs[j], key) >= 0 })
return i < len(l.Refs) && bytes.Compare(l.Refs[i], key) == 0
}
// 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
}
+317
View File
@@ -0,0 +1,317 @@
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) TestHas(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
s.list.Add(s.p5)
reflist := NewPackageRefListFromPackageList(s.list)
c.Check(reflist.Has(s.p1), Equals, true)
c.Check(reflist.Has(s.p3), Equals, true)
c.Check(reflist.Has(s.p5), Equals, true)
c.Check(reflist.Has(s.p2), Equals, true)
c.Check(reflist.Has(s.p6), Equals, false)
}
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"})
}
+263 -97
View File
@@ -1,25 +1,34 @@
// Package debian implements Debian-specific repository handling
package debian
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"
"syscall"
"time"
)
// RemoteRepo statuses
const (
MirrorIdle = iota
MirrorUpdating
)
// 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
@@ -33,27 +42,46 @@ type RemoteRepo struct {
Components []string
// List of architectures to fetch, if empty, then fetch all architectures
Architectures []string
// Should we download sources?
DownloadSources bool
// Should we download .udebs?
DownloadUdebs 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
// Status marks state of repository (being updated, no action)
Status int
// WorkerPID is PID of the process modifying the mirror (if any)
WorkerPID int
// "Snapshot" of current list of packages
packageRefs *PackageRefList
// Temporary list of package refs
tempPackageRefs *PackageRefList
// Parsed archived root
archiveRootURL *url.URL
// Current list of packages (filled while updating mirror)
packageList *PackageList
}
// 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) {
func NewRemoteRepo(name string, archiveRoot string, distribution string, components []string,
architectures []string, downloadSources bool, downloadUdebs bool) (*RemoteRepo, error) {
result := &RemoteRepo{
UUID: uuid.New(),
Name: name,
ArchiveRoot: archiveRoot,
Distribution: distribution,
Components: components,
Architectures: architectures,
UUID: uuid.New(),
Name: name,
ArchiveRoot: archiveRoot,
Distribution: distribution,
Components: components,
Architectures: architectures,
DownloadSources: downloadSources,
DownloadUdebs: downloadUdebs,
}
err := result.prepare()
@@ -61,13 +89,18 @@ func NewRemoteRepo(name string, archiveRoot string, distribution string, compone
return nil, err
}
if result.Distribution == "." || result.Distribution == "./" {
if strings.HasSuffix(result.Distribution, "/") || strings.HasPrefix(result.Distribution, ".") {
// flat repo
result.Distribution = ""
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")
}
if result.DownloadUdebs {
return nil, fmt.Errorf("debian-installer udebs aren't supported for flat repos")
}
result.Components = nil
}
@@ -76,21 +109,40 @@ func NewRemoteRepo(name string, archiveRoot string, distribution string, compone
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 {
return fmt.Sprintf("[%s]: %s %s", repo.Name, repo.ArchiveRoot, repo.Distribution)
srcFlag := ""
if repo.DownloadSources {
srcFlag += " [src]"
}
if repo.DownloadUdebs {
srcFlag += " [udeb]"
}
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 {
return repo.Distribution == ""
// 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 remore repo
// NumPackages return number of packages retrived from remote repo
func (repo *RemoteRepo) NumPackages() int {
if repo.packageRefs == nil {
return 0
@@ -103,6 +155,37 @@ func (repo *RemoteRepo) RefList() *PackageRefList {
return repo.packageRefs
}
// MarkAsUpdating puts current PID and sets status to updating
func (repo *RemoteRepo) MarkAsUpdating() {
repo.Status = MirrorUpdating
repo.WorkerPID = os.Getpid()
}
// MarkAsIdle clears updating flag
func (repo *RemoteRepo) MarkAsIdle() {
repo.Status = MirrorIdle
repo.WorkerPID = 0
}
// CheckLock returns error if mirror is being updated by another process
func (repo *RemoteRepo) CheckLock() error {
if repo.Status == MirrorIdle || repo.WorkerPID == 0 {
return nil
}
p, err := os.FindProcess(repo.WorkerPID)
if err != nil {
return nil
}
err = p.Signal(syscall.Signal(0))
if err == nil {
return fmt.Errorf("mirror is locked by update operation, PID %d", repo.WorkerPID)
}
return nil
}
// ReleaseURL returns URL to Release* files in repo root
func (repo *RemoteRepo) ReleaseURL(name string) *url.URL {
var path *url.URL
@@ -110,25 +193,44 @@ func (repo *RemoteRepo) ReleaseURL(name string) *url.URL {
if !repo.IsFlat() {
path = &url.URL{Path: fmt.Sprintf("dists/%s/%s", repo.Distribution, name)}
} else {
path = &url.URL{Path: name}
path = &url.URL{Path: filepath.Join(repo.Distribution, name)}
}
return repo.archiveRootURL.ResolveReference(path)
}
// FlatBinaryURL returns URL to Package files for flat repo
// FlatBinaryURL returns URL to Packages files for flat repo
func (repo *RemoteRepo) FlatBinaryURL() *url.URL {
path := &url.URL{Path: "Packages"}
path := &url.URL{Path: filepath.Join(repo.Distribution, "Packages")}
return repo.archiveRootURL.ResolveReference(path)
}
// BinaryURL returns URL of Packages file for given component and
// 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)
}
// UdebURL returns URL of Packages files for given component and
// architecture
func (repo *RemoteRepo) UdebURL(component string, architecture string) *url.URL {
path := &url.URL{Path: fmt.Sprintf("dists/%s/%s/debian-installer/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 {
@@ -137,27 +239,34 @@ func (repo *RemoteRepo) PackageURL(filename string) *url.URL {
}
// Fetch updates information about repository
func (repo *RemoteRepo) Fetch(d utils.Downloader, verifier utils.Verifier) error {
func (repo *RemoteRepo) Fetch(d aptly.Downloader, verifier utils.Verifier) error {
var (
release *os.File
err error
release, inrelease, releasesig *os.File
err error
)
if verifier == nil {
// 0. Just download release file to temporary URL
release, err = utils.DownloadTemp(d, repo.ReleaseURL("Release").String())
release, err = http.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil {
return err
}
} else {
// 1. try InRelease file
inrelease, err := utils.DownloadTemp(d, repo.ReleaseURL("InRelease").String())
inrelease, err = http.DownloadTemp(d, repo.ReleaseURL("InRelease").String())
if err != nil {
goto splitsignature
}
defer inrelease.Close()
release, err = verifier.VerifyClearsigned(inrelease)
err = verifier.VerifyClearsigned(inrelease)
if err != nil {
goto splitsignature
}
inrelease.Seek(0, 0)
release, err = verifier.ExtractClearsigned(inrelease)
if err != nil {
goto splitsignature
}
@@ -166,12 +275,12 @@ func (repo *RemoteRepo) Fetch(d utils.Downloader, verifier utils.Verifier) error
splitsignature:
// 2. try Release + Release.gpg
release, err = utils.DownloadTemp(d, repo.ReleaseURL("Release").String())
release, err = http.DownloadTemp(d, repo.ReleaseURL("Release").String())
if err != nil {
return err
}
releasesig, err := utils.DownloadTemp(d, repo.ReleaseURL("Release.gpg").String())
releasesig, err = http.DownloadTemp(d, repo.ReleaseURL("Release.gpg").String())
if err != nil {
return err
}
@@ -209,6 +318,9 @@ ok:
}
components := strings.Split(stanza["Components"], " ")
for i := range components {
components[i] = path.Base(components[i])
}
if len(repo.Components) == 0 {
repo.Components = components
} else {
@@ -234,7 +346,8 @@ ok:
return fmt.Errorf("unparseable hash sum line: %#v", line)
}
size, err := strconv.ParseInt(parts[1], 10, 64)
var size int64
size, err = strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return fmt.Errorf("unable to parse size: %s", err)
}
@@ -272,32 +385,47 @@ ok:
return nil
}
// Download downloads all repo files
func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageCollection, packageRepo *Repository, ignoreMismatch bool) error {
list := NewPackageList()
// DownloadPackageIndexes downloads & parses package index files
func (repo *RemoteRepo) DownloadPackageIndexes(progress aptly.Progress, d aptly.Downloader, collectionFactory *CollectionFactory,
ignoreMismatch bool) error {
if repo.packageList != nil {
panic("packageList != nil")
}
repo.packageList = NewPackageList()
d.GetProgress().Printf("Downloading & parsing package files...\n")
// Download and parse all Packages files
packagesURLs := []string{}
// Download and parse all Packages & Source files
packagesURLs := [][]string{}
if repo.IsFlat() {
packagesURLs = append(packagesURLs, repo.FlatBinaryURL().String())
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, repo.BinaryURL(component, architecture).String())
packagesURLs = append(packagesURLs, []string{repo.BinaryURL(component, architecture).String(), "binary"})
if repo.DownloadUdebs {
packagesURLs = append(packagesURLs, []string{repo.UdebURL(component, architecture).String(), "udeb"})
}
}
if repo.DownloadSources {
packagesURLs = append(packagesURLs, []string{repo.SourcesURL(component).String(), "source"})
}
}
}
for _, url := range packagesURLs {
packagesReader, packagesFile, err := utils.DownloadTryCompression(d, url, repo.ReleaseFiles, ignoreMismatch)
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 {
@@ -309,90 +437,92 @@ func (repo *RemoteRepo) Download(d utils.Downloader, packageCollection *PackageC
break
}
p := NewPackageFromControlFile(stanza)
off, _ := packagesFile.Seek(0, 1)
progress.SetBar(int(off))
list.Add(p)
var p *Package
if kind == "binary" {
p = NewPackageFromControlFile(stanza)
} else if kind == "udeb" {
p = NewUdebPackageFromControlFile(stanza)
} else if kind == "source" {
p, err = NewSourcePackageFromControlFile(stanza)
if err != nil {
return err
}
}
err = repo.packageList.Add(p)
if err != nil {
return err
}
err = collectionFactory.PackageCollection().Update(p)
if err != nil {
return err
}
}
progress.ShutdownBar()
}
d.GetProgress().Printf("Saving packages to database...\n")
return nil
}
d.GetProgress().InitBar(int64(list.Len()), false)
// ApplyFilter applies filtering to already built PackageList
func (repo *RemoteRepo) ApplyFilter(dependencyOptions int, filterQuery PackageQuery) (oldLen, newLen int, err error) {
repo.packageList.PrepareIndex()
// Save package meta information to DB
err := list.ForEach(func(p *Package) error {
d.GetProgress().AddBar(1)
return packageCollection.Update(p)
})
emptyList := NewPackageList()
emptyList.PrepareIndex()
if err != nil {
return fmt.Errorf("unable to save packages to db: %s", err)
oldLen = repo.packageList.Len()
repo.packageList, err = repo.packageList.Filter([]PackageQuery{filterQuery}, repo.FilterWithDeps, emptyList, dependencyOptions, repo.Architectures)
if repo.packageList != nil {
newLen = repo.packageList.Len()
}
return
}
d.GetProgress().ShutdownBar()
// BuildDownloadQueue builds queue, discards current PackageList
func (repo *RemoteRepo) BuildDownloadQueue(packagePool aptly.PackagePool) (queue []PackageDownloadTask, downloadSize int64, err error) {
queue = make([]PackageDownloadTask, 0, repo.packageList.Len())
seen := make(map[string]struct{}, repo.packageList.Len())
d.GetProgress().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, err := p.DownloadList(packageRepo)
if err != nil {
return err
err = repo.packageList.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]
_, found := seen[key]
if !found {
count++
queue = append(queue, task)
downloadSize += task.Checksums.Size
queued[key] = task
seen[key] = struct{}{}
}
}
return nil
})
if err != nil {
return fmt.Errorf("unable to build download queue: %s", err)
return
}
d.GetProgress().Printf("Download queue: %d items, %.2f GiB size\n", count, float64(downloadSize)/(1024.0*1024.0*1024.0))
repo.tempPackageRefs = NewPackageRefListFromPackageList(repo.packageList)
// free up package list, we don't need it after this point
repo.packageList = nil
d.GetProgress().InitBar(downloadSize, true)
// Download all package files
ch := make(chan error, len(queued))
for _, task := range queued {
d.DownloadWithChecksum(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch, task.Checksums, ignoreMismatch)
}
// Wait for all downloads to finish
errors := make([]string, 0)
for count > 0 {
err = <-ch
if err != nil {
errors = append(errors, err.Error())
}
count--
}
d.GetProgress().ShutdownBar()
if len(errors) > 0 {
return fmt.Errorf("download errors:\n %s\n", strings.Join(errors, "\n "))
}
return
}
// FinalizeDownload swaps for final value of package refs
func (repo *RemoteRepo) FinalizeDownload() {
repo.LastDownloadDate = time.Now()
repo.packageRefs = NewPackageRefListFromPackageList(list)
return nil
repo.packageRefs = repo.tempPackageRefs
}
// Encode does msgpack encoding of RemoteRepo
@@ -410,7 +540,43 @@ func (repo *RemoteRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
err := decoder.Decode(repo)
if err != nil {
return err
if strings.HasPrefix(err.Error(), "codec.decoder: readContainerLen: Unrecognized descriptor byte: hex: 80") {
// probably it is broken DB from go < 1.2, try decoding w/o time.Time
var repo11 struct {
UUID string
Name string
ArchiveRoot string
Distribution string
Components []string
Architectures []string
DownloadSources bool
Meta Stanza
LastDownloadDate []byte
ReleaseFiles map[string]utils.ChecksumInfo
Filter string
FilterWithDeps bool
}
decoder = codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
err2 := decoder.Decode(&repo11)
if err2 != nil {
return err
}
repo.UUID = repo11.UUID
repo.Name = repo11.Name
repo.ArchiveRoot = repo11.ArchiveRoot
repo.Distribution = repo11.Distribution
repo.Components = repo11.Components
repo.Architectures = repo11.Architectures
repo.DownloadSources = repo11.DownloadSources
repo.Meta = repo11.Meta
repo.ReleaseFiles = repo11.ReleaseFiles
repo.Filter = repo11.Filter
repo.FilterWithDeps = repo11.FilterWithDeps
} else {
return err
}
}
return repo.prepare()
}

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