Compare commits

..

892 Commits

Author SHA1 Message Date
Andrey Smirnov 7a4feebe6f Version bump to 0.9.5. 2015-03-13 13:26:39 +03:00
Andrey Smirnov 1d1561c6c3 Add missing files. 2015-03-12 00:50:27 +03:00
Andrey Smirnov 9a5b3aeedc System test. #193 2015-03-11 23:38:16 +03:00
Andrey Smirnov ed931e7ed4 Fix unit-tests. #153 2015-03-11 23:29:07 +03:00
Andrey Smirnov 5ff9cecc5a Regenerate man page. #153 2015-03-11 22:20:43 +03:00
Andrey Smirnov f8bca463bb Add -force-drop to aptly publish drop, ?force=1 to DELETE publish/... to drop
published repositories even if cleanup fails. #153
2015-03-11 22:02:11 +03:00
Andrey Smirnov d5c6f0b623 Collect and report unused package reference sources. #217 2015-03-11 21:40:53 +03:00
Andrey Smirnov 7e57f443ed Style fix. #193 2015-03-11 21:40:04 +03:00
Andrey Smirnov b4cf2e7065 Canonical case fixes. #193 2015-03-11 01:25:54 +03:00
Andrey Smirnov 2ceabb69e6 Remove extra \n, system tests. #217 2015-03-11 01:22:49 +03:00
Andrey Smirnov aa9d3360ba Canonical case-folding for Debian stanzas. #193 2015-03-11 00:46:39 +03:00
Andrey Smirnov 4580a64192 Make import skip file if Name/Version/Arch is empty. #193 2015-03-11 00:34:48 +03:00
Andrey Smirnov 4cb0526980 Commands * search should exit with failure on no results. #213 2015-03-11 00:31:53 +03:00
Andrey Smirnov 03e2a8d558 Regenerate man page. #217 2015-03-11 00:17:08 +03:00
Andrey Smirnov ab09cbfe3c Add -verbose and -dry-run to aptly db cleanup. #217 2015-03-11 00:16:23 +03:00
Andrey Smirnov 0467e0c929 More sophisticated color codes stripper. #217 2015-03-11 00:15:56 +03:00
Andrey Smirnov 6e1c9afdd9 Bump version to 0.9.5~dev. 2015-03-09 22:26:25 +03:00
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
Andrey Smirnov b5d025f141 Version bump for 0.5.1. 2014-05-10 18:10:19 +04:00
Andrey Smirnov 3c7a2281b2 Fix "production" libraries in Gomfile with exact SHA. 2014-05-10 18:08:12 +04:00
Andrey Smirnov be3ad21fbe Add target to build "all-in-one" source tarball for Debian. 2014-05-10 18:06:23 +04:00
Andrey Smirnov 5301e8a341 Fix pool directory for packages with version in Source: field. #44 2014-05-10 17:25:44 +04:00
Andrey Smirnov 49eed59238 Fix test on command line help. #47 2014-05-10 17:13:49 +04:00
Andrey Smirnov 3e78240b39 Remove debugging output. #47 2014-05-10 17:07:17 +04:00
Andrey Smirnov 10bbefeb25 Fix support for flat format repositories in subdirectories with common pool. #47 2014-05-10 16:56:50 +04:00
Andrey Smirnov 35eac72226 Missed file. #46 2014-05-10 13:35:06 +04:00
Andrey Smirnov 5371f94b7a Debian 7.5 has been released, update tests. 2014-05-10 13:34:46 +04:00
Andrey Smirnov 53adf39d89 Bring back automatic HTTP_PROXY setting from environment. #46 2014-05-10 13:27:01 +04:00
Andrey Smirnov bc7972ff68 Version bump for 0.6~dev. 2014-04-24 14:24:03 +04:00
Andrey Smirnov 21e8aa5519 Remove comments. 2014-04-24 01:31:31 +04:00
Andrey Smirnov f0825d93be aptly 0.5 2014-04-24 01:17:14 +04:00
Andrey Smirnov 9e538d9475 aptly version 0.5 2014-04-24 01:15:59 +04:00
Andrey Smirnov 042602f991 Update man page. #42 2014-04-24 01:03:15 +04:00
Andrey Smirnov e8a894bc88 More tests for merging. #42 2014-04-24 01:01:14 +04:00
Andrey Smirnov 59647fe6d0 Fix subtle bug in .Merge: if there are duplicate name-arch on the left, override them all. #42 2014-04-24 00:57:17 +04:00
Andrey Smirnov 37a6fb336a PackageList doesn't allow duplicates and PackageRefList does sorting on keys. #42 2014-04-23 23:47:32 +04:00
Andrey Smirnov 7c2faafa91 Simplify implementation (and improve performance) by using the fact that RefList is always sorted. #42 2014-04-23 23:46:33 +04:00
Andrey Smirnov 87295c6580 Move command description from man file to command help. #42
Man file is auto-generated from inline command help.

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

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

They're saved and loaded completely separately.
2014-03-04 16:49:26 +04:00
Andrey Smirnov 32e517b4f2 aptly no longer prints 'Saving packages to database.' 2014-03-04 16:48:37 +04:00
Andrey Smirnov 0894c41636 Use Package.UpdateFiles to touch package.Files 2014-03-04 16:48:23 +04:00
Andrey Smirnov 073374eb78 gnuplot script. 2014-03-03 20:59:37 +04:00
Andrey Smirnov d0c3659679 Gnuplot mem.dat to PNG. 2014-03-03 16:15:50 +04:00
Andrey Smirnov e055581e34 Dump correct memory stats. 2014-03-03 16:15:36 +04:00
Andrey Smirnov 735593fc84 Fix tests. 2014-03-01 23:13:07 +04:00
Andrey Smirnov 7d6387e78e Add debugging facilities. 2014-03-01 21:32:04 +04:00
Andrey Smirnov c554a5c7df Fix command help. 2014-02-28 18:04:52 +04:00
Andrey Smirnov c6e4239a22 Remove files only if whole package has been processed successfully. 2014-02-28 11:24:11 +04:00
Andrey Smirnov e881a6df00 Fix command help. [ci skip] 2014-02-27 23:51:01 +04:00
Andrey Smirnov d251a519b6 Fix race in Progress shutdown where some messages could have been lost. 2014-02-27 22:52:11 +04:00
Andrey Smirnov 268128482c Fix system tests. 2014-02-27 22:01:24 +04:00
Andrey Smirnov 91a45a2fde Display deb-src line if snapshot contains sources. 2014-02-27 21:10:52 +04:00
Andrey Smirnov 9e26207659 System test for publishing local repos. 2014-02-27 21:10:40 +04:00
Andrey Smirnov f1f008f2f5 System tests for aptly repo import. 2014-02-27 20:57:30 +04:00
Andrey Smirnov abe9a37408 System tests for aptly repo move|copy. 2014-02-27 18:06:13 +04:00
Andrey Smirnov a45d2f3ce9 Test on snapshot listing with local repo. 2014-02-27 17:48:32 +04:00
Andrey Smirnov a08cbc2edc Commands aptly repo move, copy, import. 2014-02-27 17:10:28 +04:00
Andrey Smirnov 0549db6833 System tests for aptly repo remove. 2014-02-27 16:22:18 +04:00
Andrey Smirnov 07cf61a641 Command aptly repo remove. 2014-02-27 16:14:54 +04:00
Andrey Smirnov d6dec91c93 List filtering with dependency resolution. 2014-02-27 15:54:17 +04:00
Andrey Smirnov f430e78a2b Fix system tests after Package.String() has been changed. 2014-02-27 13:15:28 +04:00
Andrey Smirnov 774d1d9aab Extract MatchDependency, change Package.String(). 2014-02-27 12:48:13 +04:00
Andrey Smirnov 03100c28ce Fix typo. 2014-02-26 23:51:13 +04:00
Andrey Smirnov 1ae8063873 Cleanup after local repo. 2014-02-26 23:50:49 +04:00
Andrey Smirnov 85f3d15e8c System tests for aptly repo drop. 2014-02-26 22:14:18 +04:00
Andrey Smirnov 565e82bd1a Command aptly repo drop. 2014-02-26 22:07:51 +04:00
Andrey Smirnov 4df80d38cf SnapshotCollection.ByLocalRepoSource method. 2014-02-26 21:30:14 +04:00
Andrey Smirnov eff530a284 aptly db cleanup test with local repos. 2014-02-26 21:21:44 +04:00
Andrey Smirnov df811ff36a Tests for aptly snapshot create from repo 2014-02-26 21:17:10 +04:00
Andrey Smirnov f19a334b2b aptly snapshot create from repo command. 2014-02-26 21:03:04 +04:00
Andrey Smirnov 544724e59e Include local repos into graph. 2014-02-26 21:02:51 +04:00
Andrey Smirnov 059abc465b Creating snapshots from local repos. 2014-02-26 21:02:34 +04:00
Andrey Smirnov 2f30cf0846 Don't draw edges on graph to non-existing entities. 2014-02-26 19:39:19 +04:00
Andrey Smirnov f643d3f04d Use original version of gom as pull requests has been merged. 2014-02-26 19:36:37 +04:00
Andrey Smirnov c7d8772b9b Use my fixed version of gom for now. 2014-02-26 17:06:27 +04:00
Andrey Smirnov 5e97e18d9d Add blank line to Gomfile. 2014-02-26 16:49:04 +04:00
Andrey Smirnov d65ed73a8a Use explicit path to gom for Travis. 2014-02-26 16:29:18 +04:00
Andrey Smirnov 10e1a85fb8 Install gom first. 2014-02-26 16:25:56 +04:00
Andrey Smirnov 7c26c3ac14 Fix all dependencies in Gomfile. 2014-02-26 16:24:54 +04:00
Andrey Smirnov 8ebc3e65a5 Fix test depending on $HOME. 2014-02-26 13:42:36 +04:00
Andrey Smirnov 653a7d8d5a System tests for aptly repo add.. 2014-02-26 13:08:55 +04:00
Andrey Smirnov 3ddf39ee58 Sort files before import, really stop importing on file problem. 2014-02-26 13:08:39 +04:00
Andrey Smirnov e0cb43fbd3 Style fixes. 2014-02-25 14:25:14 +04:00
Andrey Smirnov 70df28bdf4 New command: aptly repo add to add packages to repository. 2014-02-25 14:16:25 +04:00
Andrey Smirnov ee62dd34f7 Refactoring: Filename now contains only base path, any order of files is accepted. 2014-02-25 14:15:16 +04:00
Andrey Smirnov e94bca2733 Use different apt repository as test mirror, ppa.launchpad.net is too unstable. 2014-02-25 14:13:27 +04:00
Andrey Smirnov 943d089e7e Allow fails on go tip. 2014-02-25 13:29:19 +04:00
Andrey Smirnov da550188de Build on Go tip as well. 2014-02-25 13:25:04 +04:00
Andrey Smirnov 901ce3ed00 Add final / to archive URL if missing. 2014-02-25 13:24:51 +04:00
Andrey Smirnov 00a9e25706 Support colored printf (with non-colored output when not on terminal) in Progress. 2014-02-25 13:17:23 +04:00
Andrey Smirnov 98bd76f350 Fix snapshot system tests. 2014-02-25 12:34:01 +04:00
Andrey Smirnov 750a947479 Fix some system tests. 2014-02-25 12:12:49 +04:00
Andrey Smirnov 1400b45d9d Fix PackageCollection.Update to detect conflicts and skip updates when it's not necessary. 2014-02-25 00:40:32 +04:00
Andrey Smirnov e36971fdc3 Fix system tests. 2014-02-25 00:12:01 +04:00
Andrey Smirnov 8328c44c39 Use progress to output message to fix order of messages on screen. 2014-02-25 00:11:44 +04:00
Andrey Smirnov c06e69a485 In db cleanup include local repos. 2014-02-24 23:46:11 +04:00
Andrey Smirnov 1b10c87bad Updating reflist in local repo. 2014-02-24 23:45:47 +04:00
Andrey Smirnov 4277f09e2a Importing files into pool: interface. 2014-02-24 23:45:32 +04:00
Andrey Smirnov 65c790b6cf Importing files to package pool. 2014-02-24 23:45:17 +04:00
Andrey Smirnov 1b64612aef Handle corner-case: null reflist. 2014-02-24 12:17:24 +04:00
Andrey Smirnov 97becf199f Simplify iteration in LevelDB. 2014-02-24 12:05:51 +04:00
Andrey Smirnov 5f40e02a84 No second gpg verification for InRelease. 2014-02-23 13:49:56 +04:00
Andrey Smirnov e2067eab23 Bugfix: in cleanup ignore non-updated mirrors. 2014-02-23 13:44:45 +04:00
Andrey Smirnov 33c9c08632 Fixture example .dsc files. 2014-02-23 13:44:26 +04:00
Andrey Smirnov 1fe8a8b703 Refactor GPG clearsigned verification into extract + verification. 2014-02-23 13:44:03 +04:00
Andrey Smirnov a44742f6b8 Reading control files from .deb & .dsc files. 2014-02-23 13:20:37 +04:00
Andrey Smirnov 8951b4f42a Reading control file from .deb package. 2014-02-21 20:42:25 +04:00
Andrey Smirnov f63b0dd315 Update README. [ci skip] 2014-02-21 15:32:28 +04:00
Andrey Smirnov 196dc56dd9 Command aptly repo list. 2014-02-20 17:04:06 +04:00
Andrey Smirnov 54421a9377 Command aptly repo show. 2014-02-20 16:39:58 +04:00
Andrey Smirnov 63cd4a80bb Command aptly repo create. 2014-02-20 12:01:41 +04:00
Andrey Smirnov 8df4378f4c Don't generate default comment for local repos. 2014-02-20 12:01:22 +04:00
Andrey Smirnov c3819d6724 Fix collection of coverage for all new packages. 2014-02-19 23:34:13 +04:00
Andrey Smirnov 7da203e8d2 Local repo: model + collection. 2014-02-19 23:29:52 +04:00
Andrey Smirnov 5617385c44 Fix comments. 2014-02-19 17:16:45 +04:00
Andrey Smirnov d43a15e658 Refactor snapshot module into subcommands. 2014-02-19 15:27:47 +04:00
Andrey Smirnov e72f178ca9 Split publish into subcommands. 2014-02-19 15:12:16 +04:00
Andrey Smirnov 66cf2fe53e Split mirror subcommands into files. 2014-02-19 15:07:56 +04:00
Andrey Smirnov ccff7935bd Fixes for refactoring to cmd. 2014-02-19 15:07:41 +04:00
Andrey Smirnov eb18b04c40 Refactor commands to subpackage. 2014-02-19 14:59:00 +04:00
Andrey Smirnov 35c2178074 Add docblocks to method description in interfaces. [ci skip] 2014-02-19 13:44:14 +04:00
Andrey Smirnov 2d589bd23d Refactoring: new packages console, http, Progress is interface. 2014-02-19 13:08:55 +04:00
Andrey Smirnov bd119dbfed Style fixes. 2014-02-19 12:54:19 +04:00
Andrey Smirnov d1e16a0ef0 Refactor Repository: split into PackagePool and PublishedStorage. 2014-02-19 12:03:01 +04:00
Andrey Smirnov 7864ce241b Refactoring: replace sort.StringSlice with simply []string 2014-02-18 14:13:18 +04:00
Andrey Smirnov 190a81e141 Use ${HOME} instead of hardcoded home dir. 2014-02-18 01:11:57 +04:00
Andrey Smirnov 19af0547e8 Style fixes [no ci] 2014-02-18 01:00:49 +04:00
Andrey Smirnov e51f226cdc aptly serve with deb-src system test. 2014-02-18 00:57:02 +04:00
Andrey Smirnov dccb6d10a9 aptly db cleanup with source packages 2014-02-18 00:54:46 +04:00
Andrey Smirnov 532c85eaa6 Test for publihsing snapshot with sources. 2014-02-18 00:49:17 +04:00
Andrey Smirnov 3942734eca Update test to treat empty source as source package name-version same as binary. 2014-02-18 00:30:35 +04:00
Andrey Smirnov 3b77d7f3c7 Test for pulling packages with source following. 2014-02-18 00:22:04 +04:00
Andrey Smirnov e9449a9b15 Support different formats of supplying Source: field. 2014-02-18 00:21:31 +04:00
Andrey Smirnov 841771c18e Sort architectures for stability of result, use package architecture for dependency search. 2014-02-18 00:18:55 +04:00
Andrey Smirnov d8fe97e0cb System tests for snapshots verification with sources. 2014-02-17 20:53:22 +04:00
Andrey Smirnov 2d1c6e5cf3 Package might specify its own source pkg version. 2014-02-17 20:53:08 +04:00
Andrey Smirnov 506987d31f Support version numbers like pkg (1.4) 2014-02-17 20:52:37 +04:00
Andrey Smirnov b8fd33a92c Add wheezy snapshots with sources. 2014-02-17 20:52:13 +04:00
Andrey Smirnov 812bc6e1e1 Updating mirrors with sources. 2014-02-17 16:16:55 +04:00
Andrey Smirnov 9b0bb17908 Creating mirror with sources, listing mirrors. 2014-02-17 15:59:14 +04:00
Andrey Smirnov e3ef4038b4 Add gnuplot-maverick with sources. 2014-02-17 15:51:08 +04:00
Andrey Smirnov f32c19047f Publishing source packages index. 2014-02-17 15:41:08 +04:00
Andrey Smirnov a0b1ff8abe Support for dependencies with fixed archs (like as source dependency). 2014-02-17 15:36:16 +04:00
Andrey Smirnov c2f01c8aa1 When requiring source package, specify exact version of binary package. 2014-02-17 15:35:48 +04:00
Andrey Smirnov b19e6cfadd Update system tests for new options. 2014-02-17 15:14:29 +04:00
Andrey Smirnov af2266d572 Enable following to source dependencies. 2014-02-17 15:09:16 +04:00
Andrey Smirnov 21123ac6a4 Add config option to follow dependency to source packages. 2014-02-17 15:08:21 +04:00
Andrey Smirnov c96491e873 Follow build & source dependencies from package. 2014-02-17 15:06:26 +04:00
Andrey Smirnov 7ab456f6ff Make decorator for architecture be in curly braces, so there's no conflict with Debian "only for arch". 2014-02-17 15:05:34 +04:00
Andrey Smirnov 5af0c45e10 Support for parsing dependencies with architecture like [amd64]. 2014-02-17 14:03:41 +04:00
Andrey Smirnov 09a1b60946 Add 'deb-src' line to repos with source packages included. 2014-02-17 13:29:19 +04:00
Andrey Smirnov 54ac38c56b Test for Directory regeneration. 2014-02-17 13:03:50 +04:00
Andrey Smirnov ba178b9863 Regenerate Directory: field for source packages while publishing. 2014-02-17 13:03:27 +04:00
Andrey Smirnov d999258744 "all" architecture doesn't match "source", fix for missing checksums while publishing. 2014-02-17 11:41:33 +04:00
Andrey Smirnov e8de4db522 Free up queue as soon as we don't need it anymore. 2014-02-16 23:18:24 +04:00
Andrey Smirnov 47b3f3ed6a Reset count to zero. 2014-02-16 22:08:01 +04:00
Andrey Smirnov 73e0d8c213 Attempt to lower memory pressure a bit. 2014-02-16 21:59:10 +04:00
Andrey Smirnov a0757aadd6 Fix test. 2014-02-16 21:36:56 +04:00
Andrey Smirnov ce793c6dae List of architectures now includes optionally "source" virtual arch. 2014-02-16 21:36:25 +04:00
Andrey Smirnov 5e8b6da1db Add whitespace back. 2014-02-16 21:21:45 +04:00
Andrey Smirnov 7b41df7049 Fix system test with addition of 'Download Sources' in mirror show. 2014-02-16 02:49:54 +04:00
Andrey Smirnov e4d8ef4744 Showing download sources argument in mirror show and mirror list. 2014-02-15 16:56:10 +04:00
Andrey Smirnov fb9b90e715 Tests for downloading with sources, flat and regular repos. 2014-02-15 16:33:00 +04:00
Andrey Smirnov 5fb512f86e Add ability to expect responses in any order. 2014-02-15 16:32:28 +04:00
Andrey Smirnov b2523b4215 --with-sources, downloading source packages flag. 2014-02-14 23:58:09 +04:00
Andrey Smirnov 519082a61e Remote repos with source packages option. 2014-02-14 23:45:14 +04:00
Andrey Smirnov 36446e46a1 Add config option 'downloadSourcePackages'. 2014-02-14 23:44:46 +04:00
Andrey Smirnov e6a2f27d47 Correctly prepend directory to filenames of source package files. 2014-02-14 23:33:33 +04:00
Andrey Smirnov 2b4dfe257e Fix comments. 2014-02-14 22:05:51 +04:00
Andrey Smirnov 22c427bb96 Parsing & generation of source package stanzas. 2014-02-14 22:04:48 +04:00
Andrey Smirnov 213c1e0b4f Sort HTTP output to fix unstable system test. 2014-02-13 13:01:56 +04:00
Andrey Smirnov ad2680aeba Use batched writes to DB when saving packages from the mirror. 2014-02-13 12:32:12 +04:00
Andrey Smirnov fb6df84ec7 Fix more system tests with GPG publishing. 2014-02-13 01:02:35 +04:00
Andrey Smirnov 796489e88d Don't compain about missing keys when using custom keyring. 2014-02-13 00:05:21 +04:00
Andrey Smirnov fb2e1adb5c Use different keyring in system tests. 2014-02-12 21:25:55 +04:00
Andrey Smirnov 5bf370e18a Add new flags for specifying GPG keyring manually when signing. 2014-02-12 21:25:33 +04:00
Andrey Smirnov ced832b1c0 System test for aptly db cleanup. 2014-02-12 20:33:16 +04:00
Andrey Smirnov d4fb5853cc Add fixture pool creation from GitHub for system tests. 2014-02-12 17:24:35 +04:00
Andrey Smirnov 2557c41d49 Fix repotoken submission for new version of goveralls. 2014-02-12 17:13:02 +04:00
Andrey Smirnov ce3ae80feb Fix system tests. 2014-02-12 16:14:24 +04:00
Andrey Smirnov 7ec8d80053 Add stats on disk space savings. 2014-02-12 16:06:53 +04:00
Andrey Smirnov bd89d7c62e Command aptly db cleanup. 2014-02-12 15:29:18 +04:00
Andrey Smirnov 62ea87dc6c Removing files from package pool. 2014-02-12 15:19:04 +04:00
Andrey Smirnov 130efaa350 Relative pool path and list of all filepaths. 2014-02-12 13:49:01 +04:00
Andrey Smirnov b5da3e9680 Deleting packages & building file list from packages. 2014-02-12 13:12:07 +04:00
Andrey Smirnov 974d30b837 StrSlice substraction. 2014-02-12 12:59:02 +04:00
Andrey Smirnov 8ae1f7aab0 Fix: progress bar sometimes doesn't disappear. 2014-02-12 11:56:42 +04:00
Andrey Smirnov 3ba7bc7943 Simplify test by using WriteFile. 2014-02-12 11:22:27 +04:00
Andrey Smirnov c30862dff9 Build all package refs. 2014-02-11 21:11:36 +04:00
Andrey Smirnov bff299d268 Batch writes/deletes in LevelDB. 2014-02-11 21:11:15 +04:00
Andrey Smirnov 6b02b18c8e Substraction of PackageRefLists. 2014-02-11 18:52:30 +04:00
Andrey Smirnov 7be4404a87 Fix call to Merge with new parameter. 2014-02-11 17:33:39 +04:00
Andrey Smirnov 94616e1b06 Diffing without overrideMatching. 2014-02-11 17:26:18 +04:00
Andrey Smirnov 8d72f1a959 Database: return list of keys by prefix. 2014-02-11 17:21:35 +04:00
Andrey Smirnov aea8ae9a96 Bump version to 0.4~dev. 2014-02-11 11:28:11 +04:00
736 changed files with 876334 additions and 460801 deletions
+6
View File
@@ -26,3 +26,9 @@ coverage.html
coverage*.out coverage*.out
*.pyc *.pyc
_vendor/
gen
man/aptly.1.html
man/aptly.1.ronn
+23 -3
View File
@@ -1,15 +1,35 @@
language: go language: go
go: go:
- 1.1 - 1.3.3
- 1.2 - tip
env: env:
global: global:
- secure: "YSwtFrMqh4oUvdSQTXBXMHHLWeQgyNEL23ChIZwU0nuDGIcQZ65kipu0PzefedtUbK4ieC065YCUi4UDDh6gPotB/Wu1pnYg3dyQ7rFvhaVYAAUEpajAdXZhlx+7+J8a4FZMeC/kqiahxoRgLbthF9019ouIqhGB9zHKI6/yZwc=" - 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: install:
- make prepare - make prepare
script: make travis 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)
+33
View File
@@ -0,0 +1,33 @@
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
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/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/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 '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 'golang.org/x/tools/cmd/cover'
end
+58 -35
View File
@@ -1,63 +1,86 @@
GOVERSION=$(shell go version | awk '{print $$3;}') GOVERSION=$(shell go version | awk '{print $$3;}')
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
ifeq ($(TRAVIS), true) ifeq ($(GOVERSION), devel)
GOVERALLS?=$(HOME)/gopath/bin/goveralls
SRCPATH?=$(HOME)/gopath/src
BINPATH=$(HOME)/gopath/bin
else
GOVERALLS?=goveralls
SRCPATH?=$(GOPATH)/src
BINPATH?=$(GOPATH)/bin
endif
ifeq ($(GOVERSION), go1.2)
TRAVIS_TARGET=coveralls TRAVIS_TARGET=coveralls
PREPARE_LIST=cover-prepare GOM_ENVIRONMENT+=-development
else else
TRAVIS_TARGET=test TRAVIS_TARGET=test
PREPARE_LIST= endif
ifeq ($(TRAVIS), true)
GOM=$(HOME)/gopath/bin/gom
else
GOM=gom
endif endif
all: test check system-test all: test check system-test
prepare: $(PREPARE_LIST) prepare:
go get -d -v ./... go get -u github.com/mattn/gom
go get launchpad.net/gocheck $(GOM) $(GOM_ENVIRONMENT) install
cover-prepare:
go get github.com/golang/lint/golint
go get github.com/mattn/goveralls
go get github.com/axw/gocov/gocov
go get code.google.com/p/go.tools/cmd/cover
coverage.out: coverage.out:
go test -coverprofile=coverage.debian.out -covermode=count ./debian rm -f coverage.*.out
go test -coverprofile=coverage.utils.out -covermode=count ./utils for i in $(PACKAGES); do $(GOM) test -coverprofile=coverage.$$i.out -covermode=count ./$$i; done
go test -coverprofile=coverage.database.out -covermode=count ./database
echo "mode: count" > coverage.out echo "mode: count" > coverage.out
grep -v -h "mode: count" coverage.*.out >> coverage.out grep -v -h "mode: count" coverage.*.out >> coverage.out
rm -f coverage.*.out
coverage: coverage.out coverage: coverage.out
go tool cover -html=coverage.out $(GOM) exec go tool cover -html=coverage.out
rm -f coverage.out rm -f coverage.out
check: check:
go tool vet -all=true . $(GOM) exec go tool vet -all=true $(ALL_PACKAGES:%=./%)
golint . $(GOM) exec golint $(ALL_PACKAGES:%=./%)
system-test: install:
ifeq ($(GOVERSION), go1.2) $(GOM) build -o $(BINPATH)/aptly
system-test: install
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi 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
go install PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
PATH=$(BINPATH):$(PATH) python system/run.py --long
travis: $(TRAVIS_TARGET) system-test travis: $(TRAVIS_TARGET) system-test
test: test:
go test -v ./... -gocheck.v=true $(GOM) test -v ./... -gocheck.v=true
coveralls: coverage.out coveralls: coverage.out
@$(GOVERALLS) -service travis-ci.org -coverprofile=coverage.out $(COVERALLS_TOKEN) $(GOM) exec $(BINPATH)/goveralls -service travis-ci.org -coverprofile=coverage.out -repotoken=$(COVERALLS_TOKEN)
mem.png: mem.dat mem.gp
gnuplot mem.gp
open mem.png
package:
rm -rf root/
mkdir -p root/usr/bin/ root/usr/share/man/man1/ root/etc/bash_completion.d
cp $(BINPATH)/aptly root/usr/bin
cp man/aptly.1 root/usr/share/man/man1
(cd root/etc/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/master/aptly)
gzip root/usr/share/man/man1/aptly.1
fpm -s dir -t deb -n aptly -v $(VERSION) --url=http://www.aptly.info/ --license=MIT --vendor="Andrey Smirnov <me@smira.ru>" \
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" --deb-recommends bzip2 --deb-recommends graphviz -C root/ .
mv aptly_$(VERSION)_*.deb ~
src-package:
rm -rf aptly-$(VERSION)
mkdir -p aptly-$(VERSION)/src/github.com/smira/aptly/
cd aptly-$(VERSION)/src/github.com/smira/ && git clone https://github.com/smira/aptly && cd aptly && git checkout v$(VERSION)
cd aptly-$(VERSION)/src/github.com/smira/aptly && gom -production install
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . \( -name .git -o -name .bzr -o -name .hg \) -print | xargs rm -rf
rm -rf aptly-$(VERSION)/src/github.com/smira/aptly/_vendor/{pkg,bin}
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 .PHONY: coverage.out
+48 -10
View File
@@ -8,8 +8,17 @@ aptly
.. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD .. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD
:target: https://coveralls.io/r/smira/aptly?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. 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 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>`_. mailing list `aptly-discuss <https://groups.google.com/forum/#!forum/aptly-discuss>`_.
@@ -19,25 +28,54 @@ Aptly features: ("+" means planned features)
* take snapshots of mirrors at any point in time, fixing state of repository at some moment of time * take snapshots of mirrors at any point in time, fixing state of repository at some moment of time
* publish snapshot as Debian repository, ready to be consumed by apt * publish snapshot as Debian repository, ready to be consumed by apt
* controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies * controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies
* merge two or more snapshots into one (+) * merge two or more snapshots into one
* filter repository by search query, pulling dependencies when required (+) * filter repository by search query, pulling dependencies when required
* publish self-made packages as Debian repositories (+) * 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: Current limitations:
* source packages, debian-installer and translations not supported yet * translations are not supported yet
* signature is not verified while downloading
* cleaning up stale files is not implemented
Currently aptly is under heavy development, so please use it with care.
Download Download
-------- --------
To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
deb http://repo.aptly.info/ squeeze main
And import key that is used to sign the release::
$ apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
After that you can install aptly as any other software package::
$ apt-get update
$ apt-get install aptly
Don't worry about squeeze part in repo name: aptly package should work on Debian squeeze+,
Ubuntu 10.0+. Package contains aptly binary, man page and bash completion.
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/>`_. 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 github.com/smira/aptly go get -u github.com/mattn/gom
mkdir -p $GOPATH/src/github.com/smira/aptly
git clone https://github.com/smira/aptly $GOPATH/src/github.com/smira/aptly
cd $GOPATH/src/github.com/smira/aptly
gom -production install
gom build -o $GOPATH/bin/aptly
Aptly is using `gom <https://github.com/mattn/gom>`_ to fix external dependencies, so regular ``go get github.com/smira/aptly``
should work as well, but might fail or produce different result (if external libraries got updated).
If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_. If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_.
+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)
}
+338
View File
@@ -0,0 +1,338 @@
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) {
force := c.Request.URL.Query().Get("force") == "1"
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(), force)
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())
}
+97
View File
@@ -0,0 +1,97 @@
// Package aptly provides common infrastructure that doesn't depend directly on
// Debian or CentOS
package aptly
import (
"github.com/smira/aptly/utils"
"io"
)
// PackagePool is asbtraction of package pool storage.
//
// PackagePool stores all the package files, deduplicating them.
type PackagePool interface {
// Path returns full path to package file in pool given any name and hash of file contents
Path(filename string, hashMD5 string) (string, error)
// RelativePath returns path relative to pool's root for package files given MD5 and original filename
RelativePath(filename string, hashMD5 string) (string, error)
// FilepathList returns file paths of all the files in the pool
FilepathList(progress Progress) ([]string, error)
// Remove deletes file in package pool returns its size
Remove(path string) (size int64, err error)
// Import copies file into package pool
Import(path string, hashMD5 string) error
}
// PublishedStorage is abstraction of filesystem storing all published repositories
type PublishedStorage interface {
// MkDir creates directory recursively under public path
MkDir(path string) error
// PutFile puts file into published storage at specified path
PutFile(path string, sourceFilename string) error
// RemoveDirs removes directory structure under public path
RemoveDirs(path string, progress Progress) error
// Remove removes single file under public path
Remove(path string) error
// LinkFromPool links package file from pool to dist's pool location
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath, sourceMD5 string, force bool) error
// Filelist returns list of files under prefix
Filelist(prefix string) ([]string, error)
// RenameFile renames (moves) file
RenameFile(oldName, newName string) error
}
// LocalPublishedStorage is published storage on local filesystem
type LocalPublishedStorage interface {
// PublicPath returns root of public part
PublicPath() string
}
// PublishedStorageProvider is a thing that returns PublishedStorage by name
type PublishedStorageProvider interface {
// GetPublishedStorage returns PublishedStorage by name
GetPublishedStorage(name string) PublishedStorage
}
// Progress is a progress displaying entity, it allows progress bars & simple prints
type Progress interface {
// Writer interface to support progress bar ticking
io.Writer
// Start makes progress start its work
Start()
// Shutdown shuts down progress display
Shutdown()
// Flush returns when all queued messages are sent
Flush()
// InitBar starts progressbar for count bytes or count items
InitBar(count int64, isBytes bool)
// ShutdownBar stops progress bar and hides it
ShutdownBar()
// AddBar increments progress for progress bar
AddBar(count int)
// SetBar sets current position for progress bar
SetBar(count int)
// Printf does printf but in safe manner: not overwriting progress bar
Printf(msg string, a ...interface{})
// ColoredPrintf does printf in colored way + newline
ColoredPrintf(msg string, a ...interface{})
}
// Downloader is parallel HTTP fetcher
type Downloader interface {
// Download starts new download task
Download(url string, destination string, result chan<- error)
// DownloadWithChecksum starts new download task with checksum verification
DownloadWithChecksum(url string, destination string, result chan<- error, expected utils.ChecksumInfo, ignoreMismatch bool)
// Pause pauses task processing
Pause()
// Resume resumes task processing
Resume()
// Shutdown stops downloader after current tasks are finished,
// but doesn't process rest of queue
Shutdown()
// Abort stops downloader without waiting for shutdown
Abort()
// GetProgress returns Progress object
GetProgress() Progress
}
+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...))
}
+7
View File
@@ -0,0 +1,7 @@
package aptly
// Version of aptly
const Version = "0.9.5"
// 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
}
+97
View File
@@ -0,0 +1,97 @@
// Package cmd implements console commands
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"time"
)
// ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
if reflist == nil {
return
}
err = reflist.ForEach(func(key []byte) error {
p, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
return err2
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
// LookupOption checks boolean flag with default (usually config) and command-line
// setting
func LookupOption(defaultValue bool, flags *flag.FlagSet, name string) (result bool) {
result = defaultValue
if flags.IsSet(name) {
result = flags.Lookup(name).Value.Get().(bool)
}
return
}
// RootCommand creates root command in command tree
func RootCommand() *commander.Command {
cmd := &commander.Command{
UsageLine: os.Args[0],
Short: "Debian repository management tool",
Long: `
aptly is a tool to create partial and full mirrors of remote
repositories, manage local repositories, filter them, merge,
upgrade individual packages, take snapshots and publish them
back as Debian repositories.
aptly's goal is to establish repeatability and controlled changes
in a package-centric environment. aptly allows one to fix a set of packages
in a repository, so that package installation and upgrade becomes
deterministic. At the same time aptly allows one to perform controlled,
fine-grained changes in repository contents to transition your
package environment to new version.`,
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
Subcommands: []*commander.Command{
makeCmdConfig(),
makeCmdDb(),
makeCmdGraph(),
makeCmdMirror(),
makeCmdRepo(),
makeCmdServe(),
makeCmdSnapshot(),
makeCmdTask(),
makeCmdPublish(),
makeCmdVersion(),
makeCmdPackage(),
makeCmdAPI(),
},
}
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
cmd.Flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
if aptly.EnableDebug {
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
cmd.Flag.String("memprofile", "", "write memory profile to this file")
cmd.Flag.String("memstats", "", "write memory stats periodically to this file")
cmd.Flag.Duration("meminterval", 100*time.Millisecond, "memory stats dump interval")
}
return cmd
}
+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
}
+31
View File
@@ -0,0 +1,31 @@
package cmd
import (
ctx "github.com/smira/aptly/context"
"github.com/smira/flag"
)
var context *ctx.AptlyContext
// ShutdownContext shuts context down
func ShutdownContext() {
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
if context != nil {
panic("context already initialized")
}
context, err = ctx.NewContext(flags)
return err
}
+16
View File
@@ -0,0 +1,16 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdDb() *commander.Command {
return &commander.Command{
UsageLine: "db",
Short: "manage aptly's internal database and package pool",
Subcommands: []*commander.Command{
makeCmdDbCleanup(),
makeCmdDbRecover(),
},
}
}
+300
View File
@@ -0,0 +1,300 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"sort"
"strings"
)
// aptly db cleanup
func aptlyDbCleanup(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
verbose := context.Flags().Lookup("verbose").Value.Get().(bool)
dryRun := context.Flags().Lookup("dry-run").Value.Get().(bool)
// collect information about references packages...
existingPackageRefs := deb.NewPackageRefList()
// used only in verbose mode to report package use source
packageRefSources := map[string][]string{}
context.Progress().ColoredPrintf("@{w!}Loading mirrors, local repos, snapshots and published repos...@|")
if verbose {
context.Progress().ColoredPrintf("@{y}Loading mirrors:@|")
}
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
}
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
if verbose {
description := fmt.Sprintf("mirror %s", repo.Name)
repo.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
return nil
})
if err != nil {
return err
}
if verbose {
context.Progress().ColoredPrintf("@{y}Loading local repos:@|")
}
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
}
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
if verbose {
description := fmt.Sprintf("local repo %s", repo.Name)
repo.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
return nil
})
if err != nil {
return err
}
if verbose {
context.Progress().ColoredPrintf("@{y}Loading snapshots:@|")
}
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", snapshot.Name)
}
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return err
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false, true)
if verbose {
description := fmt.Sprintf("snapshot %s", snapshot.Name)
snapshot.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
return nil
})
if err != nil {
return err
}
if verbose {
context.Progress().ColoredPrintf("@{y}Loading published repositories:@|")
}
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(published *deb.PublishedRepo) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s:%s/%s{|}", published.Storage, published.Prefix, published.Distribution)
}
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)
if verbose {
description := fmt.Sprintf("published repository %s:%s/%s component %s",
published.Storage, published.Prefix, published.Distribution, component)
published.RefList(component).ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
return nil
})
if err != nil {
return err
}
// ... and compare it to the list of all packages
context.Progress().ColoredPrintf("@{w!}Loading list of all packages...@|")
allPackageRefs := context.CollectionFactory().PackageCollection().AllPackageRefs()
toDelete := allPackageRefs.Substract(existingPackageRefs)
// delete packages that are no longer referenced
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced packages (%d)...@|", toDelete.Len())
// database can't err as collection factory already constructed
db, _ := context.Database()
if toDelete.Len() > 0 {
if verbose {
context.Progress().ColoredPrintf("@{r}List of package keys to delete:@|")
err = toDelete.ForEach(func(ref []byte) error {
context.Progress().ColoredPrintf(" - @{r}%s@|", string(ref))
return nil
})
if err != nil {
return err
}
}
if !dryRun {
db.StartBatch()
err = toDelete.ForEach(func(ref []byte) error {
return context.CollectionFactory().PackageCollection().DeleteByKey(ref)
})
if err != nil {
return err
}
err = db.FinishBatch()
if err != nil {
return fmt.Errorf("unable to write to DB: %s", err)
}
} else {
context.Progress().ColoredPrintf("@{y!}Skipped deletion, as -dry-run has been requested.@|")
}
}
// now, build a list of files that should be present in Repository (package pool)
context.Progress().ColoredPrintf("@{w!}Building list of files referenced by packages...@|")
referencedFiles := make([]string, 0, existingPackageRefs.Len())
context.Progress().InitBar(int64(existingPackageRefs.Len()), false)
err = existingPackageRefs.ForEach(func(key []byte) error {
pkg, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
tail := ""
if verbose {
tail = fmt.Sprintf(" (sources: %s)", strings.Join(packageRefSources[string(key)], ", "))
}
if dryRun {
context.Progress().ColoredPrintf("@{r!}Unresolvable package reference, skipping (-dry-run): %s: %s%s",
string(key), err2, tail)
return nil
}
return fmt.Errorf("unable to load package %s: %s%s", string(key), err2, tail)
}
paths, err2 := pkg.FilepathList(context.PackagePool())
if err2 != nil {
return err2
}
referencedFiles = append(referencedFiles, paths...)
context.Progress().AddBar(1)
return nil
})
if err != nil {
return err
}
sort.Strings(referencedFiles)
context.Progress().ShutdownBar()
// build a list of files in the package pool
context.Progress().ColoredPrintf("@{w!}Building list of files in package pool...@|")
existingFiles, err := context.PackagePool().FilepathList(context.Progress())
if err != nil {
return fmt.Errorf("unable to collect file paths: %s", err)
}
// find files which are in the pool but not referenced by packages
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
// delete files that are no longer referenced
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced files (%d)...@|", len(filesToDelete))
if len(filesToDelete) > 0 {
if verbose {
context.Progress().ColoredPrintf("@{r}List of files to be deleted:@|")
for _, file := range filesToDelete {
context.Progress().ColoredPrintf(" - @{r}%s@|", file)
}
}
if !dryRun {
context.Progress().InitBar(int64(len(filesToDelete)), false)
var size, totalSize int64
for _, file := range filesToDelete {
size, err = context.PackagePool().Remove(file)
if err != nil {
return err
}
context.Progress().AddBar(1)
totalSize += size
}
context.Progress().ShutdownBar()
context.Progress().ColoredPrintf("@{w!}Disk space freed: %s...@|", utils.HumanBytes(totalSize))
} else {
context.Progress().ColoredPrintf("@{y!}Skipped file deletion, as -dry-run has been requested.@|")
}
}
if !dryRun {
context.Progress().ColoredPrintf("@{w!}Compacting database...@|")
err = db.CompactDB()
} else {
context.Progress().ColoredPrintf("@{y!}Skipped DB compaction, as -dry-run has been requested.@|")
}
return err
}
func makeCmdDbCleanup() *commander.Command {
cmd := &commander.Command{
Run: aptlyDbCleanup,
UsageLine: "cleanup",
Short: "cleanup DB and package pool",
Long: `
Database cleanup removes information about unreferenced packages and removes
files in the package pool that aren't used by packages anymore
Example:
$ aptly db cleanup
`,
}
cmd.Flag.Bool("verbose", false, "be verbose when loading objects/removing them")
cmd.Flag.Bool("dry-run", false, "don't delete anything")
return cmd
}
+39
View File
@@ -0,0 +1,39 @@
package cmd
import (
"github.com/smira/aptly/database"
"github.com/smira/commander"
)
// aptly db recover
func aptlyDbRecover(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
context.Progress().Printf("Recovering database...\n")
err = database.RecoverDB(context.DBPath())
return err
}
func makeCmdDbRecover() *commander.Command {
cmd := &commander.Command{
Run: aptlyDbRecover,
UsageLine: "recover",
Short: "recover DB after crash",
Long: `
Database recover does its' best to recover the database after a crash.
It is recommended to backup the DB before running recover.
Example:
$ aptly db recover
`,
}
return cmd
}
+93
View File
@@ -0,0 +1,93 @@
package cmd
import (
"bytes"
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"io"
"io/ioutil"
"os"
"os/exec"
)
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("Generating graph...\n")
graph, err := deb.BuildGraph(context.CollectionFactory())
if err != nil {
return err
}
buf := bytes.NewBufferString(graph.String())
tempfile, err := ioutil.TempFile("", "aptly-graph")
if err != nil {
return err
}
tempfile.Close()
os.Remove(tempfile.Name())
tempfilename := tempfile.Name() + ".png"
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
}
_, err = io.Copy(stdin, buf)
if err != nil {
return err
}
err = stdin.Close()
if err != nil {
return err
}
err = command.Wait()
if err != nil {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
}
return err
}
func makeCmdGraph() *commander.Command {
cmd := &commander.Command{
Run: aptlyGraph,
UsageLine: "graph",
Short: "render graph of relationships",
Long: `
Command graph displays relationship between mirrors, local repositories,
snapshots and published repositories using graphviz package to render
graph as an image.
Example:
$ aptly graph
`,
}
return cmd
}
+62
View File
@@ -0,0 +1,62 @@
package cmd
import (
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
return nil, nil
}
keyRings := flags.Lookup("keyring").Value.Get().([]string)
verifier := &utils.GpgVerifier{}
for _, keyRing := range keyRings {
verifier.AddKeyring(keyRing)
}
err := verifier.InitKeyring()
if err != nil {
return nil, err
}
return verifier, nil
}
type keyRingsFlag struct {
keyRings []string
}
func (k *keyRingsFlag) Set(value string) error {
k.keyRings = append(k.keyRings, value)
return nil
}
func (k *keyRingsFlag) Get() interface{} {
return k.keyRings
}
func (k *keyRingsFlag) String() string {
return strings.Join(k.keyRings, ",")
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
makeCmdMirrorRename(),
makeCmdMirrorEdit(),
makeCmdMirrorSearch(),
},
}
}
+103
View File
@@ -0,0 +1,103 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if !(len(args) == 2 && strings.HasPrefix(args[1], "ppa:") || len(args) >= 3) {
cmd.Usage()
return commander.ErrCommandError
}
downloadSources := LookupOption(context.Config().DownloadSourcePackages, context.Flags(), "with-sources")
downloadUdebs := context.Flags().Lookup("with-udebs").Value.Get().(bool)
var (
mirrorName, archiveURL, distribution string
components []string
)
mirrorName = args[0]
if len(args) == 2 {
archiveURL, distribution, components, err = deb.ParsePPA(args[1], context.Config())
if err != nil {
return err
}
} else {
archiveURL, distribution, components = args[1], args[2], args[3:]
}
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(),
downloadSources, downloadUdebs)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
repo.Filter = context.Flags().Lookup("filter").Value.String()
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
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)
}
err = repo.Fetch(context.Downloader(), verifier)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror",
Long: `
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
via HTTP and FTP. aptly would try download Release file from remote repository and verify its' signature. Command
line format resembles apt utlitily sources.list(5).
PPA urls could specified in short format:
$ aptly mirror create <name> ppa:<user>/<project>
Example:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
cmd.Flag.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
}
+72
View File
@@ -0,0 +1,72 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByRemoteRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Mirror `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete mirror with snapshots, use -force to override")
}
}
err = context.CollectionFactory().RemoteRepoCollection().Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Mirror `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete mirror",
Long: `
Drop deletes information about remote repository mirror <name>. Package data is not deleted
(since it could still be used by other mirrors or snapshots). If mirror is used as source
to create a snapshot, aptly would refuse to delete such mirror, use flag -force to override.
Example:
$ aptly mirror drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force mirror deletion even if used by snapshots")
return cmd
}
+91
View File
@@ -0,0 +1,91 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
context.Flags().Visit(func(flag *flag.Flag) {
switch flag.Name {
case "filter":
repo.Filter = flag.Value.String()
case "filter-with-deps":
repo.FilterWithDeps = flag.Value.Get().(bool)
case "with-sources":
repo.DownloadSources = flag.Value.Get().(bool)
case "with-udebs":
repo.DownloadUdebs = flag.Value.Get().(bool)
}
})
if repo.IsFlat() && repo.DownloadUdebs {
return fmt.Errorf("unable to edit: flat mirrors don't support udebs")
}
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
if context.GlobalFlags().Lookup("architectures").Value.String() != "" {
repo.Architectures = context.ArchitecturesList()
err = repo.Fetch(context.Downloader(), nil)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
fmt.Printf("Mirror %s successfully updated.\n", repo)
return err
}
func makeCmdMirrorEdit() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorEdit,
UsageLine: "edit <name>",
Short: "edit mirror settings",
Long: `
Command edit allows one to change settings of mirror:
filters, list of architectures.
Example:
$ aptly mirror edit -filter=nginx -filter-with-deps some-mirror
`,
Flag: *flag.NewFlagSet("aptly-mirror-edit", flag.ExitOnError),
}
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
return cmd
}
+71
View File
@@ -0,0 +1,71 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
repos := make([]string, context.CollectionFactory().RemoteRepoCollection().Len())
i := 0
context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
if raw {
repos[i] = repo.Name
} else {
repos[i] = repo.String()
}
i++
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
for _, repo := range repos {
fmt.Printf("%s\n", repo)
}
} else {
if len(repos) > 0 {
fmt.Printf("List of mirrors:\n")
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
}
return err
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors",
Long: `
List shows full list of remote repository mirrors.
Example:
$ aptly mirror list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+64
View File
@@ -0,0 +1,64 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyMirrorRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.RemoteRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().RemoteRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: mirror %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nMirror %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdMirrorRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames mirror",
Long: `
Command changes name of the mirror.Mirror name should be unique.
Example:
$ aptly mirror rename wheezy-min wheezy-main
`,
}
return cmd
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdMirrorSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search mirror for packages matching query",
Long: `
Command search displays list of packages in mirror that match package query
Example:
$ aptly mirror search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
return cmd
}
+99
View File
@@ -0,0 +1,99 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
if repo.Status == deb.MirrorUpdating {
fmt.Printf("Status: In Update (PID %d)\n", repo.WorkerPID)
}
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
downloadSources := "no"
if repo.DownloadSources {
downloadSources = "yes"
}
fmt.Printf("Download Sources: %s\n", downloadSources)
downloadUdebs := "no"
if repo.DownloadUdebs {
downloadUdebs = "yes"
}
fmt.Printf("Download .udebs: %s\n", downloadUdebs)
if repo.Filter != "" {
fmt.Printf("Filter: %s\n", repo.Filter)
filterWithDeps := "no"
if repo.FilterWithDeps {
filterWithDeps = "yes"
}
fmt.Printf("Filter With Deps: %s\n", filterWithDeps)
}
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show <name>",
Short: "show details about mirror",
Long: `
Shows detailed information about the mirror.
Example:
$ aptly mirror show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show detailed list of packages and versions stored in the mirror")
return cmd
}
+193
View File
@@ -0,0 +1,193 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"os/signal"
"strings"
)
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
}
ignoreMismatch := context.Flags().Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.Downloader(), verifier)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("Downloading & parsing package files...\n")
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if repo.Filter != "" {
context.Progress().Printf("Applying filter...\n")
var filterQuery deb.PackageQuery
filterQuery, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
var oldLen, newLen int
oldLen, newLen, err = repo.ApplyFilter(context.DependencyOptions(), filterQuery)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("Packages filtered: %d -> %d.\n", oldLen, newLen)
}
var (
downloadSize int64
queue []deb.PackageDownloadTask
)
context.Progress().Printf("Building download queue...\n")
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
defer func() {
// on any interruption, unlock the mirror
err := context.ReOpenDatabase()
if err == nil {
repo.MarkAsIdle()
context.CollectionFactory().RemoteRepoCollection().Update(repo)
}
}()
repo.MarkAsUpdating()
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CloseDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
// Catch ^C
sigch := make(chan os.Signal)
signal.Notify(sigch, os.Interrupt)
count := len(queue)
context.Progress().Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
// Download from the queue
context.Progress().InitBar(downloadSize, true)
// Download all package files
ch := make(chan error, count)
// In separate goroutine (to avoid blocking main), push queue to downloader
go func() {
for _, task := range queue {
context.Downloader().DownloadWithChecksum(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch, task.Checksums, ignoreMismatch)
}
// We don't need queue after this point
queue = nil
}()
// Wait for all downloads to finish
errors := make([]string, 0)
for count > 0 {
select {
case <-sigch:
signal.Stop(sigch)
return fmt.Errorf("unable to update: interrupted")
case err = <-ch:
if err != nil {
errors = append(errors, err.Error())
}
count--
}
}
context.Progress().ShutdownBar()
signal.Stop(sigch)
if len(errors) > 0 {
return fmt.Errorf("unable to update: download errors:\n %s\n", strings.Join(errors, "\n "))
}
err = context.ReOpenDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
repo.FinalizeDownload()
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update <name>",
Short: "update mirror",
Long: `
Updates remote mirror (downloads package files and meta information). When mirror is created,
this command should be run for the first time to fetch mirror contents. This command can be
run multiple times to get updated repository contents. If interrupted, command can be safely restarted.
Example:
$ aptly mirror update wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force update mirror even if it is locked by another process")
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Int64("download-limit", 0, "limit download speed (kbytes/sec)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
+16
View File
@@ -0,0 +1,16 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdPackage() *commander.Command {
return &commander.Command{
UsageLine: "package",
Short: "operations on packages",
Subcommands: []*commander.Command{
makeCmdPackageSearch(),
makeCmdPackageShow(),
},
}
}
+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
}
+42
View File
@@ -0,0 +1,42 @@
package cmd
import (
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
)
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
if LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
return nil, nil
}
signer := &utils.GpgSigner{}
signer.SetKey(flags.Lookup("gpg-key").Value.String())
signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").Value.String())
signer.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").Value.String())
signer.SetBatch(flags.Lookup("batch").Value.Get().(bool))
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishDrop(),
makeCmdPublishList(),
makeCmdPublishRepo(),
makeCmdPublishSnapshot(),
makeCmdPublishSwitch(),
makeCmdPublishUpdate(),
},
}
}
+54
View File
@@ -0,0 +1,54 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
if len(args) == 2 {
param = args[1]
}
storage, prefix := deb.ParsePrefix(param)
err = context.CollectionFactory().PublishedRepoCollection().Remove(context, storage, prefix, distribution,
context.CollectionFactory(), context.Progress(), context.Flags().Lookup("force-drop").Value.Get().(bool))
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
context.Progress().Printf("\nPublished repository has been removed successfully.\n")
return err
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [[<endpoint>:]<prefix>]",
Short: "remove published repository",
Long: `
Command removes whatever has been published under specified <prefix>,
publishing <endpoint> and <distribution> name.
Example:
$ aptly publish drop wheezy
`,
}
cmd.Flag.Bool("force-drop", false, "remove published repository even if some files could not be cleaned up")
return cmd
}
+80
View File
@@ -0,0 +1,80 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
published := make([]string, 0, context.CollectionFactory().PublishedRepoCollection().Len())
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
}
if raw {
published = append(published, fmt.Sprintf("%s %s", repo.StoragePrefix(), repo.Distribution))
} else {
published = append(published, repo.String())
}
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
context.CloseDatabase()
sort.Strings(published)
if raw {
for _, info := range published {
fmt.Printf("%s\n", info)
}
} else {
if len(published) == 0 {
fmt.Printf("No snapshots/local repos have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
}
return err
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "list of published repositories",
Long: `
Display list of currently published snapshots.
Example:
$ aptly publish list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+49
View File
@@ -0,0 +1,49 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdPublishRepo() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "repo <name> [[<endpoint>:]<prefix>]",
Short: "publish local repository",
Long: `
Command publishes current state of local repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple local
repositories as the arguments:
aptly publish repo -component=main,contrib repo-main repo-contrib
It is not recommended to publish local repositories directly unless the
repository is for testing purposes and changes happen frequently. For
production usage please take snapshot of repository and publish it
using publish snapshot command.
Example:
$ aptly publish repo testing
`,
Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("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
}
+211
View File
@@ -0,0 +1,211 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
var err error
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components) || len(args) > len(components)+1 {
cmd.Usage()
return commander.ErrCommandError
}
var param string
if len(args) == len(components)+1 {
param = args[len(components)]
args = args[0 : len(args)-1]
} else {
param = ""
}
storage, prefix := deb.ParsePrefix(param)
var (
sources = []interface{}{}
message string
)
if cmd.Name() == "snapshot" {
var (
snapshot *deb.Snapshot
emptyWarning = false
parts = []string{}
)
for _, name := range args {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, snapshot)
parts = append(parts, snapshot.Name)
if snapshot.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Snapshot %s has", parts[0])
} else {
message = fmt.Sprintf("Snapshots %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else if cmd.Name() == "repo" {
var (
localRepo *deb.LocalRepo
emptyWarning = false
parts = []string{}
)
for _, name := range args {
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, localRepo)
parts = append(parts, localRepo.Name)
if localRepo.NumPackages() == 0 {
emptyWarning = true
}
}
if len(parts) == 1 {
message = fmt.Sprintf("Local repo %s has", parts[0])
} else {
message = fmt.Sprintf("Local repos %s have", strings.Join(parts, ", "))
}
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else {
panic("unknown command")
}
distribution := context.Flags().Lookup("distribution").Value.String()
published, err := deb.NewPublishedRepo(storage, prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
published.Origin = cmd.Flag.Lookup("origin").Value.String()
published.Label = cmd.Flag.Lookup("label").Value.String()
duplicate := context.CollectionFactory().PublishedRepoCollection().CheckDuplicate(published)
if duplicate != nil {
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
var repoComponents string
prefix, repoComponents, distribution = published.Prefix, strings.Join(published.Components(), " "), published.Distribution
if prefix == "." {
prefix = ""
} else if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
context.Progress().Printf("\n%s been successfully published.\n", message)
if localStorage, ok := context.GetPublishedStorage(storage).(aptly.LocalPublishedStorage); ok {
context.Progress().Printf("Please setup your webserver to serve directory '%s' with autoindexing.\n",
localStorage.PublicPath())
}
context.Progress().Printf("Now you can add following line to apt sources:\n")
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
if utils.StrSliceHasItem(published.Architectures, "source") {
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
}
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "snapshot <name> [[<endpoint>:]<prefix>]",
Short: "publish snapshot",
Long: `
Command publishes snapshot as Debian repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple snapshots
as the arguments:
aptly publish snapshot -component=main,contrib snap-main snap-contrib
Example:
$ aptly publish snapshot wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("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
}
+150
View File
@@ -0,0 +1,150 @@
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
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components)+1 || len(args) > len(components)+2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
var (
names []string
snapshot *deb.Snapshot
)
if len(args) == len(components)+2 {
param = args[1]
names = args[2:]
} else {
names = args[1:]
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "snapshot" {
return fmt.Errorf("unable to update: not a snapshot publish")
}
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
publishedComponents := published.Components()
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
components = publishedComponents
}
if len(names) != len(components) {
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
}
for i, component := range components {
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)
}
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Update(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nPublish for snapshot %s has been successfully switched to new snapshot.\n", published.String())
return err
}
func makeCmdPublishSwitch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSwitch,
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
Short: "update published repository by switching to new snapshot",
Long: `
Command switches in-place published 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 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
}
+108
View File
@@ -0,0 +1,108 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return commander.ErrCommandError
}
distribution := args[0]
param := "."
if len(args) == 2 {
param = args[1]
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "local" {
return fmt.Errorf("unable to update: not a local repository publish")
}
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
components := published.Components()
for _, component := range components {
published.UpdateLocalRepo(component)
}
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().Update(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("\nPublish for local repo %s has been successfully updated.\n", published.String())
return err
}
func makeCmdPublishUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishUpdate,
UsageLine: "update <distribution> [[<endpoint>:]<prefix>]",
Short: "update published local repository",
Long: `
Command re-publishes (updates) published local repository. <distribution>
and <prefix> should be occupied with local repository published
using command aptly publish repo. Update happens in-place with
minimum possible downtime for published repository.
For multiple component published repositories, all local repositories
are updated.
Example:
$ aptly publish update wheezy ppa
`,
Flag: *flag.NewFlagSet("aptly-publish-update", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("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
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdRepo() *commander.Command {
return &commander.Command{
UsageLine: "repo",
Short: "manage local package repositories",
Subcommands: []*commander.Command{
makeCmdRepoAdd(),
makeCmdRepoCopy(),
makeCmdRepoCreate(),
makeCmdRepoDrop(),
makeCmdRepoEdit(),
makeCmdRepoImport(),
makeCmdRepoList(),
makeCmdRepoMove(),
makeCmdRepoRemove(),
makeCmdRepoShow(),
makeCmdRepoRename(),
makeCmdRepoSearch(),
},
}
}
+112
View File
@@ -0,0 +1,112 @@
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"
)
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
verifier := &utils.GpgVerifier{}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to add: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to add: %s", err)
}
context.Progress().Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool)
var packageFiles, failedFiles []string
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)
}
var processedFiles, failedFiles2 []string
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))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
if context.Flags().Lookup("remove-files").Value.Get().(bool) {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
err := os.Remove(file)
if err != nil {
return fmt.Errorf("unable to remove file: %s", err)
}
}
}
if len(failedFiles) > 0 {
context.Progress().ColoredPrintf("@y[!]@| @!Some files were skipped due to errors:@|")
for _, file := range failedFiles {
context.Progress().ColoredPrintf(" %s", file)
}
return fmt.Errorf("some files failed to be added")
}
return err
}
func makeCmdRepoAdd() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoAdd,
UsageLine: "add <name> <package file.deb>|<directory> ...",
Short: "add packages to local repository",
Long: `
Command adds packages to local repository from .deb, .udeb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.[u]deb or *.dsc
patterns. Every file discovered would be analyzed to extract metadata, package would then be created and added
to the database. Files would be imported to internal package pool. For source packages, all required files are
added automatically as well. Extra files for source package should be in the same directory as *.dsc file.
Example:
$ aptly repo add testing myapp-0.1.2.deb incoming/
`,
Flag: *flag.NewFlagSet("aptly-repo-add", flag.ExitOnError),
}
cmd.Flag.Bool("remove-files", false, "remove files that have been imported successfully into repository")
cmd.Flag.Bool("force-replace", false, "when adding package that conflicts with existing package, remove existing package")
return cmd
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoCopy() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "copy <src-name> <dst-name> <package-query> ...",
Short: "copy packages between local repositories",
Long: `
Command copy copies packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
$ aptly repo copy testing stable 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-copy", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't copy, just show what would be copied")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo := deb.NewLocalRepo(args[0], context.Flags().Lookup("comment").Value.String())
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
if err != nil {
return fmt.Errorf("unable to add local repo: %s", err)
}
fmt.Printf("\nLocal repo %s successfully added.\nYou can run 'aptly repo add %s ...' to add packages to repository.\n", repo, repo.Name)
return err
}
func makeCmdRepoCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoCreate,
UsageLine: "create <name>",
Short: "create local repository",
Long: `
Create local package repository. Repository would be empty when
created, packages could be added from files, copied or moved from
another local repository or imported from the mirror.
Example:
$ aptly repo create testing
`,
Flag: *flag.NewFlagSet("aptly-repo-create", flag.ExitOnError),
}
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "main", "default component when publishing")
return cmd
}
+80
View File
@@ -0,0 +1,80 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
published := context.CollectionFactory().PublishedRepoCollection().ByLocalRepo(repo)
if len(published) > 0 {
fmt.Printf("Local repo `%s` is published currently:\n", repo.Name)
for _, repo := range published {
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: local repo is published")
}
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Local repo `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete local repo with snapshots, use -force to override")
}
}
err = context.CollectionFactory().LocalRepoCollection().Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Local repo `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdRepoDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoDrop,
UsageLine: "drop <name>",
Short: "delete local repository",
Long: `
Drop information about deletions from local repo. Package data is not deleted
(since it could be still used by other mirrors or snapshots).
Example:
$ aptly repo drop local-repo
`,
Flag: *flag.NewFlagSet("aptly-repo-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force local repo deletion even if used by snapshots")
return cmd
}
+68
View File
@@ -0,0 +1,68 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
if context.Flags().Lookup("comment").Value.String() != "" {
repo.Comment = context.Flags().Lookup("comment").Value.String()
}
if context.Flags().Lookup("distribution").Value.String() != "" {
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
}
if context.Flags().Lookup("component").Value.String() != "" {
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
}
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
fmt.Printf("Local repo %s successfully updated.\n", repo)
return err
}
func makeCmdRepoEdit() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoEdit,
UsageLine: "edit <name>",
Short: "edit properties of local repository",
Long: `
Command edit allows one to change metadata of local repository:
comment, default distribution and component.
Example:
$ aptly repo edit -distribution=wheezy testing
`,
Flag: *flag.NewFlagSet("aptly-repo-edit", flag.ExitOnError),
}
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "", "default component when publishing")
return cmd
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoImport() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "import <src-mirror> <dst-repo> <package-query> ...",
Short: "import packages from mirror to local repository",
Long: `
Command import looks up packages matching <package-query> in mirror <src-mirror>
and copies them to local repo <dst-repo>.
Example:
$ aptly repo import wheezy-main testing nginx
`,
Flag: *flag.NewFlagSet("aptly-repo-import", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't import, just show what would be imported")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+77
View File
@@ -0,0 +1,77 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyRepoList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
repos := make([]string, context.CollectionFactory().LocalRepoCollection().Len())
i := 0
context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
if raw {
repos[i] = repo.Name
} else {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
}
i++
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
for _, repo := range repos {
fmt.Printf("%s\n", repo)
}
} else {
if len(repos) > 0 {
fmt.Printf("List of local repos:\n")
for _, repo := range repos {
fmt.Println(repo)
}
fmt.Printf("\nTo get more information about local repository, run `aptly repo show <name>`.\n")
} else {
fmt.Printf("No local repositories found, create one with `aptly repo create ...`.\n")
}
}
return err
}
func makeCmdRepoList() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoList,
UsageLine: "list",
Short: "list local repositories",
Long: `
List command shows full list of local package repositories.
Example:
$ aptly repo list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
return cmd
}
+191
View File
@@ -0,0 +1,191 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
)
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return commander.ErrCommandError
}
command := cmd.Name()
dstRepo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(dstRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
var (
srcRefList *deb.PackageRefList
srcRepo *deb.LocalRepo
)
if command == "copy" || command == "move" {
srcRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if srcRepo.UUID == dstRepo.UUID {
return fmt.Errorf("unable to %s: source and destination are the same", command)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(srcRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
srcRefList = srcRepo.RefList()
} else if command == "import" {
var srcRemoteRepo *deb.RemoteRepo
srcRemoteRepo, err = context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(srcRemoteRepo)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if srcRemoteRepo.RefList() == nil {
return fmt.Errorf("unable to %s: mirror not updated", command)
}
srcRefList = srcRemoteRepo.RefList()
} else {
panic("unexpected command")
}
context.Progress().Printf("Loading packages...\n")
dstList, err := deb.NewPackageListFromRefList(dstRepo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
srcList, err := deb.NewPackageListFromRefList(srcRefList, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
srcList.PrepareIndex()
var architecturesList []string
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
if withDeps {
dstList.PrepareIndex()
// Calculate architectures
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = dstList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
}
queries := make([]deb.PackageQuery, len(args)-2)
for i := 0; i < len(args)-2; i++ {
queries[i], err = query.Parse(args[i+2])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
}
toProcess, err := srcList.Filter(queries, withDeps, dstList, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
var verb string
if command == "move" {
verb = "moved"
} else if command == "copy" {
verb = "copied"
} else if command == "import" {
verb = "imported"
}
err = toProcess.ForEach(func(p *deb.Package) error {
err = dstList.Add(p)
if err != nil {
return err
}
if command == "move" {
srcList.Remove(p)
}
context.Progress().ColoredPrintf("@g[o]@| %s %s", p, verb)
return nil
})
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
dstRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(dstList))
err = context.CollectionFactory().LocalRepoCollection().Update(dstRepo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
if command == "move" {
srcRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(srcList))
err = context.CollectionFactory().LocalRepoCollection().Update(srcRepo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
}
}
return err
}
func makeCmdRepoMove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "move <src-name> <dst-name> <package-query> ...",
Short: "move packages between local repositories",
Long: `
Command move moves packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
$ aptly repo move testing stable 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-move", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't move, just show what would be moved")
cmd.Flag.Bool("with-deps", false, "follow dependencies when processing package-spec")
return cmd
}
+92
View File
@@ -0,0 +1,92 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoRemove(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
context.Progress().Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
queries := make([]deb.PackageQuery, len(args)-1)
for i := 0; i < len(args)-1; i++ {
queries[i], err = query.Parse(args[i+1])
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
}
list.PrepareIndex()
toRemove, err := list.Filter(queries, false, nil, 0, nil)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
toRemove.ForEach(func(p *deb.Package) error {
list.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
return nil
})
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to save: %s", err)
}
}
return err
}
func makeCmdRepoRemove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRemove,
UsageLine: "remove <name> <package-query> ...",
Short: "remove packages from local repository",
Long: `
Commands removes packages matching <package-query> from local repository
<name>. If removed packages are not referenced by other repos or
snapshots, they can be removed completely (including files) by running
'aptly db cleanup'.
Example:
$ aptly repo remove testing 'myapp (=0.1.12)'
`,
Flag: *flag.NewFlagSet("aptly-repo-add", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't remove, just show what would be removed")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyRepoRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.LocalRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().LocalRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: local repo %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nLocal repo %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdRepoRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames local repository",
Long: `
Command changes name of the local repo. Local repo name should be unique.
Example:
$ aptly repo rename wheezy-min wheezy-main
`,
}
return cmd
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search repo for packages matching query",
Long: `
Command search displays list of packages in local repository that match package query
Example:
$ aptly repo search my-software '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-repo-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyRepoShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Comment: %s\n", repo.Comment)
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(repo.RefList())
}
return err
}
func makeCmdRepoShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoShow,
UsageLine: "show <name>",
Short: "show details about local repository",
Long: `
Show command shows full information about local package repository.
ex:
$ aptly repo show testing
`,
Flag: *flag.NewFlagSet("aptly-repo-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
+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
}
+29 -17
View File
@@ -1,28 +1,33 @@
package main package cmd
import ( import (
"fmt" "fmt"
"github.com/gonuts/commander" "github.com/smira/aptly/aptly"
"github.com/gonuts/flag" "github.com/smira/aptly/deb"
"github.com/smira/aptly/debian" "github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"net" "net"
"net/http" "net/http"
"os" "os"
"sort" "sort"
"strings"
) )
func aptlyServe(cmd *commander.Command, args []string) error { func aptlyServe(cmd *commander.Command, args []string) error {
var err error var err error
publishedCollection := debian.NewPublishedRepoCollection(context.database) if len(args) != 0 {
snapshotCollection := debian.NewSnapshotCollection(context.database) cmd.Usage()
return commander.ErrCommandError
}
if publishedCollection.Len() == 0 { if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
fmt.Printf("No published repositories, unable to serve.\n") fmt.Printf("No published repositories, unable to serve.\n")
return nil return nil
} }
listen := cmd.Flag.Lookup("listen").Value.String() listen := context.Flags().Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen) listenHost, listenPort, err := net.SplitHostPort(listen)
@@ -39,11 +44,11 @@ func aptlyServe(cmd *commander.Command, args []string) error {
fmt.Printf("Serving published repositories, recommended apt sources list:\n\n") fmt.Printf("Serving published repositories, recommended apt sources list:\n\n")
sources := make(sort.StringSlice, 0, publishedCollection.Len()) sources := make(sort.StringSlice, 0, context.CollectionFactory().PublishedRepoCollection().Len())
published := make(map[string]*debian.PublishedRepo, publishedCollection.Len()) published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
err = publishedCollection.ForEach(func(repo *debian.PublishedRepo) error { err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := publishedCollection.LoadComplete(repo, snapshotCollection) err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil { if err != nil {
return err return err
} }
@@ -71,14 +76,20 @@ func aptlyServe(cmd *commander.Command, args []string) error {
} }
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n", fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component) repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
if utils.StrSliceHasItem(repo.Architectures, "source") {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
}
} }
context.database.Close() publicPath := context.GetPublishedStorage("").(aptly.LocalPublishedStorage).PublicPath()
ShutdownContext()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen) fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
err = http.ListenAndServe(listen, http.FileServer(http.Dir(context.packageRepository.PublicPath()))) err = http.ListenAndServe(listen, http.FileServer(http.Dir(publicPath)))
if err != nil { if err != nil {
return fmt.Errorf("unable to serve: %s", err) return fmt.Errorf("unable to serve: %s", err)
} }
@@ -89,12 +100,13 @@ func makeCmdServe() *commander.Command {
cmd := &commander.Command{ cmd := &commander.Command{
Run: aptlyServe, Run: aptlyServe,
UsageLine: "serve", UsageLine: "serve",
Short: "start embedded HTTP server to serve published repositories", Short: "HTTP serve published repositories",
Long: ` Long: `
Command serve starts embedded HTTP server (not suitable for real production usage) to serve Command serve starts embedded HTTP server (not suitable for real production usage) to serve
contents of public/ subdirectory of aptly's root that contains published repositories. contents of public/ subdirectory of aptly's root that contains published repositories.
ex: Example:
$ aptly serve -listen=:8080 $ aptly serve -listen=:8080
`, `,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError), Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
+25
View File
@@ -0,0 +1,25 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
makeCmdSnapshotRename(),
makeCmdSnapshotSearch(),
makeCmdSnapshotFilter(),
},
}
}
+108
View File
@@ -0,0 +1,108 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var (
err error
snapshot *deb.Snapshot
)
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
// aptly snapshot create snap from mirror mirror
var repo *deb.RemoteRepo
repoName, snapshotName := args[3], args[0]
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = deb.NewSnapshotFromRepository(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 4 && args[1] == "from" && args[2] == "repo" {
// aptly snapshot create snap from repo repo
var repo *deb.LocalRepo
localRepoName, snapshotName := args[3], args[0]
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(localRepoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = deb.NewSnapshotFromLocalRepo(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := deb.NewPackageList()
snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return commander.ErrCommandError
}
err = context.CollectionFactory().SnapshotCollection().Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create <name> from mirror <mirror-name> | from repo <repo-name> | empty",
Short: "creates snapshot of mirror (local repository) contents",
Long: `
Command create <name> from mirror makes persistent immutable snapshot of remote
repository mirror. Snapshot could be published or further modified using
merge, pull and other aptly features.
Command create <name> from repo makes persistent immutable snapshot of local
repository. Snapshot could be processed as mirror snapshots, and mixed with
snapshots of remote mirrors.
Command create <name> empty creates empty snapshot that could be used as a
basis for snapshot pull operations, for example. As snapshots are immutable,
creating one empty snapshot should be enough.
Example:
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`,
}
return cmd
}
+111
View File
@@ -0,0 +1,111 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
onlyMatching := context.Flags().Lookup("only-matching").Value.Get().(bool)
// Load <name-a> snapshot
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := context.CollectionFactory().SnapshotCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotB)
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
context.Progress().Printf("Snapshots are identical.\n")
} else {
context.Progress().Printf(" Arch | Package | Version in A | Version in B\n")
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
var verA, verB, pkg, arch, code string
if pdiff.Left == nil {
verA = "-"
verB = pdiff.Right.Version
pkg = pdiff.Right.Name
arch = pdiff.Right.Architecture
} else {
pkg = pdiff.Left.Name
arch = pdiff.Left.Architecture
verA = pdiff.Left.Version
if pdiff.Right == nil {
verB = "-"
} else {
verB = pdiff.Right.Version
}
}
if pdiff.Left == nil {
code = "@g+@|"
} else {
if pdiff.Right == nil {
code = "@r-@|"
} else {
code = "@y!@|"
}
}
context.Progress().ColoredPrintf(code+" %-6s | %-40s | %-40s | %-40s", arch, pkg, verA, verB)
}
}
return err
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "difference between two snapshots",
Long: `
Displays difference in packages between two snapshots. Snapshot is a list
of packages, so difference between snapshots is a difference between package
lists. Package could be either completely missing in one snapshot, or package
is present in both snapshots with different versions.
Example:
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
+80
View File
@@ -0,0 +1,80 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
published := context.CollectionFactory().PublishedRepoCollection().BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: snapshot is published")
}
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().BySnapshotSource(snapshot)
if len(snapshots) > 0 {
fmt.Printf("Snapshot `%s` was used as a source in following snapshots:\n", snapshot.Name)
for _, snap := range snapshots {
fmt.Printf(" * %s\n", snap)
}
return fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use -force to override")
}
}
err = context.CollectionFactory().SnapshotCollection().Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about a snapshot. If snapshot is published,
it can't be dropped.
Example:
$ aptly snapshot drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "remove snapshot even if it was used as source for other snapshots")
return cmd
}
+107
View File
@@ -0,0 +1,107 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
"strings"
)
func aptlySnapshotFilter(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return commander.ErrCommandError
}
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
// Load <source> snapshot
source, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Convert snapshot to package list
context.Progress().Printf("Loading packages (%d)...\n", source.RefList().Len())
packageList, err := deb.NewPackageListFromRefList(source.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
context.Progress().Printf("Building indexes...\n")
packageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 && withDeps {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-2)
for i, arg := range args[2:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
}
// Filter with dependencies as requested
result, err := packageList.Filter(queries, withDeps, nil, context.DependencyOptions(), architecturesList)
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Create <destination> snapshot
destination := deb.NewSnapshotFromPackageList(args[1], []*deb.Snapshot{source}, result,
fmt.Sprintf("Filtered '%s', query was: '%s'", source.Name, strings.Join(args[2:], " ")))
err = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
context.Progress().Printf("\nSnapshot %s successfully filtered.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func makeCmdSnapshotFilter() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotFilter,
UsageLine: "filter <source> <destination> <package-query> ...",
Short: "filter packages in snapshot producing another snapshot",
Long: `
Command filter does filtering in snapshot <source>, producing another
snapshot <destination>. Packages could be specified simply
as 'package-name' or as package queries.
Example:
$ aptly snapshot filter wheezy-main wheezy-required 'Priorioty (required)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-filter", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependent packages as well")
return cmd
}
+66
View File
@@ -0,0 +1,66 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
collection := context.CollectionFactory().SnapshotCollection()
if raw {
collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
fmt.Printf("%s\n", snapshot.Name)
return nil
})
} else {
if collection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
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")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
}
return err
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "list snapshots",
Long: `
Command list shows full list of snapshots created.
Example:
$ aptly snapshot list
`,
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
cmd.Flag.String("sort", "name", "display list in 'name' or creation 'time' order")
return cmd
}
+90
View File
@@ -0,0 +1,90 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"strings"
)
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return commander.ErrCommandError
}
sources := make([]*deb.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = context.CollectionFactory().SnapshotCollection().ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
latest := context.Flags().Lookup("latest").Value.Get().(bool)
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
if noRemove && latest {
return fmt.Errorf("-no-remove and -latest can't be specified together")
}
overrideMatching := !latest && !noRemove
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList(), overrideMatching, false)
}
if latest {
result.FilterLatestRefs()
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := deb.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots",
Long: `
Merge command merges several <source> snapshots into one <destination> snapshot.
Merge happens from left to right. By default, packages with the same
name-architecture pair are replaced during merge (package from latest snapshot
on the list wins). If run with only one source snapshot, merge copies <source> into
<destination>.
Example:
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
}
cmd.Flag.Bool("latest", false, "use only the latest version of each package")
cmd.Flag.Bool("no-remove", false, "don't remove duplicate arch/name packages")
return cmd
}
+174
View File
@@ -0,0 +1,174 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
"strings"
)
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return commander.ErrCommandError
}
noDeps := context.Flags().Lookup("no-deps").Value.Get().(bool)
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
allMatches := context.Flags().Lookup("all-matches").Value.Get().(bool)
// Load <name> snapshot
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := context.CollectionFactory().SnapshotCollection().ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
context.Progress().Printf("Dependencies would be pulled into snapshot:\n %s\nfrom snapshot:\n %s\nand result would be saved as new snapshot %s.\n",
snapshot, source, args[2])
// Convert snapshot to package list
context.Progress().Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := deb.NewPackageListFromRefList(snapshot.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := deb.NewPackageListFromRefList(source.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
context.Progress().Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
var archQuery deb.PackageQuery = &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: ""}
for _, arch := range architecturesList {
archQuery = &deb.OrQuery{L: &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: arch}, R: archQuery}
}
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-3)
for i, arg := range args[3:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
// Add architecture filter
queries[i] = &deb.AndQuery{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)
}
}
// If !allMatches, add only first matching name-arch package
if !seen || allMatches {
packageList.Add(pkg)
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
}
alreadySeen[key] = true
return nil
})
alreadySeen = nil
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := deb.NewSnapshotFromPackageList(args[2], []*deb.Snapshot{snapshot, source}, packageList,
fmt.Sprintf("Pulled into '%s' with '%s' as source, pull request was: '%s'", snapshot.Name, source.Name, strings.Join(args[3:], " ")))
err = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
context.Progress().Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
}
return err
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-query> ...",
Short: "pull packages from another snapshot",
Long: `
Command pull pulls new packages along with its' dependencies to snapshot <name>
from snapshot <source>. Pull can upgrade package version in <name> with
versions from <source> following dependencies. New snapshot <destination>
is created as a result of this process. Packages could be specified simply
as 'package-name' or as package queries.
Example:
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
cmd.Flag.Bool("no-remove", false, "don't remove other package versions when pulling package")
cmd.Flag.Bool("all-matches", false, "pull all the packages that satisfy the dependency version requirements")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotRename(cmd *commander.Command, args []string) error {
var (
err error
snapshot *deb.Snapshot
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().SnapshotCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: snapshot %s already exists", newName)
}
snapshot.Name = newName
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nSnapshot %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdSnapshotRename() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames snapshot",
Long: `
Command changes name of the snapshot. Snapshot name should be unique.
Example:
$ aptly snapshot rename wheezy-min wheezy-main
`,
}
return cmd
}
+129
View File
@@ -0,0 +1,129 @@
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)
}
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 makeCmdSnapshotSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> <package-query>",
Short: "search snapshot for packages matching query",
Long: `
Command search displays list of packages in snapshot that match package query
Example:
$ aptly snapshot search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-search", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
return cmd
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
return err
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show <name>",
Short: "shows details about snapshot",
Long: `
Command show displays full information about a snapshot.
Example:
$ aptly snapshot show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
+115
View File
@@ -0,0 +1,115 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return commander.ErrCommandError
}
snapshots := make([]*deb.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = context.CollectionFactory().SnapshotCollection().ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
context.Progress().Printf("Loading packages...\n")
packageList, err := deb.NewPackageListFromRefList(snapshots[0].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := deb.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
var pL *deb.PackageList
for i := 1; i < len(snapshots); i++ {
pL, err = deb.NewPackageListFromRefList(snapshots[i].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(true)
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
context.Progress().Printf("Verifying...\n")
missing, err := packageList.VerifyDependencies(context.DependencyOptions(), architecturesList, sourcePackageList, context.Progress())
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
context.Progress().Printf("All dependencies are satisfied.\n")
} else {
context.Progress().Printf("Missing dependencies (%d):\n", len(missing))
deps := make([]string, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
context.Progress().Printf(" %s\n", dep)
}
}
return err
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verify dependencies in snapshot",
Long: `
Verify does dependency resolution in snapshot <name>, possibly using additional
snapshots <source> as dependency sources. All unsatisfied dependencies are
printed.
Example:
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
}
return cmd
}
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdTask() *commander.Command {
return &commander.Command{
UsageLine: "task",
Short: "manage aptly tasks",
Subcommands: []*commander.Command{
makeCmdTaskRun(),
},
}
}
+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
}
+9 -5
View File
@@ -1,13 +1,18 @@
package main package cmd
import ( import (
"fmt" "fmt"
"github.com/gonuts/commander" "github.com/smira/aptly/aptly"
"github.com/gonuts/flag" "github.com/smira/commander"
) )
func aptlyVersion(cmd *commander.Command, args []string) error { func aptlyVersion(cmd *commander.Command, args []string) error {
fmt.Printf("aptly version: %s\n", Version) if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("aptly version: %s\n", aptly.Version)
return nil return nil
} }
@@ -22,6 +27,5 @@ Shows aptly version.
ex: ex:
$ aptly version $ aptly version
`, `,
Flag: *flag.NewFlagSet("aptly-version", flag.ExitOnError),
} }
} }
-174
View File
@@ -1,174 +0,0 @@
package main
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
graph := gographviz.NewGraph()
graph.SetDir(true)
graph.SetName("aptly")
fmt.Printf("Loading mirrors...\n")
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
err := repoCollection.LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())),
})
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
err := snapshotCollection.LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
})
if snapshot.SourceKind == "repo" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
} else if snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
publishedCollection := debian.NewPublishedRepoCollection(context.database)
publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
})
graph.AddEdge(graphvizEscape(repo.SnapshotUUID), "", graphvizEscape(repo.UUID), "", true, nil)
return nil
})
fmt.Printf("Generating graph...\n")
buf := bytes.NewBufferString(graph.String())
tempfile, err := ioutil.TempFile("", "aptly-graph")
if err != nil {
return err
}
tempfile.Close()
os.Remove(tempfile.Name())
tempfilename := tempfile.Name() + ".png"
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
command.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
}
_, err = io.Copy(stdin, buf)
if err != nil {
return err
}
err = stdin.Close()
if err != nil {
return err
}
err = command.Wait()
if err != nil {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
}
return err
}
func makeCmdGraph() *commander.Command {
cmd := &commander.Command{
Run: aptlyGraph,
UsageLine: "graph",
Short: "display graph of dependencies between aptly objects (requires graphviz)",
Long: `
Command graph displays relationship between mirrors, snapshots and published repositories using
graphviz package to render graph as image.
ex:
$ aptly graph
`,
Flag: *flag.NewFlagSet("aptly-graph", flag.ExitOnError),
}
return cmd
}
-362
View File
@@ -1,362 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getVerifier(cmd *commander.Command) (utils.Verifier, error) {
if utils.Config.GpgDisableVerify || cmd.Flag.Lookup("ignore-signatures").Value.Get().(bool) {
return nil, nil
}
verifier := &utils.GpgVerifier{}
for _, keyRing := range keyRings.keyRings {
verifier.AddKeyring(keyRing)
}
err := verifier.InitKeyring()
if err != nil {
return nil, err
}
return verifier, nil
}
type keyRingsFlag struct {
keyRings []string
}
func (k *keyRingsFlag) Set(value string) error {
k.keyRings = append(k.keyRings, value)
return nil
}
func (k *keyRingsFlag) Get() interface{} {
return k.keyRings
}
func (k *keyRingsFlag) String() string {
return strings.Join(k.keyRings, ",")
}
var keyRings = keyRingsFlag{}
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
if repoCollection.Len() > 0 {
fmt.Printf("List of mirrors:\n")
repos := make(sort.StringSlice, repoCollection.Len())
i := 0
repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
repos[i] = repo.String()
i++
return nil
})
sort.Strings(repos)
for _, repo := range repos {
fmt.Printf(" * %s\n", repo)
}
fmt.Printf("\nTo get more information about mirror, run `aptly mirror show <name>`.\n")
} else {
fmt.Printf("No mirrors found, create one with `aptly mirror create ...`.\n")
}
return err
}
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
}
repo, err := debian.NewRemoteRepo(args[0], args[1], args[2], args[3:], context.architecturesList)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to fetch mirror: %s", err)
}
repoCollection := debian.NewRemoteRepoCollection(context.database)
err = repoCollection.Add(repo)
if err != nil {
return fmt.Errorf("unable to add mirror: %s", err)
}
fmt.Printf("\nMirror %s successfully added.\nYou can run 'aptly mirror update %s' to download repository contents.\n", repo, repo.Name)
return err
}
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
fmt.Printf("Last update: %s\n", repo.LastDownloadDate.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
}
fmt.Printf("\nInformation from release file:\n")
for _, k := range utils.StrMapSortedKeys(repo.Meta) {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
} else {
ListPackagesRefList(repo.RefList())
}
}
return err
}
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := cmd.Flag.Lookup("ignore-checksums").Value.Get().(bool)
verifier, err := getVerifier(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
err = repo.Fetch(context.downloader, verifier)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = repo.Download(context.downloader, packageCollection, context.packageRepository, ignoreMismatch)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = repoCollection.Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
fmt.Printf("\nMirror `%s` has been successfully updated.\n", repo.Name)
return err
}
func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshots := snapshotCollection.ByRemoteRepoSource(repo)
if len(snapshots) > 0 {
fmt.Printf("Mirror `%s` was used to create following snapshots:\n", repo.Name)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
return fmt.Errorf("won't delete mirror with snapshots, use -force to override")
}
}
err = repoCollection.Drop(repo)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Mirror `%s` has been removed.\n", repo.Name)
return err
}
func makeCmdMirrorCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorCreate,
UsageLine: "create <name> <archive url> <distribution> [<component1> ...]",
Short: "create new mirror of Debian repository",
Long: `
Create records information about new mirror and fetches Release file (it doesn't download packages).
ex:
$ aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
`,
Flag: *flag.NewFlagSet("aptly-mirror-create", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorList() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorList,
UsageLine: "list",
Short: "list mirrors of remote repositories",
Long: `
List shows full list of remote repositories.
ex:
$ aptly mirror list
`,
Flag: *flag.NewFlagSet("aptly-mirror-list", flag.ExitOnError),
}
cmd.Flag.Bool("v", false, "enable verbose output")
return cmd
}
func makeCmdMirrorShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorShow,
UsageLine: "show <name>",
Short: "show details about remote repository mirror",
Long: `
Show shows full information about mirror.
ex:
$ aptly mirror show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdMirrorUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorUpdate,
UsageLine: "update <name>",
Short: "update packages from remote mirror",
Long: `
Update downloads list of packages and package files.
ex:
$ aptly mirror update wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Var(&keyRings, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
return cmd
}
func makeCmdMirrorDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorDrop,
UsageLine: "drop <name>",
Short: "delete remote repository mirror",
Long: `
Drop deletes information about remote repository mirror. Package data is not deleted
(it could be still used by other mirrors or snapshots).
ex:
$ aptly mirror drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-mirror-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force mirror deletion even if used by snapshots")
return cmd
}
func makeCmdMirror() *commander.Command {
return &commander.Command{
UsageLine: "mirror",
Short: "manage mirrors of remote repositories",
Subcommands: []*commander.Command{
makeCmdMirrorCreate(),
makeCmdMirrorList(),
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
},
Flag: *flag.NewFlagSet("aptly-mirror", flag.ExitOnError),
}
}
-266
View File
@@ -1,266 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/smira/aptly/utils"
"sort"
"strings"
)
func getSigner(cmd *commander.Command) (utils.Signer, error) {
if cmd.Flag.Lookup("skip-signing").Value.Get().(bool) || utils.Config.GpgDisableSign {
return nil, nil
}
signer := &utils.GpgSigner{}
key := cmd.Flag.Lookup("gpg-key").Value.String()
if key != "" {
signer.SetKey(key)
}
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
func aptlyPublishSnapshot(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
} else {
prefix = ""
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
var sourceRepo *debian.RemoteRepo
if snapshot.SourceKind == "repo" && len(snapshot.SourceIDs) == 1 {
repoCollection := debian.NewRemoteRepoCollection(context.database)
sourceRepo, _ = repoCollection.ByUUID(snapshot.SourceIDs[0])
}
component := cmd.Flag.Lookup("component").Value.String()
if component == "" {
if sourceRepo != nil && len(sourceRepo.Components) == 1 {
component = sourceRepo.Components[0]
} else {
component = "main"
}
}
distribution := cmd.Flag.Lookup("distribution").Value.String()
if distribution == "" {
if sourceRepo != nil {
distribution = sourceRepo.Distribution
}
if distribution == "" {
return fmt.Errorf("unable to guess distribution name, please specify explicitly")
}
}
published, err := debian.NewPublishedRepo(prefix, distribution, component, context.architecturesList, snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
duplicate := publishedCollecton.CheckDuplicate(published)
if duplicate != nil {
publishedCollecton.LoadComplete(duplicate, snapshotCollection)
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(cmd)
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
packageCollection := debian.NewPackageCollection(context.database)
err = published.Publish(context.packageRepository, packageCollection, signer)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = publishedCollecton.Add(published)
if err != nil {
return fmt.Errorf("unable to save to DB: %s", err)
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
fmt.Printf("\nSnapshot %s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
snapshot.Name, context.packageRepository.PublicPath())
fmt.Printf("Now you can add following line to apt sources:\n")
fmt.Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component)
fmt.Printf("Don't forget to add your GPG key to apt with apt-key.\n")
fmt.Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
return err
}
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
snapshotCollection := debian.NewSnapshotCollection(context.database)
if publishedCollecton.Len() == 0 {
fmt.Printf("No snapshots have been published. Publish a snapshot by running `aptly publish snapshot ...`.\n")
return err
}
published := make(sort.StringSlice, 0, publishedCollecton.Len())
err = publishedCollecton.ForEach(func(repo *debian.PublishedRepo) error {
err := publishedCollecton.LoadComplete(repo, snapshotCollection)
if err != nil {
return err
}
published = append(published, repo.String())
return nil
})
if err != nil {
return fmt.Errorf("unable to load list of repos: %s", err)
}
sort.Strings(published)
fmt.Printf("Published repositories:\n")
for _, description := range published {
fmt.Printf(" * %s\n", description)
}
return err
}
func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
}
distribution := args[0]
prefix := "."
if len(args) == 2 {
prefix = args[1]
}
publishedCollecton := debian.NewPublishedRepoCollection(context.database)
err = publishedCollecton.Remove(context.packageRepository, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
fmt.Printf("\nPublished repositroy has been removed successfully.\n")
return err
}
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshot,
UsageLine: "snapshot <name> [<prefix>]",
Short: "makes Debian repository out of snapshot",
Long: `
Command publish oublishes snapshot as Debian repository ready to be used by apt tools.
ex.
$ aptly publish snapshot wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
return cmd
}
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
Short: "removes files of published repository",
Long: `
Command removes whatever has been published under specified prefix and distribution name.
ex.
$ aptly publish drop wheezy
`,
Flag: *flag.NewFlagSet("aptly-publish-drop", flag.ExitOnError),
}
return cmd
}
func makeCmdPublishList() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishList,
UsageLine: "list",
Short: "displays list of published repositories",
Long: `
Display command displays list of currently published snapshots with information about published root.
ex.
$ aptly publish list
`,
Flag: *flag.NewFlagSet("aptly-publish-list", flag.ExitOnError),
}
return cmd
}
func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Short: "manage published repositories",
Subcommands: []*commander.Command{
makeCmdPublishSnapshot(),
makeCmdPublishList(),
makeCmdPublishDrop(),
},
Flag: *flag.NewFlagSet("aptly-publish", flag.ExitOnError),
}
}
-730
View File
@@ -1,730 +0,0 @@
package main
import (
"fmt"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
"github.com/smira/aptly/debian"
"github.com/wsxiaoys/terminal/color"
"sort"
"strings"
)
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
var (
err error
snapshot *debian.Snapshot
)
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
// aptly snapshot create snap from mirror mirror
repoName, snapshotName := args[3], args[0]
repoCollection := debian.NewRemoteRepoCollection(context.database)
repo, err := repoCollection.ByName(repoName)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repoCollection.LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
snapshot, err = debian.NewSnapshotFromRepository(snapshotName, repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
} else if len(args) == 2 && args[1] == "empty" {
// aptly snapshot create snap empty
snapshotName := args[0]
packageList := debian.NewPackageList()
snapshot = debian.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
err = snapshotCollection.Add(snapshot)
if err != nil {
return fmt.Errorf("unable to add snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)
return err
}
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
if snapshotCollection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
snapshots := make(sort.StringSlice, snapshotCollection.Len())
i := 0
snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
snapshots[i] = snapshot.String()
i++
return nil
})
sort.Strings(snapshots)
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
return err
}
func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
fmt.Printf("Name: %s\n", snapshot.Name)
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
fmt.Printf("Description: %s\n", snapshot.Description)
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
return err
}
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
snapshots := make([]*debian.Snapshot, len(args))
for i := range snapshots {
snapshots[i], err = snapshotCollection.ByName(args[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
err = snapshotCollection.LoadComplete(snapshots[i])
if err != nil {
return fmt.Errorf("unable to verify: %s", err)
}
}
packageList, err := debian.NewPackageListFromRefList(snapshots[0].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := debian.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
for i := 1; i < len(snapshots); i++ {
pL, err := debian.NewPackageListFromRefList(snapshots[i].RefList(), packageCollection)
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
}
}
sourcePackageList.PrepareIndex()
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
missing, err := packageList.VerifyDependencies(context.dependencyOptions, architecturesList, sourcePackageList)
if err != nil {
return fmt.Errorf("unable to verify dependencies: %s", err)
}
if len(missing) == 0 {
fmt.Printf("All dependencies are satisfied.\n")
} else {
fmt.Printf("Missing dependencies (%d):\n", len(missing))
deps := make(sort.StringSlice, len(missing))
i := 0
for _, dep := range missing {
deps[i] = dep.String()
i++
}
sort.Strings(deps)
for _, dep := range deps {
fmt.Printf(" %s\n", dep)
}
}
return err
}
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
}
noDeps := cmd.Flag.Lookup("no-deps").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name> snapshot
snapshot, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
// Load <source> snapshot
source, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
err = snapshotCollection.LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
fmt.Printf("Dependencies would be pulled into snapshot:\n %s\nfrom snapshot:\n %s\nand result would be saved as new snapshot %s.\n",
snapshot, source, args[2])
// Convert snapshot to package list
fmt.Printf("Loading packages (%d)...\n", snapshot.RefList().Len()+source.RefList().Len())
packageList, err := debian.NewPackageListFromRefList(snapshot.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList, err := debian.NewPackageListFromRefList(source.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
fmt.Printf("Building indexes...\n")
packageList.PrepareIndex()
sourcePackageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.architecturesList) > 0 {
architecturesList = context.architecturesList
} else {
architecturesList = packageList.Architectures()
}
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]debian.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = debian.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]debian.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
}
// Go over list of initial dependencies + list of dependencies found
for i := 0; i < len(dependencies); i++ {
dep := dependencies[i]
// Search for package that can satisfy dependencies
pkg := sourcePackageList.Search(dep)
if pkg == nil {
color.Printf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
fmt.Printf("\n")
continue
}
// Remove all packages with the same name and architecture
for p := packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
color.Printf("@r[-]@| %s removed", p)
fmt.Printf("\n")
p = packageList.Search(debian.Dependency{Architecture: arch, Pkg: pkg.Name})
}
// Add new discovered package
packageList.Add(pkg)
color.Printf("@g[+]@| %s added", pkg)
fmt.Printf("\n")
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := debian.NewPackageList()
pL.Add(pkg)
missing, err := pL.VerifyDependencies(context.dependencyOptions, []string{arch}, packageList)
if err != nil {
color.Printf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
fmt.Printf("\n")
}
// Append missing dependencies to the list of dependencies to satisfy
for _, misDep := range missing {
found := false
for _, d := range dependencies {
if d == misDep {
found = true
break
}
}
if !found {
dependencies = append(dependencies, misDep)
}
}
}
}
if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
fmt.Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
destination := debian.NewSnapshotFromPackageList(args[2], []*debian.Snapshot{snapshot, source}, packageList,
fmt.Sprintf("Pulled into '%s' with '%s' as source, pull request was: '%s'", snapshot.Name, source.Name, strings.Join(args[3:], " ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
}
return err
}
func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
}
onlyMatching := cmd.Flag.Lookup("only-matching").Value.Get().(bool)
snapshotCollection := debian.NewSnapshotCollection(context.database)
packageCollection := debian.NewPackageCollection(context.database)
// Load <name-a> snapshot
snapshotA, err := snapshotCollection.ByName(args[0])
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotA)
if err != nil {
return fmt.Errorf("unable to load snapshot A: %s", err)
}
// Load <name-b> snapshot
snapshotB, err := snapshotCollection.ByName(args[1])
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
err = snapshotCollection.LoadComplete(snapshotB)
if err != nil {
return fmt.Errorf("unable to load snapshot B: %s", err)
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), packageCollection)
if err != nil {
return fmt.Errorf("unable to calculate diff: %s", err)
}
if len(diff) == 0 {
fmt.Printf("Snapshots are identical.\n")
} else {
fmt.Printf(" Arch | Package | Version in A | Version in B\n")
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
var verA, verB, pkg, arch, code string
if pdiff.Left == nil {
verA = "-"
verB = pdiff.Right.Version
pkg = pdiff.Right.Name
arch = pdiff.Right.Architecture
} else {
pkg = pdiff.Left.Name
arch = pdiff.Left.Architecture
verA = pdiff.Left.Version
if pdiff.Right == nil {
verB = "-"
} else {
verB = pdiff.Right.Version
}
}
if pdiff.Left == nil {
code = "@g+@|"
} else {
if pdiff.Right == nil {
code = "@r-@|"
} else {
code = "@y!@|"
}
}
color.Printf(code+" %-6s | %-40s | %-40s | %-40s\n", arch, pkg, verA, verB)
}
}
return err
}
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
}
snapshotCollection := debian.NewSnapshotCollection(context.database)
sources := make([]*debian.Snapshot, len(args)-1)
for i := 0; i < len(args)-1; i++ {
sources[i], err = snapshotCollection.ByName(args[i+1])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
return fmt.Errorf("unable to load snapshot: %s", err)
}
}
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList())
}
sourceDescription := make([]string, len(sources))
for i, s := range sources {
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
}
// Create <destination> snapshot
destination := debian.NewSnapshotFromRefList(args[0], sources, result,
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
err = snapshotCollection.Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
}
name := args[0]
snapshotCollection := debian.NewSnapshotCollection(context.database)
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
publishedRepoCollection := debian.NewPublishedRepoCollection(context.database)
published := publishedRepoCollection.BySnapshot(snapshot)
if len(published) > 0 {
fmt.Printf("Snapshot `%s` is published currently:\n", snapshot.Name)
for _, repo := range published {
err = publishedRepoCollection.LoadComplete(repo, snapshotCollection)
if err != nil {
return fmt.Errorf("unable to load published: %s", err)
}
fmt.Printf(" * %s\n", repo)
}
return fmt.Errorf("unable to drop: snapshot is published")
}
force := cmd.Flag.Lookup("force").Value.Get().(bool)
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
fmt.Printf("Snapshot `%s` was used as a source in following snapshots:\n", snapshot.Name)
for _, snap := range snapshots {
fmt.Printf(" * %s\n", snap)
}
return fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use -force to override")
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
fmt.Printf("Snapshot `%s` has been dropped.\n", snapshot.Name)
return err
}
func makeCmdSnapshotCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotCreate,
UsageLine: "create <name> from mirror <mirror-name> | create <name> empty",
Short: "creates immutable snapshot of mirror contents",
Long: `
Command create .. from mirror makes persistent immutable snapshot of remote repository mirror. Snapshot could be
published or further modified using merge, pull and other aptly features.
Command create .. empty creates empty snapshot that could be used as a basis for snapshot pull operations, for example.
As snapshots are immutable, creating one empty snapshot should be enough.
ex.
$ aptly snapshot create wheezy-main-today from mirror wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-create", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotList() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotList,
UsageLine: "list",
Short: "lists snapshots",
Long: `
Command list shows full list of snapshots created.
ex:
$ aptly snapshot list
`,
Flag: *flag.NewFlagSet("aptly-snapshot-list", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotShow() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotShow,
UsageLine: "show <name>",
Short: "shows details about snapshot",
Long: `
Command show displays full information about snapshot.
ex.
$ aptly snapshot show wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-packages", false, "show list of packages")
return cmd
}
func makeCmdSnapshotVerify() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotVerify,
UsageLine: "verify <name> [<source> ...]",
Short: "verifies that dependencies are satisfied in snapshot",
Long: `
Verify does depenency resolution in snapshot, possibly using additional snapshots as dependency sources.
All unsatisfied dependencies are returned.
ex.
$ aptly snapshot verify wheezy-main wheezy-contrib wheezy-non-free
`,
Flag: *flag.NewFlagSet("aptly-snapshot-verify", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
Short: "performs partial upgrades (pulls new packages) from another snapshot",
Long: `
Command pull pulls new packages along with its dependencies in <name> snapshot
from <source> snapshot. Also can upgrade package version from one snapshot into
another, once again along with dependencies. New snapshot <destination> is created as result of this
process. Packages could be specified simply as 'package-name' or as dependency 'package-name (>= version)'.
ex.
$ aptly snapshot pull wheezy-main wheezy-backports wheezy-new-xorg xorg-server-server
`,
Flag: *flag.NewFlagSet("aptly-snapshot-pull", flag.ExitOnError),
}
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
return cmd
}
func makeCmdSnapshotDiff() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDiff,
UsageLine: "diff <name-a> <name-b>",
Short: "calculates difference in packages between two snapshots",
Long: `
Command diff shows list of missing and new packages, difference in package versions between two snapshots.
ex.
$ aptly snapshot diff -only-matching wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-diff", flag.ExitOnError),
}
cmd.Flag.Bool("only-matching", false, "display diff only for matching packages (don't display missing packages)")
return cmd
}
func makeCmdSnapshotMerge() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMerge,
UsageLine: "merge <destination> <source> [<source>...]",
Short: "merges snapshots into one, replacing matching packages",
Long: `
Merge merges several snapshots into one. Merge happens from left to right. Packages with the same
name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
If run with only one source snapshot, merge copies source into destination.
ex.
$ aptly snapshot merge wheezy-w-backports wheezy-main wheezy-backports
`,
Flag: *flag.NewFlagSet("aptly-snapshot-merge", flag.ExitOnError),
}
return cmd
}
func makeCmdSnapshotDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotDrop,
UsageLine: "drop <name>",
Short: "delete snapshot",
Long: `
Drop removes information about snapshot. If snapshot is published,
it can't be dropped.
ex.
$ aptly snapshot drop wheezy-main
`,
Flag: *flag.NewFlagSet("aptly-snapshot-drop", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "remove snapshot even if it was used as source for other snapshots")
return cmd
}
func makeCmdSnapshot() *commander.Command {
return &commander.Command{
UsageLine: "snapshot",
Short: "manage snapshots of repositories",
Subcommands: []*commander.Command{
makeCmdSnapshotCreate(),
makeCmdSnapshotList(),
makeCmdSnapshotShow(),
makeCmdSnapshotVerify(),
makeCmdSnapshotPull(),
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
},
Flag: *flag.NewFlagSet("aptly-snapshot", flag.ExitOnError),
}
}
-27
View File
@@ -1,27 +0,0 @@
package main
import (
"fmt"
"github.com/smira/aptly/debian"
)
//ListPackagesRefList shows list of packages in PackageRefList
func ListPackagesRefList(reflist *debian.PackageRefList) (err error) {
fmt.Printf("Packages:\n")
packageCollection := debian.NewPackageCollection(context.database)
err = reflist.ForEach(func(key []byte) error {
p, err := packageCollection.ByKey(key)
if err != nil {
return err
}
fmt.Printf(" %s\n", p)
return nil
})
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
return
}
+84 -9
View File
@@ -1,19 +1,27 @@
package utils package console
import ( import (
"fmt" "fmt"
"github.com/cheggaaa/pb" "github.com/cheggaaa/pb"
"github.com/smira/aptly/aptly"
"github.com/wsxiaoys/terminal/color"
"strings"
) )
const ( const (
codePrint = iota codePrint = iota
codeProgress codeProgress
codeHideProgress codeHideProgress
codeStop
codeFlush
codeBarEnabled
codeBarDisabled
) )
type printTask struct { type printTask struct {
code int code int
message string message string
reply chan bool
} }
// Progress is a progress displaying subroutine, it allows to show download and other operations progress // Progress is a progress displaying subroutine, it allows to show download and other operations progress
@@ -26,10 +34,14 @@ type Progress struct {
barShown bool barShown bool
} }
// Check interface
var (
_ aptly.Progress = (*Progress)(nil)
)
// NewProgress creates new progress instance // NewProgress creates new progress instance
func NewProgress() *Progress { func NewProgress() *Progress {
return &Progress{ return &Progress{
stop: make(chan bool),
stopped: make(chan bool), stopped: make(chan bool),
queue: make(chan printTask, 100), queue: make(chan printTask, 100),
} }
@@ -43,11 +55,18 @@ func (p *Progress) Start() {
// Shutdown shuts down progress display // Shutdown shuts down progress display
func (p *Progress) Shutdown() { func (p *Progress) Shutdown() {
p.ShutdownBar() p.ShutdownBar()
p.stop <- true p.queue <- printTask{code: codeStop}
<-p.stopped <-p.stopped
} }
// InitBar creates progressbar for count bytes // Flush waits for all queued messages to be displayed
func (p *Progress) Flush() {
ch := make(chan bool)
p.queue <- printTask{code: codeFlush, reply: ch}
<-ch
}
// InitBar starts progressbar for count bytes or count items
func (p *Progress) InitBar(count int64, isBytes bool) { func (p *Progress) InitBar(count int64, isBytes bool) {
if p.bar != nil { if p.bar != nil {
panic("bar already initialized") panic("bar already initialized")
@@ -64,7 +83,8 @@ func (p *Progress) InitBar(count int64, isBytes bool) {
p.bar.SetUnits(pb.U_BYTES) p.bar.SetUnits(pb.U_BYTES)
p.bar.ShowSpeed = true p.bar.ShowSpeed = true
} }
p.barShown = false
p.queue <- printTask{code: codeBarEnabled}
p.bar.Start() p.bar.Start()
} }
} }
@@ -75,6 +95,7 @@ func (p *Progress) ShutdownBar() {
return return
} }
p.bar.Finish() p.bar.Finish()
p.queue <- printTask{code: codeBarDisabled}
p.bar = nil p.bar = nil
p.queue <- printTask{code: codeHideProgress} p.queue <- printTask{code: codeHideProgress}
} }
@@ -94,16 +115,67 @@ func (p *Progress) AddBar(count int) {
} }
} }
// SetBar sets current position for progress bar
func (p *Progress) SetBar(count int) {
if p.bar != nil {
p.bar.Set(count)
}
}
// Printf does printf but in safe manner: not overwriting progress bar // Printf does printf but in safe manner: not overwriting progress bar
func (p *Progress) Printf(msg string, a ...interface{}) { func (p *Progress) Printf(msg string, a ...interface{}) {
p.queue <- printTask{code: codePrint, message: fmt.Sprintf(msg, a...)} p.queue <- printTask{code: codePrint, message: fmt.Sprintf(msg, a...)}
} }
// ColoredPrintf does printf in colored way + newline
func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
if RunningOnTerminal() {
p.queue <- printTask{code: codePrint, message: color.Sprintf(msg, a...) + "\n"}
} else {
// stip color marks
var inColorMark, inCurly bool
msg = strings.Map(func(r rune) rune {
if inColorMark {
if inCurly {
if r == '}' {
inCurly = false
inColorMark = false
return -1
}
} else {
if r == '{' {
inCurly = true
} else if r == '@' {
return '@'
} else {
inColorMark = false
}
}
return -1
}
if r == '@' {
inColorMark = true
return -1
}
return r
}, msg)
p.Printf(msg+"\n", a...)
}
}
func (p *Progress) worker() { func (p *Progress) worker() {
hasBar := false
for { for {
select { task := <-p.queue
case task := <-p.queue:
switch task.code { switch task.code {
case codeBarEnabled:
hasBar = true
case codeBarDisabled:
hasBar = false
case codePrint: case codePrint:
if p.barShown { if p.barShown {
fmt.Print("\r\033[2K") fmt.Print("\r\033[2K")
@@ -111,15 +183,18 @@ func (p *Progress) worker() {
} }
fmt.Print(task.message) fmt.Print(task.message)
case codeProgress: case codeProgress:
if hasBar {
fmt.Print("\r" + task.message) fmt.Print("\r" + task.message)
p.barShown = true p.barShown = true
}
case codeHideProgress: case codeHideProgress:
if p.barShown { if p.barShown {
fmt.Print("\r\033[2K") fmt.Print("\r\033[2K")
p.barShown = false p.barShown = false
} }
} case codeFlush:
case <-p.stop: task.reply <- true
case codeStop:
p.stopped <- true p.stopped <- true
return return
} }
+11
View File
@@ -0,0 +1,11 @@
package console
import (
"golang.org/x/crypto/ssh/terminal"
"syscall"
)
// RunningOnTerminal checks whether stdout is terminal
func RunningOnTerminal() bool {
return terminal.IsTerminal(syscall.Stdout)
}
+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
}
+127 -18
View File
@@ -7,6 +7,8 @@ import (
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
) )
// Errors for Storage // Errors for Storage
@@ -19,12 +21,19 @@ type Storage interface {
Get(key []byte) ([]byte, error) Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error Put(key []byte, value []byte) error
Delete(key []byte) error Delete(key []byte) error
KeysByPrefix(prefix []byte) [][]byte
FetchByPrefix(prefix []byte) [][]byte FetchByPrefix(prefix []byte) [][]byte
Close() error Close() error
ReOpen() error
StartBatch()
FinishBatch() error
CompactDB() error
} }
type levelDB struct { type levelDB struct {
path string
db *leveldb.DB db *leveldb.DB
batch *leveldb.Batch
} }
// Check interface // Check interface
@@ -32,19 +41,42 @@ var (
_ Storage = &levelDB{} _ Storage = &levelDB{}
) )
// OpenDB opens (creates) LevelDB database func internalOpen(path string) (*leveldb.DB, error) {
func OpenDB(path string) (Storage, error) {
o := &opt.Options{ o := &opt.Options{
Filter: filter.NewBloomFilter(10), 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 { if err != nil {
return nil, err return nil, err
} }
return &levelDB{db: db}, nil return &levelDB{db: db, path: path}, nil
} }
// RecoverDB recovers LevelDB database from corruption
func RecoverDB(path string) error {
stor, err := storage.OpenFile(path)
if err != nil {
return err
}
db, err := leveldb.Recover(stor, nil)
if err != nil {
return err
}
db.Close()
stor.Close()
return nil
}
// Get key value from database
func (l *levelDB) Get(key []byte) ([]byte, error) { func (l *levelDB) Get(key []byte) ([]byte, error) {
value, err := l.db.Get(key, nil) value, err := l.db.Get(key, nil)
if err != nil { if err != nil {
@@ -57,33 +89,110 @@ func (l *levelDB) Get(key []byte) ([]byte, error) {
return value, nil return value, nil
} }
// Put saves key to database, if key has the same value in DB already, it is not saved
func (l *levelDB) Put(key []byte, value []byte) error { func (l *levelDB) Put(key []byte, value []byte) error {
if l.batch != nil {
l.batch.Put(key, value)
return nil
}
old, err := l.db.Get(key, nil)
if err != nil {
if err != leveldb.ErrNotFound {
return err
}
} else {
if bytes.Equal(old, value) {
return nil
}
}
return l.db.Put(key, value, nil) return l.db.Put(key, value, nil)
} }
// Delete removes key from DB
func (l *levelDB) Delete(key []byte) error { func (l *levelDB) Delete(key []byte) error {
if l.batch != nil {
l.batch.Delete(key)
return nil
}
return l.db.Delete(key, nil) return l.db.Delete(key, nil)
} }
func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte { // KeysByPrefix returns all keys that start with prefix
func (l *levelDB) KeysByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0, 20) result := make([][]byte, 0, 20)
iterator := l.db.NewIterator(nil) iterator := l.db.NewIterator(nil, nil)
if iterator.Seek(prefix) { defer iterator.Release()
for bytes.HasPrefix(iterator.Key(), prefix) {
val := iterator.Value() for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
valc := make([]byte, len(val)) key := iterator.Key()
copy(valc, val) keyc := make([]byte, len(key))
result = append(result, valc) copy(keyc, key)
if !iterator.Next() { result = append(result, keyc)
break
}
}
} }
return result return result
} }
func (l *levelDB) Close() error { // FetchByPrefix returns all values with keys that start with prefix
return l.db.Close() func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0, 20)
iterator := l.db.NewIterator(nil, nil)
defer iterator.Release()
for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
val := iterator.Value()
valc := make([]byte, len(val))
copy(valc, val)
result = append(result, valc)
}
return result
}
// Close finishes DB work
func (l *levelDB) Close() error {
if l.db == nil {
return nil
}
err := l.db.Close()
l.db = nil
return err
}
// Reopen tries to re-open the database
func (l *levelDB) ReOpen() error {
if l.db != nil {
return nil
}
var err error
l.db, err = internalOpen(l.path)
return err
}
// StartBatch starts batch processing of keys
//
// All subsequent Get, Put and Delete would work on batch
func (l *levelDB) StartBatch() {
if l.batch != nil {
panic("batch already started")
}
l.batch = new(leveldb.Batch)
}
// FinishBatch finalizes the batch, saving operations
func (l *levelDB) FinishBatch() error {
if l.batch == nil {
panic("no batch")
}
err := l.db.Write(l.batch, nil)
l.batch = nil
return err
}
// CompactDB compacts database by merging layers
func (l *levelDB) CompactDB() error {
return l.db.CompactRange(util.Range{})
} }
+99 -3
View File
@@ -1,8 +1,9 @@
package database package database
import ( import (
. "launchpad.net/gocheck"
"testing" "testing"
. "gopkg.in/check.v1"
) )
// Launch gocheck tests // Launch gocheck tests
@@ -11,6 +12,7 @@ func Test(t *testing.T) {
} }
type LevelDBSuite struct { type LevelDBSuite struct {
path string
db Storage db Storage
} }
@@ -19,7 +21,8 @@ var _ = Suite(&LevelDBSuite{})
func (s *LevelDBSuite) SetUpTest(c *C) { func (s *LevelDBSuite) SetUpTest(c *C) {
var err error var err error
s.db, err = OpenDB(c.MkDir()) s.path = c.MkDir()
s.db, err = OpenDB(s.path)
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
@@ -28,6 +31,29 @@ func (s *LevelDBSuite) TearDownTest(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
func (s *LevelDBSuite) TestRecoverDB(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Check(err, IsNil)
err = s.db.Close()
c.Check(err, IsNil)
err = RecoverDB(s.path)
c.Check(err, IsNil)
s.db, err = OpenDB(s.path)
c.Check(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
}
func (s *LevelDBSuite) TestGetPut(c *C) { func (s *LevelDBSuite) TestGetPut(c *C) {
var ( var (
key = []byte("key") key = []byte("key")
@@ -64,19 +90,89 @@ func (s *LevelDBSuite) TestDelete(c *C) {
c.Assert(err, IsNil) c.Assert(err, IsNil)
} }
func (s *LevelDBSuite) TestFetchByPrefix(c *C) { func (s *LevelDBSuite) TestByPrefix(c *C) {
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{}) c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{})
s.db.Put([]byte{0x80, 0x01}, []byte{0x01}) s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
s.db.Put([]byte{0x80, 0x03}, []byte{0x03}) s.db.Put([]byte{0x80, 0x03}, []byte{0x03})
s.db.Put([]byte{0x80, 0x02}, []byte{0x02}) s.db.Put([]byte{0x80, 0x02}, []byte{0x02})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}}) c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
s.db.Put([]byte{0x90, 0x01}, []byte{0x04}) s.db.Put([]byte{0x90, 0x01}, []byte{0x04})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}}) c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
s.db.Put([]byte{0x00, 0x01}, []byte{0x05}) s.db.Put([]byte{0x00, 0x01}, []byte{0x05})
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}}) c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{}) c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
c.Check(s.db.KeysByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
}
func (s *LevelDBSuite) TestBatch(c *C) {
var (
key = []byte("key")
key2 = []byte("key2")
value = []byte("value")
value2 = []byte("value2")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
s.db.StartBatch()
s.db.Put(key2, value2)
s.db.Delete(key)
v, err := s.db.Get(key)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value)
_, err = s.db.Get(key2)
c.Check(err, ErrorMatches, "key not found")
err = s.db.FinishBatch()
c.Check(err, IsNil)
v2, err := s.db.Get(key2)
c.Check(err, IsNil)
c.Check(v2, DeepEquals, value2)
_, err = s.db.Get(key)
c.Check(err, ErrorMatches, "key not found")
c.Check(func() { s.db.FinishBatch() }, Panics, "no batch")
s.db.StartBatch()
c.Check(func() { s.db.StartBatch() }, Panics, "batch already started")
}
func (s *LevelDBSuite) TestCompactDB(c *C) {
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
s.db.Put([]byte{0x80, 0x03}, []byte{0x03})
s.db.Put([]byte{0x80, 0x02}, []byte{0x02})
c.Check(s.db.CompactDB(), IsNil)
}
func (s *LevelDBSuite) TestReOpen(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
err = s.db.Close()
c.Assert(err, IsNil)
err = s.db.ReOpen()
c.Assert(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
} }
+94
View File
@@ -0,0 +1,94 @@
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
snapshots *SnapshotCollection
localRepos *LocalRepoCollection
publishedRepos *PublishedRepoCollection
}
// NewCollectionFactory creates new factory
func NewCollectionFactory(db database.Storage) *CollectionFactory {
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)
}
return factory.packages
}
// 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)
}
return factory.remoteRepos
}
// 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)
}
return factory.snapshots
}
// 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)
}
return factory.localRepos
}
// 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
}
+99
View File
@@ -0,0 +1,99 @@
package deb
import (
"archive/tar"
"bufio"
"compress/gzip"
"fmt"
"github.com/mkrautz/goar"
"github.com/smira/aptly/utils"
"io"
"os"
"strings"
)
// GetControlFileFromDeb reads control file from deb package
func GetControlFileFromDeb(packageFile string) (Stanza, error) {
file, err := os.Open(packageFile)
if err != nil {
return nil, err
}
defer file.Close()
library := ar.NewReader(file)
for {
header, err := library.Next()
if err == io.EOF {
return nil, fmt.Errorf("unable to find control.tar.gz part")
}
if err != nil {
return nil, fmt.Errorf("unable to read .deb archive: %s", err)
}
if header.Name == "control.tar.gz" {
ungzip, err := gzip.NewReader(library)
if err != nil {
return nil, fmt.Errorf("unable to ungzip: %s", err)
}
defer ungzip.Close()
untar := tar.NewReader(ungzip)
for {
tarHeader, err := untar.Next()
if err == io.EOF {
return nil, fmt.Errorf("unable to find control file")
}
if err != nil {
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
}
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
reader := NewControlFileReader(untar)
stanza, err := reader.ReadStanza()
if err != nil {
return nil, err
}
return stanza, nil
}
}
}
}
}
// GetControlFileFromDsc reads control file from dsc package
func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, error) {
file, err := os.Open(dscFile)
if err != nil {
return nil, err
}
defer file.Close()
line, err := bufio.NewReader(file).ReadString('\n')
if err != nil {
return nil, err
}
file.Seek(0, 0)
var text *os.File
if strings.Index(line, "BEGIN PGP SIGN") != -1 {
text, err = verifier.ExtractClearsigned(file)
if err != nil {
return nil, err
}
defer text.Close()
} else {
text = file
}
reader := NewControlFileReader(text)
stanza, err := reader.ReadStanza()
if err != nil {
return nil, err
}
return stanza, nil
}
+57
View File
@@ -0,0 +1,57 @@
package deb
import (
"github.com/smira/aptly/utils"
"path/filepath"
"runtime"
. "gopkg.in/check.v1"
)
type DebSuite struct {
debFile, dscFile, dscFileNoSign string
}
var _ = Suite(&DebSuite{})
func (s *DebSuite) SetUpSuite(c *C) {
_, _File, _, _ := runtime.Caller(0)
s.debFile = filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
s.dscFile = filepath.Join(filepath.Dir(_File), "../system/files/pyspi_0.6.1-1.3.dsc")
s.dscFileNoSign = filepath.Join(filepath.Dir(_File), "../system/files/pyspi-0.6.1-1.3.stripped.dsc")
}
func (s *DebSuite) TestGetControlFileFromDeb(c *C) {
_, err := GetControlFileFromDeb("/no/such/file")
c.Check(err, ErrorMatches, ".*no such file or directory")
_, _File, _, _ := runtime.Caller(0)
_, err = GetControlFileFromDeb(_File)
c.Check(err, ErrorMatches, "unable to read .deb archive: ar: missing global header")
st, err := GetControlFileFromDeb(s.debFile)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "1.49.0.1")
c.Check(st["Package"], Equals, "libboost-program-options-dev")
}
func (s *DebSuite) TestGetControlFileFromDsc(c *C) {
verifier := &utils.GpgVerifier{}
_, err := GetControlFileFromDsc("/no/such/file", verifier)
c.Check(err, ErrorMatches, ".*no such file or directory")
_, _File, _, _ := runtime.Caller(0)
_, err = GetControlFileFromDsc(_File, verifier)
c.Check(err, ErrorMatches, "malformed stanza syntax")
st, err := GetControlFileFromDsc(s.dscFile, verifier)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "0.6.1-1.3")
c.Check(st["Source"], Equals, "pyspi")
st, err = GetControlFileFromDsc(s.dscFileNoSign, verifier)
c.Check(err, IsNil)
c.Check(st["Version"], Equals, "0.6.1-1.4")
c.Check(st["Source"], Equals, "pyspi")
}
+2
View File
@@ -0,0 +1,2 @@
// Package deb implements Debian-specific repository handling
package deb
+12
View File
@@ -0,0 +1,12 @@
package deb
import (
"testing"
. "gopkg.in/check.v1"
)
// Launch gocheck tests
func Test(t *testing.T) {
TestingT(t)
}
+112 -6
View File
@@ -1,18 +1,87 @@
package debian package deb
import ( import (
"bufio" "bufio"
"errors" "errors"
"io" "io"
"strings" "strings"
"unicode"
) )
// Stanza or paragraph of Debian control file // Stanza or paragraph of Debian control file
type Stanza map[string]string type Stanza map[string]string
// Canonical order of fields in stanza // Canonical order of fields in stanza
var canocialOrder = []string{"Origin", "Label", "Suite", "Package", "Version", "Installed-Size", "Priority", "Section", "Maintainer", // Taken from: http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/vivid/apt/vivid/view/head:/apt-pkg/tagfile.cc#L504
"Architecture", "Codename", "Date", "Architectures", "Components", "Description", "MD5sum", "MD5Sum", "SHA1", "SHA256"} 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 // Copy returns copy of Stanza
func (s Stanza) Copy() (result Stanza) { func (s Stanza) Copy() (result Stanza) {
@@ -40,8 +109,16 @@ func writeField(w *bufio.Writer, field, value string) (err error) {
} }
// WriteTo saves stanza back to stream, modifying itself on the fly // WriteTo saves stanza back to stream, modifying itself on the fly
func (s Stanza) WriteTo(w *bufio.Writer) error { func (s Stanza) WriteTo(w *bufio.Writer, isSource, isRelease bool) error {
for _, field := range canocialOrder { canonicalOrder := canonicalOrderBinary
if isSource {
canonicalOrder = canonicalOrderSource
}
if isRelease {
canonicalOrder = canonicalOrderRelease
}
for _, field := range canonicalOrder {
value, ok := s[field] value, ok := s[field]
if ok { if ok {
delete(s, field) delete(s, field)
@@ -81,6 +158,35 @@ func init() {
multilineFields["MD5Sum"] = true multilineFields["MD5Sum"] = true
} }
func canonicalCase(field string) string {
upper := strings.ToUpper(field)
switch upper {
case "SHA1", "SHA256", "SHA512":
return upper
case "MD5SUM":
return "MD5Sum"
case "NOTAUTOMATIC":
return "NotAutomatic"
case "BUTAUTOMATICUPGRADES":
return "ButAutomaticUpgrades"
}
startOfWord := true
return strings.Map(func(r rune) rune {
if startOfWord {
startOfWord = false
return unicode.ToUpper(r)
}
if r == '-' {
startOfWord = true
}
return unicode.ToLower(r)
}, field)
}
// ControlFileReader implements reading of control files stanza by stanza // ControlFileReader implements reading of control files stanza by stanza
type ControlFileReader struct { type ControlFileReader struct {
scanner *bufio.Scanner scanner *bufio.Scanner
@@ -119,7 +225,7 @@ func (c *ControlFileReader) ReadStanza() (Stanza, error) {
if len(parts) != 2 { if len(parts) != 2 {
return nil, ErrMalformedStanza return nil, ErrMalformedStanza
} }
lastField = parts[0] lastField = canonicalCase(parts[0])
_, lastFieldMultiline = multilineFields[lastField] _, lastFieldMultiline = multilineFields[lastField]
if lastFieldMultiline { if lastFieldMultiline {
stanza[lastField] = parts[1] stanza[lastField] = parts[1]
+16 -3
View File
@@ -1,10 +1,11 @@
package debian package deb
import ( import (
"bufio" "bufio"
"bytes" "bytes"
. "launchpad.net/gocheck"
"strings" "strings"
. "gopkg.in/check.v1"
) )
type ControlFileSuite struct { type ControlFileSuite struct {
@@ -107,7 +108,7 @@ func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
w := bufio.NewWriter(buf) w := bufio.NewWriter(buf)
err = stanza.Copy().WriteTo(w) err = stanza.Copy().WriteTo(w, false, false)
c.Assert(err, IsNil) c.Assert(err, IsNil)
err = w.Flush() err = w.Flush()
c.Assert(err, IsNil) c.Assert(err, IsNil)
@@ -122,6 +123,18 @@ func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
c.Assert(strings.HasPrefix(str, "Package: "), Equals, true) c.Assert(strings.HasPrefix(str, "Package: "), Equals, true)
} }
func (s *ControlFileSuite) TestCanonicalCase(c *C) {
c.Check(canonicalCase("Package"), Equals, "Package")
c.Check(canonicalCase("package"), Equals, "Package")
c.Check(canonicalCase("pAckaGe"), Equals, "Package")
c.Check(canonicalCase("MD5Sum"), Equals, "MD5Sum")
c.Check(canonicalCase("SHA1"), Equals, "SHA1")
c.Check(canonicalCase("SHA256"), Equals, "SHA256")
c.Check(canonicalCase("Package-List"), Equals, "Package-List")
c.Check(canonicalCase("package-list"), Equals, "Package-List")
c.Check(canonicalCase("packaGe-lIst"), Equals, "Package-List")
}
func (s *ControlFileSuite) BenchmarkReadStanza(c *C) { func (s *ControlFileSuite) BenchmarkReadStanza(c *C) {
for i := 0; i < c.N; i++ { for i := 0; i < c.N; i++ {
reader := bytes.NewBufferString(controlFile) reader := bytes.NewBufferString(controlFile)
+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
}
+181
View File
@@ -0,0 +1,181 @@
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
}
if p.Name == "" {
reporter.Warning("Empty package name on %s", file)
failedFiles = append(failedFiles, file)
continue
}
if p.Version == "" {
reporter.Warning("Empty version on %s", file)
failedFiles = append(failedFiles, file)
continue
}
if p.Architecture == "" {
reporter.Warning("Empty architecture on %s", file)
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
}
+488
View File
@@ -0,0 +1,488 @@
package deb
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/utils"
"sort"
)
// Dependency options
const (
// DepFollowSource pulls source packages when required
DepFollowSource = 1 << iota
// DepFollowSuggests pulls from suggests
DepFollowSuggests
// DepFollowRecommends pulls from recommends
DepFollowRecommends
// DepFollowAllVariants follows all variants if depends on "a | b"
DepFollowAllVariants
// DepFollowBuild pulls build dependencies
DepFollowBuild
)
// PackageList is list of unique (by key) packages
//
// It could be seen as repo snapshot, repo contents, result of filtering,
// merge, etc.
//
// If indexed, PackageList starts supporting searching
type PackageList struct {
// Straight list of packages as map
packages map[string]*Package
// Has index been prepared?
indexed bool
// Indexed list of packages, sorted by name internally
packagesIndex []*Package
// Map of packages for each virtual package (provides)
providesIndex map[string][]*Package
}
// 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
func NewPackageList() *PackageList {
return &PackageList{packages: make(map[string]*Package, 1000)}
}
// NewPackageListFromRefList loads packages list from PackageRefList
func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageCollection, progress aptly.Progress) (*PackageList, error) {
// empty reflist
if reflist == nil {
return NewPackageList(), nil
}
result := &PackageList{packages: make(map[string]*Package, reflist.Len())}
if progress != nil {
progress.InitBar(int64(reflist.Len()), false)
}
err := reflist.ForEach(func(key []byte) error {
p, err2 := collection.ByKey(key)
if err2 != nil {
return fmt.Errorf("unable to load package with key %s: %s", key, err2)
}
if progress != nil {
progress.AddBar(1)
}
return result.Add(p)
})
if progress != nil {
progress.ShutdownBar()
}
if err != nil {
return nil, err
}
return result, nil
}
// Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error {
key := string(p.ShortKey(""))
existing, ok := l.packages[key]
if ok {
if !existing.Equals(p) {
return &PackageConflictError{fmt.Errorf("conflict in package %s", p)}
}
return nil
}
l.packages[key] = p
if l.indexed {
for _, provides := range p.Provides {
l.providesIndex[provides] = append(l.providesIndex[provides], p)
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.lessPackages(p, l.packagesIndex[j]) })
// insert p into l.packagesIndex in position i
l.packagesIndex = append(l.packagesIndex, nil)
copy(l.packagesIndex[i+1:], l.packagesIndex[i:])
l.packagesIndex[i] = p
}
return nil
}
// ForEach calls handler for each package in list
func (l *PackageList) ForEach(handler func(*Package) error) error {
var err error
for _, p := range l.packages {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// ForEachIndexed calls handler for each package in list in indexed order
func (l *PackageList) ForEachIndexed(handler func(*Package) error) error {
if !l.indexed {
panic("list not indexed, can't iterate")
}
var err error
for _, p := range l.packagesIndex {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// Len returns number of packages in the list
func (l *PackageList) Len() int {
return len(l.packages)
}
// Append adds content from one package list to another
func (l *PackageList) Append(pl *PackageList) error {
if l.indexed {
panic("Append not supported when indexed")
}
for k, p := range pl.packages {
existing, ok := l.packages[k]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s", p)
}
} else {
l.packages[k] = p
}
}
return nil
}
// Remove removes package from the list, and updates index when required
func (l *PackageList) Remove(p *Package) {
delete(l.packages, string(p.ShortKey("")))
if l.indexed {
for _, provides := range p.Provides {
for i, pkg := range l.providesIndex[provides] {
if pkg.Equals(p) {
// remove l.ProvidesIndex[provides][i] w/o preserving order
l.providesIndex[provides][len(l.providesIndex[provides])-1], l.providesIndex[provides][i], l.providesIndex[provides] =
nil, l.providesIndex[provides][len(l.providesIndex[provides])-1], l.providesIndex[provides][:len(l.providesIndex[provides])-1]
break
}
}
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= p.Name })
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == p.Name {
if l.packagesIndex[i].Equals(p) {
// remove l.packagesIndex[i] preserving order
copy(l.packagesIndex[i:], l.packagesIndex[i+1:])
l.packagesIndex[len(l.packagesIndex)-1] = nil
l.packagesIndex = l.packagesIndex[:len(l.packagesIndex)-1]
break
}
i++
}
}
}
// Architectures returns list of architectures present in packages and flag if source packages are present.
//
// If includeSource is true, meta-architecture "source" would be present in the list
func (l *PackageList) Architectures(includeSource bool) (result []string) {
result = make([]string, 0, 10)
for _, pkg := range l.packages {
if pkg.Architecture != "all" && (pkg.Architecture != "source" || includeSource) && !utils.StrSliceHasItem(result, pkg.Architecture) {
result = append(result, pkg.Architecture)
}
}
return
}
// 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)
if l < 2 {
return s
}
if l == 2 {
if s[0] == s[1] {
return s[0:1]
}
return s
}
found := make(map[string]bool, l)
j := 0
for i, x := range s {
h := x.Hash()
if !found[h] {
found[h] = true
s[j] = s[i]
j++
}
}
return s[:j]
}
// VerifyDependencies looks for missing dependencies in package list.
//
// Analysis would be peformed for each architecture, in specified sources
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageList, progress aptly.Progress) ([]Dependency, error) {
l.PrepareIndex()
missing := make([]Dependency, 0, 128)
if progress != nil {
progress.InitBar(int64(l.Len())*int64(len(architectures)), false)
}
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packagesIndex {
if progress != nil {
progress.AddBar(1)
}
if !p.MatchesArchitecture(arch) {
continue
}
for _, dep := range p.GetDependencies(options) {
variants, err := ParseDependencyVariants(dep)
if err != nil {
return nil, fmt.Errorf("unable to process package %s: %s", p, err)
}
variants = depSliceDeduplicate(variants)
variantsMissing := make([]Dependency, 0, len(variants))
for _, dep := range variants {
if dep.Architecture == "" {
dep.Architecture = arch
}
hash := dep.Hash()
satisfied, ok := cache[hash]
if !ok {
satisfied = sources.Search(dep, false) != nil
cache[hash] = satisfied
}
if !satisfied && !ok {
variantsMissing = append(variantsMissing, dep)
}
if satisfied && options&DepFollowAllVariants == 0 {
variantsMissing = nil
break
}
}
missing = append(missing, variantsMissing...)
}
}
}
if progress != nil {
progress.ShutdownBar()
}
return missing, nil
}
// Swap swaps two packages in index
func (l *PackageList) Swap(i, j int) {
l.packagesIndex[i], l.packagesIndex[j] = l.packagesIndex[j], l.packagesIndex[i]
}
func (l *PackageList) lessPackages(iPkg, jPkg *Package) bool {
if iPkg.Name == jPkg.Name {
cmp := CompareVersions(iPkg.Version, jPkg.Version)
if cmp == 0 {
return iPkg.Architecture < jPkg.Architecture
}
return cmp == 1
}
return iPkg.Name < jPkg.Name
}
// Less compares two packages by name (lexographical) and version (latest to oldest)
func (l *PackageList) Less(i, j int) bool {
return l.lessPackages(l.packagesIndex[i], l.packagesIndex[j])
}
// PrepareIndex prepares list for indexing
func (l *PackageList) PrepareIndex() {
if l.indexed {
return
}
l.packagesIndex = make([]*Package, l.Len())
l.providesIndex = make(map[string][]*Package, 128)
i := 0
for _, p := range l.packages {
l.packagesIndex[i] = p
i++
for _, provides := range p.Provides {
l.providesIndex[provides] = append(l.providesIndex[provides], p)
}
}
sort.Sort(l)
l.indexed = true
}
// Scan searches package index using full scan
func (l *PackageList) Scan(q PackageQuery) (result *PackageList) {
result = NewPackageList()
for _, pkg := range l.packages {
if q.Matches(pkg) {
result.Add(pkg)
}
}
return
}
// SearchSupported returns true for PackageList
func (l *PackageList) SearchSupported() bool {
return true
}
// SearchByKey looks up package by exact key reference
func (l *PackageList) SearchByKey(arch, name, version string) (result *PackageList) {
result = NewPackageList()
pkg := l.packages["P"+arch+" "+name+" "+version]
if pkg != nil {
result.Add(pkg)
}
return
}
// Search searches package index for specified package(s) using optimized queries
func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
if !l.indexed {
panic("list not indexed, can't search")
}
if dep.Relation == VersionDontCare {
for _, p := range l.providesIndex[dep.Pkg] {
if dep.Architecture == "" || p.MatchesArchitecture(dep.Architecture) {
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
}
}
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= dep.Pkg })
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
p := l.packagesIndex[i]
if p.MatchesDependency(dep) {
searchResults = append(searchResults, p)
if !allMatches {
break
}
}
i++
}
return
}
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
if !l.indexed {
panic("list not indexed, can't filter")
}
result := NewPackageList()
for _, query := range queries {
result.Append(query.Query(l))
}
if withDependencies {
added := result.Len()
result.PrepareIndex()
dependencySource := NewPackageList()
if source != nil {
dependencySource.Append(source)
}
dependencySource.Append(result)
dependencySource.PrepareIndex()
// while some new dependencies were discovered
for added > 0 {
added = 0
// find missing dependencies
missing, err := result.VerifyDependencies(dependencyOptions, architecturesList, dependencySource, nil)
if err != nil {
return nil, err
}
// try to satisfy dependencies
for _, dep := range missing {
// dependency might have already been satisfied
// with packages already been added
if result.Search(dep, false) != nil {
continue
}
searchResults := l.Search(dep, false)
if searchResults != nil {
for _, p := range searchResults {
result.Add(p)
dependencySource.Add(p)
added++
if dependencyOptions&DepFollowAllVariants == 0 {
break
}
}
}
}
}
}
return result, nil
}
+426
View File
@@ -0,0 +1,426 @@
package deb
import (
"errors"
"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
p1, p2, p3, p4, p5, p6 *Package
// Mocked packages in list
packages []*Package
packages2 []*Package
sourcePackages []*Package
il *PackageList
il2 *PackageList
}
var _ = Suite(&PackageListSuite{})
func (s *PackageListSuite) SetUpTest(c *C) {
s.list = NewPackageList()
s.p1 = NewPackageFromControlFile(packageStanza.Copy())
s.p2 = NewPackageFromControlFile(packageStanza.Copy())
stanza := packageStanza.Copy()
stanza["Package"] = "mars-invaders"
s.p3 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Source"] = "unknown-planet"
s.p4 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Package"] = "lonely-strangers"
s.p5 = NewPackageFromControlFile(stanza)
stanza = packageStanza.Copy()
stanza["Version"] = "99.1"
s.p6 = NewPackageFromControlFile(stanza)
s.il = NewPackageList()
s.packages = []*Package{
{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{
{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{}},
}
}
func (s *PackageListSuite) TestAddLen(c *C) {
c.Check(s.list.Len(), Equals, 0)
c.Check(s.list.Add(s.p1), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p2), IsNil)
c.Check(s.list.Len(), Equals, 1)
c.Check(s.list.Add(s.p3), IsNil)
c.Check(s.list.Len(), Equals, 2)
c.Check(s.list.Add(s.p4), ErrorMatches, "conflict in package.*")
}
func (s *PackageListSuite) TestRemove(c *C) {
c.Check(s.list.Add(s.p1), IsNil)
c.Check(s.list.Add(s.p3), IsNil)
c.Check(s.list.Len(), Equals, 2)
s.list.Remove(s.p1)
c.Check(s.list.Len(), Equals, 1)
}
func (s *PackageListSuite) TestAddWhenIndexed(c *C) {
c.Check(s.list.Len(), Equals, 0)
s.list.PrepareIndex()
c.Check(s.list.Add(&Package{Name: "a1st", Version: "1.0", Architecture: "i386", Provides: []string{"fa", "fb"}}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
c.Check(s.list.Add(&Package{Name: "c3rd", Version: "1.0", Architecture: "i386", Provides: []string{"fa"}}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.packagesIndex[1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
c.Check(s.list.Add(&Package{Name: "b2nd", Version: "1.0", Architecture: "i386"}), IsNil)
c.Check(s.list.packagesIndex[0].Name, Equals, "a1st")
c.Check(s.list.packagesIndex[1].Name, Equals, "b2nd")
c.Check(s.list.packagesIndex[2].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fa"][0].Name, Equals, "a1st")
c.Check(s.list.providesIndex["fa"][1].Name, Equals, "c3rd")
c.Check(s.list.providesIndex["fb"][0].Name, Equals, "a1st")
}
func (s *PackageListSuite) TestRemoveWhenIndexed(c *C) {
s.il.Remove(s.packages[0])
names := make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "dpkg", "dpkg", "libx", "mailer"})
s.il.Remove(s.packages[4])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["mail-agent"], DeepEquals, []*Package{})
s.il.Remove(s.packages[9])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["package-installer"], HasLen, 2)
s.il.Remove(s.packages[1])
names = make([]string, s.il.Len())
for i, p := range s.il.packagesIndex {
names[i] = p.Name
}
c.Check(names, DeepEquals, []string{"aa", "app", "app", "app", "app", "data", "dpkg", "dpkg", "dpkg", "libx"})
c.Check(s.il.providesIndex["package-installer"], DeepEquals, []*Package{s.packages[11]})
}
func (s *PackageListSuite) TestForeach(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
Len := 0
err := s.list.ForEach(func(*Package) error {
Len++
return nil
})
c.Check(Len, Equals, 2)
c.Check(err, IsNil)
e := errors.New("a")
err = s.list.ForEach(func(*Package) error {
return e
})
c.Check(err, Equals, e)
}
func (s *PackageListSuite) TestIndex(c *C) {
c.Check(len(s.il.providesIndex), Equals, 2)
c.Check(len(s.il.providesIndex["mail-agent"]), Equals, 1)
c.Check(len(s.il.providesIndex["package-installer"]), Equals, 3)
c.Check(s.il.packagesIndex[0], Equals, s.packages[8])
}
func (s *PackageListSuite) TestAppend(c *C) {
s.list.Add(s.p1)
s.list.Add(s.p3)
err := s.list.Append(s.il)
c.Check(err, IsNil)
c.Check(s.list.Len(), Equals, 16)
list := NewPackageList()
list.Add(s.p4)
err = s.list.Append(list)
c.Check(err, ErrorMatches, "conflict.*")
s.list.PrepareIndex()
c.Check(func() { s.list.Append(s.il) }, Panics, "Append not supported when indexed")
}
func (s *PackageListSuite) TestSearch(c *C) {
//allMatches = False
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}, false) }, Panics, "list not indexed, can't search")
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}, false), DeepEquals, []*Package{s.packages[4]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}, false), IsNil)
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}, false), IsNil)
// search w/o version should return package with latest version
c.Check(s.il.Search(Dependency{Architecture: "source", Pkg: "dpkg"}, false), DeepEquals, []*Package{s.packages[13]})
// allMatches = True
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "mail-agent"}, true), Contains, []*Package{s.packages2[0], s.packages2[1]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "puppy"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionEqual, Version: "3"}, true), Contains, []*Package{s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLess, Version: "1.2"}, true), Contains, []*Package{s.packages2[2], s.packages2[3]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLess, Version: "1.1~"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.2"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1-bp1"}, true), Contains, []*Package{s.packages2[2]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.0"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreater, Version: "1.1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreater, Version: "5.0"}, true), IsNil)
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}, true), Contains, []*Package{s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}, true), Contains, []*Package{s.packages2[2], s.packages2[3], s.packages2[4], s.packages2[5]})
c.Check(s.il2.Search(Dependency{Architecture: "amd64", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "5.0"}, true), IsNil)
}
func (s *PackageListSuite) TestFilter(c *C) {
c.Check(func() { s.list.Filter([]PackageQuery{&PkgQuery{"abcd", "0.3", "i386"}}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
plString := func(l *PackageList) string {
list := make([]string, 0, l.Len())
for _, p := range l.packages {
list = append(list, p.String())
}
sort.Strings(list)
return strings.Join(list, " ")
}
result, err := s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
result, err = s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}, &PkgQuery{"dpkg", "1.7", "source"},
&PkgQuery{"dpkg", "1.8", "amd64"}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{
&DependencyQuery{Dep: Dependency{Pkg: "app"}},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}},
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}},
&DependencyQuery{Dep: Dependency{Pkg: "xyz"}},
&DependencyQuery{Dep: Dependency{Pkg: "aa", Relation: VersionGreater, Version: "3.0"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{&DependencyQuery{Dep: Dependency{Pkg: "app", Architecture: "i386"}}}, true, NewPackageList(), 0, []string{"i386"})
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
result, err = s.il.Filter([]PackageQuery{
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "0.9"}},
&DependencyQuery{Dep: Dependency{Pkg: "lib"}},
&DependencyQuery{Dep: Dependency{Pkg: "data"}}}, true, NewPackageList(), 0, []string{"i386", "amd64"})
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
result, err = s.il.Filter([]PackageQuery{&OrQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
result, err = s.il.Filter([]PackageQuery{&AndQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "")
result, err = s.il.Filter([]PackageQuery{&OrQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_i386 data_1.1~bp1_all")
result, err = s.il.Filter([]PackageQuery{&AndQuery{&FieldQuery{Field: "Version", Relation: VersionGreaterOrEqual, Value: "1.0"},
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 data_1.1~bp1_all")
result, err = s.il.Filter([]PackageQuery{&AndQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
result, err = s.il.Filter([]PackageQuery{&NotQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.6.1-3_arm dpkg_1.6.1-3_source dpkg_1.7_source libx_1.5_arm")
result, err = s.il.Filter([]PackageQuery{&AndQuery{
&FieldQuery{Field: "$Architecture", Relation: VersionRegexp, Value: "i.*6", Regexp: regexp.MustCompile("i.*6")}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
c.Check(err, IsNil)
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
}
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
missing, err := s.il.VerifyDependencies(0, []string{"i386"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(0, []string{"i386", "amd64"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
missing, err = s.il.VerifyDependencies(0, []string{"arm"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.il.VerifyDependencies(DepFollowAllVariants, []string{"arm"}, s.il, nil)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{{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)
}
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"i386", "amd64"}, s.il, nil)
c.Check(err, IsNil)
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{{Pkg: "libx", Relation: VersionEqual, Version: "1.5", Architecture: "source"}})
_, err = s.il.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.il, nil)
c.Check(err, ErrorMatches, "unable to process package app_1.0_s390:.*")
}
func (s *PackageListSuite) TestArchitectures(c *C) {
archs := s.il.Architectures(true)
sort.Strings(archs)
c.Check(archs, DeepEquals, []string{"amd64", "arm", "i386", "s390", "source"})
archs = s.il.Architectures(false)
sort.Strings(archs)
c.Check(archs, DeepEquals, []string{"amd64", "arm", "i386", "s390"})
}
+226
View File
@@ -0,0 +1,226 @@
package deb
import (
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt"
"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 `json:"-"`
// User-assigned name
Name string
// Comment
Comment string
// DefaultDistribution
DefaultDistribution string `codec:",omitempty"`
// DefaultComponent
DefaultComponent string `codec:",omitempty"`
// "Snapshot" of current list of packages
packageRefs *PackageRefList
}
// NewLocalRepo creates new instance of Debian local repository
func NewLocalRepo(name string, comment string) *LocalRepo {
return &LocalRepo{
UUID: uuid.New(),
Name: name,
Comment: comment,
}
}
// String interface
func (repo *LocalRepo) String() string {
if repo.Comment != "" {
return fmt.Sprintf("[%s]: %s", repo.Name, repo.Comment)
}
return fmt.Sprintf("[%s]", repo.Name)
}
// NumPackages return number of packages in local repo
func (repo *LocalRepo) NumPackages() int {
if repo.packageRefs == nil {
return 0
}
return repo.packageRefs.Len()
}
// RefList returns package list for repo
func (repo *LocalRepo) RefList() *PackageRefList {
return repo.packageRefs
}
// UpdateRefList changes package list for local repo
func (repo *LocalRepo) UpdateRefList(reflist *PackageRefList) {
repo.packageRefs = reflist
}
// Encode does msgpack encoding of LocalRepo
func (repo *LocalRepo) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(repo)
return buf.Bytes()
}
// Decode decodes msgpack representation into LocalRepo
func (repo *LocalRepo) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(repo)
}
// Key is a unique id in DB
func (repo *LocalRepo) Key() []byte {
return []byte("L" + repo.UUID)
}
// RefKey is a unique id for package reference list
func (repo *LocalRepo) RefKey() []byte {
return []byte("E" + repo.UUID)
}
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
type LocalRepoCollection struct {
*sync.RWMutex
db database.Storage
list []*LocalRepo
}
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
result := &LocalRepoCollection{
RWMutex: &sync.RWMutex{},
db: db,
}
blobs := db.FetchByPrefix([]byte("L"))
result.list = make([]*LocalRepo, 0, len(blobs))
for _, blob := range blobs {
r := &LocalRepo{}
if err := r.Decode(blob); err != nil {
log.Printf("Error decoding repo: %s\n", err)
} else {
result.list = append(result.list, r)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *LocalRepoCollection) Add(repo *LocalRepo) error {
for _, r := range collection.list {
if r.Name == repo.Name {
return fmt.Errorf("local repo with name %s already exists", repo.Name)
}
}
err := collection.Update(repo)
if err != nil {
return err
}
collection.list = append(collection.list, repo)
return nil
}
// Update stores updated information about repo in DB
func (collection *LocalRepoCollection) Update(repo *LocalRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
if repo.packageRefs != nil {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
if err != nil {
return err
}
}
return nil
}
// LoadComplete loads additional information for local repo
func (collection *LocalRepoCollection) LoadComplete(repo *LocalRepo) error {
encoded, err := collection.db.Get(repo.RefKey())
if err == database.ErrNotFound {
return nil
}
if err != nil {
return err
}
repo.packageRefs = &PackageRefList{}
return repo.packageRefs.Decode(encoded)
}
// ByName looks up repository by name
func (collection *LocalRepoCollection) ByName(name string) (*LocalRepo, error) {
for _, r := range collection.list {
if r.Name == name {
return r, nil
}
}
return nil, fmt.Errorf("local repo with name %s not found", name)
}
// ByUUID looks up repository by uuid
func (collection *LocalRepoCollection) ByUUID(uuid string) (*LocalRepo, error) {
for _, r := range collection.list {
if r.UUID == uuid {
return r, nil
}
}
return nil, fmt.Errorf("local repo with uuid %s not found", uuid)
}
// ForEach runs method for each repository
func (collection *LocalRepoCollection) ForEach(handler func(*LocalRepo) error) error {
var err error
for _, r := range collection.list {
err = handler(r)
if err != nil {
return err
}
}
return err
}
// Len returns number of remote repos
func (collection *LocalRepoCollection) Len() int {
return len(collection.list)
}
// Drop removes remote repo from collection
func (collection *LocalRepoCollection) Drop(repo *LocalRepo) error {
repoPosition := -1
for i, r := range collection.list {
if r == repo {
repoPosition = i
break
}
}
if repoPosition == -1 {
panic("local repo not found!")
}
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
err := collection.db.Delete(repo.Key())
if err != nil {
return err
}
return collection.db.Delete(repo.RefKey())
}
+196
View File
@@ -0,0 +1,196 @@
package deb
import (
"errors"
"github.com/smira/aptly/database"
. "gopkg.in/check.v1"
)
type LocalRepoSuite struct {
db database.Storage
list *PackageList
reflist *PackageRefList
repo *LocalRepo
}
var _ = Suite(&LocalRepoSuite{})
func (s *LocalRepoSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.list = NewPackageList()
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
s.reflist = NewPackageRefListFromPackageList(s.list)
s.repo = NewLocalRepo("lrepo", "Super repo")
s.repo.packageRefs = s.reflist
}
func (s *LocalRepoSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *LocalRepoSuite) TestString(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").String(), Equals, "[lrepo]: My first repo")
c.Check(NewLocalRepo("lrepo2", "").String(), Equals, "[lrepo2]")
}
func (s *LocalRepoSuite) TestNumPackages(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").NumPackages(), Equals, 0)
c.Check(s.repo.NumPackages(), Equals, 2)
}
func (s *LocalRepoSuite) TestRefList(c *C) {
c.Check(NewLocalRepo("lrepo", "My first repo").RefList(), IsNil)
c.Check(s.repo.RefList(), Equals, s.reflist)
}
func (s *LocalRepoSuite) TestUpdateRefList(c *C) {
s.repo.UpdateRefList(nil)
c.Check(s.repo.RefList(), IsNil)
}
func (s *LocalRepoSuite) TestEncodeDecode(c *C) {
repo := &LocalRepo{}
err := repo.Decode(s.repo.Encode())
c.Assert(err, IsNil)
c.Check(repo.Name, Equals, s.repo.Name)
c.Check(repo.Comment, Equals, s.repo.Comment)
}
func (s *LocalRepoSuite) TestKey(c *C) {
c.Assert(len(s.repo.Key()), Equals, 37)
c.Assert(s.repo.Key()[0], Equals, byte('L'))
}
func (s *LocalRepoSuite) TestRefKey(c *C) {
c.Assert(len(s.repo.RefKey()), Equals, 37)
c.Assert(s.repo.RefKey()[0], Equals, byte('E'))
c.Assert(s.repo.RefKey()[1:], DeepEquals, s.repo.Key()[1:])
}
type LocalRepoCollectionSuite struct {
db database.Storage
collection *LocalRepoCollection
list *PackageList
reflist *PackageRefList
}
var _ = Suite(&LocalRepoCollectionSuite{})
func (s *LocalRepoCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewLocalRepoCollection(s.db)
s.list = NewPackageList()
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
s.reflist = NewPackageRefListFromPackageList(s.list)
}
func (s *LocalRepoCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *LocalRepoCollectionSuite) TestAddByName(c *C) {
r, err := s.collection.ByName("local1")
c.Assert(err, ErrorMatches, "*.not found")
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Add(repo), IsNil)
c.Assert(s.collection.Add(repo), ErrorMatches, ".*already exists")
r, err = s.collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
collection := NewLocalRepoCollection(s.db)
r, err = collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
}
func (s *LocalRepoCollectionSuite) TestByUUID(c *C) {
r, err := s.collection.ByUUID("some-uuid")
c.Assert(err, ErrorMatches, "*.not found")
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Add(repo), IsNil)
r, err = s.collection.ByUUID(repo.UUID)
c.Assert(err, IsNil)
c.Assert(r.String(), Equals, repo.String())
}
func (s *LocalRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
repo := NewLocalRepo("local1", "Comment 1")
c.Assert(s.collection.Update(repo), IsNil)
collection := NewLocalRepoCollection(s.db)
r, err := collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.packageRefs, IsNil)
repo.packageRefs = s.reflist
c.Assert(s.collection.Update(repo), IsNil)
collection = NewLocalRepoCollection(s.db)
r, err = collection.ByName("local1")
c.Assert(err, IsNil)
c.Assert(r.packageRefs, IsNil)
c.Assert(r.NumPackages(), Equals, 0)
c.Assert(s.collection.LoadComplete(r), IsNil)
c.Assert(r.NumPackages(), Equals, 2)
}
func (s *LocalRepoCollectionSuite) TestForEachAndLen(c *C) {
repo := NewLocalRepo("local1", "Comment 1")
s.collection.Add(repo)
count := 0
err := s.collection.ForEach(func(*LocalRepo) error {
count++
return nil
})
c.Assert(count, Equals, 1)
c.Assert(err, IsNil)
c.Check(s.collection.Len(), Equals, 1)
e := errors.New("c")
err = s.collection.ForEach(func(*LocalRepo) error {
return e
})
c.Assert(err, Equals, e)
}
func (s *LocalRepoCollectionSuite) TestDrop(c *C) {
repo1 := NewLocalRepo("local1", "Comment 1")
s.collection.Add(repo1)
repo2 := NewLocalRepo("local2", "Comment 2")
s.collection.Add(repo2)
r1, _ := s.collection.ByUUID(repo1.UUID)
c.Check(r1, Equals, repo1)
err := s.collection.Drop(repo1)
c.Check(err, IsNil)
_, err = s.collection.ByUUID(repo1.UUID)
c.Check(err, ErrorMatches, "local repo .* not found")
collection := NewLocalRepoCollection(s.db)
_, err = collection.ByName("local1")
c.Check(err, ErrorMatches, "local repo .* not found")
r2, _ := collection.ByName("local2")
c.Check(r2.String(), Equals, repo2.String())
c.Check(func() { s.collection.Drop(repo1) }, Panics, "local repo not found!")
}

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