Compare commits

..

564 Commits

Author SHA1 Message Date
Andrey Smirnov 4b3b961b69 Version bump to 0.9.1 2015-03-06 15:05:33 +03:00
Andrey Smirnov e63adffdf5 Introduce back reflist merging without conflict removal. aptly db cleanup requires
full reference list collection. #217

Fixes bug with aptly db cleanup removing conflicting packages.
2015-03-06 14:54:29 +03:00
Andrey Smirnov d00659b0cb Recommend graphviz for Debian packages. 2015-03-03 22:04:10 +03:00
Andrey Smirnov 66e73782e5 Version bump to 0.9. 2015-03-03 21:00:10 +03:00
Andrey Smirnov 68f332628d Tests on publish update/switch and delete. #116 2015-03-03 20:57:40 +03:00
Andrey Smirnov 01c0d19243 Fix bug with components not being cleaned up after publish update. #116 2015-03-03 20:49:49 +03:00
Andrey Smirnov eb0443ed51 Tests for publish list API + verify that publishing actually creates files. #116 2015-03-03 20:35:48 +03:00
Andrey Smirnov 4b974b038c Test for snapshot publishing. #116 2015-03-01 22:36:22 +03:00
Andrey Smirnov 2d9ee81c95 Update status code. #116 2015-03-01 22:33:05 +03:00
Andrey Smirnov 5c9d4d2844 More tests for repo drop. #116 2015-03-01 22:32:41 +03:00
Andrey Smirnov 49a9ad79dd Adjust return code for publish create call. #116 2015-03-01 22:31:56 +03:00
Andrey Smirnov 7e60466c7b Fix system test. 2015-03-01 22:14:37 +03:00
Andrey Smirnov 233ad2528f Update system tests for new publishing APIs. #116 2015-03-01 19:56:47 +03:00
Andrey Smirnov 2f1afa54c2 Publish update/drop APIs, rework prefix, move publishing APIs. #116 2015-03-01 19:55:02 +03:00
Andrey Smirnov 6bf910ea56 Update dependency on ssh/terminal. 2015-02-28 23:00:45 +03:00
Andrey Smirnov 8fcfedf708 Lock down package pool to make sure that we have no concurrent access. #116 2015-02-28 22:10:21 +03:00
Andrey Smirnov 26b46ee2a0 Fix confusing comment. #116 2015-02-28 19:55:52 +03:00
Andrey Smirnov e33a2a6f96 Reverting, as this change was making API LESS RESTful. My bad.
Revert "Make snapshot creation API more RESTful: accept snapshot name from URL. #116"

This reverts commit 06dc1ef9a4.
2015-02-28 19:55:09 +03:00
Andrey Smirnov 06dc1ef9a4 Make snapshot creation API more RESTful: accept snapshot name from URL. #116 2015-02-28 19:32:28 +03:00
Andrey Smirnov 4c57c358b7 One more attempt to fix system tests. 2015-02-26 09:28:12 +03:00
Andrey Smirnov 65532b3dbf Fix system tests. 2015-02-25 23:07:32 +03:00
Andrey Smirnov fb25dec58e Consistently rename response fields to CamelCase. #116 2015-02-25 22:24:03 +03:00
Andrey Smirnov e320499f84 Mention SwiftPublishEndpoints. smira/aptly#191 2015-02-25 21:24:48 +03:00
Andrey Smirnov 4715b12f16 Simpler apt-key command. #202 2015-02-22 17:41:15 +03:00
Andrey Smirnov c6a30a30de Update README. 2015-02-22 17:39:26 +03:00
Andrey Smirnov 618d06678c Style fixes from go vet. 2015-02-22 14:36:14 +03:00
Andrey Smirnov 903d4cefba gofmt -s 2015-02-22 14:29:09 +03:00
Andrey Smirnov 79292dc6c8 Update system tests. #191 2015-02-22 13:47:14 +03:00
Andrey Smirnov 43414be2ee Fix bug with aptly locking up on swift published storage lookup. #191 2015-02-22 13:42:06 +03:00
Andrey Smirnov 3c34ae6071 Update CLI help for aptly publish switch. #208 2015-02-21 22:32:51 +03:00
Andrey Smirnov 642957e3a3 Update Brightbox Orbit credentials. 2015-02-21 22:26:20 +03:00
Andrey Smirnov e5d646c007 Merge branch 'sbadia-swift'
Conflicts:
	AUTHORS
2015-02-21 01:08:48 +03:00
Andrey Smirnov e0f811dab1 Travis CI setup to run Swift tests. #191 2015-02-20 23:59:46 +03:00
Andrey Smirnov 48b8311150 Style fixes. #191 2015-02-20 23:58:00 +03:00
Andrey Smirnov 8111460e36 Update aptly man page. #191 2015-02-20 23:48:55 +03:00
Andrey Smirnov 0490d0c928 Remove .swift.sh, it is now private. 2015-02-20 23:47:55 +03:00
Andrey Smirnov b323e315d1 Add comments where required. 2015-02-20 23:47:17 +03:00
Andrey Smirnov 77f928db69 Fix variable shadowing. 2015-02-20 23:45:25 +03:00
Andrey Smirnov b67f3dd6f7 gofmt #191 2015-02-20 23:43:40 +03:00
Andrey Smirnov 88ff4493b0 Publish list API. #116 2015-02-20 11:12:16 +03:00
Andrey Smirnov 6e8fd6e907 Update to latest cheggaaa/pb with my pull request merged. 2015-02-20 00:46:13 +03:00
Andrey Smirnov 9c3095e42c Fix data race in p.Bar being read and written from different goroutines. 2015-02-19 01:53:32 +03:00
Andrey Smirnov c737b8c544 Flush CollectionFactory every 15 minutes. #116 2015-02-16 00:46:31 +03:00
Andrey Smirnov 87cecac4ea Lock down Context. #116 2015-02-16 00:33:10 +03:00
Andrey Smirnov 76ee53e9f8 Eliminate data races by using API without Progress. #116 2015-02-16 00:32:45 +03:00
Andrey Smirnov f153c7c3ea We're building on go1.3+, so remove workaround for FreeBSD. 2015-02-16 00:15:54 +03:00
Andrey Smirnov 36792bba29 Update progressbar version. 2015-02-16 00:15:30 +03:00
Andrey Smirnov 0b05964faa Add ability to Flush CollectionFactory. #116 2015-02-15 23:57:54 +03:00
Andrey Smirnov ff00a5a026 Add @alexanderguy to AUTHORS. [ci skip] 2015-02-13 20:14:56 +03:00
Andrey Smirnov fc0310f468 Merge pull request #201 from alexanderguy/add-suite
d-i requires the Suite field in order to validate a mirror.
2015-02-13 20:14:08 +03:00
Alexander Guy 63bf30b890 d-i requires the Suite field in order to validate a mirror.
Debian's installer validates a mirror by downloading a Release,
and then cross-checking it based on its Codename and Suite.  Without
a Suite field, the installer becomes unhappy (e.g. segfaults) and
won't continue the install.

Making the Codename and Suite the same validates with no problem.
2015-02-12 13:38:56 -08:00
Andrey Smirnov 3004473bbb Update to correct reference to ncw/swift. 2015-02-11 22:07:36 +03:00
Andrey Smirnov 4356fe5cbe Merge branch 'sbadia-swift' of github.com:sbadia/aptly into sbadia-swift 2015-02-11 21:58:51 +03:00
Sylvain Baubeau d6271b6542 Use upstream version of ncw/swift 2015-02-11 21:52:16 +03:00
Sebastien Badia 26a65b2336 swift: Fallback to TempAuth 2015-02-11 15:36:16 +01:00
Sebastien Badia 20adfd49a7 swift: Add support for Swift API v1 (without Keystone)
This commit also add a workaround for bug/feature[1]
the password is changed every time :-)

[1]https://github.com/ccollicutt/docker-swift-onlyone/commit/c9f5e41b745eee18e7ddc807481bc9729d8cdac0
2015-02-11 15:35:45 +01:00
Sylvain Baubeau 355a98b51f Use upstream version of ncw/swift 2015-02-10 09:17:54 +01:00
Andrey Smirnov 1f73a34a54 Don't put empty Source: fields into package stanza. #195 2015-02-09 19:23:41 +03:00
Andrey Smirnov 7925af9fd6 Exit with failure if aptly package search yields no results. #188 2015-02-07 23:33:44 +03:00
Andrey Smirnov fb03a3baf9 Add @shadeslayer and @rra to list of AUTHORS. [ci skip] 2015-02-07 23:24:10 +03:00
Andrey Smirnov f097cd20c1 Add @sbadia to AUTHORS. #191 2015-02-07 20:05:16 +03:00
Andrey Smirnov 0489ba9d16 Swift startup script. #191
(Doesn't work yet)
2015-02-07 20:03:51 +03:00
Andrey Smirnov 46b3f8fbaf PEP8 fixes. #191 2015-02-07 19:39:58 +03:00
Andrey Smirnov cacd0cf103 Merge branch 'swift' of https://github.com/sbadia/aptly into sbadia-swift 2015-02-07 19:18:04 +03:00
Andrey Smirnov c933668c16 Merge branch 'mkoval-feature/RepoUpdateAPI' 2015-02-07 19:01:24 +03:00
Andrey Smirnov 24418ab0a4 Small fixes to publish update API. #174 2015-02-07 19:01:06 +03:00
Andrey Smirnov 4963d0a1d7 Add @mkoval to list of AUTHORS. 2015-02-07 18:53:25 +03:00
Andrey Smirnov ea8bfeb8a7 Merge branch 'feature/RepoUpdateAPI' of https://github.com/mkoval/aptly into mkoval-feature/RepoUpdateAPI 2015-02-07 18:52:29 +03:00
Andrey Smirnov a582493a6e Packages show API with tests. #116 2015-02-07 18:50:52 +03:00
Andrey Smirnov 930f76887b Final version of system tests for snapshot API. #168 2015-02-07 18:37:10 +03:00
Andrey Smirnov a4201a40d2 Allow to override architectures when publishing. #116 2015-02-07 18:36:42 +03:00
Andrey Smirnov 4990bb98e5 Update gin to latest available version. #116 2015-02-07 18:36:14 +03:00
Andrey Smirnov 00d4674aa5 Update (fix) system test. 2015-02-07 13:23:30 +03:00
Andrey Smirnov 06b4016338 More fixes related to locking and overall operations. #168 2015-02-06 22:44:25 +03:00
Andrey Smirnov c1b2e4fabb Fix for snapshot creation APIs: locking, package existence checks, consistency checks. #168
More system tests.
2015-02-06 22:37:57 +03:00
Andrey Smirnov f438637a98 Don't expose UUIDs in API. #168
Probably we should expose sources, but not as UUIDs. TODO.
2015-02-06 22:37:14 +03:00
Andrey Smirnov ce208f347e Merge branch 'lebauce-snapshot-api' 2015-02-06 20:18:52 +03:00
Andrey Smirnov 06502584cf Check component names (that they do exist) before publish switching. #192 2015-02-06 20:16:00 +03:00
Sebastien Badia 0f22dc590a Fix config tests and update man page
Fix ConfigSuite.TestSaveConfig, ConfigShowTest and CreateConfigTest
tests
2015-02-05 21:27:09 +01:00
Sylvain Baubeau 11716f06f0 Add test suite for the Swift backend 2015-02-05 18:03:35 +01:00
Sylvain Baubeau 1ba06e828d Remove prefix in Filelist and RemoveDir 2015-02-05 17:56:16 +01:00
Sebastien Badia bc357a19a1 Added swift python tests 2015-02-05 17:56:09 +01:00
Sylvain Baubeau 9004f8578c Detect if bulk-delete is supported 2015-02-05 17:54:09 +01:00
Sebastien Badia 7f038be1cb Add swift backend for repository publishing 2015-02-05 17:54:09 +01:00
Andrey Smirnov 13fc1122f0 Use Python requests URL params instead of manual GET params. #168 2015-02-05 01:56:55 +03:00
Andrey Smirnov cb99cbec58 Fix final system test. #168 2015-02-05 01:53:07 +03:00
Andrey Smirnov 5d16cf06cf Gobuild is gone. [ci skip] 2015-02-05 01:49:29 +03:00
Andrey Smirnov b0489117c8 Update system tests for new Package serialization. #168 2015-02-05 01:48:14 +03:00
Andrey Smirnov fa2eef564c Enhance Package JSON representation with Key, ShortKey and FilesHash. #168 2015-02-05 01:47:10 +03:00
Andrey Smirnov d20300b152 Whitespace fix. #168 2015-02-05 01:46:57 +03:00
Andrey Smirnov 398303235a Custom JSON marshalling for PackageDiff, updated test for snapshot diff API. #168 2015-02-05 01:34:02 +03:00
Andrey Smirnov 25d048fe49 Add Package serialization to JSON via stanza. #168 2015-02-05 01:26:55 +03:00
Andrey Smirnov 8c15a0ca95 Add AlekSi/pointer to dependencies. #168 2015-02-05 01:24:44 +03:00
Andrey Smirnov 8e8ff8ba65 Revert "Make files hash a type for proper JSON serialization"
This reverts commit e138212593.
2015-02-05 00:27:14 +03:00
Andrey Smirnov 1b0eb9d45a Attempt to fix #189 and #130: disable Amazon workaround when using proxy. 2015-02-03 21:49:55 +03:00
Andrey Smirnov 403c7272cd When loading package index for the mirror, ignore duplicate packages (and print about them). #183 2015-01-31 21:27:26 +03:00
Andrey Smirnov 0412646151 Add @bcandrea to list of authors. 2015-01-30 19:26:57 +03:00
Andrey Smirnov 0725003107 Merge pull request #186 from bcandrea/master
Update dependency definition in conflicts (fixes #185)
2015-01-30 19:24:56 +03:00
Andrea Bernardo Ciddio 7a1553dc55 Update dependency definition in conflicts (fixes #185)
The dependency specified when looking for conflicting packages
does not take into account the package version, as by default the
Relation field in the Dependency structure is set to VersionDontCare.
This should fix #185.
2015-01-30 16:22:27 +00:00
Andrey Smirnov 8375a2c30f Update system test. 2015-01-30 18:39:50 +03:00
Andrey Smirnov 5bbbdb3c19 Use long gpg key IDs. #178 2015-01-30 18:33:05 +03:00
Andrey Smirnov 1fd80c40d0 Add --no-default-keyring to example command. #182 2015-01-30 18:24:30 +03:00
Andrey Smirnov ae5ab2d138 Use https:// in example when talking about Release.key download. #179 2015-01-30 18:19:28 +03:00
Andrey Smirnov eb087fd291 When generating index files, make udeb forced false for "source" architecture. #180
Otherwise two index files are generated (source arch, "udeb" true/false) which end up
sharing same final filename, and empty one might overwrite "real" one.
2015-01-26 21:16:44 +03:00
Andrey Smirnov 3f6491b8a3 Non-working test on format=details. 2015-01-26 21:06:28 +03:00
Andrey Smirnov 9250479846 Extract common part of show and search packages from snapshots and repos. #168 2015-01-24 22:23:16 +03:00
Andrey Smirnov 9c60421bd6 Python style fixes. #168 2015-01-24 21:53:06 +03:00
Andrey Smirnov ebea4f10a0 Make snapshot diff GET, not POST (as it doesn't change anything in the system). #168 2015-01-24 21:51:33 +03:00
Andrey Smirnov d828732307 Refactoring: make snapshot sorting non-intrusive to collection contents. #168 2015-01-22 22:01:00 +03:00
Andrey Smirnov 7c3629337c Merge branch 'snapshot-api' of https://github.com/lebauce/aptly into lebauce-snapshot-api
Conflicts:
	api/router.go
	system/t12_api/__init__.py
2015-01-22 21:29:58 +03:00
Michael Koval a29034caa5 Implemented apiPublishUpdateSwitch. 2015-01-21 02:23:08 -05:00
Andrey Smirnov c1fd633ed7 Add Sylvain Baubeau to authors. [ci skip] 2015-01-19 22:06:15 +03:00
Andrey Smirnov bd2cc45524 Fix order of 'Component' and 'Architecture' fields. #172 2015-01-19 22:03:31 +03:00
Andrey Smirnov 0665f2231a Sort packages when generating Packages index file. #172 2015-01-19 21:39:02 +03:00
Andrey Smirnov 836abdc81e Bring back "Archive" into canonical order. #172 2015-01-19 21:33:00 +03:00
Andrey Smirnov 876eeedb14 Update canonical order of fields in stanza to match what apt tools generate. #172 2015-01-19 21:17:58 +03:00
Andrey Smirnov fd502264a9 Install graphviz in Travis. #169 2015-01-13 22:16:30 +03:00
Andrey Smirnov b155eaa91c Merge branch 'lebauce-graph-api' 2015-01-13 22:15:37 +03:00
Andrey Smirnov a0d7ae28bf Simple tests for graph generation API. #169 2015-01-13 22:15:06 +03:00
Andrey Smirnov 2816647809 Allow to generate graph in formats supported by dot. #169 2015-01-13 19:22:24 +03:00
Andrey Smirnov 67ce828eeb Lock collections before building graph. #169 2015-01-13 19:17:19 +03:00
Andrey Smirnov 427c42f4b8 Move graph into deb/ package, passing collection factory. #169 2015-01-13 19:10:39 +03:00
Andrey Smirnov b50cb70a0e Merge branch 'graph-api' of https://github.com/lebauce/aptly into lebauce-graph-api 2015-01-13 19:04:07 +03:00
Andrey Smirnov c832a5cdc4 Add missed file. #167 2015-01-13 18:50:09 +03:00
Andrey Smirnov 1bd625f17f Merge branch 'lebauce-api-version' 2015-01-13 18:49:57 +03:00
Andrey Smirnov a0fa0becc2 Add system test on version API. #167 2015-01-13 18:49:32 +03:00
Andrey Smirnov d489694ea9 Refactoring: simplify version generation. Rename API to /api/version. #167 2015-01-13 18:47:41 +03:00
Andrey Smirnov 982b5dc886 Merge branch 'api-version' of https://github.com/lebauce/aptly into lebauce-api-version 2015-01-13 18:44:10 +03:00
Andrey Smirnov 1ddaecfb94 New Debian version: update system tests. 2015-01-12 20:14:18 +03:00
Andrey Smirnov 129c34806c Pass --no-use-agent when running with --passphare flag. #162 2015-01-12 20:00:18 +03:00
Sylvain Baubeau 6c7f3b3bbd Add /api route to show API version #116 2015-01-12 10:56:54 +01:00
Sylvain Baubeau 38cb6bd133 Graph REST API #116 2015-01-12 10:56:28 +01:00
Andrey Smirnov 98ca0cdf33 Publish repo REST API. #116 2015-01-07 16:11:34 +03:00
Andrey Smirnov 6e32e3dcf4 Fix some variable shadowing. 2015-01-07 16:11:13 +03:00
Sylvain Baubeau 6a1a871dda Lock snapshot collection before sorting 2015-01-06 18:06:59 +01:00
Sylvain Baubeau 6bc7048166 Fix wrong method comment 2015-01-06 18:06:53 +01:00
Andrey Smirnov 87fbd5201b Add Chris Read to authors. 2015-01-05 14:24:47 +03:00
Andrey Smirnov dcf5798229 Merge remote-tracking branch 'cread/gocheck' 2015-01-05 14:23:38 +03:00
Andrey Smirnov 382ad10cf7 Update man page. 2014-12-28 13:44:24 +03:00
Antonio Santos ddb2dd7eb6 Fix typo 2014-12-26 17:51:51 +01:00
Andrey Smirnov 9b1b43c8b4 Add custom JSON representation of PublishedRepo. #116 2014-12-26 00:58:23 +03:00
Andrey Smirnov ee7d84205b Update system test. 2014-12-23 01:19:37 +03:00
Andrey Smirnov 93e8e18ca6 Document lock order acquisition. #116 [ci skip] 2014-12-23 00:59:29 +03:00
Andrey Smirnov d586f31247 Move ParsePrefix into common code. #116 2014-12-23 00:50:28 +03:00
Sylvain Baubeau dd9fc8e40e Allow API creation of snapshots using package references 2014-12-18 18:17:43 +01:00
Sylvain Baubeau d847cba870 Make repos and snapshots API return JSON objects for packages when asked 2014-12-18 18:16:35 +01:00
Sylvain Baubeau d983e10d08 Add snapshots API test suite 2014-12-18 16:33:15 +01:00
Sylvain Baubeau a6fc65ff4e Sanitize snapshots API return codes 2014-12-18 12:05:16 +01:00
Sylvain Baubeau 85f38cd739 Allow setting description on snapshots using API 2014-12-18 11:55:54 +01:00
Sylvain Baubeau acde6ff2b2 Fix wrong methods comments 2014-12-18 11:55:45 +01:00
Sylvain Baubeau c733129de9 Add search API for packages in snapshots 2014-12-18 11:16:49 +01:00
Sylvain Baubeau e138212593 Make files hash a type for proper JSON serialization 2014-12-18 11:14:56 +01:00
Sylvain Baubeau 64ef342121 Add /snapshots/ API. #116
Implements :
	- CRUD
	- Difference between snapshots
2014-12-15 10:47:35 +01:00
Sylvain Baubeau 66c9bb86f5 Move command line snapshot sorting to common snapshot code 2014-12-15 10:44:46 +01:00
Andrey Smirnov 923e2e1e50 Update system test. 2014-12-12 09:27:17 +03:00
Andrey Smirnov 0e552eda55 When merging reflists, never allow duplicate (name, version, arch) tuples. #154
Even for conficting packages.
2014-12-11 21:56:50 +03:00
Andrey Smirnov ba32d16c8a Fix system test. 2014-12-09 11:44:40 +03:00
Andrey Smirnov 4320144024 Merge pull request #155 from davewongillies/master
Typo in an error message fix
2014-12-09 11:26:18 +03:00
David Gillies 2564564601 Typo in an error message fix 2014-12-09 15:57:26 +11:00
Andrey Smirnov f228ad811b Accept MD5 in package from 'MD5Sum' as well. #151 2014-11-29 16:18:23 +03:00
Andrey Smirnov 5fe442f191 Fix issue with missing comma in JSON. 2014-11-28 22:57:01 +03:00
Andrey Smirnov 50c4aba9ab Upgrade to latest goleveldb. #150 2014-11-28 22:56:28 +03:00
Andrey Smirnov b3627738c2 Add Gitter.im badge. [ci skip] 2014-11-27 01:25:58 +03:00
Andrey Smirnov eec6743fe4 Use ${HOME}, fix test. #146 2014-11-27 00:55:28 +03:00
Andrey Smirnov 42bf2f5e98 Fix one more system test. #147 2014-11-27 00:53:31 +03:00
Andrey Smirnov 35b9a8ea91 Don't sign repo in test. #146 2014-11-27 00:34:04 +03:00
Andrey Smirnov 26c0502307 Introduce new flag -force-components to aptly mirror create to ignore
components in Release file while doing checks. #147
2014-11-27 00:32:34 +03:00
Andrey Smirnov 5fa487e2dc Add gitter.im notification. 2014-11-26 23:51:21 +03:00
Andrey Smirnov d9c62780c2 When doing db cleanup, consider package references stored in PublishedRepos
of local repo publishes. #146
2014-11-26 23:41:18 +03:00
Andrey Smirnov 8c54e15a11 Add information about nightly builds, go 1.3. [ci skip] 2014-11-25 23:46:58 +03:00
Andrey Smirnov cc2cc16004 Fix system test. 2014-11-18 12:16:47 +03:00
Andrey Smirnov 726f12c537 Repos APIs: searching for packages, adding and deleting packages from the repo. #116 2014-11-18 00:50:59 +03:00
Andrey Smirnov f1c235f5c5 Fix error message. 2014-11-16 14:01:46 +03:00
Andrey Smirnov 9072ba5981 Revert part of test that shouldn't have been comitted yet. #140 2014-11-14 00:25:40 +03:00
Andrey Smirnov 7beb90d4fc Strings() for PackageList: turning list into sequence of package Ids. #116 2014-11-14 00:19:58 +03:00
Andrey Smirnov 61e22743af Fix for stripping part with slashes from component names. #140
Now aptly won't strip by default, but if distribution contains slash (like 'wheezy/updates')
and component starts with last part of distribution ('updates/main'), it would be stripped.
2014-11-14 00:13:58 +03:00
Andrey Smirnov 036baa2264 Exclude "source" architecture from list of Release architectures. #140 2014-11-14 00:01:41 +03:00
Andrey Smirnov ccd8c2551f Update location of go tool cover. 2014-11-11 01:25:52 +03:00
Andrey Smirnov 74f9787884 Implementation of upload file to local repo APIs. #116 2014-11-11 01:12:52 +03:00
Andrey Smirnov 83af66a8f6 Refactoring: move package files importing code to common deb from command. #116 2014-11-06 00:13:16 +03:00
Chris Read daf887e54f Upgrade gocheck 2014-11-05 13:27:15 -06:00
Andrey Smirnov 552b11e28d Workaround for '+' escaping in Amazon S3. #130 2014-11-05 02:46:01 +03:00
Andrey Smirnov 80a88a2248 Merge branch 'shadeslayer-master' 2014-10-26 19:54:11 +03:00
Andrey Smirnov 4c1d6d1463 Fix operator precedence. #131 2014-10-26 19:53:49 +03:00
Rohan Garg 140a11c04a Continue even when a server replies with 403
Amazon S3 replies with a 403 when accessing files that don't exist,
while this should be fixed at Amazon, we can workaround it in aptly
for the moment.
2014-10-25 21:41:57 +02:00
Andrey Smirnov 7be2ef8b85 Don't fallback between compression methods available unless we get strictly HTTP 404. #129 #125
Prior to that, some real errors could have been masked away by that fallback.
2014-10-24 08:51:04 +04:00
Andrey Smirnov d2d21c3df7 Update system test. 2014-10-23 11:03:19 +04:00
Andrey Smirnov f81a91bde9 First step of aptly repo add refactoring: extract collection of files. #116 2014-10-23 01:07:53 +04:00
Andrey Smirnov 7efd0de67c Fix shadowing of variable. #116 2014-10-23 00:57:27 +04:00
Andrey Smirnov 6a9db17460 Stubs for API calls. #116 2014-10-23 00:41:24 +04:00
Andrey Smirnov b1053826e3 Debian 7.7 has been released, update tests. 2014-10-20 12:22:19 +04:00
Andrey Smirnov 18953c1c90 Merge pull request #128 from rra/master
Allow variation of formatting of Debian control.tar.gz
2014-10-20 10:12:23 +04:00
Russ Allbery aeb85a1b3c Allow variation of formatting of Debian control.tar.gz
While all the normal Debian package building tools create a
control.tar.gz archive member that contains files with a leading
"./" path element, such as "./control", dpkg and other archive tools
like reprepro are happy with omitting the "./" path element and
having files like "control".  Allow for either for compatibility
with weird packages that people may want to import into aptly.
2014-10-17 13:36:58 -07:00
Andrey Smirnov 0a6d57ea1a Fixing system tests. 2014-10-17 02:03:48 +04:00
Andrey Smirnov aa4dee3c60 Merge branch 'queeno-fix_truncation_bug' 2014-10-17 00:55:09 +04:00
Andrey Smirnov 9fbe33b356 System test for file override from pool. #127 2014-10-17 00:54:42 +04:00
Andrey Smirnov 2a9871e2e9 We should never ever overwrite files in package pool. #127 2014-10-17 00:54:15 +04:00
Simon Aquino 951b6e9004 Test to avoid published file truncation when added to repo
This test will make sure that when a published file is added to repo, it
doesn't get truncated.
2014-10-16 16:57:50 +01:00
Simon Aquino 2173d3ab65 Fix file truncation bug
This commix prevents files from being truncated when attempting to add
a hard-linked package which already exists inside the pool directory.
2014-10-16 15:01:36 +01:00
Andrey Smirnov 9c834f410c API for file upload. #116 2014-10-16 00:04:50 +04:00
Andrey Smirnov eef44f5cd5 Correctly set config override. #123
Without that, config override gets set in parent class propagating
to all tests.
2014-10-14 19:27:47 +04:00
Andrey Smirnov aa77ea2835 Test for config show. #123 2014-10-14 18:28:53 +04:00
Andrey Smirnov 119bb0195b Merge branch 'queeno-add_config_show_command-2' 2014-10-14 18:26:45 +04:00
Andrey Smirnov 017dca57ed Re-generate man page. #123 #96 2014-10-14 18:26:27 +04:00
Andrey Smirnov 88351503b0 Fix misprint. #96 2014-10-14 18:26:12 +04:00
Andrey Smirnov 37b2d49aea A bit more style fixes. #123 2014-10-14 18:22:57 +04:00
Andrey Smirnov 0afb1f4306 Style fixes. 2014-10-14 18:20:09 +04:00
Andrey Smirnov 6ac0658478 Merge branch 'add_config_show_command' of https://github.com/queeno/aptly into queeno-add_config_show_command-2 2014-10-14 18:17:21 +04:00
Andrey Smirnov 50c8e35a90 Re-enable task run command. #96 2014-10-14 00:47:17 +04:00
Andrey Smirnov d6c3389d7c First steps towards /files/ API (file upload). #116 2014-10-14 00:47:17 +04:00
Andrey Smirnov 6b83213cf4 Add UploadPath to Context. #116 2014-10-14 00:47:17 +04:00
Simon Aquino 24927f9a29 config_show should output pretty json
This commit changes the output of aptly config show to be pretty json
rather than YAML.
2014-10-13 18:29:45 +01:00
Simon Aquino ecbb9ad20c Fixed failing system test
Added config command to aptly main output.
2014-10-13 15:58:41 +01:00
Simon Aquino 81e9189853 Config show now outputs a clean data structure 2014-10-13 15:58:41 +01:00
Simon Aquino 8efb7903b2 Added map to to_string and tabs to config_show 2014-10-13 15:58:41 +01:00
Simon Aquino c1995beff1 Config_show prints out strings,structs,ints,bools
More development for aptly config show
2014-10-13 15:58:41 +01:00
Simon Aquino 192152b215 Config command created - config show started 2014-10-13 15:58:41 +01:00
Andrey Smirnov 972e8c1373 Merge pull request #124 from queeno/queeno_username_change
Change simonaquino's github username to queeno
2014-10-11 20:57:00 +04:00
Simon Aquino c501fc63f8 Change simonaquino's github username to queeno
simonaquino changed its github username to queeno.
2014-10-11 16:39:48 +00:00
Andrey Smirnov bf08ad800f Attempt to build in Travis with python & virtualenv. #116 2014-10-10 19:28:04 +04:00
Andrey Smirnov ebc223a895 System tests for API. #116 2014-10-10 18:35:39 +04:00
Andrey Smirnov 01b1f23d6b Merge branch '122-gpg-batch' 2014-10-10 17:54:50 +04:00
Andrey Smirnov 6b08b64d62 Add latest contributors. 2014-10-10 17:53:49 +04:00
Andrey Smirnov 89bb20388f Fix unit tests. #122 2014-10-10 17:52:09 +04:00
Andrey Smirnov 9857789204 Regenerate man page. #122 2014-10-10 17:52:00 +04:00
Andrey Smirnov 6d1efe0200 Docstrings, gofmt. #122 2014-10-10 17:50:43 +04:00
Andrey Smirnov a85aa11ecd Update flag description/include it everywhere. #122 2014-10-10 17:50:08 +04:00
Andrey Smirnov 27ea769ad3 Merge branch 'master' of https://github.com/freehck/aptly into 122-gpg-batch 2014-10-10 17:44:55 +04:00
Andrey Smirnov 523d0d0945 Library for API system tests. #116 2014-10-10 17:43:04 +04:00
Andrey Smirnov 53f7fef4cf Force flag for API repos delete. #116 2014-10-10 17:18:47 +04:00
Dmitrii Kashin b590efa45f Merge branch 'master' of https://github.com/freehck/aptly 2014-10-10 04:22:43 +04:00
Dmitrii Kashin 59055d7fbd Add batch flag for publish commands 2014-10-10 04:04:44 +04:00
Ivan Kurnosov 22bcacf143 Typo in a error message fix
`s/Unknwon/Unknown/`
2014-10-09 16:19:15 +13:00
Andrey Smirnov 877109b3b7 Don't build under go1.2 (gin incompatible), use go 1.3.3. #116 2014-10-08 17:27:44 +04:00
Andrey Smirnov 10056b8571 Add first /repos/ API, command api serve. #116 2014-10-08 16:19:15 +04:00
Andrey Smirnov ac983ff65d Add RwMutexes to all collections. #116 2014-10-08 16:16:07 +04:00
Andrey Smirnov 2ed76f1e4c Add method to convert reflist to list of strings. #116 2014-10-08 16:15:54 +04:00
Andrey Smirnov cb6b18acfe Add missing dependency to github.com/jlaffaye/ftp. 2014-10-08 16:15:12 +04:00
Andrey Smirnov 3cd8c5adab Add gin and its dependencies. #116 2014-10-08 16:13:26 +04:00
Andrey Smirnov dd7b7b5f20 Refactor RefList.FilterLatestRefs to be method instead of standalone func. 2014-10-07 19:29:01 +04:00
Andrey Smirnov 1f6880fcad Disable api command, it's not committed yet. #117 2014-10-07 15:26:03 +04:00
Andrey Smirnov c8d9bef686 Close database before writing first byte to stdout. #117
Should fix conflict with commands like 'aptly xxx list -raw | xargs -n 1 aptly xxxx'
2014-10-07 15:20:27 +04:00
Andrey Smirnov 8a787d2c35 Refactor by separating AptlyContext into separate package. #116 2014-10-06 21:54:15 +04:00
Andrey Smirnov 159608cef3 Make LocalRepo JSON-serializable. #116 2014-10-06 21:08:46 +04:00
Andrey Smirnov 52c5934eb6 Lock down CollectionFactory. #116 2014-10-06 19:55:46 +04:00
Andrey Smirnov e4b9e974d2 bytes.Equal should be faster than bytes.Compare. 2014-10-06 15:17:25 +04:00
Andrey Smirnov d541b4f137 Version bump to 0.9~dev. 2014-10-06 15:07:31 +04:00
Andrey Smirnov eece643ea5 Include bash completion into source tarball. 2014-10-05 00:40:47 +04:00
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
543 changed files with 180348 additions and 79826 deletions
+18 -3
View File
@@ -1,14 +1,20 @@
language: go
go:
- 1.1
- 1.2.1
- 1.3.3
- tip
env:
global:
- secure: "YSwtFrMqh4oUvdSQTXBXMHHLWeQgyNEL23ChIZwU0nuDGIcQZ65kipu0PzefedtUbK4ieC065YCUi4UDDh6gPotB/Wu1pnYg3dyQ7rFvhaVYAAUEpajAdXZhlx+7+J8a4FZMeC/kqiahxoRgLbthF9019ouIqhGB9zHKI6/yZwc="
- secure: "V7OjWrfQ8UbktgT036jYQPb/7GJT3Ol9LObDr8FYlzsQ+F1uj2wLac6ePuxcOS4FwWOJinWGM1h+JiFkbxbyFqfRNJ0jj0O2p93QyDojxFVOn1mXqqvV66KFqAWR2Vzkny/gDvj8LTvdB1cgAIm2FNOkQc6E1BFnyWS2sN9ea5E="
- secure: "OxiVNmre2JzUszwPNNilKDgIqtfX2gnRSsVz6nuySB1uO2yQsOQmKWJ9cVYgH2IB5H8eWXKOhexcSE28kz6TPLRuEcU9fnqKY3uEkdwm7rJfz9lf+7C4bJEUdA1OIzJppjnWUiXxD7CEPL1DlnMZM24eDQYqa/4WKACAgkK53gE="
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y python-virtualenv graphviz
- virtualenv env
- . env/bin/activate
- pip install boto requests python-swiftclient
install:
- make prepare
@@ -18,3 +24,12 @@ script: make travis
matrix:
allow_failures:
- go: tip
notifications:
webhooks:
urls:
- "https://webhooks.gitter.im/e/c691da114a41eed6ec45"
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
+17
View File
@@ -0,0 +1,17 @@
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/queeno)
* Vincent Batoufflet (https://github.com/vbatoufflet)
* Ivan Kurnosov (https://github.com/zerkms)
* Dmitrii Kashin (https://github.com/freehck)
* Chris Read (https://github.com/cread)
* Rohan Garg (https://github.com/shadeslayer)
* Russ Allbery (https://github.com/rra)
* Sylvain Baubeau (https://github.com/lebauce)
* Andrea Bernardo Ciddio (https://github.com/bcandrea)
* Michael Koval (https://github.com/mkoval)
* Alexander Guy (https://github.com/alexanderguy)
* Sebastien Badia (https://github.com/sbadia)
+19 -8
View File
@@ -1,22 +1,33 @@
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 => '212766062629'
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/smira/commander', :commit => '082a3ce267a8225a8ccf94deaf18901223d38fed'
gom 'github.com/smira/flag', :commit => '0d0aac2addb39050f45e92c5a6252926096dc841'
gom 'github.com/AlekSi/pointer', :commit => '5f6d527dae3d678b46fbb20331ddf44e2b841943'
gom 'github.com/cheggaaa/pb', :commit => '2c1b74620cc58a81ac152ee2d322e28c806d81ed'
gom 'github.com/gin-gonic/gin', :commit => 'b1758d3bfa09e61ddbc1c9a627e936eec6a170de'
gom 'github.com/jlaffaye/ftp', :commit => 'fec71e62e457557fbe85cefc847a048d57815d76'
gom 'github.com/julienschmidt/httprouter', :commit => '46807412fe50aaceb73bb57061c2230fd26a1640'
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/syndtr/goleveldb/leveldb', :commit => 'ff3719c6816e2cd194f05058452d660608e178ac'
gom 'github.com/ncw/swift', :commit => '384ef27c70645e285f8bb9d02276bf654d06027e'
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/gosnappy/snappy', :commit => 'ce8acff4829e0c2458a67ead32390ac0a381c862'
gom 'github.com/syndtr/goleveldb/leveldb', :commit => '97e257099d2ab9578151ba85e2641e2cd14d3ca8'
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
gom 'github.com/vaughan0/go-ini', :commit => 'a98ad7ee00ec53921f08832bc06ecf7fd600e6a1'
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
gom 'golang.org/x/crypto/ssh/terminal', :commit => 'a7ead6ddf06233883deca151dffaef2effbf498f'
group :test do
gom 'launchpad.net/gocheck'
gom 'gopkg.in/check.v1'
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'
gom 'golang.org/x/tools/cmd/cover'
end
+8 -9
View File
@@ -1,6 +1,6 @@
GOVERSION=$(shell go version | awk '{print $$3;}')
PACKAGES=database deb files http utils
ALL_PACKAGES=aptly cmd console database deb files http utils
PACKAGES=context database deb files http query swift s3 utils
ALL_PACKAGES=api aptly context cmd console database deb files http query swift s3 utils
BINPATH=$(abspath ./_vendor/bin)
GOM_ENVIRONMENT=-test
PYTHON?=python
@@ -36,16 +36,14 @@ coverage: coverage.out
rm -f coverage.out
check:
$(GOM) exec go tool vet -all=true -shadow=true $(ALL_PACKAGES:%=./%)
$(GOM) exec go tool vet -all=true $(ALL_PACKAGES:%=./%)
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
install:
$(GOM) build -o $(BINPATH)/aptly
system-test: install
ifeq ($(GOVERSION),$(filter $(GOVERSION),go1.2 go1.2.1 devel))
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
endif
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
@@ -69,7 +67,7 @@ package:
(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" -C root/ .
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" --deb-recommends bzip2 --deb-recommends graphviz -C root/ .
mv aptly_$(VERSION)_*.deb ~
src-package:
@@ -77,11 +75,12 @@ src-package:
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 -print | xargs rm -rf
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . -name .bzr -print | xargs rm -rf
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . -name .hg -print | xargs rm -rf
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}
mkdir -p aptly-$(VERSION)/bash_completion.d
(cd aptly-$(VERSION)/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/$(VERSION)/aptly)
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
+19 -6
View File
@@ -8,8 +8,17 @@ aptly
.. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD
:target: https://coveralls.io/r/smira/aptly?branch=HEAD
.. image:: https://badges.gitter.im/Join Chat.svg
:target: https://gitter.im/smira/aptly?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. image:: http://goreportcard.com/badge/gojp/goreportcard
:target: http://goreportcard.com/report/gojp/goreportcard
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>`_.
@@ -20,14 +29,15 @@ Aptly features: ("+" means planned features)
* 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 (+)
* filter repository by search query, pulling dependencies when required
* publish self-made packages as Debian repositories
* REST API for remote access
* mirror repositories "as-is" (without resigning with user's key) (+)
* support for yum repositories (+)
Current limitations:
* debian-installer and translations not supported yet
* translations are not supported yet
Download
--------
@@ -38,8 +48,7 @@ To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
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 -
$ apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
After that you can install aptly as any other software package::
@@ -49,9 +58,13 @@ After that you can install aptly as any other software package::
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.
If you would like to use nightly builds (unstable), please use following repository::
deb http://repo.aptly.info/ nightly main
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.3+ required)::
go get -u github.com/mattn/gom
mkdir -p $GOPATH/src/github.com/smira/aptly
+113
View File
@@ -0,0 +1,113 @@
// Package api provides implementation of aptly REST API
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"sort"
"time"
)
// Lock order acquisition (canonical):
// 1. RemoteRepoCollection
// 2. LocalRepoCollection
// 3. SnapshotCollection
// 4. PublishedRepoCollection
// GET /api/version
func apiVersion(c *gin.Context) {
c.JSON(200, gin.H{"Version": aptly.Version})
}
// Periodically flushes CollectionFactory to free up memory used by collections,
// flushing caches.
//
// Should be run in goroutine!
func cacheFlusher() {
ticker := time.Tick(15 * time.Minute)
for {
<-ticker
// lock everything to eliminate in-progress calls
r := context.CollectionFactory().RemoteRepoCollection()
r.Lock()
defer r.Unlock()
l := context.CollectionFactory().LocalRepoCollection()
l.Lock()
defer l.Unlock()
s := context.CollectionFactory().SnapshotCollection()
s.Lock()
defer s.Unlock()
p := context.CollectionFactory().PublishedRepoCollection()
p.Lock()
defer p.Unlock()
// all collections locked, flush them
context.CollectionFactory().Flush()
}
}
// Common piece of code to show list of packages,
// with searching & details if requested
func showPackages(c *gin.Context, reflist *deb.PackageRefList) {
result := []*deb.Package{}
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(404, err)
return
}
queryS := c.Request.URL.Query().Get("q")
if queryS != "" {
q, err := query.Parse(c.Request.URL.Query().Get("q"))
if err != nil {
c.Fail(400, err)
return
}
withDeps := c.Request.URL.Query().Get("withDeps") == "1"
architecturesList := []string{}
if withDeps {
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = list.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
c.Fail(400, fmt.Errorf("unable to determine list of architectures, please specify explicitly"))
return
}
}
list.PrepareIndex()
list, err = list.Filter([]deb.PackageQuery{q}, withDeps,
nil, context.DependencyOptions(), architecturesList)
if err != nil {
c.Fail(500, fmt.Errorf("unable to search: %s", err))
}
}
if c.Request.URL.Query().Get("format") == "details" {
list.ForEach(func(p *deb.Package) error {
result = append(result, p)
return nil
})
c.JSON(200, result)
} else {
c.JSON(200, list.Strings())
}
}
+185
View File
@@ -0,0 +1,185 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"os"
"path/filepath"
"strings"
)
func verifyPath(path string) bool {
path = filepath.Clean(path)
for _, part := range strings.Split(path, string(filepath.Separator)) {
if part == ".." || part == "." {
return false
}
}
return true
}
func verifyDir(c *gin.Context) bool {
if !verifyPath(c.Params.ByName("dir")) {
c.Fail(400, fmt.Errorf("wrong dir"))
return false
}
return true
}
// GET /files
func apiFilesListDirs(c *gin.Context) {
list := []string{}
err := filepath.Walk(context.UploadPath(), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == context.UploadPath() {
return nil
}
if info.IsDir() {
list = append(list, filepath.Base(path))
return filepath.SkipDir
}
return nil
})
if err != nil && !os.IsNotExist(err) {
c.Fail(400, err)
return
}
c.JSON(200, list)
}
// POST /files/:dir/
func apiFilesUpload(c *gin.Context) {
if !verifyDir(c) {
return
}
path := filepath.Join(context.UploadPath(), c.Params.ByName("dir"))
err := os.MkdirAll(path, 0777)
if err != nil {
c.Fail(500, err)
return
}
err = c.Request.ParseMultipartForm(10 * 1024 * 1024)
if err != nil {
c.Fail(400, err)
return
}
stored := []string{}
for _, files := range c.Request.MultipartForm.File {
for _, file := range files {
src, err := file.Open()
if err != nil {
c.Fail(500, err)
return
}
defer src.Close()
destPath := filepath.Join(path, filepath.Base(file.Filename))
dst, err := os.Create(destPath)
if err != nil {
c.Fail(500, err)
return
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
c.Fail(500, err)
return
}
stored = append(stored, filepath.Join(c.Params.ByName("dir"), filepath.Base(file.Filename)))
}
}
c.JSON(200, stored)
}
// GET /files/:dir
func apiFilesListFiles(c *gin.Context) {
if !verifyDir(c) {
return
}
list := []string{}
root := filepath.Join(context.UploadPath(), c.Params.ByName("dir"))
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == root {
return nil
}
list = append(list, filepath.Base(path))
return nil
})
if err != nil {
if os.IsNotExist(err) {
c.Fail(404, err)
} else {
c.Fail(500, err)
}
return
}
c.JSON(200, list)
}
// DELETE /files/:dir
func apiFilesDeleteDir(c *gin.Context) {
if !verifyDir(c) {
return
}
err := os.RemoveAll(filepath.Join(context.UploadPath(), c.Params.ByName("dir")))
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// DELETE /files/:dir/:name
func apiFilesDeleteFile(c *gin.Context) {
if !verifyDir(c) {
return
}
if !verifyPath(c.Params.ByName("name")) {
c.Fail(400, fmt.Errorf("wrong file"))
return
}
err := os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("name")))
if err != nil {
if err1, ok := err.(*os.PathError); !ok || !os.IsNotExist(err1.Err) {
c.Fail(500, err)
return
}
}
c.JSON(200, gin.H{})
}
+75
View File
@@ -0,0 +1,75 @@
package api
import (
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/deb"
"io"
"mime"
"os"
"os/exec"
)
// GET /api/graph.:ext
func apiGraph(c *gin.Context) {
var (
err error
output []byte
)
ext := c.Params.ByName("ext")
factory := context.CollectionFactory()
factory.RemoteRepoCollection().RLock()
defer factory.RemoteRepoCollection().RUnlock()
factory.LocalRepoCollection().RLock()
defer factory.LocalRepoCollection().RUnlock()
factory.SnapshotCollection().RLock()
defer factory.SnapshotCollection().RUnlock()
factory.PublishedRepoCollection().RLock()
defer factory.PublishedRepoCollection().RUnlock()
graph, err := deb.BuildGraph(factory)
if err != nil {
c.JSON(500, err)
return
}
buf := bytes.NewBufferString(graph.String())
command := exec.Command("dot", "-T"+ext)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
c.Fail(500, err)
return
}
_, err = io.Copy(stdin, buf)
if err != nil {
c.Fail(500, err)
return
}
err = stdin.Close()
if err != nil {
c.Fail(500, err)
return
}
output, err = command.Output()
if err != nil {
c.Fail(500, fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err))
return
}
mimeType := mime.TypeByExtension("." + ext)
if mimeType == "" {
mimeType = "application/octet-stream"
}
c.Data(200, mimeType, output)
}
+16
View File
@@ -0,0 +1,16 @@
package api
import (
"github.com/gin-gonic/gin"
)
// GET /api/packages/:key
func apiPackagesShow(c *gin.Context) {
p, err := context.CollectionFactory().PackageCollection().ByKey([]byte(c.Params.ByName("key")))
if err != nil {
c.Fail(404, err)
return
}
c.JSON(200, p)
}
+336
View File
@@ -0,0 +1,336 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"strings"
)
// SigningOptions is a shared between publish API GPG options structure
type SigningOptions struct {
Skip bool
Batch bool
GpgKey string
Keyring string
SecretKeyring string
Passphrase string
PassphraseFile string
}
func getSigner(options *SigningOptions) (utils.Signer, error) {
if options.Skip {
return nil, nil
}
signer := &utils.GpgSigner{}
signer.SetKey(options.GpgKey)
signer.SetKeyRing(options.Keyring, options.SecretKeyring)
signer.SetPassphrase(options.Passphrase, options.PassphraseFile)
signer.SetBatch(options.Batch)
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
// Replace '_' with '/' and double '__' with single '_'
func parseEscapedPath(path string) string {
result := strings.Replace(strings.Replace(path, "_", "/", -1), "//", "_", -1)
if result == "" {
result = "."
}
return result
}
// GET /publish
func apiPublishList(c *gin.Context) {
localCollection := context.CollectionFactory().LocalRepoCollection()
localCollection.RLock()
defer localCollection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.RLock()
defer collection.RUnlock()
result := make([]*deb.PublishedRepo, 0, collection.Len())
err := collection.ForEach(func(repo *deb.PublishedRepo) error {
err := collection.LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
}
result = append(result, repo)
return nil
})
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, result)
}
// POST /publish/:prefix
func apiPublishRepoOrSnapshot(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
var b struct {
SourceKind string `binding:"required"`
Sources []struct {
Component string
Name string `binding:"required"`
} `binding:"required"`
Distribution string
Label string
Origin string
ForceOverwrite bool
Architectures []string
Signing SigningOptions
}
if !c.Bind(&b) {
return
}
signer, err := getSigner(&b.Signing)
if err != nil {
c.Fail(500, fmt.Errorf("unable to initialize GPG signer: %s", err))
return
}
if len(b.Sources) == 0 {
c.Fail(400, fmt.Errorf("unable to publish: soures are empty"))
return
}
var components []string
var sources []interface{}
if b.SourceKind == "snapshot" {
var snapshot *deb.Snapshot
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
for _, source := range b.Sources {
components = append(components, source.Component)
snapshot, err = snapshotCollection.ByName(source.Name)
if err != nil {
c.Fail(404, fmt.Errorf("unable to publish: %s", err))
return
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
sources = append(sources, snapshot)
}
} else if b.SourceKind == "local" {
var localRepo *deb.LocalRepo
localCollection := context.CollectionFactory().LocalRepoCollection()
localCollection.RLock()
defer localCollection.RUnlock()
for _, source := range b.Sources {
components = append(components, source.Component)
localRepo, err = localCollection.ByName(source.Name)
if err != nil {
c.Fail(404, fmt.Errorf("unable to publish: %s", err))
return
}
err = localCollection.LoadComplete(localRepo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
}
sources = append(sources, localRepo)
}
} else {
c.Fail(400, fmt.Errorf("unknown SourceKind"))
return
}
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
published, err := deb.NewPublishedRepo(storage, prefix, b.Distribution, b.Architectures, components, sources, context.CollectionFactory())
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
published.Origin = b.Origin
published.Label = b.Label
duplicate := collection.CheckDuplicate(published)
if duplicate != nil {
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
c.Fail(400, fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate))
return
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, nil, b.ForceOverwrite)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
err = collection.Add(published)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
}
c.JSON(201, published)
}
// PUT /publish/:prefix/:distribution
func apiPublishUpdateSwitch(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
var b struct {
ForceOverwrite bool
Signing SigningOptions
Snapshots []struct {
Component string `binding:"required"`
Name string `binding:"required"`
}
}
if !c.Bind(&b) {
return
}
signer, err := getSigner(&b.Signing)
if err != nil {
c.Fail(500, fmt.Errorf("unable to initialize GPG signer: %s", err))
return
}
// published.LoadComplete would touch local repo collection
localRepoCollection := context.CollectionFactory().LocalRepoCollection()
localRepoCollection.RLock()
defer localRepoCollection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
c.Fail(404, fmt.Errorf("unable to update: %s", err))
return
}
err = collection.LoadComplete(published, context.CollectionFactory())
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
return
}
var updatedComponents []string
if published.SourceKind == "local" {
if len(b.Snapshots) > 0 {
c.Fail(400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
return
}
updatedComponents = published.Components()
for _, component := range updatedComponents {
published.UpdateLocalRepo(component)
}
} else if published.SourceKind == "snapshot" {
publishedComponents := published.Components()
for _, snapshotInfo := range b.Snapshots {
if !utils.StrSliceHasItem(publishedComponents, snapshotInfo.Component) {
c.Fail(404, fmt.Errorf("component %s is not in published repository", snapshotInfo.Component))
return
}
snapshot, err := snapshotCollection.ByName(snapshotInfo.Name)
if err != nil {
c.Fail(404, err)
return
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
published.UpdateSnapshot(snapshotInfo.Component, snapshot)
updatedComponents = append(updatedComponents, snapshotInfo.Component)
}
} else {
c.Fail(500, fmt.Errorf("unknown published repository type"))
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, nil, b.ForceOverwrite)
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
}
err = collection.Update(published)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
}
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
context.GetPublishedStorage(storage), context.CollectionFactory(), nil)
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
}
c.JSON(200, published)
}
// DELETE /publish/:prefix/:distribution
func apiPublishDrop(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
// published.LoadComplete would touch local repo collection
localRepoCollection := context.CollectionFactory().LocalRepoCollection()
localRepoCollection.RLock()
defer localRepoCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
err := collection.Remove(context, storage, prefix, distribution,
context.CollectionFactory(), context.Progress())
if err != nil {
c.Fail(500, fmt.Errorf("unable to drop: %s", err))
return
}
c.JSON(200, gin.H{})
}
+370
View File
@@ -0,0 +1,370 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"os"
"path/filepath"
)
// GET /api/repos
func apiReposList(c *gin.Context) {
result := []*deb.LocalRepo{}
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
context.CollectionFactory().LocalRepoCollection().ForEach(func(r *deb.LocalRepo) error {
result = append(result, r)
return nil
})
c.JSON(200, result)
}
// POST /api/repos
func apiReposCreate(c *gin.Context) {
var b struct {
Name string `binding:"required"`
Comment string
DefaultDistribution string
DefaultComponent string
}
if !c.Bind(&b) {
return
}
repo := deb.NewLocalRepo(b.Name, b.Comment)
repo.DefaultComponent = b.DefaultComponent
repo.DefaultDistribution = b.DefaultDistribution
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
err := context.CollectionFactory().LocalRepoCollection().Add(repo)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, repo)
}
// PUT /api/repos/:name
func apiReposEdit(c *gin.Context) {
var b struct {
Comment string
DefaultDistribution string
DefaultComponent string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
if b.Comment != "" {
repo.Comment = b.Comment
}
if b.DefaultDistribution != "" {
repo.DefaultDistribution = b.DefaultDistribution
}
if b.DefaultComponent != "" {
repo.DefaultComponent = b.DefaultComponent
}
err = collection.Update(repo)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, repo)
}
// GET /api/repos/:name
func apiReposShow(c *gin.Context) {
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
c.JSON(200, repo)
}
// DELETE /api/repos/:name
func apiReposDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
publishedCollection.RLock()
defer publishedCollection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
published := publishedCollection.ByLocalRepo(repo)
if len(published) > 0 {
c.Fail(409, fmt.Errorf("unable to drop, local repo is published"))
return
}
if !force {
snapshots := snapshotCollection.ByLocalRepoSource(repo)
if len(snapshots) > 0 {
c.Fail(409, fmt.Errorf("unable to drop, local repo has snapshots, use ?force=1 to override"))
return
}
}
err = collection.Drop(repo)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// GET /api/repos/:name/packages
func apiReposPackagesShow(c *gin.Context) {
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
showPackages(c, repo.RefList())
}
// Handler for both add and delete
func apiReposPackagesAddDelete(c *gin.Context, cb func(list *deb.PackageList, p *deb.Package) error) {
var b struct {
PackageRefs []string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, err)
return
}
// verify package refs and build package list
for _, ref := range b.PackageRefs {
var p *deb.Package
p, err = context.CollectionFactory().PackageCollection().ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
c.Fail(404, fmt.Errorf("package %s: %s", ref, err))
} else {
c.Fail(500, err)
}
return
}
err = cb(list, p)
if err != nil {
c.Fail(400, err)
return
}
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save: %s", err))
return
}
c.JSON(200, repo)
}
// POST /repos/:name/packages
func apiReposPackagesAdd(c *gin.Context) {
apiReposPackagesAddDelete(c, func(list *deb.PackageList, p *deb.Package) error {
return list.Add(p)
})
}
// DELETE /repos/:name/packages
func apiReposPackagesDelete(c *gin.Context) {
apiReposPackagesAddDelete(c, func(list *deb.PackageList, p *deb.Package) error {
list.Remove(p)
return nil
})
}
// POST /repos/:name/file/:dir/:file
func apiReposPackageFromFile(c *gin.Context) {
// redirect all work to dir method
apiReposPackageFromDir(c)
}
// POST /repos/:name/file/:dir
func apiReposPackageFromDir(c *gin.Context) {
forceReplace := c.Request.URL.Query().Get("forceReplace") == "1"
noRemove := c.Request.URL.Query().Get("noRemove") == "1"
if !verifyDir(c) {
return
}
fileParam := c.Params.ByName("file")
if fileParam != "" && !verifyPath(fileParam) {
c.Fail(400, fmt.Errorf("wrong file"))
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
verifier := &utils.GpgVerifier{}
var (
sources []string
packageFiles, failedFiles []string
processedFiles, failedFiles2 []string
reporter = &aptly.RecordingResultReporter{
Warnings: []string{},
AddedLines: []string{},
RemovedLines: []string{},
}
list *deb.PackageList
)
if fileParam == "" {
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"))}
} else {
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))}
}
packageFiles, failedFiles, err = deb.CollectPackageFiles(sources, reporter)
if err != nil {
c.Fail(500, fmt.Errorf("unable to collect package files: %s", err))
return
}
list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, fmt.Errorf("unable to load packages: %s", err))
return
}
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
context.CollectionFactory().PackageCollection(), reporter)
failedFiles = append(failedFiles, failedFiles2...)
if err != nil {
c.Fail(500, fmt.Errorf("unable to import package files: %s", err))
return
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save: %s", err))
return
}
if !noRemove {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
err := os.Remove(file)
if err != nil {
reporter.Warning("unable to remove file %s: %s", file, err)
}
}
// atempt to remove dir, if it fails, that's fine: probably it's not empty
os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir")))
}
if failedFiles == nil {
failedFiles = []string{}
}
c.JSON(200, gin.H{
"Report": reporter,
"FailedFiles": failedFiles,
})
}
+82
View File
@@ -0,0 +1,82 @@
package api
import (
"github.com/gin-gonic/gin"
ctx "github.com/smira/aptly/context"
"net/http"
)
var context *ctx.AptlyContext
// Router returns prebuilt with routes http.Handler
func Router(c *ctx.AptlyContext) http.Handler {
context = c
go cacheFlusher()
router := gin.Default()
router.Use(gin.ErrorLogger())
root := router.Group("/api")
{
root.GET("/version", apiVersion)
}
{
root.GET("/repos", apiReposList)
root.POST("/repos", apiReposCreate)
root.GET("/repos/:name", apiReposShow)
root.PUT("/repos/:name", apiReposEdit)
root.DELETE("/repos/:name", apiReposDrop)
root.GET("/repos/:name/packages", apiReposPackagesShow)
root.POST("/repos/:name/packages", apiReposPackagesAdd)
root.DELETE("/repos/:name/packages", apiReposPackagesDelete)
root.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile)
root.POST("/repos/:name/file/:dir", apiReposPackageFromDir)
root.POST("/repos/:name/snapshots", apiSnapshotsCreateFromRepository)
}
{
root.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
}
{
root.GET("/files", apiFilesListDirs)
root.POST("/files/:dir", apiFilesUpload)
root.GET("/files/:dir", apiFilesListFiles)
root.DELETE("/files/:dir", apiFilesDeleteDir)
root.DELETE("/files/:dir/:name", apiFilesDeleteFile)
}
{
root.GET("/publish", apiPublishList)
root.POST("/publish", apiPublishRepoOrSnapshot)
root.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
root.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
root.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
}
{
root.GET("/snapshots", apiSnapshotsList)
root.POST("/snapshots", apiSnapshotsCreate)
root.PUT("/snapshots/:name", apiSnapshotsUpdate)
root.GET("/snapshots/:name", apiSnapshotsShow)
root.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages)
root.DELETE("/snapshots/:name", apiSnapshotsDrop)
root.GET("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff)
}
{
root.GET("/packages/:key", apiPackagesShow)
}
{
root.GET("/graph.:ext", apiGraph)
}
return router
}
+410
View File
@@ -0,0 +1,410 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
)
// GET /api/snapshots
func apiSnapshotsList(c *gin.Context) {
SortMethodString := c.Request.URL.Query().Get("sort")
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
if SortMethodString == "" {
SortMethodString = "name"
}
result := []*deb.Snapshot{}
collection.ForEachSorted(SortMethodString, func(snapshot *deb.Snapshot) error {
result = append(result, snapshot)
return nil
})
c.JSON(200, result)
}
// POST /api/mirrors/:name/snapshots/
func apiSnapshotsCreateFromMirror(c *gin.Context) {
var (
err error
repo *deb.RemoteRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().RemoteRepoCollection()
collection.RLock()
defer collection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = repo.CheckLock()
if err != nil {
c.Fail(409, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/snapshots
func apiSnapshotsCreate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
SourceSnapshots []string
PackageRefs []string
}
if !c.Bind(&b) {
return
}
if b.Description == "" {
if len(b.SourceSnapshots)+len(b.PackageRefs) == 0 {
b.Description = "Created as empty"
}
}
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
sources := make([]*deb.Snapshot, len(b.SourceSnapshots))
for i := range b.SourceSnapshots {
sources[i], err = snapshotCollection.ByName(b.SourceSnapshots[i])
if err != nil {
c.Fail(404, err)
return
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
c.Fail(500, err)
return
}
}
list := deb.NewPackageList()
// verify package refs and build package list
for _, ref := range b.PackageRefs {
var p *deb.Package
p, err = context.CollectionFactory().PackageCollection().ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
c.Fail(404, fmt.Errorf("package %s: %s", ref, err))
} else {
c.Fail(500, err)
}
return
}
err = list.Add(p)
if err != nil {
c.Fail(400, err)
return
}
}
snapshot = deb.NewSnapshotFromRefList(b.Name, sources, deb.NewPackageRefListFromPackageList(list), b.Description)
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/repos/:name/snapshots
func apiSnapshotsCreateFromRepository(c *gin.Context) {
var (
err error
repo *deb.LocalRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// PUT /api/snapshots/:name
func apiSnapshotsUpdate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().SnapshotCollection()
collection.Lock()
defer collection.Unlock()
snapshot, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
_, err = collection.ByName(b.Name)
if err == nil {
c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
return
}
if b.Name != "" {
snapshot.Name = b.Name
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, snapshot)
}
// GET /api/snapshots/:name
func apiSnapshotsShow(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, snapshot)
}
// DELETE /api/snapshots/:name
func apiSnapshotsDrop(c *gin.Context) {
name := c.Params.ByName("name")
force := c.Request.URL.Query().Get("force") == "1"
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
publishedCollection.RLock()
defer publishedCollection.RUnlock()
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
c.Fail(404, err)
return
}
published := publishedCollection.BySnapshot(snapshot)
if len(published) > 0 {
c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
return
}
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
return
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// GET /api/snapshots/:name/diff/:withSnapshot
func apiSnapshotsDiff(c *gin.Context) {
onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshotA, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshotA)
if err != nil {
c.Fail(500, err)
return
}
err = collection.LoadComplete(snapshotB)
if err != nil {
c.Fail(500, err)
return
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
if err != nil {
c.Fail(500, err)
return
}
result := []deb.PackageDiff{}
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
result = append(result, pdiff)
}
c.JSON(200, result)
}
// GET /api/snapshots/:name/packages
func apiSnapshotsSearchPackages(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
showPackages(c, snapshot.RefList())
}
+17 -8
View File
@@ -5,7 +5,6 @@ package aptly
import (
"github.com/smira/aptly/utils"
"io"
"os"
)
// PackagePool is asbtraction of package pool storage.
@@ -26,26 +25,34 @@ type PackagePool interface {
// PublishedStorage is abstraction of filesystem storing all published repositories
type PublishedStorage interface {
// PublicPath returns root of public part
PublicPath() string
// MkDir creates directory recursively under public path
MkDir(path string) error
// CreateFile creates file for writing under public path
CreateFile(path string) (*os.File, 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 string) error
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath, sourceMD5 string, force bool) error
// Filelist returns list of files under prefix
Filelist(prefix string) ([]string, error)
// ChecksumsForFile proxies requests to utils.ChecksumsForFile, joining public path
ChecksumsForFile(path string) (utils.ChecksumInfo, 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
@@ -83,6 +90,8 @@ type Downloader interface {
// 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
}
+67
View File
@@ -0,0 +1,67 @@
package aptly
import (
"fmt"
)
// ResultReporter is abstraction for result reporting from complex processing functions
type ResultReporter interface {
// Warning is non-fatal error message
Warning(msg string, a ...interface{})
// Removed is signal that something has been removed
Removed(msg string, a ...interface{})
// Added is signal that something has been added
Added(msg string, a ...interface{})
}
// ConsoleResultReporter is implementation of ResultReporter that prints in colors to console
type ConsoleResultReporter struct {
Progress Progress
}
// Check interface
var (
_ ResultReporter = &ConsoleResultReporter{}
)
// Warning is non-fatal error message (yellow)
func (c *ConsoleResultReporter) Warning(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@y[!]@| @!"+msg+"@|", a...)
}
// Removed is signal that something has been removed (red)
func (c *ConsoleResultReporter) Removed(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@r[-]@| "+msg, a...)
}
// Added is signal that something has been added (green)
func (c *ConsoleResultReporter) Added(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@g[+]@| "+msg, a...)
}
// RecordingResultReporter is implementation of ResultReporter that collects all messages
type RecordingResultReporter struct {
Warnings []string
AddedLines []string `json:"Added"`
RemovedLines []string `json:"Removed"`
}
// Check interface
var (
_ ResultReporter = &RecordingResultReporter{}
)
// Warning is non-fatal error message
func (r *RecordingResultReporter) Warning(msg string, a ...interface{}) {
r.Warnings = append(r.Warnings, fmt.Sprintf(msg, a...))
}
// Removed is signal that something has been removed
func (r *RecordingResultReporter) Removed(msg string, a ...interface{}) {
r.RemovedLines = append(r.RemovedLines, fmt.Sprintf(msg, a...))
}
// Added is signal that something has been added
func (r *RecordingResultReporter) Added(msg string, a ...interface{}) {
r.AddedLines = append(r.AddedLines, fmt.Sprintf(msg, a...))
}
+1 -1
View File
@@ -1,7 +1,7 @@
package aptly
// Version of aptly
const Version = "0.5.1"
const Version = "0.9.1"
// Enable debugging features?
const EnableDebug = false
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdAPI() *commander.Command {
return &commander.Command{
UsageLine: "api",
Short: "start API server/issue requests",
Subcommands: []*commander.Command{
makeCmdAPIServe(),
},
}
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/api"
"github.com/smira/commander"
"github.com/smira/flag"
"net/http"
)
func aptlyAPIServe(cmd *commander.Command, args []string) error {
var (
err error
)
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
listen := context.Flags().Lookup("listen").Value.String()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
err = http.ListenAndServe(listen, api.Router(context))
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
return err
}
func makeCmdAPIServe() *commander.Command {
cmd := &commander.Command{
Run: aptlyAPIServe,
UsageLine: "serve",
Short: "start API HTTP service",
Long: `
Stat HTTP server with aptly REST API.
Example:
$ aptly api serve -listen=:8080
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
}
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
return cmd
}
+18 -2
View File
@@ -34,6 +34,18 @@ func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
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{
@@ -46,21 +58,25 @@ upgrade individual packages, take snapshots and publish them
back as Debian repositories.
aptly's goal is to establish repeatability and controlled changes
in a package-centric environment. aptly allows to fix a set of packages
in a 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 to perform controlled,
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{
makeCmdConfig(),
makeCmdDb(),
makeCmdGraph(),
makeCmdMirror(),
makeCmdRepo(),
makeCmdServe(),
makeCmdSnapshot(),
makeCmdTask(),
makeCmdPublish(),
makeCmdVersion(),
makeCmdPackage(),
makeCmdAPI(),
},
}
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdConfig() *commander.Command {
return &commander.Command{
UsageLine: "config",
Short: "manage aptly configuration",
Subcommands: []*commander.Command{
makeCmdConfigShow(),
},
}
}
+38
View File
@@ -0,0 +1,38 @@
package cmd
import (
"encoding/json"
"fmt"
"github.com/smira/commander"
)
func aptlyConfigShow(cmd *commander.Command, args []string) error {
config := context.Config()
prettyJSON, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("unable to dump the config file: %s", err)
}
fmt.Println(string(prettyJSON))
return nil
}
func makeCmdConfigShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyConfigShow,
UsageLine: "show",
Short: "show current aptly's config",
Long: `
Command show displays the current aptly configuration.
Example:
$ aptly config show
`,
}
return cmd
}
+13 -263
View File
@@ -1,281 +1,31 @@
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/utils"
ctx "github.com/smira/aptly/context"
"github.com/smira/flag"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"time"
)
// AptlyContext is a common context shared by all commands
type AptlyContext struct {
flags *flag.FlagSet
configLoaded bool
progress aptly.Progress
downloader aptly.Downloader
database database.Storage
packagePool aptly.PackagePool
publishedStorage aptly.PublishedStorage
collectionFactory *deb.CollectionFactory
dependencyOptions int
architecturesList []string
// Debug features
fileCPUProfile *os.File
fileMemProfile *os.File
fileMemStats *os.File
}
var context *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) {
panic(&FatalError{ReturnCode: 1, Message: err.Error()})
}
// Config loads and returns current configuration
func (context *AptlyContext) Config() *utils.ConfigStructure {
if !context.configLoaded {
var err error
configLocation := context.flags.Lookup("config").Value.String()
if configLocation != "" {
err = utils.LoadConfig(configLocation, &utils.Config)
if err != nil {
Fatal(err)
}
} else {
configLocations := []string{
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
"/etc/aptly.conf",
}
for _, configLocation := range configLocations {
err = utils.LoadConfig(configLocation, &utils.Config)
if err == nil {
break
}
if !os.IsNotExist(err) {
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
}
}
if err != nil {
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
utils.SaveConfig(configLocations[0], &utils.Config)
}
}
context.configLoaded = true
}
return &utils.Config
}
// DependencyOptions calculates options related to dependecy handling
func (context *AptlyContext) DependencyOptions() int {
if context.dependencyOptions == -1 {
context.dependencyOptions = 0
if context.Config().DepFollowSuggests || context.flags.Lookup("dep-follow-suggests").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSuggests
}
if context.Config().DepFollowRecommends || context.flags.Lookup("dep-follow-recommends").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowRecommends
}
if context.Config().DepFollowAllVariants || context.flags.Lookup("dep-follow-all-variants").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if context.Config().DepFollowSource || context.flags.Lookup("dep-follow-source").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSource
}
}
return context.dependencyOptions
}
// ArchitecturesList returns list of architectures fixed via command line or config
func (context *AptlyContext) ArchitecturesList() []string {
if context.architecturesList == nil {
context.architecturesList = context.Config().Architectures
optionArchitectures := context.flags.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
}
return context.architecturesList
}
// Progress creates or returns Progress object
func (context *AptlyContext) Progress() aptly.Progress {
if context.progress == nil {
context.progress = console.NewProgress()
context.progress.Start()
}
return context.progress
}
// Downloader returns instance of current downloader
func (context *AptlyContext) Downloader() aptly.Downloader {
if context.downloader == nil {
context.downloader = http.NewDownloader(context.Config().DownloadConcurrency, context.Progress())
}
return context.downloader
}
// DBPath builds path to database
func (context *AptlyContext) DBPath() string {
return filepath.Join(context.Config().RootDir, "db")
}
// Database opens and returns current instance of database
func (context *AptlyContext) Database() (database.Storage, error) {
if context.database == nil {
var err error
context.database, err = database.OpenDB(context.DBPath())
if err != nil {
return nil, fmt.Errorf("can't open database: %s", err)
}
}
return context.database, nil
}
// CollectionFactory builds factory producing all kinds of collections
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
if context.collectionFactory == nil {
db, err := context.Database()
if err != nil {
Fatal(err)
}
context.collectionFactory = deb.NewCollectionFactory(db)
}
return context.collectionFactory
}
// PackagePool returns instance of PackagePool
func (context *AptlyContext) PackagePool() aptly.PackagePool {
if context.packagePool == nil {
context.packagePool = files.NewPackagePool(context.Config().RootDir)
}
return context.packagePool
}
// PublishedStorage returns instance of PublishedStorage
func (context *AptlyContext) PublishedStorage() aptly.PublishedStorage {
if context.publishedStorage == nil {
context.publishedStorage = files.NewPublishedStorage(context.Config().RootDir)
}
return context.publishedStorage
}
var context *ctx.AptlyContext
// ShutdownContext shuts context down
func ShutdownContext() {
if aptly.EnableDebug {
if context.fileMemProfile != nil {
pprof.WriteHeapProfile(context.fileMemProfile)
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
if context.fileCPUProfile != nil {
pprof.StopCPUProfile()
context.fileCPUProfile.Close()
context.fileCPUProfile = nil
}
if context.fileMemProfile != nil {
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
}
if context.database != nil {
context.database.Close()
}
if context.downloader != nil {
context.downloader.Shutdown()
}
if context.progress != nil {
context.progress.Shutdown()
}
context.Shutdown()
}
// CleanupContext does partial shutdown of context
func CleanupContext() {
context.Cleanup()
}
// InitContext initializes context with default settings
func InitContext(flags *flag.FlagSet) error {
var err error
context = &AptlyContext{flags: flags, dependencyOptions: -1}
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
}
}
}()
}
if context != nil {
panic("context already initialized")
}
return nil
context, err = ctx.NewContext(flags)
return err
}
+23 -5
View File
@@ -14,20 +14,20 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
// collect information about references packages...
existingPackageRefs := deb.NewPackageRefList()
context.Progress().Printf("Loading mirrors, local repos and snapshots...\n")
context.Progress().Printf("Loading mirrors, local repos, snapshots and published repos...\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)
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
}
return nil
})
@@ -41,7 +41,7 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
return err
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
}
return nil
})
@@ -54,7 +54,25 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
if err != nil {
return err
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false, true)
return nil
})
if err != nil {
return err
}
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(published *deb.PublishedRepo) error {
if published.SourceKind != "local" {
return nil
}
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if err != nil {
return err
}
for _, component := range published.Components() {
existingPackageRefs = existingPackageRefs.Merge(published.RefList(component), false, true)
}
return nil
})
if err != nil {
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyDbRecover(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
context.Progress().Printf("Recovering database...\n")
+7 -115
View File
@@ -2,7 +2,6 @@ package cmd
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
@@ -10,128 +9,21 @@ import (
"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")
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", 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())),
})
existingNodes[repo.UUID] = true
return nil
})
if err != nil {
return err
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
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", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "mediumseagreen",
"label": graphvizEscape(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", 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" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
_, exists := existingNodes[uuid]
if exists {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(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", 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, ", "))),
})
_, exists := existingNodes[repo.SourceUUID]
if exists {
graph.AddEdge(graphvizEscape(repo.SourceUUID), "", graphvizEscape(repo.UUID), "", true, nil)
}
return nil
})
fmt.Printf("Generating graph...\n")
graph, err := deb.BuildGraph(context.CollectionFactory())
if err != nil {
return err
}
buf := bytes.NewBufferString(graph.String())
+4 -1
View File
@@ -8,7 +8,7 @@ import (
)
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
if context.Config().GpgDisableVerify || flags.Lookup("ignore-signatures").Value.Get().(bool) {
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
return nil, nil
}
@@ -54,6 +54,9 @@ func makeCmdMirror() *commander.Command {
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
makeCmdMirrorRename(),
makeCmdMirrorEdit(),
makeCmdMirrorSearch(),
},
}
}
+23 -5
View File
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
@@ -12,10 +13,11 @@ 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 err
return commander.ErrCommandError
}
downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool)
downloadSources := LookupOption(context.Config().DownloadSourcePackages, context.Flags(), "with-sources")
downloadUdebs := context.Flags().Lookup("with-udebs").Value.Get().(bool)
var (
mirrorName, archiveURL, distribution string
@@ -32,12 +34,24 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
archiveURL, distribution, components = args[1], args[2], args[3:]
}
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(), downloadSources)
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(),
downloadSources, downloadUdebs)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
verifier, err := getVerifier(context.flags)
repo.Filter = context.Flags().Lookup("filter").Value.String()
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
repo.SkipComponentCheck = context.Flags().Lookup("force-components").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)
}
@@ -63,7 +77,7 @@ func makeCmdMirrorCreate() *commander.Command {
Short: "create new mirror",
Long: `
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
via HTTP. aptly would try download Release file from remote repository and verify its' signature. Command
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:
@@ -79,6 +93,10 @@ Example:
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.Bool("force-components", false, "(only with component list) skip check that requested components are listed in Release file")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
+7 -2
View File
@@ -10,7 +10,7 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -20,7 +20,12 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: %s", err)
}
force := context.flags.Lookup("force").Value.Get().(bool)
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)
+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
}
+3 -1
View File
@@ -11,7 +11,7 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -28,6 +28,8 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
+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
}
+19 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
@@ -12,7 +13,7 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -28,6 +29,9 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
}
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, ", "))
@@ -37,6 +41,19 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
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 {
@@ -49,7 +66,7 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
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")
+121 -4
View File
@@ -2,15 +2,21 @@ 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 err
return commander.ErrCommandError
}
name := args[0]
@@ -25,9 +31,17 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := context.flags.Lookup("ignore-checksums").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
}
verifier, err := getVerifier(context.flags)
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)
}
@@ -37,11 +51,112 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
err = repo.Download(context.Progress(), context.Downloader(), context.CollectionFactory(), context.PackagePool(), ignoreMismatch)
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)
@@ -68,8 +183,10 @@ Example:
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(),
},
}
}
+52
View File
@@ -0,0 +1,52 @@
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())
if result.Len() == 0 {
return fmt.Errorf("no results")
}
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, p.IsSource, false)
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 matching 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
}
+3 -1
View File
@@ -7,13 +7,15 @@ import (
)
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
if flags.Lookup("skip-signing").Value.Get().(bool) || context.Config().GpgDisableSign {
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())
signer.SetBatch(flags.Lookup("batch").Value.Get().(bool))
err := signer.Init()
if err != nil {
+10 -7
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
@@ -9,17 +10,19 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
if len(args) == 2 {
prefix = args[1]
param = args[1]
}
err = context.CollectionFactory().PublishedRepoCollection().Remove(context.PublishedStorage(), prefix, distribution,
storage, prefix := deb.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)
@@ -33,11 +36,11 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
UsageLine: "drop <distribution> [[<endpoint>:]<prefix>]",
Short: "remove published repository",
Long: `
Command removes whatever has been published under specified <prefix> and
<distribution> name.
Command removes whatever has been published under specified <prefix>,
publishing <endpoint> and <distribution> name.
Example:
+4 -2
View File
@@ -11,7 +11,7 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -25,7 +25,7 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
}
if raw {
published = append(published, fmt.Sprintf("%s %s", repo.Prefix, repo.Distribution))
published = append(published, fmt.Sprintf("%s %s", repo.StoragePrefix(), repo.Distribution))
} else {
published = append(published, repo.String())
}
@@ -36,6 +36,8 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to load list of repos: %s", err)
}
context.CloseDatabase()
sort.Strings(published)
if raw {
+12 -2
View File
@@ -8,13 +8,19 @@ import (
func makeCmdPublishRepo() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "repo <name> [<prefix>]",
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
@@ -27,13 +33,17 @@ Example:
Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.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("batch", false, "run GPG with detached tty")
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
}
+108 -39
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
@@ -11,61 +12,107 @@ import (
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components) || len(args) > len(components)+1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
var param string
if len(args) == len(components)+1 {
param = args[len(components)]
args = args[0 : len(args)-1]
} else {
prefix = ""
param = ""
}
storage, prefix := deb.ParsePrefix(param)
var (
source interface{}
sources = []interface{}{}
message string
)
if cmd.Name() == "snapshot" {
var snapshot *deb.Snapshot
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
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
}
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if len(parts) == 1 {
message = fmt.Sprintf("Snapshot %s has", parts[0])
} else {
message = fmt.Sprintf("Snapshots %s have", strings.Join(parts, ", "))
}
source = snapshot
message = fmt.Sprintf("Snapshot %s", snapshot.Name)
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
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
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
}
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if len(parts) == 1 {
message = fmt.Sprintf("Local repo %s has", parts[0])
} else {
message = fmt.Sprintf("Local repos %s have", strings.Join(parts, ", "))
}
source = localRepo
message = fmt.Sprintf("Local repo %s", localRepo.Name)
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")
}
component := context.flags.Lookup("component").Value.String()
distribution := context.flags.Lookup("distribution").Value.String()
distribution := context.Flags().Lookup("distribution").Value.String()
published, err := deb.NewPublishedRepo(prefix, distribution, component, context.ArchitecturesList(), source, context.CollectionFactory())
published, err := deb.NewPublishedRepo(storage, prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
@@ -78,12 +125,18 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(context.flags)
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
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)
}
@@ -93,19 +146,25 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
prefix, component, distribution = published.Prefix, published.Component, published.Distribution
var repoComponents string
prefix, repoComponents, distribution = published.Prefix, strings.Join(published.Components(), " "), published.Distribution
if prefix == "." {
prefix = ""
} else if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
context.Progress().Printf("\n%s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
message, context.PublishedStorage().PublicPath())
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, component)
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, component)
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
}
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
@@ -116,13 +175,19 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "snapshot <name> [<prefix>]",
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
@@ -130,13 +195,17 @@ Example:
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("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("batch", false, "run GPG with detached tty")
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
}
+70 -27
View File
@@ -3,45 +3,42 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 || len(args) > 3 {
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components)+1 || len(args) > len(components)+2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
var (
name string
names []string
snapshot *deb.Snapshot
)
if len(args) == 3 {
prefix = args[1]
name = args[2]
if len(args) == len(components)+2 {
param = args[1]
names = args[2:]
} else {
name = args[1]
names = args[1:]
}
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
@@ -55,14 +52,45 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
published.UpdateSnapshot(snapshot)
publishedComponents := published.Components()
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
components = publishedComponents
}
signer, err := getSigner(context.flags)
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 {
if !utils.StrSliceHasItem(publishedComponents, component) {
return fmt.Errorf("unable to switch: component %s is not in published repository", component)
}
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
published.UpdateSnapshot(component, snapshot)
}
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
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)
}
@@ -72,8 +100,8 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component,
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
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)
}
@@ -86,22 +114,37 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
func makeCmdPublishSwitch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSwitch,
UsageLine: "switch <distribution> [<prefix>] <new-snapshot>",
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).
Command switches in-place published snapshots 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 switch -component=main,contrib wheezy wh-main wh-contrib
Example:
$ aptly publish update wheezy ppa wheezy-7.5
$ aptly publish switch wheezy ppa wheezy-7.5
This command would switch published repository (with one component) named ppa/wheezy
(prefix ppa, dsitribution wheezy to new snapshot 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("batch", false, "run GPG with detached tty")
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
}
+27 -10
View File
@@ -11,19 +11,20 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
if len(args) == 2 {
prefix = args[1]
param = args[1]
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
@@ -37,14 +38,23 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
published.UpdateLocalRepo()
components := published.Components()
for _, component := range components {
published.UpdateLocalRepo(component)
}
signer, err := getSigner(context.flags)
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
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)
}
@@ -54,8 +64,8 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component,
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
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)
}
@@ -68,7 +78,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
func makeCmdPublishUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishUpdate,
UsageLine: "update <distribution> [<prefix>]",
UsageLine: "update <distribution> [[<endpoint>:]<prefix>]",
Short: "update published local repository",
Long: `
Command re-publishes (updates) published local repository. <distribution>
@@ -76,6 +86,9 @@ 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
@@ -85,7 +98,11 @@ Example:
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("batch", false, "run GPG with detached tty")
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
}
+2
View File
@@ -19,6 +19,8 @@ func makeCmdRepo() *commander.Command {
makeCmdRepoMove(),
makeCmdRepoRemove(),
makeCmdRepoShow(),
makeCmdRepoRename(),
makeCmdRepoSearch(),
},
}
}
+26 -118
View File
@@ -2,21 +2,19 @@ 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"
"os"
"path/filepath"
"sort"
"strings"
)
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -40,122 +38,22 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to load packages: %s", err)
}
packageFiles := []string{}
forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool)
for _, location := range args[1:] {
info, err2 := os.Stat(location)
if err2 != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2)
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
}
var packageFiles, failedFiles []string
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, path)
}
return nil
})
} else {
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, location)
} else {
context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
continue
}
}
packageFiles, failedFiles, err = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()})
if err != nil {
return fmt.Errorf("unable to collect package files: %s", err)
}
processedFiles := []string{}
sort.Strings(packageFiles)
var processedFiles, failedFiles2 []string
for _, file := range packageFiles {
var (
stanza deb.Stanza
p *deb.Package
)
candidateProcessedFiles := []string{}
isSourcePackage := strings.HasSuffix(file, ".dsc")
if isSourcePackage {
stanza, err = deb.GetControlFileFromDsc(file, verifier)
if err == nil {
stanza["Package"] = stanza["Source"]
delete(stanza, "Source")
p, err = deb.NewSourcePackageFromControlFile(stanza)
}
} else {
stanza, err = deb.GetControlFileFromDeb(file)
p = deb.NewPackageFromControlFile(stanza)
}
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
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)
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)
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)
continue
}
err = list.Add(p)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
continue
}
context.Progress().ColoredPrintf("@g[+]@| %s added@|", p)
processedFiles = append(processedFiles, candidateProcessedFiles...)
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
context.CollectionFactory().PackageCollection(), &aptly.ConsoleResultReporter{Progress: context.Progress()})
failedFiles = append(failedFiles, failedFiles2...)
if err != nil {
return fmt.Errorf("unable to import package files: %s", err)
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
@@ -165,7 +63,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save: %s", err)
}
if context.flags.Lookup("remove-files").Value.Get().(bool) {
if context.Flags().Lookup("remove-files").Value.Get().(bool) {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
@@ -176,6 +74,15 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
}
}
if len(failedFiles) > 0 {
context.Progress().ColoredPrintf("@y[!]@| @!Some files were skipped due to errors:@|")
for _, file := range failedFiles {
context.Progress().ColoredPrintf(" %s", file)
}
return fmt.Errorf("some files failed to be added")
}
return err
}
@@ -185,8 +92,8 @@ func makeCmdRepoAdd() *commander.Command {
UsageLine: "add <name> <package file.deb>|<directory> ...",
Short: "add packages to local repository",
Long: `
Command adds packages to local repository from .deb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.deb or *.dsc
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.
@@ -199,6 +106,7 @@ Example:
}
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
}
+2 -2
View File
@@ -8,10 +8,10 @@ import (
func makeCmdRepoCopy() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "copy <src-name> <dst-name> <package-spec> ...",
UsageLine: "copy <src-name> <dst-name> <package-query> ...",
Short: "copy packages between local repositories",
Long: `
Command copy copies packages matching <package-spec> from local repo
Command copy copies packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
+4 -4
View File
@@ -11,12 +11,12 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
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()
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 {
+2 -2
View File
@@ -10,7 +10,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -34,7 +34,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: local repo is published")
}
force := context.flags.Lookup("force").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
+8 -8
View File
@@ -10,7 +10,7 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0])
@@ -23,16 +23,16 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
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("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("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()
if context.Flags().Lookup("component").Value.String() != "" {
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
}
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
@@ -50,7 +50,7 @@ func makeCmdRepoEdit() *commander.Command {
UsageLine: "edit <name>",
Short: "edit properties of local repository",
Long: `
Command edit allows to change metadata of local repository:
Command edit allows one to change metadata of local repository:
comment, default distribution and component.
Example:
+2 -2
View File
@@ -8,10 +8,10 @@ import (
func makeCmdRepoImport() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "import <src-mirror> <dst-repo> <package-spec> ...",
UsageLine: "import <src-mirror> <dst-repo> <package-query> ...",
Short: "import packages from mirror to local repository",
Long: `
Command import looks up packages matching <package-spec> in mirror <src-mirror>
Command import looks up packages matching <package-query> in mirror <src-mirror>
and copies them to local repo <dst-repo>.
Example:
+3 -1
View File
@@ -11,7 +11,7 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -33,6 +33,8 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
+15 -6
View File
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
@@ -12,7 +13,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
return commander.ErrCommandError
}
command := cmd.Name()
@@ -86,7 +87,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var architecturesList []string
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
if withDeps {
dstList.PrepareIndex()
@@ -105,7 +106,15 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
}
}
toProcess, err := srcList.Filter(args[2:], withDeps, dstList, context.DependencyOptions(), architecturesList)
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)
}
@@ -136,7 +145,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to %s: %s", command, err)
}
if context.flags.Lookup("dry-run").Value.Get().(bool) {
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))
@@ -162,10 +171,10 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
func makeCmdRepoMove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "move <src-name> <dst-name> <package-spec> ...",
UsageLine: "move <src-name> <dst-name> <package-query> ...",
Short: "move packages between local repositories",
Long: `
Command move moves packages matching <package-spec> from local repo
Command move moves packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
+14 -5
View File
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -11,7 +12,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -33,8 +34,16 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
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(args[1:], false, nil, 0, nil)
toRemove, err := list.Filter(queries, false, nil, 0, nil)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
@@ -45,7 +54,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
return nil
})
if context.flags.Lookup("dry-run").Value.Get().(bool) {
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))
@@ -62,10 +71,10 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
func makeCmdRepoRemove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRemove,
UsageLine: "remove <name> <package-spec> ...",
UsageLine: "remove <name> <package-query> ...",
Short: "remove packages from local repository",
Long: `
Commands removes packages matching <package-spec> from local repository
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'.
+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
}
+2 -2
View File
@@ -10,7 +10,7 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -31,7 +31,7 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
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)
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(repo.RefList())
}
+45
View File
@@ -0,0 +1,45 @@
package cmd
import (
"fmt"
ctx "github.com/smira/aptly/context"
"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.(*ctx.FatalError)
if !ok {
panic(r)
}
fmt.Println("ERROR:", fatal.Message)
returnCode = fatal.ReturnCode
}
}()
returnCode = 0
flags, args, err := cmd.ParseFlags(cmdArgs)
if err != nil {
ctx.Fatal(err)
}
if initContext {
err = InitContext(flags)
if err != nil {
ctx.Fatal(err)
}
defer ShutdownContext()
}
context.UpdateFlags(flags)
err = cmd.Dispatch(args)
if err != nil {
ctx.Fatal(err)
}
return
}
+11 -4
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
@@ -10,17 +11,23 @@ import (
"net/http"
"os"
"sort"
"strings"
)
func aptlyServe(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := context.flags.Lookup("listen").Value.String()
listen := context.Flags().Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen)
@@ -69,15 +76,15 @@ 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, repo.Component)
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
}
}
publicPath := context.PublishedStorage().PublicPath()
publicPath := context.GetPublishedStorage("").(aptly.LocalPublishedStorage).PublicPath()
ShutdownContext()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
+3
View File
@@ -17,6 +17,9 @@ func makeCmdSnapshot() *commander.Command {
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
makeCmdSnapshotRename(),
makeCmdSnapshotSearch(),
makeCmdSnapshotFilter(),
},
}
}
+6 -1
View File
@@ -23,6 +23,11 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
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)
@@ -61,7 +66,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
return commander.ErrCommandError
}
err = context.CollectionFactory().SnapshotCollection().Add(snapshot)
+2 -2
View File
@@ -10,10 +10,10 @@ func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool)
onlyMatching := context.Flags().Lookup("only-matching").Value.Get().(bool)
// Load <name-a> snapshot
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
+2 -2
View File
@@ -10,7 +10,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -35,7 +35,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: snapshot is published")
}
force := context.flags.Lookup("force").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().BySnapshotSource(snapshot)
if len(snapshots) > 0 {
+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
}
+17 -23
View File
@@ -4,43 +4,36 @@ import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
snapshots := make([]string, context.CollectionFactory().SnapshotCollection().Len())
i := 0
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
if raw {
snapshots[i] = snapshot.Name
} else {
snapshots[i] = snapshot.String()
}
i++
return nil
})
sort.Strings(snapshots)
collection := context.CollectionFactory().SnapshotCollection()
if raw {
for _, snapshot := range snapshots {
fmt.Printf("%s\n", snapshot)
}
collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
fmt.Printf("%s\n", snapshot.Name)
return nil
})
} else {
if len(snapshots) > 0 {
if collection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
err = collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
fmt.Printf(" * %s\n", snapshot.String())
return nil
})
if err != nil {
return err
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
@@ -48,8 +41,8 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
}
return err
return err
}
func makeCmdSnapshotList() *commander.Command {
@@ -67,6 +60,7 @@ Example:
}
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
}
+12 -6
View File
@@ -11,7 +11,7 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
sources := make([]*deb.Snapshot, len(args)-1)
@@ -28,17 +28,22 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
}
}
latest := context.flags.Lookup("latest").Value.Get().(bool)
overrideMatching := !latest
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)
result = result.Merge(sources[i].RefList(), overrideMatching, false)
}
if latest {
deb.FilterLatestRefs(result)
result.FilterLatestRefs()
}
sourceDescription := make([]string, len(sources))
@@ -79,6 +84,7 @@ Example:
}
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
}
+53 -70
View File
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
@@ -13,11 +14,12 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
return commander.ErrCommandError
}
noDeps := context.flags.Lookup("no-deps").Value.Get().(bool)
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
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])
@@ -75,79 +77,59 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]deb.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = deb.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
// 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}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]deb.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
// 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{L: queries[i], R: 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)
}
}
// 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 {
context.Progress().ColoredPrintf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
continue
}
if !noRemove {
// Remove all packages with the same name and architecture
for p := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
p = packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name})
}
}
// Add new discovered package
// If !allMatches, add only first matching name-arch package
if !seen || allMatches {
packageList.Add(pkg)
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := deb.NewPackageList()
pL.Add(pkg)
var missing []deb.Dependency
missing, err = pL.VerifyDependencies(context.DependencyOptions(), []string{arch}, packageList, nil)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
}
// 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 context.flags.Lookup("dry-run").Value.Get().(bool) {
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
@@ -167,14 +149,14 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
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 dependency 'package-name (>= version)'.
as 'package-name' or as package queries.
Example:
@@ -186,6 +168,7 @@ Example:
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
}
+2 -2
View File
@@ -10,7 +10,7 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -30,7 +30,7 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
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)
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
snapshots := make([]*deb.Snapshot, len(args))
+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(),
},
}
}
+154
View File
@@ -0,0 +1,154 @@
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{}
var finfo os.FileInfo
if finfo, err = os.Stat(filename); os.IsNotExist(err) || finfo.IsDir() {
return fmt.Errorf("no such file, %s\n", filename)
}
fmt.Print("Reading file...\n\n")
var file *os.File
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 {
err := context.ReOpenDatabase()
if err != nil {
return fmt.Errorf("failed to reopen DB: %s", err)
}
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 organise 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
}
+5
View File
@@ -7,6 +7,11 @@ import (
)
func aptlyVersion(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("aptly version: %s\n", aptly.Version)
return nil
}
+12 -1
View File
@@ -14,6 +14,8 @@ const (
codeHideProgress
codeStop
codeFlush
codeBarEnabled
codeBarDisabled
)
type printTask struct {
@@ -81,6 +83,8 @@ func (p *Progress) InitBar(count int64, isBytes bool) {
p.bar.SetUnits(pb.U_BYTES)
p.bar.ShowSpeed = true
}
p.queue <- printTask{code: codeBarEnabled}
p.bar.Start()
}
}
@@ -91,6 +95,7 @@ func (p *Progress) ShutdownBar() {
return
}
p.bar.Finish()
p.queue <- printTask{code: codeBarDisabled}
p.bar = nil
p.queue <- printTask{code: codeHideProgress}
}
@@ -151,9 +156,15 @@ func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
}
func (p *Progress) worker() {
hasBar := false
for {
task := <-p.queue
switch task.code {
case codeBarEnabled:
hasBar = true
case codeBarDisabled:
hasBar = false
case codePrint:
if p.barShown {
fmt.Print("\r\033[2K")
@@ -161,7 +172,7 @@ func (p *Progress) worker() {
}
fmt.Print(task.message)
case codeProgress:
if p.bar != nil {
if hasBar {
fmt.Print("\r" + task.message)
p.barShown = true
}
+1 -3
View File
@@ -1,9 +1,7 @@
// +build !freebsd
package console
import (
"code.google.com/p/go.crypto/ssh/terminal"
"golang.org/x/crypto/ssh/terminal"
"syscall"
)
-10
View File
@@ -1,10 +0,0 @@
// +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
}
+490
View File
@@ -0,0 +1,490 @@
// Package context provides single entry to all resources
package context
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/swift"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"sync"
"time"
)
// AptlyContext is a common context shared by all commands
type AptlyContext struct {
sync.Mutex
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
}
// 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 {
context.Lock()
defer context.Unlock()
return context.config()
}
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
}
// LookupOption checks boolean flag with default (usually config) and command-line
// setting
func (context *AptlyContext) LookupOption(defaultValue bool, name string) (result bool) {
context.Lock()
defer context.Unlock()
return context.lookupOption(defaultValue, name)
}
func (context *AptlyContext) lookupOption(defaultValue bool, name string) (result bool) {
result = defaultValue
if context.globalFlags.IsSet(name) {
result = context.globalFlags.Lookup(name).Value.Get().(bool)
}
return
}
// DependencyOptions calculates options related to dependecy handling
func (context *AptlyContext) DependencyOptions() int {
context.Lock()
defer context.Unlock()
if context.dependencyOptions == -1 {
context.dependencyOptions = 0
if context.lookupOption(context.config().DepFollowSuggests, "dep-follow-suggests") {
context.dependencyOptions |= deb.DepFollowSuggests
}
if context.lookupOption(context.config().DepFollowRecommends, "dep-follow-recommends") {
context.dependencyOptions |= deb.DepFollowRecommends
}
if context.lookupOption(context.config().DepFollowAllVariants, "dep-follow-all-variants") {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if context.lookupOption(context.config().DepFollowSource, "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 {
context.Lock()
defer context.Unlock()
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 {
context.Lock()
defer context.Unlock()
return context._progress()
}
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 {
context.Lock()
defer context.Unlock()
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 {
context.Lock()
defer context.Unlock()
return context.dbPath()
}
// 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) {
context.Lock()
defer context.Unlock()
return context._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 {
context.Lock()
defer context.Unlock()
if context.database == nil {
return nil
}
return context.database.Close()
}
// ReOpenDatabase reopens the db after close
func (context *AptlyContext) ReOpenDatabase() error {
context.Lock()
defer context.Unlock()
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 {
context.Lock()
defer context.Unlock()
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 {
context.Lock()
defer context.Unlock()
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 {
context.Lock()
defer context.Unlock()
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 if strings.HasPrefix(name, "swift:") {
params, ok := context.config().SwiftPublishRoots[name[6:]]
if !ok {
Fatal(fmt.Errorf("published Swift storage %v not configured", name[6:]))
}
var err error
publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
params.AuthURL, params.Tenant, params.TenantID, params.Container, params.Prefix)
if err != nil {
Fatal(err)
}
} else {
Fatal(fmt.Errorf("unknown published storage format: %v", name))
}
context.publishedStorages[name] = publishedStorage
}
return publishedStorage
}
// UploadPath builds path to upload storage
func (context *AptlyContext) UploadPath() string {
return filepath.Join(context.Config().RootDir, "upload")
}
// UpdateFlags sets internal copy of flags in the context
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
context.Lock()
defer context.Unlock()
context.flags = flags
}
// Flags returns current command flags
func (context *AptlyContext) Flags() *flag.FlagSet {
context.Lock()
defer context.Unlock()
return context.flags
}
// GlobalFlags returns flags passed to all commands
func (context *AptlyContext) GlobalFlags() *flag.FlagSet {
context.Lock()
defer context.Unlock()
return context.globalFlags
}
// Shutdown shuts context down
func (context *AptlyContext) Shutdown() {
context.Lock()
defer context.Unlock()
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
}
}
// Cleanup does partial shutdown of context
func (context *AptlyContext) Cleanup() {
context.Lock()
defer context.Unlock()
if context.downloader != nil {
context.downloader.Shutdown()
context.downloader = nil
}
if context.progress != nil {
context.progress.Shutdown()
context.progress = nil
}
}
// NewContext initializes context with default settings
func NewContext(flags *flag.FlagSet) (*AptlyContext, error) {
var err error
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 nil, err
}
pprof.StartCPUProfile(context.fileCPUProfile)
}
memprofile := flags.Lookup("memprofile").Value.String()
if memprofile != "" {
context.fileMemProfile, err = os.Create(memprofile)
if err != nil {
return nil, 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 nil, 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 context, nil
}
+28 -6
View File
@@ -24,12 +24,14 @@ type Storage interface {
KeysByPrefix(prefix []byte) [][]byte
FetchByPrefix(prefix []byte) [][]byte
Close() error
ReOpen() error
StartBatch()
FinishBatch() error
CompactDB() error
}
type levelDB struct {
path string
db *leveldb.DB
batch *leveldb.Batch
}
@@ -39,17 +41,21 @@ 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
@@ -95,7 +101,7 @@ func (l *levelDB) Put(key []byte, value []byte) error {
return err
}
} else {
if bytes.Compare(old, value) == 0 {
if bytes.Equal(old, value) {
return nil
}
}
@@ -147,7 +153,23 @@ func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
// Close finishes DB work
func (l *levelDB) Close() error {
return l.db.Close()
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
+22 -1
View File
@@ -1,8 +1,9 @@
package database
import (
. "launchpad.net/gocheck"
"testing"
. "gopkg.in/check.v1"
)
// Launch gocheck tests
@@ -155,3 +156,23 @@ func (s *LevelDBSuite) TestCompactDB(c *C) {
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)
}
+30 -1
View File
@@ -2,10 +2,12 @@ package deb
import (
"github.com/smira/aptly/database"
"sync"
)
// CollectionFactory is a single place to generate all desired collections
type CollectionFactory struct {
*sync.Mutex
db database.Storage
packages *PackageCollection
remoteRepos *RemoteRepoCollection
@@ -16,11 +18,14 @@ type CollectionFactory struct {
// NewCollectionFactory creates new factory
func NewCollectionFactory(db database.Storage) *CollectionFactory {
return &CollectionFactory{db: db}
return &CollectionFactory{Mutex: &sync.Mutex{}, db: db}
}
// PackageCollection returns (or creates) new PackageCollection
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
factory.Lock()
defer factory.Unlock()
if factory.packages == nil {
factory.packages = NewPackageCollection(factory.db)
}
@@ -30,6 +35,9 @@ func (factory *CollectionFactory) PackageCollection() *PackageCollection {
// RemoteRepoCollection returns (or creates) new RemoteRepoCollection
func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.remoteRepos == nil {
factory.remoteRepos = NewRemoteRepoCollection(factory.db)
}
@@ -39,6 +47,9 @@ func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
// SnapshotCollection returns (or creates) new SnapshotCollection
func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
factory.Lock()
defer factory.Unlock()
if factory.snapshots == nil {
factory.snapshots = NewSnapshotCollection(factory.db)
}
@@ -48,6 +59,9 @@ func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
// LocalRepoCollection returns (or creates) new LocalRepoCollection
func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.localRepos == nil {
factory.localRepos = NewLocalRepoCollection(factory.db)
}
@@ -57,9 +71,24 @@ func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
// PublishedRepoCollection returns (or creates) new PublishedRepoCollection
func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.publishedRepos == nil {
factory.publishedRepos = NewPublishedRepoCollection(factory.db)
}
return factory.publishedRepos
}
// Flush removes all references to collections, so that memory could be reclaimed
func (factory *CollectionFactory) Flush() {
factory.Lock()
defer factory.Unlock()
factory.localRepos = nil
factory.snapshots = nil
factory.remoteRepos = nil
factory.publishedRepos = nil
factory.packages = nil
}
+1 -1
View File
@@ -47,7 +47,7 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
}
if tarHeader.Name == "./control" {
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
reader := NewControlFileReader(untar)
stanza, err := reader.ReadStanza()
if err != nil {
+2 -1
View File
@@ -2,9 +2,10 @@ package deb
import (
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"path/filepath"
"runtime"
. "gopkg.in/check.v1"
)
type DebSuite struct {
+2 -1
View File
@@ -1,8 +1,9 @@
package deb
import (
. "launchpad.net/gocheck"
"testing"
. "gopkg.in/check.v1"
)
// Launch gocheck tests
+80 -4
View File
@@ -11,8 +11,76 @@ 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"}
// Taken from: http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/vivid/apt/vivid/view/head:/apt-pkg/tagfile.cc#L504
var (
canonicalOrderRelease = []string{
"Origin",
"Label",
"Archive",
"Suite",
"Version",
"Codename",
"Date",
"Architectures",
"Architecture",
"Components",
"Component",
"Description",
"MD5Sum",
"SHA1",
"SHA256",
}
canonicalOrderBinary = []string{
"Package",
"Essential",
"Status",
"Priority",
"Section",
"Installed-Size",
"Maintainer",
"Original-Maintainer",
"Architecture",
"Source",
"Version",
"Replaces",
"Provides",
"Depends",
"Pre-Depends",
"Recommends",
"Suggests",
"Conflicts",
"Breaks",
"Conffiles",
"Filename",
"Size",
"MD5Sum",
"MD5sum",
"SHA1",
"SHA256",
"Description",
}
canonicalOrderSource = []string{
"Package",
"Source",
"Binary",
"Version",
"Priority",
"Section",
"Maintainer",
"Original-Maintainer",
"Build-Depends",
"Build-Depends-Indep",
"Build-Conflicts",
"Build-Conflicts-Indep",
"Architecture",
"Standards-Version",
"Format",
"Directory",
"Files",
}
)
// Copy returns copy of Stanza
func (s Stanza) Copy() (result Stanza) {
@@ -40,8 +108,16 @@ func writeField(w *bufio.Writer, field, value string) (err error) {
}
// WriteTo saves stanza back to stream, modifying itself on the fly
func (s Stanza) WriteTo(w *bufio.Writer) error {
for _, field := range canocialOrder {
func (s Stanza) WriteTo(w *bufio.Writer, isSource, isRelease bool) error {
canonicalOrder := canonicalOrderBinary
if isSource {
canonicalOrder = canonicalOrderSource
}
if isRelease {
canonicalOrder = canonicalOrderRelease
}
for _, field := range canonicalOrder {
value, ok := s[field]
if ok {
delete(s, field)
+3 -2
View File
@@ -3,8 +3,9 @@ package deb
import (
"bufio"
"bytes"
. "launchpad.net/gocheck"
"strings"
. "gopkg.in/check.v1"
)
type ControlFileSuite struct {
@@ -107,7 +108,7 @@ func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
buf := &bytes.Buffer{}
w := bufio.NewWriter(buf)
err = stanza.Copy().WriteTo(w)
err = stanza.Copy().WriteTo(w, false, false)
c.Assert(err, IsNil)
err = w.Flush()
c.Assert(err, IsNil)
+120
View File
@@ -0,0 +1,120 @@
package deb
import (
"code.google.com/p/gographviz"
"fmt"
"strings"
)
// BuildGraph generates graph contents from aptly object database
func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, error) {
var err error
graph := gographviz.NewEscape()
graph.SetDir(true)
graph.SetName("aptly")
existingNodes := map[string]bool{}
err = collectionFactory.RemoteRepoCollection().ForEach(func(repo *RemoteRepo) error {
err := 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 nil, err
}
err = collectionFactory.LocalRepoCollection().ForEach(func(repo *LocalRepo) error {
err := 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 nil, err
}
collectionFactory.SnapshotCollection().ForEach(func(snapshot *Snapshot) error {
existingNodes[snapshot.UUID] = true
return nil
})
err = collectionFactory.SnapshotCollection().ForEach(func(snapshot *Snapshot) error {
err := 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 nil, err
}
collectionFactory.PublishedRepoCollection().ForEach(func(repo *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
})
return graph, nil
}
+163
View File
@@ -0,0 +1,163 @@
package deb
import (
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"os"
"path/filepath"
"sort"
"strings"
)
// CollectPackageFiles walks filesystem collecting all candidates for package files
func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, failedFiles []string, err error) {
for _, location := range locations {
info, err2 := os.Stat(location)
if err2 != nil {
reporter.Warning("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 {
reporter.Warning("Unknown file extension: %s", location)
failedFiles = append(failedFiles, location)
continue
}
}
}
sort.Strings(packageFiles)
return
}
// ImportPackageFiles imports files into local repository
func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace bool, verifier utils.Verifier,
pool aptly.PackagePool, collection *PackageCollection, reporter aptly.ResultReporter) (processedFiles []string, failedFiles []string, err error) {
if forceReplace {
list.PrepareIndex()
}
for _, file := range packageFiles {
var (
stanza Stanza
p *Package
)
candidateProcessedFiles := []string{}
isSourcePackage := strings.HasSuffix(file, ".dsc")
isUdebPackage := strings.HasSuffix(file, ".udeb")
if isSourcePackage {
stanza, err = GetControlFileFromDsc(file, verifier)
if err == nil {
stanza["Package"] = stanza["Source"]
delete(stanza, "Source")
p, err = NewSourcePackageFromControlFile(stanza)
}
} else {
stanza, err = GetControlFileFromDeb(file)
if isUdebPackage {
p = NewUdebPackageFromControlFile(stanza)
} else {
p = NewPackageFromControlFile(stanza)
}
}
if err != nil {
reporter.Warning("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 nil, nil, err
}
if isSourcePackage {
p.UpdateFiles(append(p.Files(), PackageFile{Filename: filepath.Base(file), Checksums: checksums}))
} else {
p.UpdateFiles([]PackageFile{{Filename: filepath.Base(file), Checksums: checksums}})
}
err = pool.Import(file, checksums.MD5)
if err != nil {
reporter.Warning("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 = pool.Import(sourceFile, f.Checksums.MD5)
if err != nil {
reporter.Warning("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 = collection.Update(p)
if err != nil {
reporter.Warning("Unable to save package %s: %s", p, err)
failedFiles = append(failedFiles, file)
continue
}
if forceReplace {
conflictingPackages := list.Search(Dependency{Pkg: p.Name, Version: p.Version, Relation: VersionEqual, Architecture: p.Architecture}, true)
for _, cp := range conflictingPackages {
reporter.Removed("%s removed due to conflict with package being added", cp)
list.Remove(cp)
}
}
err = list.Add(p)
if err != nil {
reporter.Warning("Unable to add package to repo %s: %s", p, err)
failedFiles = append(failedFiles, file)
continue
}
reporter.Added("%s added", p)
processedFiles = append(processedFiles, candidateProcessedFiles...)
}
err = nil
return
}
+260
View File
@@ -0,0 +1,260 @@
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 {
if arch == "source" {
udeb = false
}
key := fmt.Sprintf("pi-%s-%s-%v", 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 {
if arch == "source" {
udeb = false
}
key := fmt.Sprintf("ri-%s-%s-%v", 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
}
+137 -86
View File
@@ -5,7 +5,6 @@ import (
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
// Dependency options
@@ -39,9 +38,15 @@ type PackageList struct {
providesIndex map[string][]*Package
}
// PackageConflictError means that package can't be added to the list due to error
type PackageConflictError struct {
error
}
// Verify interface
var (
_ sort.Interface = &PackageList{}
_ PackageCatalog = &PackageList{}
)
// NewPackageList creates empty package list
@@ -86,11 +91,11 @@ func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageColle
// Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error {
key := string(p.Key(""))
key := string(p.ShortKey(""))
existing, ok := l.packages[key]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
return &PackageConflictError{fmt.Errorf("conflict in package %s", p)}
}
return nil
}
@@ -101,7 +106,7 @@ func (l *PackageList) Add(p *Package) error {
l.providesIndex[provides] = append(l.providesIndex[provides], p)
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= p.Name })
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)
@@ -123,6 +128,22 @@ func (l *PackageList) ForEach(handler func(*Package) error) error {
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)
@@ -137,7 +158,7 @@ func (l *PackageList) Append(pl *PackageList) error {
existing, ok := l.packages[k]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
return fmt.Errorf("conflict in package %s", p)
}
} else {
l.packages[k] = p
@@ -149,7 +170,7 @@ func (l *PackageList) Append(pl *PackageList) error {
// Remove removes package from the list, and updates index when required
func (l *PackageList) Remove(p *Package) {
delete(l.packages, string(p.Key("")))
delete(l.packages, string(p.ShortKey("")))
if l.indexed {
for _, provides := range p.Provides {
for i, pkg := range l.providesIndex[provides] {
@@ -189,6 +210,19 @@ func (l *PackageList) Architectures(includeSource bool) (result []string) {
return
}
// Strings builds list of strings with package keys
func (l *PackageList) Strings() []string {
result := make([]string, l.Len())
i := 0
for _, p := range l.packages {
result[i] = string(p.Key(""))
i += 1
}
return result
}
// depSliceDeduplicate removes dups in slice of Dependencies
func depSliceDeduplicate(s []Dependency) []Dependency {
l := len(s)
@@ -220,6 +254,7 @@ func depSliceDeduplicate(s []Dependency) []Dependency {
//
// 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 {
@@ -229,7 +264,7 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packages {
for _, p := range l.packagesIndex {
if progress != nil {
progress.AddBar(1)
}
@@ -247,7 +282,6 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
variants = depSliceDeduplicate(variants)
variantsMissing := make([]Dependency, 0, len(variants))
missingCount := 0
for _, dep := range variants {
if dep.Architecture == "" {
@@ -255,35 +289,23 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
}
hash := dep.Hash()
r, ok := cache[hash]
if ok {
if !r {
missingCount++
}
continue
satisfied, ok := cache[hash]
if !ok {
satisfied = sources.Search(dep, false) != nil
cache[hash] = satisfied
}
if sources.Search(dep) == nil {
if !satisfied && !ok {
variantsMissing = append(variantsMissing, dep)
missingCount++
} else {
cache[hash] = true
}
if satisfied && options&DepFollowAllVariants == 0 {
variantsMissing = nil
break
}
}
if options&DepFollowAllVariants == DepFollowAllVariants {
missing = append(missing, variantsMissing...)
for _, dep := range variantsMissing {
cache[dep.Hash()] = false
}
} else {
if missingCount == len(variants) {
missing = append(missing, variantsMissing...)
for _, dep := range variantsMissing {
cache[dep.Hash()] = false
}
}
}
missing = append(missing, variantsMissing...)
}
}
}
@@ -300,13 +322,29 @@ func (l *PackageList) Swap(i, j int) {
l.packagesIndex[i], l.packagesIndex[j] = l.packagesIndex[j], l.packagesIndex[i]
}
// Compare compares two names in lexographical order
func (l *PackageList) 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.packagesIndex[i].Name < l.packagesIndex[j].Name
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)
@@ -325,16 +363,49 @@ func (l *PackageList) PrepareIndex() {
l.indexed = true
}
// Search searches package index for specified package
func (l *PackageList) Search(dep Dependency) *Package {
// 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 p.MatchesArchitecture(dep.Architecture) {
return p
if dep.Architecture == "" || p.MatchesArchitecture(dep.Architecture) {
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
}
}
@@ -344,16 +415,21 @@ func (l *PackageList) Search(dep Dependency) *Package {
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
p := l.packagesIndex[i]
if p.MatchesDependency(dep) {
return p
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
i++
}
return nil
return
}
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
func (l *PackageList) Filter(queries []string, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
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")
}
@@ -361,53 +437,17 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
result := NewPackageList()
for _, query := range queries {
isDepQuery := strings.IndexAny(query, " (){}=<>") != -1
if !isDepQuery {
// try to interpret query as package string representation
// convert Package.String() to Package.Key()
i := strings.Index(query, "_")
if i != -1 {
pkg, query := query[:i], query[i+1:]
j := strings.LastIndex(query, "_")
if j != -1 {
version, arch := query[:j], query[j+1:]
p := l.packages["P"+arch+" "+pkg+" "+version]
if p != nil {
result.Add(p)
continue
}
}
}
}
// try as dependency
dep, err := ParseDependency(query)
if err != nil {
if isDepQuery {
return nil, err
}
// parsing failed, but probably that wasn't a dep query
continue
}
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) {
result.Add(p)
}
i++
}
result.Append(query.Query(l))
}
if withDependencies {
added := result.Len()
result.PrepareIndex()
dependencySource := NewPackageList()
dependencySource.Append(source)
if source != nil {
dependencySource.Append(source)
}
dependencySource.Append(result)
dependencySource.PrepareIndex()
@@ -423,11 +463,22 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
// try to satisfy dependencies
for _, dep := range missing {
p := l.Search(dep)
if p != nil {
result.Add(p)
dependencySource.Add(p)
added++
// 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
}
}
}
}
}
+173 -50
View File
@@ -2,11 +2,49 @@ package deb
import (
"errors"
. "launchpad.net/gocheck"
"regexp"
"sort"
"strings"
. "gopkg.in/check.v1"
)
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
@@ -14,8 +52,10 @@ type PackageListSuite struct {
// Mocked packages in list
packages []*Package
packages2 []*Package
sourcePackages []*Package
il *PackageList
il2 *PackageList
}
var _ = Suite(&PackageListSuite{})
@@ -29,7 +69,7 @@ func (s *PackageListSuite) SetUpTest(c *C) {
stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Size"] = "42"
stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers"
@@ -40,31 +80,45 @@ func (s *PackageListSuite) SetUpTest(c *C) {
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{}},
{Name: "lib", Version: "1.0", Architecture: "i386", Source: "lib (0.9)", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}}},
{Name: "dpkg", Version: "1.7", Architecture: "i386", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
{Name: "data", Version: "1.1~bp1", Architecture: "all", Source: "app", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
{Name: "app", Version: "1.1~bp1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
{Name: "mailer", Version: "3.5.8", Architecture: "i386", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
{Name: "app", Version: "1.1~bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
{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"}}},
{Name: "app", Version: "1.0", Architecture: "s390", deps: &PackageDependencies{PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
{Name: "aa", Version: "2.0-1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
{Name: "libx", Version: "1.5", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
{Name: "dpkg", Version: "1.6.1-3", Architecture: "arm", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
{Name: "dpkg", Version: "1.6.1-3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
{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{
{Name: "mailer", Version: "3.5.8", Architecture: "amd64", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
{Name: "sendmail", Version: "1.0", Architecture: "amd64", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
{Name: "app", Version: "1.1-bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
{Name: "app", Version: "1.1-bp2", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
{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"}}},
{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{}},
{Name: "postfix", Version: "1.3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
{Name: "app", Version: "1.1~bp1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
{Name: "aa", Version: "2.0-1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
{Name: "lib", Version: "0.9", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
}
}
@@ -196,35 +250,60 @@ func (s *PackageListSuite) TestAppend(c *C) {
}
func (s *PackageListSuite) TestSearch(c *C) {
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}) }, Panics, "list not indexed, can't search")
//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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}), Equals, s.packages[4])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}, 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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: 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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: 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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: 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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: 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"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}), IsNil)
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([]string{"abcd_0.3_i386"}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
_, err := s.il.Filter([]string{"app >3)"}, false, nil, 0, nil)
c.Check(err, ErrorMatches, "unable to parse dependency.*")
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())
@@ -237,25 +316,69 @@ func (s *PackageListSuite) TestFilter(c *C) {
return strings.Join(list, " ")
}
result, err := s.il.Filter([]string{"app_1.1~bp1_i386"}, false, nil, 0, nil)
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([]string{"app_1.1~bp1_i386", "dpkg_1.7_source", "dpkg_1.8_amd64"}, false, nil, 0, nil)
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([]string{"app", "dpkg (>>1.6.1-3)", "app (>=1.0)", "xyz", "aa (>>3.0)"}, false, nil, 0, nil)
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([]string{"app {i386}"}, true, NewPackageList(), 0, []string{"i386"})
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([]string{"app (>=0.9)", "lib", "data"}, true, NewPackageList(), 0, []string{"i386", "amd64"})
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) {
@@ -265,7 +388,7 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
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"}})
c.Check(missing, DeepEquals, []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)
@@ -273,8 +396,8 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
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"}})
c.Check(missing, DeepEquals, []Dependency{{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "arm"},
{Pkg: "mail-agent", Relation: VersionDontCare, Version: "", Architecture: "arm"}})
for _, p := range s.sourcePackages {
s.il.Add(p)
@@ -282,11 +405,11 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
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"}})
c.Check(missing, DeepEquals, []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"}})
c.Check(missing, DeepEquals, []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:.*")
+6 -3
View File
@@ -7,12 +7,13 @@ import (
"github.com/smira/aptly/database"
"github.com/ugorji/go/codec"
"log"
"sync"
)
// LocalRepo is a collection of packages created locally
type LocalRepo struct {
// Permanent internal ID
UUID string
UUID string `json:"-"`
// User-assigned name
Name string
// Comment
@@ -88,6 +89,7 @@ func (repo *LocalRepo) RefKey() []byte {
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
type LocalRepoCollection struct {
*sync.RWMutex
db database.Storage
list []*LocalRepo
}
@@ -95,7 +97,8 @@ type LocalRepoCollection struct {
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
result := &LocalRepoCollection{
db: db,
RWMutex: &sync.RWMutex{},
db: db,
}
blobs := db.FetchByPrefix([]byte("L"))
@@ -104,7 +107,7 @@ func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
for _, blob := range blobs {
r := &LocalRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding mirror: %s\n", err)
log.Printf("Error decoding repo: %s\n", err)
} else {
result.list = append(result.list, r)
}
+2 -1
View File
@@ -3,7 +3,8 @@ package deb
import (
"errors"
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
. "gopkg.in/check.v1"
)
type LocalRepoSuite struct {
+134 -10
View File
@@ -1,6 +1,7 @@
package deb
import (
"encoding/json"
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
@@ -24,8 +25,12 @@ type Package struct {
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
@@ -34,6 +39,11 @@ type Package struct {
collection *PackageCollection
}
// Check interface
var (
_ json.Marshaler = &Package{}
)
// NewPackageFromControlFile creates Package from parsed Debian control file
func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{
@@ -41,6 +51,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
Version: input["Version"],
Architecture: input["Architecture"],
Source: input["Source"],
V06Plus: true,
}
delete(input, "Package")
@@ -50,12 +61,18 @@ func NewPackageFromControlFile(input Stanza) *Package {
filesize, _ := strconv.ParseInt(input["Size"], 10, 64)
md5, ok := input["MD5sum"]
if !ok {
// there are some broken repos out there with MD5 in wrong field
md5 = input["MD5Sum"]
}
result.UpdateFiles(PackageFiles{PackageFile{
Filename: filepath.Base(input["Filename"]),
downloadPath: filepath.Dir(input["Filename"]),
Checksums: utils.ChecksumInfo{
Size: filesize,
MD5: strings.TrimSpace(input["MD5sum"]),
MD5: strings.TrimSpace(md5),
SHA1: strings.TrimSpace(input["SHA1"]),
SHA256: strings.TrimSpace(input["SHA256"]),
},
@@ -63,6 +80,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
delete(input, "Filename")
delete(input, "MD5sum")
delete(input, "MD5Sum")
delete(input, "SHA1")
delete(input, "SHA256")
delete(input, "Size")
@@ -89,6 +107,7 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
Version: input["Version"],
Architecture: "source",
SourceArchitecture: input["Architecture"],
V06Plus: true,
}
delete(input, "Package")
@@ -165,9 +184,26 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
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 {
return []byte(prefix + "P" + p.Architecture + " " + p.Name + " " + p.Version)
if p.V06Plus {
return []byte(fmt.Sprintf("%sP%s %s %s %08x", prefix, p.Architecture, p.Name, p.Version, p.FilesHash))
}
return p.ShortKey(prefix)
}
// ShortKey returns key for the package that should be unique in one list
func (p *Package) ShortKey(prefix string) []byte {
return []byte(fmt.Sprintf("%sP%s %s %s", prefix, p.Architecture, p.Name, p.Version))
}
// String creates readable representation
@@ -175,6 +211,82 @@ func (p *Package) String() string {
return fmt.Sprintf("%s_%s_%s", p.Name, p.Version, p.Architecture)
}
// MarshalJSON implements json.Marshaller interface
func (p *Package) MarshalJSON() ([]byte, error) {
stanza := p.Stanza()
stanza["FilesHash"] = fmt.Sprintf("%08x", p.FilesHash)
stanza["Key"] = string(p.Key(""))
stanza["ShortKey"] = string(p.ShortKey(""))
return json.Marshal(stanza)
}
// 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]
}
}
// MatchesArchitecture checks whether packages matches specified architecture
func (p *Package) MatchesArchitecture(arch string) bool {
if p.Architecture == "all" && arch != "source" {
@@ -186,19 +298,23 @@ func (p *Package) MatchesArchitecture(arch string) bool {
// MatchesDependency checks whether package matches specified dependency
func (p *Package) MatchesDependency(dep Dependency) bool {
if dep.Pkg != p.Name {
return false
}
if dep.Architecture != "" && !p.MatchesArchitecture(dep.Architecture) {
return false
}
if dep.Relation == VersionDontCare {
return true
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
@@ -210,6 +326,11 @@ func (p *Package) MatchesDependency(dep Dependency) bool {
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")
@@ -305,7 +426,9 @@ func (p *Package) Stanza() (result Stanza) {
result["Architecture"] = p.SourceArchitecture
} else {
result["Architecture"] = p.Architecture
result["Source"] = p.Source
if p.Source != "" {
result["Source"] = p.Source
}
}
if p.IsSource {
@@ -376,7 +499,8 @@ func (p *Package) Equals(p2 *Package) bool {
}
// LinkFromPool links package file from pool to dist's pool location
func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packagePool aptly.PackagePool, prefix string, component string) error {
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
@@ -391,7 +515,7 @@ func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packageP
relPath := filepath.Join("pool", component, poolDir)
publishedDirectory := filepath.Join(prefix, relPath)
err = publishedStorage.LinkFromPool(publishedDirectory, packagePool, sourcePath)
err = publishedStorage.LinkFromPool(publishedDirectory, packagePool, sourcePath, f.Checksums.MD5, force)
if err != nil {
return err
}
+61 -27
View File
@@ -12,12 +12,19 @@ import (
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,
db: db,
codecHandle: &codec.MsgpackHandle{},
}
}
@@ -53,7 +60,7 @@ func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
if len(encoded) > 2 && (encoded[0] != 0xc1 || encoded[1] != 0x1) {
oldp := &oldPackage{}
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(oldp)
if err != nil {
return nil, err
@@ -83,12 +90,12 @@ func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
p.UpdateFiles(PackageFiles(oldp.Files))
// Save in new format
err = collection.internalUpdate(p)
err = collection.Update(p)
if err != nil {
return nil, err
}
} else {
decoder := codec.NewDecoderBytes(encoded[2:], &codec.MsgpackHandle{})
decoder := codec.NewDecoderBytes(encoded[2:], collection.codecHandle)
err = decoder.Decode(p)
if err != nil {
return nil, err
@@ -109,7 +116,7 @@ func (collection *PackageCollection) loadExtra(p *Package) *Stanza {
stanza := &Stanza{}
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(stanza)
if err != nil {
panic("unable to decode extra")
@@ -127,7 +134,7 @@ func (collection *PackageCollection) loadDependencies(p *Package) *PackageDepend
deps := &PackageDependencies{}
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(deps)
if err != nil {
panic("unable to decode deps")
@@ -145,7 +152,7 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
files := &PackageFiles{}
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
err = decoder.Decode(files)
if err != nil {
panic("unable to decode files")
@@ -156,26 +163,7 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
// Update adds or updates information about package in DB checking for conficts first
func (collection *PackageCollection) Update(p *Package) error {
existing, err := collection.ByKey(p.Key(""))
if err == nil {
// if .Files is different, consider to be conflict
if p.FilesHash != existing.FilesHash {
return fmt.Errorf("unable to save: %s, conflict with existing packge", p)
}
// ok, .Files are the same, but maybe some meta-data is different, proceed to saving
} else {
if err != database.ErrNotFound {
return err
}
// ok, package doesn't exist yet
}
return collection.internalUpdate(p)
}
// internalUpdate updates information in DB about package and offloaded fields
func (collection *PackageCollection) internalUpdate(p *Package) error {
encoder := codec.NewEncoder(&collection.encodeBuffer, &codec.MsgpackHandle{})
encoder := codec.NewEncoder(&collection.encodeBuffer, collection.codecHandle)
collection.encodeBuffer.Reset()
collection.encodeBuffer.WriteByte(0xc1)
@@ -254,3 +242,49 @@ func (collection *PackageCollection) DeleteByKey(key []byte) error {
}
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
}
+2 -15
View File
@@ -3,7 +3,8 @@ package deb
import (
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
. "gopkg.in/check.v1"
)
type PackageCollectionSuite struct {
@@ -48,20 +49,6 @@ func (s *PackageCollectionSuite) TestUpdate(c *C) {
c.Assert(err, IsNil)
c.Assert(res.Equals(s.p), Equals, false)
c.Assert(res.Equals(p2), Equals, true)
// change file info
p2 = NewPackageFromControlFile(packageStanza.Copy())
p2.UpdateFiles(nil)
res, err = s.collection.ByKey(p2.Key(""))
err = s.collection.Update(p2)
c.Assert(err, ErrorMatches, ".*conflict with existing packge")
p2 = NewPackageFromControlFile(packageStanza.Copy())
files := p2.Files()
files[0].Checksums.MD5 = "abcdef"
p2.UpdateFiles(files)
res, err = s.collection.ByKey(p2.Key(""))
err = s.collection.Update(p2)
c.Assert(err, ErrorMatches, ".*conflict with existing packge")
}
func (s *PackageCollectionSuite) TestByKey(c *C) {
+2 -1
View File
@@ -3,9 +3,10 @@ package deb
import (
"github.com/smira/aptly/files"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"os"
"path/filepath"
. "gopkg.in/check.v1"
)
type PackageFilesSuite struct {
+136 -4
View File
@@ -4,9 +4,11 @@ import (
"bytes"
"github.com/smira/aptly/files"
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
"os"
"path/filepath"
"regexp"
. "gopkg.in/check.v1"
)
type PackageSuite struct {
@@ -27,6 +29,7 @@ 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")
@@ -39,11 +42,27 @@ func (s *PackageSuite) TestNewFromPara(c *C) {
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")
@@ -87,10 +106,21 @@ func (s *PackageSuite) TestWithProvides(c *C) {
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()
@@ -108,6 +138,65 @@ func (s *PackageSuite) TestString(c *C) {
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)
@@ -163,6 +252,9 @@ func (s *PackageSuite) TestMatchesDependency(c *C) {
// 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)
@@ -193,6 +285,29 @@ func (s *PackageSuite) TestMatchesDependency(c *C) {
// <=
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) {
@@ -255,13 +370,13 @@ func (s *PackageSuite) TestLinkFromPool(c *C) {
c.Assert(err, IsNil)
file.Close()
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free")
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")
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free", false)
c.Check(err, IsNil)
c.Check(p.Extra()["Directory"], Equals, "pool/non-free/a/alien-arena")
}
@@ -284,7 +399,7 @@ func (s *PackageSuite) TestDownloadList(c *C) {
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,
@@ -365,3 +480,20 @@ 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`
+2 -1
View File
@@ -2,7 +2,8 @@ package deb
import (
"github.com/smira/aptly/utils"
. "launchpad.net/gocheck"
. "gopkg.in/check.v1"
)
type PpaSuite struct {
+617 -350
View File
File diff suppressed because it is too large Load Diff

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