mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-15 07:00:52 +00:00
Compare commits
528 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a4feebe6f | |||
| 1d1561c6c3 | |||
| 9a5b3aeedc | |||
| ed931e7ed4 | |||
| 5ff9cecc5a | |||
| f8bca463bb | |||
| d5c6f0b623 | |||
| 7e57f443ed | |||
| b4cf2e7065 | |||
| 2ceabb69e6 | |||
| aa9d3360ba | |||
| 4580a64192 | |||
| 4cb0526980 | |||
| 03e2a8d558 | |||
| ab09cbfe3c | |||
| 0467e0c929 | |||
| 6e1c9afdd9 | |||
| 4b3b961b69 | |||
| e63adffdf5 | |||
| d00659b0cb | |||
| 66e73782e5 | |||
| 68f332628d | |||
| 01c0d19243 | |||
| eb0443ed51 | |||
| 4b974b038c | |||
| 2d9ee81c95 | |||
| 5c9d4d2844 | |||
| 49a9ad79dd | |||
| 7e60466c7b | |||
| 233ad2528f | |||
| 2f1afa54c2 | |||
| 6bf910ea56 | |||
| 8fcfedf708 | |||
| 26b46ee2a0 | |||
| e33a2a6f96 | |||
| 06dc1ef9a4 | |||
| 4c57c358b7 | |||
| 65532b3dbf | |||
| fb25dec58e | |||
| e320499f84 | |||
| 4715b12f16 | |||
| c6a30a30de | |||
| 618d06678c | |||
| 903d4cefba | |||
| 79292dc6c8 | |||
| 43414be2ee | |||
| 3c34ae6071 | |||
| 642957e3a3 | |||
| e5d646c007 | |||
| e0f811dab1 | |||
| 48b8311150 | |||
| 8111460e36 | |||
| 0490d0c928 | |||
| b323e315d1 | |||
| 77f928db69 | |||
| b67f3dd6f7 | |||
| 88ff4493b0 | |||
| 6e8fd6e907 | |||
| 9c3095e42c | |||
| c737b8c544 | |||
| 87cecac4ea | |||
| 76ee53e9f8 | |||
| f153c7c3ea | |||
| 36792bba29 | |||
| 0b05964faa | |||
| ff00a5a026 | |||
| fc0310f468 | |||
| 63bf30b890 | |||
| 3004473bbb | |||
| 4356fe5cbe | |||
| d6271b6542 | |||
| 26a65b2336 | |||
| 20adfd49a7 | |||
| 355a98b51f | |||
| 1f73a34a54 | |||
| 7925af9fd6 | |||
| fb03a3baf9 | |||
| f097cd20c1 | |||
| 0489ba9d16 | |||
| 46b3f8fbaf | |||
| cacd0cf103 | |||
| c933668c16 | |||
| 24418ab0a4 | |||
| 4963d0a1d7 | |||
| ea8bfeb8a7 | |||
| a582493a6e | |||
| 930f76887b | |||
| a4201a40d2 | |||
| 4990bb98e5 | |||
| 00d4674aa5 | |||
| 06b4016338 | |||
| c1b2e4fabb | |||
| f438637a98 | |||
| ce208f347e | |||
| 06502584cf | |||
| 0f22dc590a | |||
| 11716f06f0 | |||
| 1ba06e828d | |||
| bc357a19a1 | |||
| 9004f8578c | |||
| 7f038be1cb | |||
| 13fc1122f0 | |||
| cb99cbec58 | |||
| 5d16cf06cf | |||
| b0489117c8 | |||
| fa2eef564c | |||
| d20300b152 | |||
| 398303235a | |||
| 25d048fe49 | |||
| 8c15a0ca95 | |||
| 8e8ff8ba65 | |||
| 1b0eb9d45a | |||
| 403c7272cd | |||
| 0412646151 | |||
| 0725003107 | |||
| 7a1553dc55 | |||
| 8375a2c30f | |||
| 5bbbdb3c19 | |||
| 1fd80c40d0 | |||
| ae5ab2d138 | |||
| eb087fd291 | |||
| 3f6491b8a3 | |||
| 9250479846 | |||
| 9c60421bd6 | |||
| ebea4f10a0 | |||
| d828732307 | |||
| 7c3629337c | |||
| a29034caa5 | |||
| c1fd633ed7 | |||
| bd2cc45524 | |||
| 0665f2231a | |||
| 836abdc81e | |||
| 876eeedb14 | |||
| fd502264a9 | |||
| b155eaa91c | |||
| a0d7ae28bf | |||
| 2816647809 | |||
| 67ce828eeb | |||
| 427c42f4b8 | |||
| b50cb70a0e | |||
| c832a5cdc4 | |||
| 1bd625f17f | |||
| a0fa0becc2 | |||
| d489694ea9 | |||
| 982b5dc886 | |||
| 1ddaecfb94 | |||
| 129c34806c | |||
| 6c7f3b3bbd | |||
| 38cb6bd133 | |||
| 98ca0cdf33 | |||
| 6e32e3dcf4 | |||
| 6a1a871dda | |||
| 6bc7048166 | |||
| 87fbd5201b | |||
| dcf5798229 | |||
| 382ad10cf7 | |||
| ddb2dd7eb6 | |||
| 9b1b43c8b4 | |||
| ee7d84205b | |||
| 93e8e18ca6 | |||
| d586f31247 | |||
| dd9fc8e40e | |||
| d847cba870 | |||
| d983e10d08 | |||
| a6fc65ff4e | |||
| 85f38cd739 | |||
| acde6ff2b2 | |||
| c733129de9 | |||
| e138212593 | |||
| 64ef342121 | |||
| 66c9bb86f5 | |||
| 923e2e1e50 | |||
| 0e552eda55 | |||
| ba32d16c8a | |||
| 4320144024 | |||
| 2564564601 | |||
| f228ad811b | |||
| 5fe442f191 | |||
| 50c4aba9ab | |||
| b3627738c2 | |||
| eec6743fe4 | |||
| 42bf2f5e98 | |||
| 35b9a8ea91 | |||
| 26c0502307 | |||
| 5fa487e2dc | |||
| d9c62780c2 | |||
| 8c54e15a11 | |||
| cc2cc16004 | |||
| 726f12c537 | |||
| f1c235f5c5 | |||
| 9072ba5981 | |||
| 7beb90d4fc | |||
| 61e22743af | |||
| 036baa2264 | |||
| ccd8c2551f | |||
| 74f9787884 | |||
| 83af66a8f6 | |||
| daf887e54f | |||
| 552b11e28d | |||
| 80a88a2248 | |||
| 4c1d6d1463 | |||
| 140a11c04a | |||
| 7be2ef8b85 | |||
| d2d21c3df7 | |||
| f81a91bde9 | |||
| 7efd0de67c | |||
| 6a9db17460 | |||
| b1053826e3 | |||
| 18953c1c90 | |||
| aeb85a1b3c | |||
| 0a6d57ea1a | |||
| aa4dee3c60 | |||
| 9fbe33b356 | |||
| 2a9871e2e9 | |||
| 951b6e9004 | |||
| 2173d3ab65 | |||
| 9c834f410c | |||
| eef44f5cd5 | |||
| aa77ea2835 | |||
| 119bb0195b | |||
| 017dca57ed | |||
| 88351503b0 | |||
| 37b2d49aea | |||
| 0afb1f4306 | |||
| 6ac0658478 | |||
| 50c8e35a90 | |||
| d6c3389d7c | |||
| 6b83213cf4 | |||
| 24927f9a29 | |||
| ecbb9ad20c | |||
| 81e9189853 | |||
| 8efb7903b2 | |||
| c1995beff1 | |||
| 192152b215 | |||
| 972e8c1373 | |||
| c501fc63f8 | |||
| bf08ad800f | |||
| ebc223a895 | |||
| 01b1f23d6b | |||
| 6b08b64d62 | |||
| 89bb20388f | |||
| 9857789204 | |||
| 6d1efe0200 | |||
| a85aa11ecd | |||
| 27ea769ad3 | |||
| 523d0d0945 | |||
| 53f7fef4cf | |||
| b590efa45f | |||
| 59055d7fbd | |||
| 22bcacf143 | |||
| 877109b3b7 | |||
| 10056b8571 | |||
| ac983ff65d | |||
| 2ed76f1e4c | |||
| cb6b18acfe | |||
| 3cd8c5adab | |||
| dd7b7b5f20 | |||
| 1f6880fcad | |||
| c8d9bef686 | |||
| 8a787d2c35 | |||
| 159608cef3 | |||
| 52c5934eb6 | |||
| e4b9e974d2 | |||
| d541b4f137 | |||
| eece643ea5 | |||
| 14bd443d4d | |||
| 9109c60c43 | |||
| 445ecbe8f3 | |||
| 27de979733 | |||
| ad11053412 | |||
| a356f3dff9 | |||
| 1042894123 | |||
| 43eb993160 | |||
| d190ffd39a | |||
| 93c1c7aaab | |||
| 3e5ba27cb7 | |||
| cd3b24799a | |||
| a0870f6726 | |||
| d45b456334 | |||
| 91c753ad2f | |||
| 40509f73b3 | |||
| 1daa076d65 | |||
| aeae6009c4 | |||
| 8049d69793 | |||
| 8aa1954ba7 | |||
| a02a90a3d8 | |||
| f303aabf26 | |||
| 735cbac60d | |||
| 5d69871ca4 | |||
| 1afbae8f7c | |||
| 1ed647e1b0 | |||
| 01b8e9eda5 | |||
| f43d514804 | |||
| 7e8f692b2c | |||
| 4b50f817d7 | |||
| e123e4dfac | |||
| 4fb09d9e85 | |||
| d9b23167bc | |||
| 2c84faaf8d | |||
| 6514b87e3e | |||
| bd34ba4088 | |||
| fae6e977c3 | |||
| 2ae34cd873 | |||
| b365e5e0b2 | |||
| e171f90fd5 | |||
| db499f872d | |||
| 8f9944117c | |||
| ea399a335a | |||
| 976ddb5ff9 | |||
| 7d8600b840 | |||
| 7ad1bb387b | |||
| 2fbf465fbf | |||
| fa786332de | |||
| 5e1bd0ff0e | |||
| 9c92b81706 | |||
| 144ccbf809 | |||
| a11805efb4 | |||
| 5b6cea2d62 | |||
| d4699a3b24 | |||
| 09a695a128 | |||
| ec4d2bcefe | |||
| 3040aceb7f | |||
| 61d8639a8a | |||
| b47754a106 | |||
| 1b08b7311f | |||
| 0130fc0392 | |||
| de32595d29 | |||
| 95e5fdd34a | |||
| a05f00d9f1 | |||
| 97158ef37b | |||
| f01ac06d97 | |||
| a549778754 | |||
| 47d952f712 | |||
| 166f31c34d | |||
| 4940fdc951 | |||
| 7ae785f5a3 | |||
| 09c8421648 | |||
| 6a2059150f | |||
| 9b3dfe920d | |||
| 72f8e4ab61 | |||
| 755944652f | |||
| b29d42d023 | |||
| f19ece776d | |||
| 839763c0b9 | |||
| c56ecab06f | |||
| 02d86422a8 | |||
| 65efe0cd2a | |||
| 468b1f11b9 | |||
| 608870265c | |||
| ed03a7c69e | |||
| 5a42c60af4 | |||
| f66302ef31 | |||
| 346a7bcce9 | |||
| 9bee7cdd08 | |||
| 74eee3496c | |||
| 3ef5429212 | |||
| 3030e66d4c | |||
| 9ae5a5ffb2 | |||
| 833d37d22c | |||
| 03ec1f97a7 | |||
| a2df51b40e | |||
| ae906f525e | |||
| b4a5a55cac | |||
| 6003764ff5 | |||
| 099a82c816 | |||
| ef992e2b44 | |||
| 68e600974d | |||
| 39a1f0ec2d | |||
| 318fc5b7f4 | |||
| 72e54aa3d1 | |||
| 91ff904ac4 | |||
| b59471ad35 | |||
| 6ff601f4a2 | |||
| 0c09bdedaa | |||
| dfc1f27d4c | |||
| 005cee572e | |||
| 18e3ed5d64 | |||
| 3c7696ef7e | |||
| b2779d7a88 | |||
| cdd34b4759 | |||
| 1f2ddca32b | |||
| df06dc356b | |||
| b6c82f073f | |||
| 9a03b5f696 | |||
| 047270540a | |||
| eff3823edf | |||
| 9d02f057c6 | |||
| 8387586cc8 | |||
| b433e7dad5 | |||
| dec4bdee71 | |||
| bb6593d21e | |||
| fe879acf9c | |||
| 5b8390c644 | |||
| d558791070 | |||
| 38ea595c9a | |||
| c03b7929d4 | |||
| d122ab6013 | |||
| a7b594d076 | |||
| e07bcf8e51 | |||
| da6d5b7cf8 | |||
| 15ef5c63c5 | |||
| 625a38c578 | |||
| 03a79ebe4c | |||
| 60fa0aa68e | |||
| 04bd9929e1 | |||
| 8407e70347 | |||
| bf91744078 | |||
| 2c470c1535 | |||
| a18011bdc0 | |||
| af8af0f3d7 | |||
| 89d26b7dc6 | |||
| 8649ee3b37 | |||
| b9c8a8d9da | |||
| c5922737ed | |||
| 772111ad26 | |||
| d7ef1a0c4b | |||
| bd221bf869 | |||
| 0485a36de1 | |||
| 77d6a10984 | |||
| 8015966663 | |||
| 94114f2c3d | |||
| 2906369a3b | |||
| 521c52f600 | |||
| 52bb33dc69 | |||
| 71d90947c9 | |||
| b3a4936e06 | |||
| 237d25fe5b | |||
| de0954732a | |||
| 915b0d1697 | |||
| 6d026afc69 | |||
| 27a5578d30 | |||
| 96e878a2e0 | |||
| 7a7bb56557 | |||
| 076ecd586f | |||
| c54406e29f | |||
| b260b0010a | |||
| fbf1bc14b7 | |||
| f12cf935ba | |||
| 4e169c3d10 | |||
| ea2bfea2a3 | |||
| cf4619784e | |||
| 69ad2ccd84 | |||
| fe1046a7a3 | |||
| ce1df9447d | |||
| 2a7a2de84a | |||
| 238bdfad96 | |||
| 56d777af0a | |||
| a632469890 | |||
| 3601cc15ed | |||
| 61cd4c6af1 | |||
| 401bb768d7 | |||
| 5880d11899 | |||
| ed6e261bd0 | |||
| fb660efeb5 | |||
| 80de65f28d | |||
| 9893e4af3d | |||
| 7416cc403d | |||
| 83ceee1e3f | |||
| a54a366c95 | |||
| fb1e28b91b | |||
| 86206df58d | |||
| 1d49a717b9 | |||
| 3b0b0b76ec | |||
| 904b9e101b | |||
| 9fb8a0ea4b | |||
| bc27c6e14d | |||
| ae3c98c210 | |||
| 34f545b8cf | |||
| d523d2b415 | |||
| e320ac31d5 | |||
| e08d44ff0a | |||
| 898870038a | |||
| c485cf41f7 | |||
| d54ef1e921 | |||
| b42fd71acf | |||
| ede5449440 | |||
| eef49516ef | |||
| e745747370 | |||
| 7e5b2ae8f5 | |||
| ada3ae0094 | |||
| d262a131cc | |||
| f0e69144ed | |||
| a7cb40ee7a | |||
| 2a9b2f87f9 | |||
| 9af10bc422 | |||
| bdbb5acb11 | |||
| 81d506b226 | |||
| 1c30b2b9de | |||
| 566604d4ba | |||
| 58a57f2b2c | |||
| 20d744f398 | |||
| 360981de4a | |||
| 79016f7f98 | |||
| 1a92d8bfe9 | |||
| d3707b4cfe | |||
| de1fa85127 | |||
| d9b35cea01 | |||
| b75b4d1488 | |||
| da55f18b0e | |||
| 165dd0053e | |||
| 22a4e6b67b | |||
| 20513e1c16 | |||
| b4ea963744 | |||
| 429788db0f | |||
| 1e70e954da | |||
| 319f3e6bb2 | |||
| 56915c4357 | |||
| e1348ab88f | |||
| 026dc540d2 | |||
| 44ce4c8a77 | |||
| 980102462b | |||
| 86b0860463 | |||
| e311d41dd7 | |||
| c3ce886990 | |||
| 959ecf696c | |||
| 48d01f5700 | |||
| aeecc1ec91 | |||
| 685a4de4e7 | |||
| 667efc2b90 | |||
| 3cf281965b | |||
| e19a615641 | |||
| ff77fbf5d9 | |||
| 856dd7021c | |||
| ebc47f7d5d | |||
| 082fda62b5 | |||
| 3199fd85fb | |||
| 0c6951fcd2 | |||
| 35e57026ac |
+18
-3
@@ -1,14 +1,20 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.1
|
- 1.3.3
|
||||||
- 1.2.1
|
|
||||||
- tip
|
- 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
|
||||||
|
|
||||||
@@ -18,3 +24,12 @@ script: make travis
|
|||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- 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
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -1,22 +1,33 @@
|
|||||||
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
|
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
|
||||||
gom 'code.google.com/p/go.crypto/ssh/terminal', :commit => '7aa593ce8cea'
|
|
||||||
gom 'code.google.com/p/gographviz', :commit => '454bc64fdfa2'
|
gom 'code.google.com/p/gographviz', :commit => '454bc64fdfa2'
|
||||||
|
gom 'code.google.com/p/mxk/go1/flowcontrol', :commit => '5ff2502e2556'
|
||||||
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
|
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
|
||||||
gom 'github.com/cheggaaa/pb', :commit => '74be7a1388046f374ac36e93d46f5d56e856f827'
|
gom 'github.com/AlekSi/pointer', :commit => '5f6d527dae3d678b46fbb20331ddf44e2b841943'
|
||||||
gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
|
gom 'github.com/cheggaaa/pb', :commit => '2c1b74620cc58a81ac152ee2d322e28c806d81ed'
|
||||||
gom 'github.com/smira/flag', :commit => '0d0aac2addb39050f45e92c5a6252926096dc841'
|
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/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
|
||||||
gom 'github.com/syndtr/goleveldb/leveldb', :commit => 'ff3719c6816e2cd194f05058452d660608e178ac'
|
gom 'github.com/ncw/swift', :commit => '384ef27c70645e285f8bb9d02276bf654d06027e'
|
||||||
|
gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
|
||||||
|
gom 'github.com/smira/flag', :commit => '357ed3e599ffcbd4aeaa828e1d10da2df3ea5107'
|
||||||
|
gom 'github.com/smira/go-ftp-protocol/protocol', :commit => '066b75c2b70dca7ae10b1b88b47534a3c31ccfaa'
|
||||||
|
gom 'github.com/syndtr/gosnappy/snappy', :commit => 'ce8acff4829e0c2458a67ead32390ac0a381c862'
|
||||||
|
gom 'github.com/syndtr/goleveldb/leveldb', :commit => '97e257099d2ab9578151ba85e2641e2cd14d3ca8'
|
||||||
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
|
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
|
||||||
|
gom 'github.com/vaughan0/go-ini', :commit => 'a98ad7ee00ec53921f08832bc06ecf7fd600e6a1'
|
||||||
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
|
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
|
||||||
|
gom 'golang.org/x/crypto/ssh/terminal', :commit => 'a7ead6ddf06233883deca151dffaef2effbf498f'
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gom 'launchpad.net/gocheck'
|
gom 'gopkg.in/check.v1'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gom 'github.com/golang/lint/golint'
|
gom 'github.com/golang/lint/golint'
|
||||||
gom 'github.com/mattn/goveralls'
|
gom 'github.com/mattn/goveralls'
|
||||||
gom 'github.com/axw/gocov/gocov'
|
gom 'github.com/axw/gocov/gocov'
|
||||||
gom 'code.google.com/p/go.tools/cmd/cover'
|
gom 'golang.org/x/tools/cmd/cover'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
GOVERSION=$(shell go version | awk '{print $$3;}')
|
GOVERSION=$(shell go version | awk '{print $$3;}')
|
||||||
PACKAGES=database deb files http utils
|
PACKAGES=context database deb files http query swift s3 utils
|
||||||
ALL_PACKAGES=aptly cmd console database deb files http utils
|
ALL_PACKAGES=api aptly context cmd console database deb files http query swift s3 utils
|
||||||
BINPATH=$(abspath ./_vendor/bin)
|
BINPATH=$(abspath ./_vendor/bin)
|
||||||
GOM_ENVIRONMENT=-test
|
GOM_ENVIRONMENT=-test
|
||||||
PYTHON?=python
|
PYTHON?=python
|
||||||
@@ -36,16 +36,14 @@ coverage: coverage.out
|
|||||||
rm -f coverage.out
|
rm -f coverage.out
|
||||||
|
|
||||||
check:
|
check:
|
||||||
$(GOM) exec go tool vet -all=true -shadow=true $(ALL_PACKAGES:%=./%)
|
$(GOM) exec go tool vet -all=true $(ALL_PACKAGES:%=./%)
|
||||||
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
|
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(GOM) build -o $(BINPATH)/aptly
|
$(GOM) build -o $(BINPATH)/aptly
|
||||||
|
|
||||||
system-test: install
|
system-test: install
|
||||||
ifeq ($(GOVERSION),$(filter $(GOVERSION),go1.2 go1.2.1 devel))
|
|
||||||
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
|
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
|
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
|
||||||
PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
|
PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
|
||||||
|
|
||||||
@@ -69,7 +67,7 @@ package:
|
|||||||
(cd root/etc/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/master/aptly)
|
(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
|
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>" \
|
fpm -s dir -t deb -n aptly -v $(VERSION) --url=http://www.aptly.info/ --license=MIT --vendor="Andrey Smirnov <me@smira.ru>" \
|
||||||
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" -C root/ .
|
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" --deb-recommends bzip2 --deb-recommends graphviz -C root/ .
|
||||||
mv aptly_$(VERSION)_*.deb ~
|
mv aptly_$(VERSION)_*.deb ~
|
||||||
|
|
||||||
src-package:
|
src-package:
|
||||||
@@ -77,11 +75,12 @@ src-package:
|
|||||||
mkdir -p aptly-$(VERSION)/src/github.com/smira/aptly/
|
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/ && 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 && gom -production install
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . -name .git -print | xargs rm -rf
|
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . \( -name .git -o -name .bzr -o -name .hg \) -print | xargs rm -rf
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . -name .bzr -print | xargs rm -rf
|
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . -name .hg -print | xargs rm -rf
|
|
||||||
rm -rf aptly-$(VERSION)/src/github.com/smira/aptly/_vendor/{pkg,bin}
|
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)
|
tar cyf aptly-$(VERSION)-src.tar.bz2 aptly-$(VERSION)
|
||||||
rm -rf 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
|
||||||
|
|||||||
+19
-6
@@ -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>`_.
|
||||||
|
|
||||||
@@ -20,14 +29,15 @@ Aptly features: ("+" means planned features)
|
|||||||
* 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) (+)
|
* mirror repositories "as-is" (without resigning with user's key) (+)
|
||||||
* support for yum repositories (+)
|
* support for yum repositories (+)
|
||||||
|
|
||||||
Current limitations:
|
Current limitations:
|
||||||
|
|
||||||
* debian-installer and translations not supported yet
|
* translations are not supported yet
|
||||||
|
|
||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
@@ -38,8 +48,7 @@ To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
|
|||||||
|
|
||||||
And import key that is used to sign the release::
|
And import key that is used to sign the release::
|
||||||
|
|
||||||
$ gpg --keyserver keys.gnupg.net --recv-keys 2A194991
|
$ apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
|
||||||
$ gpg -a --export 2A194991 | sudo apt-key add -
|
|
||||||
|
|
||||||
After that you can install aptly as any other software package::
|
After that you can install aptly as any other software package::
|
||||||
|
|
||||||
@@ -49,9 +58,13 @@ After that you can install aptly as any other software package::
|
|||||||
Don't worry about squeeze part in repo name: aptly package should work on Debian squeeze+,
|
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.
|
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 -u github.com/mattn/gom
|
go get -u github.com/mattn/gom
|
||||||
mkdir -p $GOPATH/src/github.com/smira/aptly
|
mkdir -p $GOPATH/src/github.com/smira/aptly
|
||||||
|
|||||||
+113
@@ -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
@@ -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{})
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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
@@ -0,0 +1,410 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET /api/snapshots
|
||||||
|
func apiSnapshotsList(c *gin.Context) {
|
||||||
|
SortMethodString := c.Request.URL.Query().Get("sort")
|
||||||
|
|
||||||
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
if SortMethodString == "" {
|
||||||
|
SortMethodString = "name"
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []*deb.Snapshot{}
|
||||||
|
collection.ForEachSorted(SortMethodString, func(snapshot *deb.Snapshot) error {
|
||||||
|
result = append(result, snapshot)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
c.JSON(200, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/mirrors/:name/snapshots/
|
||||||
|
func apiSnapshotsCreateFromMirror(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
repo *deb.RemoteRepo
|
||||||
|
snapshot *deb.Snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
var b struct {
|
||||||
|
Name string `binding:"required"`
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.Bind(&b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
collection := context.CollectionFactory().RemoteRepoCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
snapshotCollection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
snapshotCollection.Lock()
|
||||||
|
defer snapshotCollection.Unlock()
|
||||||
|
|
||||||
|
repo, err = collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo.CheckLock()
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(409, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(repo)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Description != "" {
|
||||||
|
snapshot.Description = b.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.Add(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(201, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/snapshots
|
||||||
|
func apiSnapshotsCreate(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
snapshot *deb.Snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
var b struct {
|
||||||
|
Name string `binding:"required"`
|
||||||
|
Description string
|
||||||
|
SourceSnapshots []string
|
||||||
|
PackageRefs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.Bind(&b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Description == "" {
|
||||||
|
if len(b.SourceSnapshots)+len(b.PackageRefs) == 0 {
|
||||||
|
b.Description = "Created as empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshotCollection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
snapshotCollection.Lock()
|
||||||
|
defer snapshotCollection.Unlock()
|
||||||
|
|
||||||
|
sources := make([]*deb.Snapshot, len(b.SourceSnapshots))
|
||||||
|
|
||||||
|
for i := range b.SourceSnapshots {
|
||||||
|
sources[i], err = snapshotCollection.ByName(b.SourceSnapshots[i])
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.LoadComplete(sources[i])
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list := deb.NewPackageList()
|
||||||
|
|
||||||
|
// verify package refs and build package list
|
||||||
|
for _, ref := range b.PackageRefs {
|
||||||
|
var p *deb.Package
|
||||||
|
|
||||||
|
p, err = context.CollectionFactory().PackageCollection().ByKey([]byte(ref))
|
||||||
|
if err != nil {
|
||||||
|
if err == database.ErrNotFound {
|
||||||
|
c.Fail(404, fmt.Errorf("package %s: %s", ref, err))
|
||||||
|
} else {
|
||||||
|
c.Fail(500, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = list.Add(p)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = deb.NewSnapshotFromRefList(b.Name, sources, deb.NewPackageRefListFromPackageList(list), b.Description)
|
||||||
|
|
||||||
|
err = snapshotCollection.Add(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(201, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/repos/:name/snapshots
|
||||||
|
func apiSnapshotsCreateFromRepository(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
repo *deb.LocalRepo
|
||||||
|
snapshot *deb.Snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
var b struct {
|
||||||
|
Name string `binding:"required"`
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.Bind(&b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
collection := context.CollectionFactory().LocalRepoCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
snapshotCollection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
snapshotCollection.Lock()
|
||||||
|
defer snapshotCollection.Unlock()
|
||||||
|
|
||||||
|
repo, err = collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(repo)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Description != "" {
|
||||||
|
snapshot.Description = b.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.Add(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(201, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT /api/snapshots/:name
|
||||||
|
func apiSnapshotsUpdate(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
snapshot *deb.Snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
var b struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.Bind(&b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
collection.Lock()
|
||||||
|
defer collection.Unlock()
|
||||||
|
|
||||||
|
snapshot, err = collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = collection.ByName(b.Name)
|
||||||
|
if err == nil {
|
||||||
|
c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Name != "" {
|
||||||
|
snapshot.Name = b.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Description != "" {
|
||||||
|
snapshot.Description = b.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/snapshots/:name
|
||||||
|
func apiSnapshotsShow(c *gin.Context) {
|
||||||
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
snapshot, err := collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/snapshots/:name
|
||||||
|
func apiSnapshotsDrop(c *gin.Context) {
|
||||||
|
name := c.Params.ByName("name")
|
||||||
|
force := c.Request.URL.Query().Get("force") == "1"
|
||||||
|
|
||||||
|
snapshotCollection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
snapshotCollection.Lock()
|
||||||
|
defer snapshotCollection.Unlock()
|
||||||
|
|
||||||
|
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
|
||||||
|
publishedCollection.RLock()
|
||||||
|
defer publishedCollection.RUnlock()
|
||||||
|
|
||||||
|
snapshot, err := snapshotCollection.ByName(name)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
published := publishedCollection.BySnapshot(snapshot)
|
||||||
|
|
||||||
|
if len(published) > 0 {
|
||||||
|
c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !force {
|
||||||
|
snapshots := snapshotCollection.BySnapshotSource(snapshot)
|
||||||
|
if len(snapshots) > 0 {
|
||||||
|
c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.Drop(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, gin.H{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/snapshots/:name/diff/:withSnapshot
|
||||||
|
func apiSnapshotsDiff(c *gin.Context) {
|
||||||
|
onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"
|
||||||
|
|
||||||
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
snapshotA, err := collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(snapshotA)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(snapshotB)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate diff
|
||||||
|
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []deb.PackageDiff{}
|
||||||
|
|
||||||
|
for _, pdiff := range diff {
|
||||||
|
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, pdiff)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/snapshots/:name/packages
|
||||||
|
func apiSnapshotsSearchPackages(c *gin.Context) {
|
||||||
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
collection.RLock()
|
||||||
|
defer collection.RUnlock()
|
||||||
|
|
||||||
|
snapshot, err := collection.ByName(c.Params.ByName("name"))
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showPackages(c, snapshot.RefList())
|
||||||
|
}
|
||||||
+17
-8
@@ -5,7 +5,6 @@ package aptly
|
|||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PackagePool is asbtraction of package pool storage.
|
// PackagePool is asbtraction of package pool storage.
|
||||||
@@ -26,26 +25,34 @@ type PackagePool interface {
|
|||||||
|
|
||||||
// PublishedStorage is abstraction of filesystem storing all published repositories
|
// PublishedStorage is abstraction of filesystem storing all published repositories
|
||||||
type PublishedStorage interface {
|
type PublishedStorage interface {
|
||||||
// PublicPath returns root of public part
|
|
||||||
PublicPath() string
|
|
||||||
// MkDir creates directory recursively under public path
|
// MkDir creates directory recursively under public path
|
||||||
MkDir(path string) error
|
MkDir(path string) error
|
||||||
// CreateFile creates file for writing under public path
|
// PutFile puts file into published storage at specified path
|
||||||
CreateFile(path string) (*os.File, error)
|
PutFile(path string, sourceFilename string) error
|
||||||
// RemoveDirs removes directory structure under public path
|
// RemoveDirs removes directory structure under public path
|
||||||
RemoveDirs(path string, progress Progress) error
|
RemoveDirs(path string, progress Progress) error
|
||||||
// Remove removes single file under public path
|
// Remove removes single file under public path
|
||||||
Remove(path string) error
|
Remove(path string) error
|
||||||
// LinkFromPool links package file from pool to dist's pool location
|
// LinkFromPool links package file from pool to dist's pool location
|
||||||
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath string) error
|
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath, sourceMD5 string, force bool) error
|
||||||
// Filelist returns list of files under prefix
|
// Filelist returns list of files under prefix
|
||||||
Filelist(prefix string) ([]string, error)
|
Filelist(prefix string) ([]string, error)
|
||||||
// ChecksumsForFile proxies requests to utils.ChecksumsForFile, joining public path
|
|
||||||
ChecksumsForFile(path string) (utils.ChecksumInfo, error)
|
|
||||||
// RenameFile renames (moves) file
|
// RenameFile renames (moves) file
|
||||||
RenameFile(oldName, newName string) error
|
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
|
// Progress is a progress displaying entity, it allows progress bars & simple prints
|
||||||
type Progress interface {
|
type Progress interface {
|
||||||
// Writer interface to support progress bar ticking
|
// Writer interface to support progress bar ticking
|
||||||
@@ -83,6 +90,8 @@ type Downloader interface {
|
|||||||
// Shutdown stops downloader after current tasks are finished,
|
// Shutdown stops downloader after current tasks are finished,
|
||||||
// but doesn't process rest of queue
|
// but doesn't process rest of queue
|
||||||
Shutdown()
|
Shutdown()
|
||||||
|
// Abort stops downloader without waiting for shutdown
|
||||||
|
Abort()
|
||||||
// GetProgress returns Progress object
|
// GetProgress returns Progress object
|
||||||
GetProgress() Progress
|
GetProgress() Progress
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package aptly
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResultReporter is abstraction for result reporting from complex processing functions
|
||||||
|
type ResultReporter interface {
|
||||||
|
// Warning is non-fatal error message
|
||||||
|
Warning(msg string, a ...interface{})
|
||||||
|
// Removed is signal that something has been removed
|
||||||
|
Removed(msg string, a ...interface{})
|
||||||
|
// Added is signal that something has been added
|
||||||
|
Added(msg string, a ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsoleResultReporter is implementation of ResultReporter that prints in colors to console
|
||||||
|
type ConsoleResultReporter struct {
|
||||||
|
Progress Progress
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var (
|
||||||
|
_ ResultReporter = &ConsoleResultReporter{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Warning is non-fatal error message (yellow)
|
||||||
|
func (c *ConsoleResultReporter) Warning(msg string, a ...interface{}) {
|
||||||
|
c.Progress.ColoredPrintf("@y[!]@| @!"+msg+"@|", a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removed is signal that something has been removed (red)
|
||||||
|
func (c *ConsoleResultReporter) Removed(msg string, a ...interface{}) {
|
||||||
|
c.Progress.ColoredPrintf("@r[-]@| "+msg, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added is signal that something has been added (green)
|
||||||
|
func (c *ConsoleResultReporter) Added(msg string, a ...interface{}) {
|
||||||
|
c.Progress.ColoredPrintf("@g[+]@| "+msg, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordingResultReporter is implementation of ResultReporter that collects all messages
|
||||||
|
type RecordingResultReporter struct {
|
||||||
|
Warnings []string
|
||||||
|
AddedLines []string `json:"Added"`
|
||||||
|
RemovedLines []string `json:"Removed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var (
|
||||||
|
_ ResultReporter = &RecordingResultReporter{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Warning is non-fatal error message
|
||||||
|
func (r *RecordingResultReporter) Warning(msg string, a ...interface{}) {
|
||||||
|
r.Warnings = append(r.Warnings, fmt.Sprintf(msg, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removed is signal that something has been removed
|
||||||
|
func (r *RecordingResultReporter) Removed(msg string, a ...interface{}) {
|
||||||
|
r.RemovedLines = append(r.RemovedLines, fmt.Sprintf(msg, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added is signal that something has been added
|
||||||
|
func (r *RecordingResultReporter) Added(msg string, a ...interface{}) {
|
||||||
|
r.AddedLines = append(r.AddedLines, fmt.Sprintf(msg, a...))
|
||||||
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package aptly
|
package aptly
|
||||||
|
|
||||||
// Version of aptly
|
// Version of aptly
|
||||||
const Version = "0.6"
|
const Version = "0.9.5"
|
||||||
|
|
||||||
// Enable debugging features?
|
// Enable debugging features?
|
||||||
const EnableDebug = false
|
const EnableDebug = false
|
||||||
|
|||||||
+15
@@ -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(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/smira/aptly/api"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyAPIServe(cmd *commander.Command, args []string) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(args) != 0 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
listen := context.Flags().Lookup("listen").Value.String()
|
||||||
|
|
||||||
|
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
|
||||||
|
|
||||||
|
err = http.ListenAndServe(listen, api.Router(context))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to serve: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdAPIServe() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyAPIServe,
|
||||||
|
UsageLine: "serve",
|
||||||
|
Short: "start API HTTP service",
|
||||||
|
Long: `
|
||||||
|
Stat HTTP server with aptly REST API.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly api serve -listen=:8080
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
}
|
||||||
+18
-2
@@ -34,6 +34,18 @@ func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
|
|||||||
return
|
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
|
// RootCommand creates root command in command tree
|
||||||
func RootCommand() *commander.Command {
|
func RootCommand() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
@@ -46,21 +58,25 @@ upgrade individual packages, take snapshots and publish them
|
|||||||
back as Debian repositories.
|
back as Debian repositories.
|
||||||
|
|
||||||
aptly's goal is to establish repeatability and controlled changes
|
aptly's goal is to establish repeatability and controlled changes
|
||||||
in a package-centric environment. aptly allows to fix a set of packages
|
in a package-centric environment. aptly allows one to fix a set of packages
|
||||||
in a repository, so that package installation and upgrade becomes
|
in a repository, so that package installation and upgrade becomes
|
||||||
deterministic. At the same time aptly allows to perform controlled,
|
deterministic. At the same time aptly allows one to perform controlled,
|
||||||
fine-grained changes in repository contents to transition your
|
fine-grained changes in repository contents to transition your
|
||||||
package environment to new version.`,
|
package environment to new version.`,
|
||||||
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
|
||||||
Subcommands: []*commander.Command{
|
Subcommands: []*commander.Command{
|
||||||
|
makeCmdConfig(),
|
||||||
makeCmdDb(),
|
makeCmdDb(),
|
||||||
makeCmdGraph(),
|
makeCmdGraph(),
|
||||||
makeCmdMirror(),
|
makeCmdMirror(),
|
||||||
makeCmdRepo(),
|
makeCmdRepo(),
|
||||||
makeCmdServe(),
|
makeCmdServe(),
|
||||||
makeCmdSnapshot(),
|
makeCmdSnapshot(),
|
||||||
|
makeCmdTask(),
|
||||||
makeCmdPublish(),
|
makeCmdPublish(),
|
||||||
makeCmdVersion(),
|
makeCmdVersion(),
|
||||||
|
makeCmdPackage(),
|
||||||
|
makeCmdAPI(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyConfigShow(cmd *commander.Command, args []string) error {
|
||||||
|
|
||||||
|
config := context.Config()
|
||||||
|
prettyJSON, err := json.MarshalIndent(config, "", " ")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to dump the config file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(prettyJSON))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdConfigShow() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyConfigShow,
|
||||||
|
UsageLine: "show",
|
||||||
|
Short: "show current aptly's config",
|
||||||
|
Long: `
|
||||||
|
Command show displays the current aptly configuration.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly config show
|
||||||
|
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
+13
-268
@@ -1,286 +1,31 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
ctx "github.com/smira/aptly/context"
|
||||||
"github.com/smira/aptly/aptly"
|
|
||||||
"github.com/smira/aptly/console"
|
|
||||||
"github.com/smira/aptly/database"
|
|
||||||
"github.com/smira/aptly/deb"
|
|
||||||
"github.com/smira/aptly/files"
|
|
||||||
"github.com/smira/aptly/http"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"github.com/smira/commander"
|
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"runtime/pprof"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AptlyContext is a common context shared by all commands
|
var context *ctx.AptlyContext
|
||||||
type AptlyContext struct {
|
|
||||||
flags *flag.FlagSet
|
|
||||||
configLoaded bool
|
|
||||||
|
|
||||||
progress aptly.Progress
|
|
||||||
downloader aptly.Downloader
|
|
||||||
database database.Storage
|
|
||||||
packagePool aptly.PackagePool
|
|
||||||
publishedStorage aptly.PublishedStorage
|
|
||||||
collectionFactory *deb.CollectionFactory
|
|
||||||
dependencyOptions int
|
|
||||||
architecturesList []string
|
|
||||||
// Debug features
|
|
||||||
fileCPUProfile *os.File
|
|
||||||
fileMemProfile *os.File
|
|
||||||
fileMemStats *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
var context *AptlyContext
|
|
||||||
|
|
||||||
// FatalError is type for panicking to abort execution with non-zero
|
|
||||||
// exit code and print meaningful explanation
|
|
||||||
type FatalError struct {
|
|
||||||
ReturnCode int
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal panics and aborts execution with exit code 1
|
|
||||||
func Fatal(err error) {
|
|
||||||
returnCode := 1
|
|
||||||
if err == commander.ErrFlagError || err == commander.ErrCommandError {
|
|
||||||
returnCode = 2
|
|
||||||
}
|
|
||||||
panic(&FatalError{ReturnCode: returnCode, Message: err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config loads and returns current configuration
|
|
||||||
func (context *AptlyContext) Config() *utils.ConfigStructure {
|
|
||||||
if !context.configLoaded {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
configLocation := context.flags.Lookup("config").Value.String()
|
|
||||||
if configLocation != "" {
|
|
||||||
err = utils.LoadConfig(configLocation, &utils.Config)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
configLocations := []string{
|
|
||||||
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
|
|
||||||
"/etc/aptly.conf",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, configLocation := range configLocations {
|
|
||||||
err = utils.LoadConfig(configLocation, &utils.Config)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
|
|
||||||
utils.SaveConfig(configLocations[0], &utils.Config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.configLoaded = true
|
|
||||||
|
|
||||||
}
|
|
||||||
return &utils.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// DependencyOptions calculates options related to dependecy handling
|
|
||||||
func (context *AptlyContext) DependencyOptions() int {
|
|
||||||
if context.dependencyOptions == -1 {
|
|
||||||
context.dependencyOptions = 0
|
|
||||||
if context.Config().DepFollowSuggests || context.flags.Lookup("dep-follow-suggests").Value.Get().(bool) {
|
|
||||||
context.dependencyOptions |= deb.DepFollowSuggests
|
|
||||||
}
|
|
||||||
if context.Config().DepFollowRecommends || context.flags.Lookup("dep-follow-recommends").Value.Get().(bool) {
|
|
||||||
context.dependencyOptions |= deb.DepFollowRecommends
|
|
||||||
}
|
|
||||||
if context.Config().DepFollowAllVariants || context.flags.Lookup("dep-follow-all-variants").Value.Get().(bool) {
|
|
||||||
context.dependencyOptions |= deb.DepFollowAllVariants
|
|
||||||
}
|
|
||||||
if context.Config().DepFollowSource || context.flags.Lookup("dep-follow-source").Value.Get().(bool) {
|
|
||||||
context.dependencyOptions |= deb.DepFollowSource
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.dependencyOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArchitecturesList returns list of architectures fixed via command line or config
|
|
||||||
func (context *AptlyContext) ArchitecturesList() []string {
|
|
||||||
if context.architecturesList == nil {
|
|
||||||
context.architecturesList = context.Config().Architectures
|
|
||||||
optionArchitectures := context.flags.Lookup("architectures").Value.String()
|
|
||||||
if optionArchitectures != "" {
|
|
||||||
context.architecturesList = strings.Split(optionArchitectures, ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.architecturesList
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress creates or returns Progress object
|
|
||||||
func (context *AptlyContext) Progress() aptly.Progress {
|
|
||||||
if context.progress == nil {
|
|
||||||
context.progress = console.NewProgress()
|
|
||||||
context.progress.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.progress
|
|
||||||
}
|
|
||||||
|
|
||||||
// Downloader returns instance of current downloader
|
|
||||||
func (context *AptlyContext) Downloader() aptly.Downloader {
|
|
||||||
if context.downloader == nil {
|
|
||||||
context.downloader = http.NewDownloader(context.Config().DownloadConcurrency, context.Progress())
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.downloader
|
|
||||||
}
|
|
||||||
|
|
||||||
// DBPath builds path to database
|
|
||||||
func (context *AptlyContext) DBPath() string {
|
|
||||||
return filepath.Join(context.Config().RootDir, "db")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database opens and returns current instance of database
|
|
||||||
func (context *AptlyContext) Database() (database.Storage, error) {
|
|
||||||
if context.database == nil {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
context.database, err = database.OpenDB(context.DBPath())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't open database: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.database, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CollectionFactory builds factory producing all kinds of collections
|
|
||||||
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
|
|
||||||
if context.collectionFactory == nil {
|
|
||||||
db, err := context.Database()
|
|
||||||
if err != nil {
|
|
||||||
Fatal(err)
|
|
||||||
}
|
|
||||||
context.collectionFactory = deb.NewCollectionFactory(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.collectionFactory
|
|
||||||
}
|
|
||||||
|
|
||||||
// PackagePool returns instance of PackagePool
|
|
||||||
func (context *AptlyContext) PackagePool() aptly.PackagePool {
|
|
||||||
if context.packagePool == nil {
|
|
||||||
context.packagePool = files.NewPackagePool(context.Config().RootDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.packagePool
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublishedStorage returns instance of PublishedStorage
|
|
||||||
func (context *AptlyContext) PublishedStorage() aptly.PublishedStorage {
|
|
||||||
if context.publishedStorage == nil {
|
|
||||||
context.publishedStorage = files.NewPublishedStorage(context.Config().RootDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.publishedStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShutdownContext shuts context down
|
// ShutdownContext shuts context down
|
||||||
func ShutdownContext() {
|
func ShutdownContext() {
|
||||||
if aptly.EnableDebug {
|
context.Shutdown()
|
||||||
if context.fileMemProfile != nil {
|
}
|
||||||
pprof.WriteHeapProfile(context.fileMemProfile)
|
|
||||||
context.fileMemProfile.Close()
|
// CleanupContext does partial shutdown of context
|
||||||
context.fileMemProfile = nil
|
func CleanupContext() {
|
||||||
}
|
context.Cleanup()
|
||||||
if context.fileCPUProfile != nil {
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
context.fileCPUProfile.Close()
|
|
||||||
context.fileCPUProfile = nil
|
|
||||||
}
|
|
||||||
if context.fileMemProfile != nil {
|
|
||||||
context.fileMemProfile.Close()
|
|
||||||
context.fileMemProfile = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if context.database != nil {
|
|
||||||
context.database.Close()
|
|
||||||
}
|
|
||||||
if context.downloader != nil {
|
|
||||||
context.downloader.Shutdown()
|
|
||||||
}
|
|
||||||
if context.progress != nil {
|
|
||||||
context.progress.Shutdown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitContext initializes context with default settings
|
// InitContext initializes context with default settings
|
||||||
func InitContext(flags *flag.FlagSet) error {
|
func InitContext(flags *flag.FlagSet) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
context = &AptlyContext{flags: flags, dependencyOptions: -1}
|
if context != nil {
|
||||||
|
panic("context already initialized")
|
||||||
if aptly.EnableDebug {
|
|
||||||
cpuprofile := flags.Lookup("cpuprofile").Value.String()
|
|
||||||
if cpuprofile != "" {
|
|
||||||
context.fileCPUProfile, err = os.Create(cpuprofile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pprof.StartCPUProfile(context.fileCPUProfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
memprofile := flags.Lookup("memprofile").Value.String()
|
|
||||||
if memprofile != "" {
|
|
||||||
context.fileMemProfile, err = os.Create(memprofile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memstats := flags.Lookup("memstats").Value.String()
|
|
||||||
if memstats != "" {
|
|
||||||
interval := flags.Lookup("meminterval").Value.Get().(time.Duration)
|
|
||||||
|
|
||||||
context.fileMemStats, err = os.Create(memstats)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
context.fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
var stats runtime.MemStats
|
|
||||||
|
|
||||||
start := time.Now().UnixNano()
|
|
||||||
|
|
||||||
for {
|
|
||||||
runtime.ReadMemStats(&stats)
|
|
||||||
if context.fileMemStats != nil {
|
|
||||||
context.fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
|
|
||||||
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
|
|
||||||
time.Sleep(interval)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
context, err = ctx.NewContext(flags)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+169
-36
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// aptly db cleanup
|
// aptly db cleanup
|
||||||
@@ -17,31 +18,98 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
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...
|
// collect information about references packages...
|
||||||
existingPackageRefs := deb.NewPackageRefList()
|
existingPackageRefs := deb.NewPackageRefList()
|
||||||
|
|
||||||
context.Progress().Printf("Loading mirrors, local repos and snapshots...\n")
|
// 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 {
|
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)
|
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
context.Progress().ColoredPrintf("@{y}Loading local repos:@|")
|
||||||
|
}
|
||||||
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
|
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)
|
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
@@ -49,12 +117,32 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
if verbose {
|
||||||
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
|
|
||||||
|
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
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,38 +150,65 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... and compare it to the list of all packages
|
// ... and compare it to the list of all packages
|
||||||
context.Progress().Printf("Loading list of all packages...\n")
|
context.Progress().ColoredPrintf("@{w!}Loading list of all packages...@|")
|
||||||
allPackageRefs := context.CollectionFactory().PackageCollection().AllPackageRefs()
|
allPackageRefs := context.CollectionFactory().PackageCollection().AllPackageRefs()
|
||||||
|
|
||||||
toDelete := allPackageRefs.Substract(existingPackageRefs)
|
toDelete := allPackageRefs.Substract(existingPackageRefs)
|
||||||
|
|
||||||
// delete packages that are no longer referenced
|
// delete packages that are no longer referenced
|
||||||
context.Progress().Printf("Deleting unreferenced packages (%d)...\n", toDelete.Len())
|
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced packages (%d)...@|", toDelete.Len())
|
||||||
|
|
||||||
// database can't err as collection factory already constructed
|
// database can't err as collection factory already constructed
|
||||||
db, _ := context.Database()
|
db, _ := context.Database()
|
||||||
db.StartBatch()
|
|
||||||
err = toDelete.ForEach(func(ref []byte) error {
|
|
||||||
return context.CollectionFactory().PackageCollection().DeleteByKey(ref)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.FinishBatch()
|
if toDelete.Len() > 0 {
|
||||||
if err != nil {
|
if verbose {
|
||||||
return fmt.Errorf("unable to write to DB: %s", err)
|
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)
|
// now, build a list of files that should be present in Repository (package pool)
|
||||||
context.Progress().Printf("Building list of files referenced by packages...\n")
|
context.Progress().ColoredPrintf("@{w!}Building list of files referenced by packages...@|")
|
||||||
referencedFiles := make([]string, 0, existingPackageRefs.Len())
|
referencedFiles := make([]string, 0, existingPackageRefs.Len())
|
||||||
context.Progress().InitBar(int64(existingPackageRefs.Len()), false)
|
context.Progress().InitBar(int64(existingPackageRefs.Len()), false)
|
||||||
|
|
||||||
err = existingPackageRefs.ForEach(func(key []byte) error {
|
err = existingPackageRefs.ForEach(func(key []byte) error {
|
||||||
pkg, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
|
pkg, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
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())
|
paths, err2 := pkg.FilepathList(context.PackagePool())
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
@@ -112,7 +227,7 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
context.Progress().ShutdownBar()
|
context.Progress().ShutdownBar()
|
||||||
|
|
||||||
// build a list of files in the package pool
|
// build a list of files in the package pool
|
||||||
context.Progress().Printf("Building list of files in package pool...\n")
|
context.Progress().ColoredPrintf("@{w!}Building list of files in package pool...@|")
|
||||||
existingFiles, err := context.PackagePool().FilepathList(context.Progress())
|
existingFiles, err := context.PackagePool().FilepathList(context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to collect file paths: %s", err)
|
return fmt.Errorf("unable to collect file paths: %s", err)
|
||||||
@@ -122,28 +237,43 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
|
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
|
||||||
|
|
||||||
// delete files that are no longer referenced
|
// delete files that are no longer referenced
|
||||||
context.Progress().Printf("Deleting unreferenced files (%d)...\n", len(filesToDelete))
|
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced files (%d)...@|", len(filesToDelete))
|
||||||
|
|
||||||
if len(filesToDelete) > 0 {
|
if len(filesToDelete) > 0 {
|
||||||
context.Progress().InitBar(int64(len(filesToDelete)), false)
|
if verbose {
|
||||||
|
context.Progress().ColoredPrintf("@{r}List of files to be deleted:@|")
|
||||||
var size, totalSize int64
|
for _, file := range filesToDelete {
|
||||||
for _, file := range filesToDelete {
|
context.Progress().ColoredPrintf(" - @{r}%s@|", file)
|
||||||
size, err = context.PackagePool().Remove(file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().AddBar(1)
|
|
||||||
totalSize += size
|
|
||||||
}
|
}
|
||||||
context.Progress().ShutdownBar()
|
|
||||||
|
|
||||||
context.Progress().Printf("Disk space freed: %s...\n", utils.HumanBytes(totalSize))
|
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.@|")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().Printf("Compacting database...\n")
|
if !dryRun {
|
||||||
err = db.CompactDB()
|
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
|
return err
|
||||||
}
|
}
|
||||||
@@ -163,5 +293,8 @@ Example:
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flag.Bool("verbose", false, "be verbose when loading objects/removing them")
|
||||||
|
cmd.Flag.Bool("dry-run", false, "don't delete anything")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-116
@@ -2,7 +2,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"code.google.com/p/gographviz"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyGraph(cmd *commander.Command, args []string) error {
|
func aptlyGraph(cmd *commander.Command, args []string) error {
|
||||||
@@ -21,121 +19,11 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
graph := gographviz.NewEscape()
|
|
||||||
graph.SetDir(true)
|
|
||||||
graph.SetName("aptly")
|
|
||||||
|
|
||||||
existingNodes := map[string]bool{}
|
|
||||||
|
|
||||||
fmt.Printf("Loading mirrors...\n")
|
|
||||||
|
|
||||||
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
|
|
||||||
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.AddNode("aptly", repo.UUID, map[string]string{
|
|
||||||
"shape": "Mrecord",
|
|
||||||
"style": "filled",
|
|
||||||
"fillcolor": "darkgoldenrod1",
|
|
||||||
"label": fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
|
|
||||||
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
|
|
||||||
strings.Join(repo.Architectures, ", "), repo.NumPackages()),
|
|
||||||
})
|
|
||||||
existingNodes[repo.UUID] = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Loading local repos...\n")
|
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
|
|
||||||
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.AddNode("aptly", repo.UUID, map[string]string{
|
|
||||||
"shape": "Mrecord",
|
|
||||||
"style": "filled",
|
|
||||||
"fillcolor": "mediumseagreen",
|
|
||||||
"label": fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
|
|
||||||
repo.Name, repo.Comment, repo.NumPackages()),
|
|
||||||
})
|
|
||||||
existingNodes[repo.UUID] = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Loading snapshots...\n")
|
|
||||||
|
|
||||||
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
|
||||||
existingNodes[snapshot.UUID] = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
|
||||||
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
description := snapshot.Description
|
|
||||||
if snapshot.SourceKind == "repo" {
|
|
||||||
description = "Snapshot from repo"
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.AddNode("aptly", snapshot.UUID, map[string]string{
|
|
||||||
"shape": "Mrecord",
|
|
||||||
"style": "filled",
|
|
||||||
"fillcolor": "cadetblue1",
|
|
||||||
"label": fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages()),
|
|
||||||
})
|
|
||||||
|
|
||||||
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
|
|
||||||
for _, uuid := range snapshot.SourceIDs {
|
|
||||||
_, exists := existingNodes[uuid]
|
|
||||||
if exists {
|
|
||||||
graph.AddEdge(uuid, snapshot.UUID, true, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Loading published repos...\n")
|
|
||||||
|
|
||||||
context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
|
|
||||||
graph.AddNode("aptly", repo.UUID, map[string]string{
|
|
||||||
"shape": "Mrecord",
|
|
||||||
"style": "filled",
|
|
||||||
"fillcolor": "darkolivegreen1",
|
|
||||||
"label": fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution,
|
|
||||||
strings.Join(repo.Components(), " "), strings.Join(repo.Architectures, ", ")),
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, uuid := range repo.Sources {
|
|
||||||
_, exists := existingNodes[uuid]
|
|
||||||
if exists {
|
|
||||||
graph.AddEdge(uuid, repo.UUID, true, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Printf("Generating graph...\n")
|
fmt.Printf("Generating graph...\n")
|
||||||
|
graph, err := deb.BuildGraph(context.CollectionFactory())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
buf := bytes.NewBufferString(graph.String())
|
buf := bytes.NewBufferString(graph.String())
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
|
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
|
||||||
if context.Config().GpgDisableVerify || flags.Lookup("ignore-signatures").Value.Get().(bool) {
|
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +54,9 @@ func makeCmdMirror() *commander.Command {
|
|||||||
makeCmdMirrorShow(),
|
makeCmdMirrorShow(),
|
||||||
makeCmdMirrorDrop(),
|
makeCmdMirrorDrop(),
|
||||||
makeCmdMirrorUpdate(),
|
makeCmdMirrorUpdate(),
|
||||||
|
makeCmdMirrorRename(),
|
||||||
|
makeCmdMirrorEdit(),
|
||||||
|
makeCmdMirrorSearch(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-4
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -15,7 +16,8 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool)
|
downloadSources := LookupOption(context.Config().DownloadSourcePackages, context.Flags(), "with-sources")
|
||||||
|
downloadUdebs := context.Flags().Lookup("with-udebs").Value.Get().(bool)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mirrorName, archiveURL, distribution string
|
mirrorName, archiveURL, distribution string
|
||||||
@@ -32,12 +34,24 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
|
|||||||
archiveURL, distribution, components = args[1], args[2], args[3:]
|
archiveURL, distribution, components = args[1], args[2], args[3:]
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(), downloadSources)
|
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(),
|
||||||
|
downloadSources, downloadUdebs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create mirror: %s", err)
|
return fmt.Errorf("unable to create mirror: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
verifier, err := getVerifier(context.flags)
|
repo.Filter = context.Flags().Lookup("filter").Value.String()
|
||||||
|
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
|
||||||
|
repo.SkipComponentCheck = context.Flags().Lookup("force-components").Value.Get().(bool)
|
||||||
|
|
||||||
|
if repo.Filter != "" {
|
||||||
|
_, err = query.Parse(repo.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create mirror: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifier, err := getVerifier(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
|
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
|
||||||
}
|
}
|
||||||
@@ -63,7 +77,7 @@ func makeCmdMirrorCreate() *commander.Command {
|
|||||||
Short: "create new mirror",
|
Short: "create new mirror",
|
||||||
Long: `
|
Long: `
|
||||||
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
|
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
|
||||||
via HTTP. aptly would try download Release file from remote repository and verify its' signature. Command
|
via HTTP and FTP. aptly would try download Release file from remote repository and verify its' signature. Command
|
||||||
line format resembles apt utlitily sources.list(5).
|
line format resembles apt utlitily sources.list(5).
|
||||||
|
|
||||||
PPA urls could specified in short format:
|
PPA urls could specified in short format:
|
||||||
@@ -79,6 +93,10 @@ Example:
|
|||||||
|
|
||||||
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
|
cmd.Flag.Bool("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-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)")
|
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
+6
-1
@@ -20,7 +20,12 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to drop: %s", err)
|
return fmt.Errorf("unable to drop: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
force := context.flags.Lookup("force").Value.Get().(bool)
|
err = repo.CheckLock()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to drop: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
force := context.Flags().Lookup("force").Value.Get().(bool)
|
||||||
if !force {
|
if !force {
|
||||||
snapshots := context.CollectionFactory().SnapshotCollection().ByRemoteRepoSource(repo)
|
snapshots := context.CollectionFactory().SnapshotCollection().ByRemoteRepoSource(repo)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -28,6 +28,8 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
context.CloseDatabase()
|
||||||
|
|
||||||
sort.Strings(repos)
|
sort.Strings(repos)
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+18
-1
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
@@ -28,6 +29,9 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Name: %s\n", repo.Name)
|
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("Archive Root URL: %s\n", repo.ArchiveRoot)
|
||||||
fmt.Printf("Distribution: %s\n", repo.Distribution)
|
fmt.Printf("Distribution: %s\n", repo.Distribution)
|
||||||
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
|
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
|
||||||
@@ -37,6 +41,19 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
|||||||
downloadSources = "yes"
|
downloadSources = "yes"
|
||||||
}
|
}
|
||||||
fmt.Printf("Download Sources: %s\n", downloadSources)
|
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() {
|
if repo.LastDownloadDate.IsZero() {
|
||||||
fmt.Printf("Last update: never\n")
|
fmt.Printf("Last update: never\n")
|
||||||
} else {
|
} else {
|
||||||
@@ -49,7 +66,7 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("%s: %s\n", k, repo.Meta[k])
|
fmt.Printf("%s: %s\n", k, repo.Meta[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
|
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
|
||||||
if withPackages {
|
if withPackages {
|
||||||
if repo.LastDownloadDate.IsZero() {
|
if repo.LastDownloadDate.IsZero() {
|
||||||
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
|
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
|
||||||
|
|||||||
+120
-3
@@ -2,8 +2,14 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
||||||
@@ -25,9 +31,17 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreMismatch := context.flags.Lookup("ignore-checksums").Value.Get().(bool)
|
force := context.Flags().Lookup("force").Value.Get().(bool)
|
||||||
|
if !force {
|
||||||
|
err = repo.CheckLock()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
verifier, err := getVerifier(context.flags)
|
ignoreMismatch := context.Flags().Lookup("ignore-checksums").Value.Get().(bool)
|
||||||
|
|
||||||
|
verifier, err := getVerifier(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
|
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
|
||||||
}
|
}
|
||||||
@@ -37,11 +51,112 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo.Download(context.Progress(), context.Downloader(), context.CollectionFactory(), context.PackagePool(), ignoreMismatch)
|
context.Progress().Printf("Downloading & parsing package files...\n")
|
||||||
|
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
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)
|
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
@@ -68,8 +183,10 @@ Example:
|
|||||||
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
|
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-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.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)")
|
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printReferencesTo(p *deb.Package) (err error) {
|
||||||
|
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
|
||||||
|
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if repo.RefList() != nil {
|
||||||
|
if repo.RefList().Has(p) {
|
||||||
|
fmt.Printf(" mirror %s\n", repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
|
||||||
|
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if repo.RefList() != nil {
|
||||||
|
if repo.RefList().Has(p) {
|
||||||
|
fmt.Printf(" local repo %s\n", repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
||||||
|
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if snapshot.RefList().Has(p) {
|
||||||
|
fmt.Printf(" snapshot %s\n", snapshot)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func aptlyPackageShow(cmd *commander.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
if len(args) != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := query.Parse(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to show: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
withFiles := context.Flags().Lookup("with-files").Value.Get().(bool)
|
||||||
|
withReferences := context.Flags().Lookup("with-references").Value.Get().(bool)
|
||||||
|
|
||||||
|
w := bufio.NewWriter(os.Stdout)
|
||||||
|
|
||||||
|
result := q.Query(context.CollectionFactory().PackageCollection())
|
||||||
|
|
||||||
|
err = result.ForEach(func(p *deb.Package) error {
|
||||||
|
p.Stanza().WriteTo(w, p.IsSource, false)
|
||||||
|
w.Flush()
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
if withFiles {
|
||||||
|
fmt.Printf("Files in the pool:\n")
|
||||||
|
for _, f := range p.Files() {
|
||||||
|
path, err := context.PackagePool().Path(f.Filename, f.Checksums.MD5)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf(" %s\n", path)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if withReferences {
|
||||||
|
fmt.Printf("References to package:\n")
|
||||||
|
printReferencesTo(p)
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to show: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPackageShow() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPackageShow,
|
||||||
|
UsageLine: "show <package-query>",
|
||||||
|
Short: "show details about packages matching query",
|
||||||
|
Long: `
|
||||||
|
Command shows displays detailed meta-information about packages
|
||||||
|
matching query. Information from Debian control file is displayed.
|
||||||
|
Optionally information about package files and
|
||||||
|
inclusion into mirrors/snapshots/local repos is shown.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly package show nginx-light_1.2.1-2.2+wheezy2_i386'
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-package-show", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flag.Bool("with-files", false, "display information about files from package pool")
|
||||||
|
cmd.Flag.Bool("with-references", false, "display information about mirrors, snapshots and local repos referencing this package")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
+3
-1
@@ -7,13 +7,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
|
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
|
||||||
if flags.Lookup("skip-signing").Value.Get().(bool) || context.Config().GpgDisableSign {
|
if LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := &utils.GpgSigner{}
|
signer := &utils.GpgSigner{}
|
||||||
signer.SetKey(flags.Lookup("gpg-key").Value.String())
|
signer.SetKey(flags.Lookup("gpg-key").Value.String())
|
||||||
signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").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()
|
err := signer.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+12
-7
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,14 +14,16 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distribution := args[0]
|
distribution := args[0]
|
||||||
prefix := "."
|
param := "."
|
||||||
|
|
||||||
if len(args) == 2 {
|
if len(args) == 2 {
|
||||||
prefix = args[1]
|
param = args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().PublishedRepoCollection().Remove(context.PublishedStorage(), prefix, distribution,
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
context.CollectionFactory(), context.Progress())
|
|
||||||
|
err = context.CollectionFactory().PublishedRepoCollection().Remove(context, storage, prefix, distribution,
|
||||||
|
context.CollectionFactory(), context.Progress(), context.Flags().Lookup("force-drop").Value.Get().(bool))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to remove: %s", err)
|
return fmt.Errorf("unable to remove: %s", err)
|
||||||
}
|
}
|
||||||
@@ -33,11 +36,11 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPublishDrop() *commander.Command {
|
func makeCmdPublishDrop() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishDrop,
|
Run: aptlyPublishDrop,
|
||||||
UsageLine: "drop <distribution> [<prefix>]",
|
UsageLine: "drop <distribution> [[<endpoint>:]<prefix>]",
|
||||||
Short: "remove published repository",
|
Short: "remove published repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command removes whatever has been published under specified <prefix> and
|
Command removes whatever has been published under specified <prefix>,
|
||||||
<distribution> name.
|
publishing <endpoint> and <distribution> name.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -45,5 +48,7 @@ Example:
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flag.Bool("force-drop", false, "remove published repository even if some files could not be cleaned up")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -25,7 +25,7 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
published = append(published, fmt.Sprintf("%s %s", repo.Prefix, repo.Distribution))
|
published = append(published, fmt.Sprintf("%s %s", repo.StoragePrefix(), repo.Distribution))
|
||||||
} else {
|
} else {
|
||||||
published = append(published, repo.String())
|
published = append(published, repo.String())
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,8 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to load list of repos: %s", err)
|
return fmt.Errorf("unable to load list of repos: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.CloseDatabase()
|
||||||
|
|
||||||
sort.Strings(published)
|
sort.Strings(published)
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
|
|||||||
+5
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
func makeCmdPublishRepo() *commander.Command {
|
func makeCmdPublishRepo() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishSnapshotOrRepo,
|
Run: aptlyPublishSnapshotOrRepo,
|
||||||
UsageLine: "repo <name> [<prefix>]",
|
UsageLine: "repo <name> [[<endpoint>:]<prefix>]",
|
||||||
Short: "publish local repository",
|
Short: "publish local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command publishes current state of local repository ready to be consumed
|
Command publishes current state of local repository ready to be consumed
|
||||||
@@ -37,9 +37,13 @@ Example:
|
|||||||
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
||||||
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
|
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("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("skip-signing", false, "don't sign Release files with GPG")
|
||||||
cmd.Flag.String("origin", "", "origin name to publish")
|
cmd.Flag.String("origin", "", "origin name to publish")
|
||||||
cmd.Flag.String("label", "", "label 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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-11
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
@@ -12,20 +13,21 @@ import (
|
|||||||
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
if len(args) < len(components) || len(args) > len(components)+1 {
|
if len(args) < len(components) || len(args) > len(components)+1 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix string
|
var param string
|
||||||
if len(args) == len(components)+1 {
|
if len(args) == len(components)+1 {
|
||||||
prefix = args[len(components)]
|
param = args[len(components)]
|
||||||
args = args[0 : len(args)-1]
|
args = args[0 : len(args)-1]
|
||||||
} else {
|
} else {
|
||||||
prefix = ""
|
param = ""
|
||||||
}
|
}
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sources = []interface{}{}
|
sources = []interface{}{}
|
||||||
@@ -108,9 +110,9 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
panic("unknown command")
|
panic("unknown command")
|
||||||
}
|
}
|
||||||
|
|
||||||
distribution := context.flags.Lookup("distribution").Value.String()
|
distribution := context.Flags().Lookup("distribution").Value.String()
|
||||||
|
|
||||||
published, err := deb.NewPublishedRepo(prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
|
published, err := deb.NewPublishedRepo(storage, prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to publish: %s", err)
|
return fmt.Errorf("unable to publish: %s", err)
|
||||||
}
|
}
|
||||||
@@ -123,12 +125,18 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
|
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := getSigner(context.flags)
|
signer, err := getSigner(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
|
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
|
||||||
|
if forceOverwrite {
|
||||||
|
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
|
||||||
|
"the same package pool.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to publish: %s", err)
|
return fmt.Errorf("unable to publish: %s", err)
|
||||||
}
|
}
|
||||||
@@ -146,8 +154,13 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
prefix += "/"
|
prefix += "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().Printf("\n%s been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
|
context.Progress().Printf("\n%s been successfully published.\n", message)
|
||||||
message, context.PublishedStorage().PublicPath())
|
|
||||||
|
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("Now you can add following line to apt sources:\n")
|
||||||
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
|
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
|
||||||
if utils.StrSliceHasItem(published.Architectures, "source") {
|
if utils.StrSliceHasItem(published.Architectures, "source") {
|
||||||
@@ -162,7 +175,7 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPublishSnapshot() *commander.Command {
|
func makeCmdPublishSnapshot() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishSnapshotOrRepo,
|
Run: aptlyPublishSnapshotOrRepo,
|
||||||
UsageLine: "snapshot <name> [<prefix>]",
|
UsageLine: "snapshot <name> [[<endpoint>:]<prefix>]",
|
||||||
Short: "publish snapshot",
|
Short: "publish snapshot",
|
||||||
Long: `
|
Long: `
|
||||||
Command publishes snapshot as Debian repository ready to be consumed
|
Command publishes snapshot as Debian repository ready to be consumed
|
||||||
@@ -186,9 +199,13 @@ Example:
|
|||||||
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
||||||
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
|
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("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("skip-signing", false, "don't sign Release files with GPG")
|
||||||
cmd.Flag.String("origin", "", "origin name to publish")
|
cmd.Flag.String("origin", "", "origin name to publish")
|
||||||
cmd.Flag.String("label", "", "label 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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-11
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -11,7 +12,7 @@ import (
|
|||||||
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
components := strings.Split(context.flags.Lookup("component").Value.String(), ",")
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
if len(args) < len(components)+1 || len(args) > len(components)+2 {
|
if len(args) < len(components)+1 || len(args) > len(components)+2 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
@@ -19,7 +20,7 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distribution := args[0]
|
distribution := args[0]
|
||||||
prefix := "."
|
param := "."
|
||||||
|
|
||||||
var (
|
var (
|
||||||
names []string
|
names []string
|
||||||
@@ -27,15 +28,17 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if len(args) == len(components)+2 {
|
if len(args) == len(components)+2 {
|
||||||
prefix = args[1]
|
param = args[1]
|
||||||
names = args[2:]
|
names = args[2:]
|
||||||
} else {
|
} else {
|
||||||
names = args[1:]
|
names = args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
|
||||||
var published *deb.PublishedRepo
|
var published *deb.PublishedRepo
|
||||||
|
|
||||||
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
|
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -59,6 +62,10 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, component := range components {
|
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])
|
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(names[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to switch: %s", err)
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
@@ -72,12 +79,18 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
published.UpdateSnapshot(component, snapshot)
|
published.UpdateSnapshot(component, snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := getSigner(context.flags)
|
signer, err := getSigner(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
|
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
|
||||||
|
if forceOverwrite {
|
||||||
|
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
|
||||||
|
"the same package pool.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to publish: %s", err)
|
return fmt.Errorf("unable to publish: %s", err)
|
||||||
}
|
}
|
||||||
@@ -88,7 +101,7 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
||||||
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
|
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -101,10 +114,10 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPublishSwitch() *commander.Command {
|
func makeCmdPublishSwitch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishSwitch,
|
Run: aptlyPublishSwitch,
|
||||||
UsageLine: "switch <distribution> [<prefix>] <new-snapshot>",
|
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
|
||||||
Short: "update published repository by switching to new snapshot",
|
Short: "update published repository by switching to new snapshot",
|
||||||
Long: `
|
Long: `
|
||||||
Command switches in-place published repository with new snapshot contents. All
|
Command switches in-place published snapshots with new snapshot contents. All
|
||||||
publishing parameters are preserved (architecture list, distribution,
|
publishing parameters are preserved (architecture list, distribution,
|
||||||
component).
|
component).
|
||||||
|
|
||||||
@@ -112,19 +125,26 @@ For multiple component repositories, flag -component should be given with
|
|||||||
list of components to update. Corresponding snapshots should be given in the
|
list of components to update. Corresponding snapshots should be given in the
|
||||||
same order, e.g.:
|
same order, e.g.:
|
||||||
|
|
||||||
aptly publish update -component=main,contrib wheezy wh-main wh-contrib
|
aptly publish switch -component=main,contrib wheezy wh-main wh-contrib
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly publish update wheezy ppa wheezy-7.5
|
$ aptly publish switch wheezy ppa wheezy-7.5
|
||||||
|
|
||||||
|
This command would switch published repository (with one component) named ppa/wheezy
|
||||||
|
(prefix ppa, dsitribution wheezy to new snapshot wheezy-7.5).
|
||||||
`,
|
`,
|
||||||
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
||||||
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
|
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("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("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.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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-7
@@ -15,15 +15,16 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distribution := args[0]
|
distribution := args[0]
|
||||||
prefix := "."
|
param := "."
|
||||||
|
|
||||||
if len(args) == 2 {
|
if len(args) == 2 {
|
||||||
prefix = args[1]
|
param = args[1]
|
||||||
}
|
}
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
|
||||||
var published *deb.PublishedRepo
|
var published *deb.PublishedRepo
|
||||||
|
|
||||||
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
|
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -42,12 +43,18 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
published.UpdateLocalRepo(component)
|
published.UpdateLocalRepo(component)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := getSigner(context.flags)
|
signer, err := getSigner(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
return fmt.Errorf("unable to initialize GPG signer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
|
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
|
||||||
|
if forceOverwrite {
|
||||||
|
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
|
||||||
|
"the same package pool.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to publish: %s", err)
|
return fmt.Errorf("unable to publish: %s", err)
|
||||||
}
|
}
|
||||||
@@ -58,7 +65,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
||||||
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
|
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -71,7 +78,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPublishUpdate() *commander.Command {
|
func makeCmdPublishUpdate() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishUpdate,
|
Run: aptlyPublishUpdate,
|
||||||
UsageLine: "update <distribution> [<prefix>]",
|
UsageLine: "update <distribution> [[<endpoint>:]<prefix>]",
|
||||||
Short: "update published local repository",
|
Short: "update published local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command re-publishes (updates) published local repository. <distribution>
|
Command re-publishes (updates) published local repository. <distribution>
|
||||||
@@ -91,7 +98,11 @@ Example:
|
|||||||
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
|
||||||
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
|
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("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("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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ func makeCmdRepo() *commander.Command {
|
|||||||
makeCmdRepoMove(),
|
makeCmdRepoMove(),
|
||||||
makeCmdRepoRemove(),
|
makeCmdRepoRemove(),
|
||||||
makeCmdRepoShow(),
|
makeCmdRepoShow(),
|
||||||
|
makeCmdRepoRename(),
|
||||||
|
makeCmdRepoSearch(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-125
@@ -2,14 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
||||||
@@ -40,130 +38,22 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to load packages: %s", err)
|
return fmt.Errorf("unable to load packages: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
packageFiles := []string{}
|
forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool)
|
||||||
failedFiles := []string{}
|
|
||||||
|
|
||||||
for _, location := range args[1:] {
|
var packageFiles, failedFiles []string
|
||||||
info, err2 := os.Stat(location)
|
|
||||||
if err2 != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2)
|
|
||||||
failedFiles = append(failedFiles, location)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
err2 = filepath.Walk(location, func(path string, info os.FileInfo, err3 error) error {
|
|
||||||
if err3 != nil {
|
|
||||||
return err3
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
|
packageFiles, failedFiles, err = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()})
|
||||||
packageFiles = append(packageFiles, path)
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("unable to collect package files: %s", err)
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
|
|
||||||
packageFiles = append(packageFiles, location)
|
|
||||||
} else {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
|
|
||||||
failedFiles = append(failedFiles, location)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processedFiles := []string{}
|
var processedFiles, failedFiles2 []string
|
||||||
sort.Strings(packageFiles)
|
|
||||||
|
|
||||||
for _, file := range packageFiles {
|
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
||||||
var (
|
context.CollectionFactory().PackageCollection(), &aptly.ConsoleResultReporter{Progress: context.Progress()})
|
||||||
stanza deb.Stanza
|
failedFiles = append(failedFiles, failedFiles2...)
|
||||||
p *deb.Package
|
if err != nil {
|
||||||
)
|
return fmt.Errorf("unable to import package files: %s", err)
|
||||||
|
|
||||||
candidateProcessedFiles := []string{}
|
|
||||||
isSourcePackage := strings.HasSuffix(file, ".dsc")
|
|
||||||
|
|
||||||
if isSourcePackage {
|
|
||||||
stanza, err = deb.GetControlFileFromDsc(file, verifier)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
stanza["Package"] = stanza["Source"]
|
|
||||||
delete(stanza, "Source")
|
|
||||||
|
|
||||||
p, err = deb.NewSourcePackageFromControlFile(stanza)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stanza, err = deb.GetControlFileFromDeb(file)
|
|
||||||
p = deb.NewPackageFromControlFile(stanza)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
|
|
||||||
failedFiles = append(failedFiles, file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var checksums utils.ChecksumInfo
|
|
||||||
checksums, err = utils.ChecksumsForFile(file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isSourcePackage {
|
|
||||||
p.UpdateFiles(append(p.Files(), deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}))
|
|
||||||
} else {
|
|
||||||
p.UpdateFiles([]deb.PackageFile{deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}})
|
|
||||||
}
|
|
||||||
|
|
||||||
err = context.PackagePool().Import(file, checksums.MD5)
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err)
|
|
||||||
failedFiles = append(failedFiles, file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateProcessedFiles = append(candidateProcessedFiles, file)
|
|
||||||
|
|
||||||
// go over all files, except for the last one (.dsc/.deb itself)
|
|
||||||
for _, f := range p.Files() {
|
|
||||||
if filepath.Base(f.Filename) == filepath.Base(file) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(f.Filename))
|
|
||||||
err = context.PackagePool().Import(sourceFile, f.Checksums.MD5)
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err)
|
|
||||||
failedFiles = append(failedFiles, file)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateProcessedFiles = append(candidateProcessedFiles, sourceFile)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// some files haven't been imported
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = context.CollectionFactory().PackageCollection().Update(p)
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err)
|
|
||||||
failedFiles = append(failedFiles, file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = list.Add(p)
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
|
|
||||||
failedFiles = append(failedFiles, file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Progress().ColoredPrintf("@g[+]@| %s added@|", p)
|
|
||||||
processedFiles = append(processedFiles, candidateProcessedFiles...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
||||||
@@ -173,7 +63,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to save: %s", err)
|
return fmt.Errorf("unable to save: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.flags.Lookup("remove-files").Value.Get().(bool) {
|
if context.Flags().Lookup("remove-files").Value.Get().(bool) {
|
||||||
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
||||||
|
|
||||||
for _, file := range processedFiles {
|
for _, file := range processedFiles {
|
||||||
@@ -202,8 +92,8 @@ func makeCmdRepoAdd() *commander.Command {
|
|||||||
UsageLine: "add <name> <package file.deb>|<directory> ...",
|
UsageLine: "add <name> <package file.deb>|<directory> ...",
|
||||||
Short: "add packages to local repository",
|
Short: "add packages to local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command adds packages to local repository from .deb (binary packages) and .dsc (source packages) files.
|
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 *.deb or *.dsc
|
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
|
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
|
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.
|
added automatically as well. Extra files for source package should be in the same directory as *.dsc file.
|
||||||
@@ -216,6 +106,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("remove-files", false, "remove files that have been imported successfully into repository")
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -8,10 +8,10 @@ import (
|
|||||||
func makeCmdRepoCopy() *commander.Command {
|
func makeCmdRepoCopy() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyRepoMoveCopyImport,
|
Run: aptlyRepoMoveCopyImport,
|
||||||
UsageLine: "copy <src-name> <dst-name> <package-spec> ...",
|
UsageLine: "copy <src-name> <dst-name> <package-query> ...",
|
||||||
Short: "copy packages between local repositories",
|
Short: "copy packages between local repositories",
|
||||||
Long: `
|
Long: `
|
||||||
Command copy copies packages matching <package-spec> from local repo
|
Command copy copies packages matching <package-query> from local repo
|
||||||
<src-name> to local repo <dst-name>.
|
<src-name> to local repo <dst-name>.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|||||||
+3
-3
@@ -14,9 +14,9 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := deb.NewLocalRepo(args[0], context.flags.Lookup("comment").Value.String())
|
repo := deb.NewLocalRepo(args[0], context.Flags().Lookup("comment").Value.String())
|
||||||
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
|
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
|
||||||
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
|
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
|
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to drop: local repo is published")
|
return fmt.Errorf("unable to drop: local repo is published")
|
||||||
}
|
}
|
||||||
|
|
||||||
force := context.flags.Lookup("force").Value.Get().(bool)
|
force := context.Flags().Lookup("force").Value.Get().(bool)
|
||||||
if !force {
|
if !force {
|
||||||
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
|
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -23,16 +23,16 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to edit: %s", err)
|
return fmt.Errorf("unable to edit: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.flags.Lookup("comment").Value.String() != "" {
|
if context.Flags().Lookup("comment").Value.String() != "" {
|
||||||
repo.Comment = context.flags.Lookup("comment").Value.String()
|
repo.Comment = context.Flags().Lookup("comment").Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.flags.Lookup("distribution").Value.String() != "" {
|
if context.Flags().Lookup("distribution").Value.String() != "" {
|
||||||
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
|
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.flags.Lookup("component").Value.String() != "" {
|
if context.Flags().Lookup("component").Value.String() != "" {
|
||||||
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
|
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
||||||
@@ -50,7 +50,7 @@ func makeCmdRepoEdit() *commander.Command {
|
|||||||
UsageLine: "edit <name>",
|
UsageLine: "edit <name>",
|
||||||
Short: "edit properties of local repository",
|
Short: "edit properties of local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command edit allows to change metadata of local repository:
|
Command edit allows one to change metadata of local repository:
|
||||||
comment, default distribution and component.
|
comment, default distribution and component.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|||||||
+2
-2
@@ -8,10 +8,10 @@ import (
|
|||||||
func makeCmdRepoImport() *commander.Command {
|
func makeCmdRepoImport() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyRepoMoveCopyImport,
|
Run: aptlyRepoMoveCopyImport,
|
||||||
UsageLine: "import <src-mirror> <dst-repo> <package-spec> ...",
|
UsageLine: "import <src-mirror> <dst-repo> <package-query> ...",
|
||||||
Short: "import packages from mirror to local repository",
|
Short: "import packages from mirror to local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Command import looks up packages matching <package-spec> in mirror <src-mirror>
|
Command import looks up packages matching <package-query> in mirror <src-mirror>
|
||||||
and copies them to local repo <dst-repo>.
|
and copies them to local repo <dst-repo>.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
context.CloseDatabase()
|
||||||
|
|
||||||
sort.Strings(repos)
|
sort.Strings(repos)
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
|
|||||||
+14
-5
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -86,7 +87,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
var architecturesList []string
|
var architecturesList []string
|
||||||
|
|
||||||
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
|
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
|
||||||
|
|
||||||
if withDeps {
|
if withDeps {
|
||||||
dstList.PrepareIndex()
|
dstList.PrepareIndex()
|
||||||
@@ -105,7 +106,15 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toProcess, err := srcList.Filter(args[2:], withDeps, dstList, context.DependencyOptions(), architecturesList)
|
queries := make([]deb.PackageQuery, len(args)-2)
|
||||||
|
for i := 0; i < len(args)-2; i++ {
|
||||||
|
queries[i], err = query.Parse(args[i+2])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to %s: %s", command, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toProcess, err := srcList.Filter(queries, withDeps, dstList, context.DependencyOptions(), architecturesList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to %s: %s", command, err)
|
return fmt.Errorf("unable to %s: %s", command, err)
|
||||||
}
|
}
|
||||||
@@ -136,7 +145,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to %s: %s", command, err)
|
return fmt.Errorf("unable to %s: %s", command, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.flags.Lookup("dry-run").Value.Get().(bool) {
|
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
|
||||||
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
|
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
|
||||||
} else {
|
} else {
|
||||||
dstRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(dstList))
|
dstRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(dstList))
|
||||||
@@ -162,10 +171,10 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdRepoMove() *commander.Command {
|
func makeCmdRepoMove() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyRepoMoveCopyImport,
|
Run: aptlyRepoMoveCopyImport,
|
||||||
UsageLine: "move <src-name> <dst-name> <package-spec> ...",
|
UsageLine: "move <src-name> <dst-name> <package-query> ...",
|
||||||
Short: "move packages between local repositories",
|
Short: "move packages between local repositories",
|
||||||
Long: `
|
Long: `
|
||||||
Command move moves packages matching <package-spec> from local repo
|
Command move moves packages matching <package-query> from local repo
|
||||||
<src-name> to local repo <dst-name>.
|
<src-name> to local repo <dst-name>.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|||||||
+13
-4
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
@@ -33,8 +34,16 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to load packages: %s", err)
|
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()
|
list.PrepareIndex()
|
||||||
toRemove, err := list.Filter(args[1:], false, nil, 0, nil)
|
toRemove, err := list.Filter(queries, false, nil, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to remove: %s", err)
|
return fmt.Errorf("unable to remove: %s", err)
|
||||||
}
|
}
|
||||||
@@ -45,7 +54,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if context.flags.Lookup("dry-run").Value.Get().(bool) {
|
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
|
||||||
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
|
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
|
||||||
} else {
|
} else {
|
||||||
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
||||||
@@ -62,10 +71,10 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdRepoRemove() *commander.Command {
|
func makeCmdRepoRemove() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyRepoRemove,
|
Run: aptlyRepoRemove,
|
||||||
UsageLine: "remove <name> <package-spec> ...",
|
UsageLine: "remove <name> <package-query> ...",
|
||||||
Short: "remove packages from local repository",
|
Short: "remove packages from local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Commands removes packages matching <package-spec> from local repository
|
Commands removes packages matching <package-query> from local repository
|
||||||
<name>. If removed packages are not referenced by other repos or
|
<name>. If removed packages are not referenced by other repos or
|
||||||
snapshots, they can be removed completely (including files) by running
|
snapshots, they can be removed completely (including files) by running
|
||||||
'aptly db cleanup'.
|
'aptly db cleanup'.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+1
-1
@@ -31,7 +31,7 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
|
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
|
||||||
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
|
fmt.Printf("Number of packages: %d\n", repo.NumPackages())
|
||||||
|
|
||||||
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
|
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
|
||||||
if withPackages {
|
if withPackages {
|
||||||
ListPackagesRefList(repo.RefList())
|
ListPackagesRefList(repo.RefList())
|
||||||
}
|
}
|
||||||
|
|||||||
+45
@@ -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
|
||||||
|
}
|
||||||
+3
-2
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
@@ -26,7 +27,7 @@ func aptlyServe(cmd *commander.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
listen := context.flags.Lookup("listen").Value.String()
|
listen := context.Flags().Lookup("listen").Value.String()
|
||||||
|
|
||||||
listenHost, listenPort, err := net.SplitHostPort(listen)
|
listenHost, listenPort, err := net.SplitHostPort(listen)
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ func aptlyServe(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publicPath := context.PublishedStorage().PublicPath()
|
publicPath := context.GetPublishedStorage("").(aptly.LocalPublishedStorage).PublicPath()
|
||||||
ShutdownContext()
|
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)
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ func makeCmdSnapshot() *commander.Command {
|
|||||||
makeCmdSnapshotDiff(),
|
makeCmdSnapshotDiff(),
|
||||||
makeCmdSnapshotMerge(),
|
makeCmdSnapshotMerge(),
|
||||||
makeCmdSnapshotDrop(),
|
makeCmdSnapshotDrop(),
|
||||||
|
makeCmdSnapshotRename(),
|
||||||
|
makeCmdSnapshotSearch(),
|
||||||
|
makeCmdSnapshotFilter(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to create snapshot: %s", err)
|
return fmt.Errorf("unable to create snapshot: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = repo.CheckLock()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create snapshot: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create snapshot: %s", err)
|
return fmt.Errorf("unable to create snapshot: %s", err)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool)
|
onlyMatching := context.Flags().Lookup("only-matching").Value.Get().(bool)
|
||||||
|
|
||||||
// Load <name-a> snapshot
|
// Load <name-a> snapshot
|
||||||
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
|
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to drop: snapshot is published")
|
return fmt.Errorf("unable to drop: snapshot is published")
|
||||||
}
|
}
|
||||||
|
|
||||||
force := context.flags.Lookup("force").Value.Get().(bool)
|
force := context.Flags().Lookup("force").Value.Get().(bool)
|
||||||
if !force {
|
if !force {
|
||||||
snapshots := context.CollectionFactory().SnapshotCollection().BySnapshotSource(snapshot)
|
snapshots := context.CollectionFactory().SnapshotCollection().BySnapshotSource(snapshot)
|
||||||
if len(snapshots) > 0 {
|
if len(snapshots) > 0 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
+16
-22
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotList(cmd *commander.Command, args []string) error {
|
func aptlySnapshotList(cmd *commander.Command, args []string) error {
|
||||||
@@ -15,32 +14,26 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
|
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
|
||||||
|
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
|
||||||
|
|
||||||
snapshots := make([]string, context.CollectionFactory().SnapshotCollection().Len())
|
collection := context.CollectionFactory().SnapshotCollection()
|
||||||
|
|
||||||
i := 0
|
|
||||||
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
|
||||||
if raw {
|
|
||||||
snapshots[i] = snapshot.Name
|
|
||||||
} else {
|
|
||||||
snapshots[i] = snapshot.String()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
sort.Strings(snapshots)
|
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
for _, snapshot := range snapshots {
|
collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
|
||||||
fmt.Printf("%s\n", snapshot)
|
fmt.Printf("%s\n", snapshot.Name)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
if len(snapshots) > 0 {
|
if collection.Len() > 0 {
|
||||||
fmt.Printf("List of snapshots:\n")
|
fmt.Printf("List of snapshots:\n")
|
||||||
|
|
||||||
for _, snapshot := range snapshots {
|
err = collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
|
||||||
fmt.Printf(" * %s\n", snapshot)
|
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")
|
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
|
||||||
@@ -48,8 +41,8 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
|
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCmdSnapshotList() *commander.Command {
|
func makeCmdSnapshotList() *commander.Command {
|
||||||
@@ -67,6 +60,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,22 +28,22 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
latest := context.flags.Lookup("latest").Value.Get().(bool)
|
latest := context.Flags().Lookup("latest").Value.Get().(bool)
|
||||||
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
|
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
|
||||||
|
|
||||||
if noRemove && latest {
|
if noRemove && latest {
|
||||||
return fmt.Errorf("-no-remove and -latest can't be specified together")
|
return fmt.Errorf("-no-remove and -latest can't be specified together")
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideMatching := !latest && !noRemove
|
overrideMatching := !latest && !noRemove
|
||||||
|
|
||||||
result := sources[0].RefList()
|
result := sources[0].RefList()
|
||||||
for i := 1; i < len(sources); i++ {
|
for i := 1; i < len(sources); i++ {
|
||||||
result = result.Merge(sources[i].RefList(), overrideMatching)
|
result = result.Merge(sources[i].RefList(), overrideMatching, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if latest {
|
if latest {
|
||||||
deb.FilterLatestRefs(result)
|
result.FilterLatestRefs()
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceDescription := make([]string, len(sources))
|
sourceDescription := make([]string, len(sources))
|
||||||
@@ -84,7 +84,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("latest", false, "use only the latest version of each package")
|
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")
|
cmd.Flag.Bool("no-remove", false, "don't remove duplicate arch/name packages")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-69
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -16,8 +17,9 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
noDeps := context.flags.Lookup("no-deps").Value.Get().(bool)
|
noDeps := context.Flags().Lookup("no-deps").Value.Get().(bool)
|
||||||
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
|
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
|
||||||
|
allMatches := context.Flags().Lookup("all-matches").Value.Get().(bool)
|
||||||
|
|
||||||
// Load <name> snapshot
|
// Load <name> snapshot
|
||||||
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
|
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
|
||||||
@@ -75,79 +77,59 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
|
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial dependencies out of arguments
|
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
|
||||||
initialDependencies := make([]deb.Dependency, len(args)-3)
|
var archQuery deb.PackageQuery = &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: ""}
|
||||||
for i, arg := range args[3:] {
|
for _, arch := range architecturesList {
|
||||||
initialDependencies[i], err = deb.ParseDependency(arg)
|
archQuery = &deb.OrQuery{L: &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: arch}, R: archQuery}
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse argument: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform pull
|
// Initial queries out of arguments
|
||||||
for _, arch := range architecturesList {
|
queries := make([]deb.PackageQuery, len(args)-3)
|
||||||
dependencies := make([]deb.Dependency, len(initialDependencies), 2*len(initialDependencies))
|
for i, arg := range args[3:] {
|
||||||
for i := range dependencies {
|
queries[i], err = query.Parse(arg)
|
||||||
dependencies[i] = initialDependencies[i]
|
if err != nil {
|
||||||
dependencies[i].Architecture = arch
|
return fmt.Errorf("unable to parse query: %s", err)
|
||||||
|
}
|
||||||
|
// Add architecture filter
|
||||||
|
queries[i] = &deb.AndQuery{L: queries[i], R: archQuery}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter with dependencies as requested
|
||||||
|
result, err := sourcePackageList.Filter(queries, !noDeps, packageList, context.DependencyOptions(), architecturesList)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to pull: %s", err)
|
||||||
|
}
|
||||||
|
result.PrepareIndex()
|
||||||
|
|
||||||
|
alreadySeen := map[string]bool{}
|
||||||
|
|
||||||
|
result.ForEachIndexed(func(pkg *deb.Package) error {
|
||||||
|
key := pkg.Architecture + "_" + pkg.Name
|
||||||
|
_, seen := alreadySeen[key]
|
||||||
|
|
||||||
|
// If we haven't seen such name-architecture pair and were instructed to remove, remove it
|
||||||
|
if !noRemove && !seen {
|
||||||
|
// Remove all packages with the same name and architecture
|
||||||
|
pS := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}, true)
|
||||||
|
for _, p := range pS {
|
||||||
|
packageList.Remove(p)
|
||||||
|
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go over list of initial dependencies + list of dependencies found
|
// If !allMatches, add only first matching name-arch package
|
||||||
for i := 0; i < len(dependencies); i++ {
|
if !seen || allMatches {
|
||||||
dep := dependencies[i]
|
|
||||||
|
|
||||||
// Search for package that can satisfy dependencies
|
|
||||||
pkg := sourcePackageList.Search(dep)
|
|
||||||
if pkg == nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !noRemove {
|
|
||||||
// Remove all packages with the same name and architecture
|
|
||||||
for p := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}); p != nil; {
|
|
||||||
packageList.Remove(p)
|
|
||||||
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
|
|
||||||
p = packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new discovered package
|
|
||||||
packageList.Add(pkg)
|
packageList.Add(pkg)
|
||||||
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
|
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
|
||||||
|
|
||||||
if noDeps {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find missing dependencies for single added package
|
|
||||||
pL := deb.NewPackageList()
|
|
||||||
pL.Add(pkg)
|
|
||||||
|
|
||||||
var missing []deb.Dependency
|
|
||||||
missing, err = pL.VerifyDependencies(context.DependencyOptions(), []string{arch}, packageList, nil)
|
|
||||||
if err != nil {
|
|
||||||
context.Progress().ColoredPrintf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append missing dependencies to the list of dependencies to satisfy
|
|
||||||
for _, misDep := range missing {
|
|
||||||
found := false
|
|
||||||
for _, d := range dependencies {
|
|
||||||
if d == misDep {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
dependencies = append(dependencies, misDep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if context.flags.Lookup("dry-run").Value.Get().(bool) {
|
alreadySeen[key] = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
alreadySeen = nil
|
||||||
|
|
||||||
|
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
|
||||||
context.Progress().Printf("\nNot creating snapshot, as dry run was requested.\n")
|
context.Progress().Printf("\nNot creating snapshot, as dry run was requested.\n")
|
||||||
} else {
|
} else {
|
||||||
// Create <destination> snapshot
|
// Create <destination> snapshot
|
||||||
@@ -167,14 +149,14 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdSnapshotPull() *commander.Command {
|
func makeCmdSnapshotPull() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlySnapshotPull,
|
Run: aptlySnapshotPull,
|
||||||
UsageLine: "pull <name> <source> <destination> <package-name> ...",
|
UsageLine: "pull <name> <source> <destination> <package-query> ...",
|
||||||
Short: "pull packages from another snapshot",
|
Short: "pull packages from another snapshot",
|
||||||
Long: `
|
Long: `
|
||||||
Command pull pulls new packages along with its' dependencies to snapshot <name>
|
Command pull pulls new packages along with its' dependencies to snapshot <name>
|
||||||
from snapshot <source>. Pull can upgrade package version in <name> with
|
from snapshot <source>. Pull can upgrade package version in <name> with
|
||||||
versions from <source> following dependencies. New snapshot <destination>
|
versions from <source> following dependencies. New snapshot <destination>
|
||||||
is created as a result of this process. Packages could be specified simply
|
is created as a result of this process. Packages could be specified simply
|
||||||
as 'package-name' or as dependency 'package-name (>= version)'.
|
as 'package-name' or as package queries.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -186,6 +168,7 @@ Example:
|
|||||||
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
|
cmd.Flag.Bool("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-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("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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("Description: %s\n", snapshot.Description)
|
fmt.Printf("Description: %s\n", snapshot.Description)
|
||||||
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
|
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
|
||||||
|
|
||||||
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
|
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
|
||||||
if withPackages {
|
if withPackages {
|
||||||
ListPackagesRefList(snapshot.RefList())
|
ListPackagesRefList(snapshot.RefList())
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -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
@@ -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
|
||||||
|
}
|
||||||
+31
-9
@@ -14,6 +14,8 @@ const (
|
|||||||
codeHideProgress
|
codeHideProgress
|
||||||
codeStop
|
codeStop
|
||||||
codeFlush
|
codeFlush
|
||||||
|
codeBarEnabled
|
||||||
|
codeBarDisabled
|
||||||
)
|
)
|
||||||
|
|
||||||
type printTask struct {
|
type printTask struct {
|
||||||
@@ -81,6 +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.queue <- printTask{code: codeBarEnabled}
|
||||||
p.bar.Start()
|
p.bar.Start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,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}
|
||||||
}
|
}
|
||||||
@@ -128,19 +133,30 @@ func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
|
|||||||
p.queue <- printTask{code: codePrint, message: color.Sprintf(msg, a...) + "\n"}
|
p.queue <- printTask{code: codePrint, message: color.Sprintf(msg, a...) + "\n"}
|
||||||
} else {
|
} else {
|
||||||
// stip color marks
|
// stip color marks
|
||||||
var prev rune
|
var inColorMark, inCurly bool
|
||||||
msg = strings.Map(func(r rune) rune {
|
msg = strings.Map(func(r rune) rune {
|
||||||
if prev == '@' {
|
if inColorMark {
|
||||||
prev = 0
|
if inCurly {
|
||||||
if r == '@' {
|
if r == '}' {
|
||||||
return r
|
inCurly = false
|
||||||
|
inColorMark = false
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if r == '{' {
|
||||||
|
inCurly = true
|
||||||
|
} else if r == '@' {
|
||||||
|
return '@'
|
||||||
|
} else {
|
||||||
|
inColorMark = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
prev = r
|
|
||||||
if r == '@' {
|
|
||||||
return -1
|
|
||||||
|
|
||||||
|
if r == '@' {
|
||||||
|
inColorMark = true
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
@@ -151,9 +167,15 @@ func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Progress) worker() {
|
func (p *Progress) worker() {
|
||||||
|
hasBar := false
|
||||||
|
|
||||||
for {
|
for {
|
||||||
task := <-p.queue
|
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")
|
||||||
@@ -161,7 +183,7 @@ func (p *Progress) worker() {
|
|||||||
}
|
}
|
||||||
fmt.Print(task.message)
|
fmt.Print(task.message)
|
||||||
case codeProgress:
|
case codeProgress:
|
||||||
if p.bar != nil {
|
if hasBar {
|
||||||
fmt.Print("\r" + task.message)
|
fmt.Print("\r" + task.message)
|
||||||
p.barShown = true
|
p.barShown = true
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,9 +1,7 @@
|
|||||||
// +build !freebsd
|
|
||||||
|
|
||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// +build freebsd
|
|
||||||
|
|
||||||
package console
|
|
||||||
|
|
||||||
// RunningOnTerminal checks whether stdout is terminal
|
|
||||||
//
|
|
||||||
// Stub for FreeBSD, until in go1.3 terminal.IsTerminal would start working for FreeBSD
|
|
||||||
func RunningOnTerminal() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,490 @@
|
|||||||
|
// Package context provides single entry to all resources
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/console"
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/files"
|
||||||
|
"github.com/smira/aptly/http"
|
||||||
|
"github.com/smira/aptly/s3"
|
||||||
|
"github.com/smira/aptly/swift"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AptlyContext is a common context shared by all commands
|
||||||
|
type AptlyContext struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
flags, globalFlags *flag.FlagSet
|
||||||
|
configLoaded bool
|
||||||
|
|
||||||
|
progress aptly.Progress
|
||||||
|
downloader aptly.Downloader
|
||||||
|
database database.Storage
|
||||||
|
packagePool aptly.PackagePool
|
||||||
|
publishedStorages map[string]aptly.PublishedStorage
|
||||||
|
collectionFactory *deb.CollectionFactory
|
||||||
|
dependencyOptions int
|
||||||
|
architecturesList []string
|
||||||
|
// Debug features
|
||||||
|
fileCPUProfile *os.File
|
||||||
|
fileMemProfile *os.File
|
||||||
|
fileMemStats *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var _ aptly.PublishedStorageProvider = &AptlyContext{}
|
||||||
|
|
||||||
|
// FatalError is type for panicking to abort execution with non-zero
|
||||||
|
// exit code and print meaningful explanation
|
||||||
|
type FatalError struct {
|
||||||
|
ReturnCode int
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal panics and aborts execution with exit code 1
|
||||||
|
func Fatal(err error) {
|
||||||
|
returnCode := 1
|
||||||
|
if err == commander.ErrFlagError || err == commander.ErrCommandError {
|
||||||
|
returnCode = 2
|
||||||
|
}
|
||||||
|
panic(&FatalError{ReturnCode: returnCode, Message: err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config loads and returns current configuration
|
||||||
|
func (context *AptlyContext) Config() *utils.ConfigStructure {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context.config()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *AptlyContext) config() *utils.ConfigStructure {
|
||||||
|
if !context.configLoaded {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
configLocation := context.globalFlags.Lookup("config").Value.String()
|
||||||
|
if configLocation != "" {
|
||||||
|
err = utils.LoadConfig(configLocation, &utils.Config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configLocations := []string{
|
||||||
|
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
|
||||||
|
"/etc/aptly.conf",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, configLocation := range configLocations {
|
||||||
|
err = utils.LoadConfig(configLocation, &utils.Config)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
|
||||||
|
utils.SaveConfig(configLocations[0], &utils.Config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.configLoaded = true
|
||||||
|
|
||||||
|
}
|
||||||
|
return &utils.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupOption checks boolean flag with default (usually config) and command-line
|
||||||
|
// setting
|
||||||
|
func (context *AptlyContext) LookupOption(defaultValue bool, name string) (result bool) {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context.lookupOption(defaultValue, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *AptlyContext) lookupOption(defaultValue bool, name string) (result bool) {
|
||||||
|
result = defaultValue
|
||||||
|
|
||||||
|
if context.globalFlags.IsSet(name) {
|
||||||
|
result = context.globalFlags.Lookup(name).Value.Get().(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DependencyOptions calculates options related to dependecy handling
|
||||||
|
func (context *AptlyContext) DependencyOptions() int {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.dependencyOptions == -1 {
|
||||||
|
context.dependencyOptions = 0
|
||||||
|
if context.lookupOption(context.config().DepFollowSuggests, "dep-follow-suggests") {
|
||||||
|
context.dependencyOptions |= deb.DepFollowSuggests
|
||||||
|
}
|
||||||
|
if context.lookupOption(context.config().DepFollowRecommends, "dep-follow-recommends") {
|
||||||
|
context.dependencyOptions |= deb.DepFollowRecommends
|
||||||
|
}
|
||||||
|
if context.lookupOption(context.config().DepFollowAllVariants, "dep-follow-all-variants") {
|
||||||
|
context.dependencyOptions |= deb.DepFollowAllVariants
|
||||||
|
}
|
||||||
|
if context.lookupOption(context.config().DepFollowSource, "dep-follow-source") {
|
||||||
|
context.dependencyOptions |= deb.DepFollowSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.dependencyOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchitecturesList returns list of architectures fixed via command line or config
|
||||||
|
func (context *AptlyContext) ArchitecturesList() []string {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.architecturesList == nil {
|
||||||
|
context.architecturesList = context.config().Architectures
|
||||||
|
optionArchitectures := context.globalFlags.Lookup("architectures").Value.String()
|
||||||
|
if optionArchitectures != "" {
|
||||||
|
context.architecturesList = strings.Split(optionArchitectures, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.architecturesList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress creates or returns Progress object
|
||||||
|
func (context *AptlyContext) Progress() aptly.Progress {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context._progress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *AptlyContext) _progress() aptly.Progress {
|
||||||
|
if context.progress == nil {
|
||||||
|
context.progress = console.NewProgress()
|
||||||
|
context.progress.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.progress
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downloader returns instance of current downloader
|
||||||
|
func (context *AptlyContext) Downloader() aptly.Downloader {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.downloader == nil {
|
||||||
|
var downloadLimit int64
|
||||||
|
limitFlag := context.flags.Lookup("download-limit")
|
||||||
|
if limitFlag != nil {
|
||||||
|
downloadLimit = limitFlag.Value.Get().(int64)
|
||||||
|
}
|
||||||
|
if downloadLimit == 0 {
|
||||||
|
downloadLimit = context.config().DownloadLimit
|
||||||
|
}
|
||||||
|
context.downloader = http.NewDownloader(context.config().DownloadConcurrency,
|
||||||
|
downloadLimit*1024, context._progress())
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.downloader
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBPath builds path to database
|
||||||
|
func (context *AptlyContext) DBPath() string {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context.dbPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBPath builds path to database
|
||||||
|
func (context *AptlyContext) dbPath() string {
|
||||||
|
return filepath.Join(context.config().RootDir, "db")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database opens and returns current instance of database
|
||||||
|
func (context *AptlyContext) Database() (database.Storage, error) {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context._database()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *AptlyContext) _database() (database.Storage, error) {
|
||||||
|
if context.database == nil {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
context.database, err = database.OpenDB(context.dbPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't open database: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.database, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseDatabase closes the db temporarily
|
||||||
|
func (context *AptlyContext) CloseDatabase() error {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.database == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.database.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReOpenDatabase reopens the db after close
|
||||||
|
func (context *AptlyContext) ReOpenDatabase() error {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.database == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxTries = 10
|
||||||
|
const Delay = 10 * time.Second
|
||||||
|
|
||||||
|
for try := 0; try < MaxTries; try++ {
|
||||||
|
err := context.database.ReOpen()
|
||||||
|
if err == nil || strings.Index(err.Error(), "resource temporarily unavailable") == -1 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context._progress().Printf("Unable to reopen database, sleeping %s\n", Delay)
|
||||||
|
<-time.After(Delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unable to reopen the DB, maximum number of retries reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionFactory builds factory producing all kinds of collections
|
||||||
|
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.collectionFactory == nil {
|
||||||
|
db, err := context._database()
|
||||||
|
if err != nil {
|
||||||
|
Fatal(err)
|
||||||
|
}
|
||||||
|
context.collectionFactory = deb.NewCollectionFactory(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.collectionFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackagePool returns instance of PackagePool
|
||||||
|
func (context *AptlyContext) PackagePool() aptly.PackagePool {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.packagePool == nil {
|
||||||
|
context.packagePool = files.NewPackagePool(context.config().RootDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.packagePool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublishedStorage returns instance of PublishedStorage
|
||||||
|
func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedStorage {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
publishedStorage, ok := context.publishedStorages[name]
|
||||||
|
if !ok {
|
||||||
|
if name == "" {
|
||||||
|
publishedStorage = files.NewPublishedStorage(context.config().RootDir)
|
||||||
|
} else if strings.HasPrefix(name, "s3:") {
|
||||||
|
params, ok := context.config().S3PublishRoots[name[3:]]
|
||||||
|
if !ok {
|
||||||
|
Fatal(fmt.Errorf("published S3 storage %v not configured", name[3:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
publishedStorage, err = s3.NewPublishedStorage(params.AccessKeyID, params.SecretAccessKey,
|
||||||
|
params.Region, params.Bucket, params.ACL, params.Prefix, params.StorageClass,
|
||||||
|
params.EncryptionMethod, params.PlusWorkaround)
|
||||||
|
if err != nil {
|
||||||
|
Fatal(err)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(name, "swift:") {
|
||||||
|
params, ok := context.config().SwiftPublishRoots[name[6:]]
|
||||||
|
if !ok {
|
||||||
|
Fatal(fmt.Errorf("published Swift storage %v not configured", name[6:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
|
||||||
|
params.AuthURL, params.Tenant, params.TenantID, params.Container, params.Prefix)
|
||||||
|
if err != nil {
|
||||||
|
Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Fatal(fmt.Errorf("unknown published storage format: %v", name))
|
||||||
|
}
|
||||||
|
context.publishedStorages[name] = publishedStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
return publishedStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadPath builds path to upload storage
|
||||||
|
func (context *AptlyContext) UploadPath() string {
|
||||||
|
return filepath.Join(context.Config().RootDir, "upload")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFlags sets internal copy of flags in the context
|
||||||
|
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
context.flags = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags returns current command flags
|
||||||
|
func (context *AptlyContext) Flags() *flag.FlagSet {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalFlags returns flags passed to all commands
|
||||||
|
func (context *AptlyContext) GlobalFlags() *flag.FlagSet {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
return context.globalFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown shuts context down
|
||||||
|
func (context *AptlyContext) Shutdown() {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if aptly.EnableDebug {
|
||||||
|
if context.fileMemProfile != nil {
|
||||||
|
pprof.WriteHeapProfile(context.fileMemProfile)
|
||||||
|
context.fileMemProfile.Close()
|
||||||
|
context.fileMemProfile = nil
|
||||||
|
}
|
||||||
|
if context.fileCPUProfile != nil {
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
context.fileCPUProfile.Close()
|
||||||
|
context.fileCPUProfile = nil
|
||||||
|
}
|
||||||
|
if context.fileMemProfile != nil {
|
||||||
|
context.fileMemProfile.Close()
|
||||||
|
context.fileMemProfile = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if context.database != nil {
|
||||||
|
context.database.Close()
|
||||||
|
context.database = nil
|
||||||
|
}
|
||||||
|
if context.downloader != nil {
|
||||||
|
context.downloader.Abort()
|
||||||
|
context.downloader = nil
|
||||||
|
}
|
||||||
|
if context.progress != nil {
|
||||||
|
context.progress.Shutdown()
|
||||||
|
context.progress = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup does partial shutdown of context
|
||||||
|
func (context *AptlyContext) Cleanup() {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.downloader != nil {
|
||||||
|
context.downloader.Shutdown()
|
||||||
|
context.downloader = nil
|
||||||
|
}
|
||||||
|
if context.progress != nil {
|
||||||
|
context.progress.Shutdown()
|
||||||
|
context.progress = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext initializes context with default settings
|
||||||
|
func NewContext(flags *flag.FlagSet) (*AptlyContext, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
context := &AptlyContext{
|
||||||
|
flags: flags,
|
||||||
|
globalFlags: flags,
|
||||||
|
dependencyOptions: -1,
|
||||||
|
publishedStorages: map[string]aptly.PublishedStorage{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if aptly.EnableDebug {
|
||||||
|
cpuprofile := flags.Lookup("cpuprofile").Value.String()
|
||||||
|
if cpuprofile != "" {
|
||||||
|
context.fileCPUProfile, err = os.Create(cpuprofile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pprof.StartCPUProfile(context.fileCPUProfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
memprofile := flags.Lookup("memprofile").Value.String()
|
||||||
|
if memprofile != "" {
|
||||||
|
context.fileMemProfile, err = os.Create(memprofile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memstats := flags.Lookup("memstats").Value.String()
|
||||||
|
if memstats != "" {
|
||||||
|
interval := flags.Lookup("meminterval").Value.Get().(time.Duration)
|
||||||
|
|
||||||
|
context.fileMemStats, err = os.Create(memstats)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
context.fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var stats runtime.MemStats
|
||||||
|
|
||||||
|
start := time.Now().UnixNano()
|
||||||
|
|
||||||
|
for {
|
||||||
|
runtime.ReadMemStats(&stats)
|
||||||
|
if context.fileMemStats != nil {
|
||||||
|
context.fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
|
||||||
|
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
|
||||||
|
time.Sleep(interval)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
+28
-6
@@ -24,12 +24,14 @@ type Storage interface {
|
|||||||
KeysByPrefix(prefix []byte) [][]byte
|
KeysByPrefix(prefix []byte) [][]byte
|
||||||
FetchByPrefix(prefix []byte) [][]byte
|
FetchByPrefix(prefix []byte) [][]byte
|
||||||
Close() error
|
Close() error
|
||||||
|
ReOpen() error
|
||||||
StartBatch()
|
StartBatch()
|
||||||
FinishBatch() error
|
FinishBatch() error
|
||||||
CompactDB() error
|
CompactDB() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type levelDB struct {
|
type levelDB struct {
|
||||||
|
path string
|
||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
batch *leveldb.Batch
|
batch *leveldb.Batch
|
||||||
}
|
}
|
||||||
@@ -39,17 +41,21 @@ 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
|
// RecoverDB recovers LevelDB database from corruption
|
||||||
@@ -95,7 +101,7 @@ func (l *levelDB) Put(key []byte, value []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if bytes.Compare(old, value) == 0 {
|
if bytes.Equal(old, value) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +153,23 @@ func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
|
|||||||
|
|
||||||
// Close finishes DB work
|
// Close finishes DB work
|
||||||
func (l *levelDB) Close() error {
|
func (l *levelDB) Close() error {
|
||||||
return l.db.Close()
|
if l.db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := l.db.Close()
|
||||||
|
l.db = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen tries to re-open the database
|
||||||
|
func (l *levelDB) ReOpen() error {
|
||||||
|
if l.db != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
l.db, err = internalOpen(l.path)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartBatch starts batch processing of keys
|
// StartBatch starts batch processing of keys
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -155,3 +156,23 @@ func (s *LevelDBSuite) TestCompactDB(c *C) {
|
|||||||
|
|
||||||
c.Check(s.db.CompactDB(), IsNil)
|
c.Check(s.db.CompactDB(), IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LevelDBSuite) TestReOpen(c *C) {
|
||||||
|
var (
|
||||||
|
key = []byte("key")
|
||||||
|
value = []byte("value")
|
||||||
|
)
|
||||||
|
|
||||||
|
err := s.db.Put(key, value)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
err = s.db.Close()
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
err = s.db.ReOpen()
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
result, err := s.db.Get(key)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(result, DeepEquals, value)
|
||||||
|
}
|
||||||
|
|||||||
+30
-1
@@ -2,10 +2,12 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CollectionFactory is a single place to generate all desired collections
|
// CollectionFactory is a single place to generate all desired collections
|
||||||
type CollectionFactory struct {
|
type CollectionFactory struct {
|
||||||
|
*sync.Mutex
|
||||||
db database.Storage
|
db database.Storage
|
||||||
packages *PackageCollection
|
packages *PackageCollection
|
||||||
remoteRepos *RemoteRepoCollection
|
remoteRepos *RemoteRepoCollection
|
||||||
@@ -16,11 +18,14 @@ type CollectionFactory struct {
|
|||||||
|
|
||||||
// NewCollectionFactory creates new factory
|
// NewCollectionFactory creates new factory
|
||||||
func NewCollectionFactory(db database.Storage) *CollectionFactory {
|
func NewCollectionFactory(db database.Storage) *CollectionFactory {
|
||||||
return &CollectionFactory{db: db}
|
return &CollectionFactory{Mutex: &sync.Mutex{}, db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageCollection returns (or creates) new PackageCollection
|
// PackageCollection returns (or creates) new PackageCollection
|
||||||
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
|
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
if factory.packages == nil {
|
if factory.packages == nil {
|
||||||
factory.packages = NewPackageCollection(factory.db)
|
factory.packages = NewPackageCollection(factory.db)
|
||||||
}
|
}
|
||||||
@@ -30,6 +35,9 @@ func (factory *CollectionFactory) PackageCollection() *PackageCollection {
|
|||||||
|
|
||||||
// RemoteRepoCollection returns (or creates) new RemoteRepoCollection
|
// RemoteRepoCollection returns (or creates) new RemoteRepoCollection
|
||||||
func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
|
func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
if factory.remoteRepos == nil {
|
if factory.remoteRepos == nil {
|
||||||
factory.remoteRepos = NewRemoteRepoCollection(factory.db)
|
factory.remoteRepos = NewRemoteRepoCollection(factory.db)
|
||||||
}
|
}
|
||||||
@@ -39,6 +47,9 @@ func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
|
|||||||
|
|
||||||
// SnapshotCollection returns (or creates) new SnapshotCollection
|
// SnapshotCollection returns (or creates) new SnapshotCollection
|
||||||
func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
|
func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
if factory.snapshots == nil {
|
if factory.snapshots == nil {
|
||||||
factory.snapshots = NewSnapshotCollection(factory.db)
|
factory.snapshots = NewSnapshotCollection(factory.db)
|
||||||
}
|
}
|
||||||
@@ -48,6 +59,9 @@ func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
|
|||||||
|
|
||||||
// LocalRepoCollection returns (or creates) new LocalRepoCollection
|
// LocalRepoCollection returns (or creates) new LocalRepoCollection
|
||||||
func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
|
func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
if factory.localRepos == nil {
|
if factory.localRepos == nil {
|
||||||
factory.localRepos = NewLocalRepoCollection(factory.db)
|
factory.localRepos = NewLocalRepoCollection(factory.db)
|
||||||
}
|
}
|
||||||
@@ -57,9 +71,24 @@ func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
|
|||||||
|
|
||||||
// PublishedRepoCollection returns (or creates) new PublishedRepoCollection
|
// PublishedRepoCollection returns (or creates) new PublishedRepoCollection
|
||||||
func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollection {
|
func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
if factory.publishedRepos == nil {
|
if factory.publishedRepos == nil {
|
||||||
factory.publishedRepos = NewPublishedRepoCollection(factory.db)
|
factory.publishedRepos = NewPublishedRepoCollection(factory.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
return factory.publishedRepos
|
return factory.publishedRepos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush removes all references to collections, so that memory could be reclaimed
|
||||||
|
func (factory *CollectionFactory) Flush() {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
|
factory.localRepos = nil
|
||||||
|
factory.snapshots = nil
|
||||||
|
factory.remoteRepos = nil
|
||||||
|
factory.publishedRepos = nil
|
||||||
|
factory.packages = nil
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
|
|||||||
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
|
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tarHeader.Name == "./control" {
|
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
|
||||||
reader := NewControlFileReader(untar)
|
reader := NewControlFileReader(untar)
|
||||||
stanza, err := reader.ReadStanza()
|
stanza, err := reader.ReadStanza()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+2
-1
@@ -2,9 +2,10 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DebSuite struct {
|
type DebSuite struct {
|
||||||
|
|||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Launch gocheck tests
|
// Launch gocheck tests
|
||||||
|
|||||||
+111
-5
@@ -5,14 +5,83 @@ import (
|
|||||||
"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{"Package", "Origin", "Label", "Suite", "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]
|
||||||
|
|||||||
+15
-2
@@ -3,8 +3,9 @@ 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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+134
-83
@@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dependency options
|
// Dependency options
|
||||||
@@ -39,9 +38,15 @@ type PackageList struct {
|
|||||||
providesIndex map[string][]*Package
|
providesIndex map[string][]*Package
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PackageConflictError means that package can't be added to the list due to error
|
||||||
|
type PackageConflictError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
// Verify interface
|
// Verify interface
|
||||||
var (
|
var (
|
||||||
_ sort.Interface = &PackageList{}
|
_ sort.Interface = &PackageList{}
|
||||||
|
_ PackageCatalog = &PackageList{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPackageList creates empty package list
|
// NewPackageList creates empty package list
|
||||||
@@ -90,7 +95,7 @@ func (l *PackageList) Add(p *Package) error {
|
|||||||
existing, ok := l.packages[key]
|
existing, ok := l.packages[key]
|
||||||
if ok {
|
if ok {
|
||||||
if !existing.Equals(p) {
|
if !existing.Equals(p) {
|
||||||
return fmt.Errorf("conflict in package %s", p)
|
return &PackageConflictError{fmt.Errorf("conflict in package %s", p)}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -101,7 +106,7 @@ func (l *PackageList) Add(p *Package) error {
|
|||||||
l.providesIndex[provides] = append(l.providesIndex[provides], p)
|
l.providesIndex[provides] = append(l.providesIndex[provides], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= p.Name })
|
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.lessPackages(p, l.packagesIndex[j]) })
|
||||||
|
|
||||||
// insert p into l.packagesIndex in position i
|
// insert p into l.packagesIndex in position i
|
||||||
l.packagesIndex = append(l.packagesIndex, nil)
|
l.packagesIndex = append(l.packagesIndex, nil)
|
||||||
@@ -123,6 +128,22 @@ func (l *PackageList) ForEach(handler func(*Package) error) error {
|
|||||||
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
|
// Len returns number of packages in the list
|
||||||
func (l *PackageList) Len() int {
|
func (l *PackageList) Len() int {
|
||||||
return len(l.packages)
|
return len(l.packages)
|
||||||
@@ -189,6 +210,19 @@ func (l *PackageList) Architectures(includeSource bool) (result []string) {
|
|||||||
return
|
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
|
// depSliceDeduplicate removes dups in slice of Dependencies
|
||||||
func depSliceDeduplicate(s []Dependency) []Dependency {
|
func depSliceDeduplicate(s []Dependency) []Dependency {
|
||||||
l := len(s)
|
l := len(s)
|
||||||
@@ -220,6 +254,7 @@ func depSliceDeduplicate(s []Dependency) []Dependency {
|
|||||||
//
|
//
|
||||||
// Analysis would be peformed for each architecture, in specified sources
|
// 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) {
|
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageList, progress aptly.Progress) ([]Dependency, error) {
|
||||||
|
l.PrepareIndex()
|
||||||
missing := make([]Dependency, 0, 128)
|
missing := make([]Dependency, 0, 128)
|
||||||
|
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
@@ -229,7 +264,7 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
|
|||||||
for _, arch := range architectures {
|
for _, arch := range architectures {
|
||||||
cache := make(map[string]bool, 2048)
|
cache := make(map[string]bool, 2048)
|
||||||
|
|
||||||
for _, p := range l.packages {
|
for _, p := range l.packagesIndex {
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
progress.AddBar(1)
|
progress.AddBar(1)
|
||||||
}
|
}
|
||||||
@@ -247,7 +282,6 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
|
|||||||
variants = depSliceDeduplicate(variants)
|
variants = depSliceDeduplicate(variants)
|
||||||
|
|
||||||
variantsMissing := make([]Dependency, 0, len(variants))
|
variantsMissing := make([]Dependency, 0, len(variants))
|
||||||
missingCount := 0
|
|
||||||
|
|
||||||
for _, dep := range variants {
|
for _, dep := range variants {
|
||||||
if dep.Architecture == "" {
|
if dep.Architecture == "" {
|
||||||
@@ -255,35 +289,23 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash := dep.Hash()
|
hash := dep.Hash()
|
||||||
r, ok := cache[hash]
|
satisfied, ok := cache[hash]
|
||||||
if ok {
|
if !ok {
|
||||||
if !r {
|
satisfied = sources.Search(dep, false) != nil
|
||||||
missingCount++
|
cache[hash] = satisfied
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sources.Search(dep) == nil {
|
if !satisfied && !ok {
|
||||||
variantsMissing = append(variantsMissing, dep)
|
variantsMissing = append(variantsMissing, dep)
|
||||||
missingCount++
|
}
|
||||||
} else {
|
|
||||||
cache[hash] = true
|
if satisfied && options&DepFollowAllVariants == 0 {
|
||||||
|
variantsMissing = nil
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options&DepFollowAllVariants == DepFollowAllVariants {
|
missing = append(missing, variantsMissing...)
|
||||||
missing = append(missing, variantsMissing...)
|
|
||||||
for _, dep := range variantsMissing {
|
|
||||||
cache[dep.Hash()] = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if missingCount == len(variants) {
|
|
||||||
missing = append(missing, variantsMissing...)
|
|
||||||
for _, dep := range variantsMissing {
|
|
||||||
cache[dep.Hash()] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,13 +322,29 @@ func (l *PackageList) Swap(i, j int) {
|
|||||||
l.packagesIndex[i], l.packagesIndex[j] = l.packagesIndex[j], l.packagesIndex[i]
|
l.packagesIndex[i], l.packagesIndex[j] = l.packagesIndex[j], l.packagesIndex[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare compares two names in lexographical order
|
func (l *PackageList) lessPackages(iPkg, jPkg *Package) bool {
|
||||||
|
if iPkg.Name == jPkg.Name {
|
||||||
|
cmp := CompareVersions(iPkg.Version, jPkg.Version)
|
||||||
|
if cmp == 0 {
|
||||||
|
return iPkg.Architecture < jPkg.Architecture
|
||||||
|
}
|
||||||
|
return cmp == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return iPkg.Name < jPkg.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less compares two packages by name (lexographical) and version (latest to oldest)
|
||||||
func (l *PackageList) Less(i, j int) bool {
|
func (l *PackageList) Less(i, j int) bool {
|
||||||
return l.packagesIndex[i].Name < l.packagesIndex[j].Name
|
return l.lessPackages(l.packagesIndex[i], l.packagesIndex[j])
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareIndex prepares list for indexing
|
// PrepareIndex prepares list for indexing
|
||||||
func (l *PackageList) PrepareIndex() {
|
func (l *PackageList) PrepareIndex() {
|
||||||
|
if l.indexed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
l.packagesIndex = make([]*Package, l.Len())
|
l.packagesIndex = make([]*Package, l.Len())
|
||||||
l.providesIndex = make(map[string][]*Package, 128)
|
l.providesIndex = make(map[string][]*Package, 128)
|
||||||
|
|
||||||
@@ -325,16 +363,49 @@ func (l *PackageList) PrepareIndex() {
|
|||||||
l.indexed = true
|
l.indexed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search searches package index for specified package
|
// Scan searches package index using full scan
|
||||||
func (l *PackageList) Search(dep Dependency) *Package {
|
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 {
|
if !l.indexed {
|
||||||
panic("list not indexed, can't search")
|
panic("list not indexed, can't search")
|
||||||
}
|
}
|
||||||
|
|
||||||
if dep.Relation == VersionDontCare {
|
if dep.Relation == VersionDontCare {
|
||||||
for _, p := range l.providesIndex[dep.Pkg] {
|
for _, p := range l.providesIndex[dep.Pkg] {
|
||||||
if p.MatchesArchitecture(dep.Architecture) {
|
if dep.Architecture == "" || p.MatchesArchitecture(dep.Architecture) {
|
||||||
return p
|
searchResults = append(searchResults, p)
|
||||||
|
|
||||||
|
if !allMatches {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,16 +415,21 @@ func (l *PackageList) Search(dep Dependency) *Package {
|
|||||||
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
|
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
|
||||||
p := l.packagesIndex[i]
|
p := l.packagesIndex[i]
|
||||||
if p.MatchesDependency(dep) {
|
if p.MatchesDependency(dep) {
|
||||||
return p
|
searchResults = append(searchResults, p)
|
||||||
|
|
||||||
|
if !allMatches {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
|
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
|
||||||
func (l *PackageList) Filter(queries []string, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
|
func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
|
||||||
if !l.indexed {
|
if !l.indexed {
|
||||||
panic("list not indexed, can't filter")
|
panic("list not indexed, can't filter")
|
||||||
}
|
}
|
||||||
@@ -361,53 +437,17 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
|
|||||||
result := NewPackageList()
|
result := NewPackageList()
|
||||||
|
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
isDepQuery := strings.IndexAny(query, " (){}=<>") != -1
|
result.Append(query.Query(l))
|
||||||
|
|
||||||
if !isDepQuery {
|
|
||||||
// try to interpret query as package string representation
|
|
||||||
|
|
||||||
// convert Package.String() to Package.Key()
|
|
||||||
i := strings.Index(query, "_")
|
|
||||||
if i != -1 {
|
|
||||||
pkg, query := query[:i], query[i+1:]
|
|
||||||
j := strings.LastIndex(query, "_")
|
|
||||||
if j != -1 {
|
|
||||||
version, arch := query[:j], query[j+1:]
|
|
||||||
p := l.packages["P"+arch+" "+pkg+" "+version]
|
|
||||||
if p != nil {
|
|
||||||
result.Add(p)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try as dependency
|
|
||||||
dep, err := ParseDependency(query)
|
|
||||||
if err != nil {
|
|
||||||
if isDepQuery {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// parsing failed, but probably that wasn't a dep query
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= dep.Pkg })
|
|
||||||
|
|
||||||
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
|
|
||||||
p := l.packagesIndex[i]
|
|
||||||
if p.MatchesDependency(dep) {
|
|
||||||
result.Add(p)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if withDependencies {
|
if withDependencies {
|
||||||
added := result.Len()
|
added := result.Len()
|
||||||
|
result.PrepareIndex()
|
||||||
|
|
||||||
dependencySource := NewPackageList()
|
dependencySource := NewPackageList()
|
||||||
dependencySource.Append(source)
|
if source != nil {
|
||||||
|
dependencySource.Append(source)
|
||||||
|
}
|
||||||
dependencySource.Append(result)
|
dependencySource.Append(result)
|
||||||
dependencySource.PrepareIndex()
|
dependencySource.PrepareIndex()
|
||||||
|
|
||||||
@@ -423,11 +463,22 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
|
|||||||
|
|
||||||
// try to satisfy dependencies
|
// try to satisfy dependencies
|
||||||
for _, dep := range missing {
|
for _, dep := range missing {
|
||||||
p := l.Search(dep)
|
// dependency might have already been satisfied
|
||||||
if p != nil {
|
// with packages already been added
|
||||||
result.Add(p)
|
if result.Search(dep, false) != nil {
|
||||||
dependencySource.Add(p)
|
continue
|
||||||
added++
|
}
|
||||||
|
|
||||||
|
searchResults := l.Search(dep, false)
|
||||||
|
if searchResults != nil {
|
||||||
|
for _, p := range searchResults {
|
||||||
|
result.Add(p)
|
||||||
|
dependencySource.Add(p)
|
||||||
|
added++
|
||||||
|
if dependencyOptions&DepFollowAllVariants == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+172
-49
@@ -2,11 +2,49 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
. "launchpad.net/gocheck"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"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 {
|
type PackageListSuite struct {
|
||||||
// Simple list with "real" packages from stanzas
|
// Simple list with "real" packages from stanzas
|
||||||
list *PackageList
|
list *PackageList
|
||||||
@@ -14,8 +52,10 @@ type PackageListSuite struct {
|
|||||||
|
|
||||||
// Mocked packages in list
|
// Mocked packages in list
|
||||||
packages []*Package
|
packages []*Package
|
||||||
|
packages2 []*Package
|
||||||
sourcePackages []*Package
|
sourcePackages []*Package
|
||||||
il *PackageList
|
il *PackageList
|
||||||
|
il2 *PackageList
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Suite(&PackageListSuite{})
|
var _ = Suite(&PackageListSuite{})
|
||||||
@@ -40,31 +80,45 @@ func (s *PackageListSuite) SetUpTest(c *C) {
|
|||||||
|
|
||||||
s.il = NewPackageList()
|
s.il = NewPackageList()
|
||||||
s.packages = []*Package{
|
s.packages = []*Package{
|
||||||
&Package{Name: "lib", Version: "1.0", Architecture: "i386", Source: "lib (0.9)", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}}},
|
{Name: "lib", Version: "1.0", Architecture: "i386", Source: "lib (0.9)", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}}},
|
||||||
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
{Name: "dpkg", Version: "1.7", Architecture: "i386", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all", Source: "app", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
{Name: "data", Version: "1.1~bp1", Architecture: "all", Source: "app", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
||||||
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
{Name: "app", Version: "1.1~bp1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
||||||
&Package{Name: "mailer", Version: "3.5.8", Architecture: "i386", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
|
{Name: "mailer", Version: "3.5.8", Architecture: "i386", Source: "postfix (1.3)", Provides: []string{"mail-agent"}, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "app", Version: "1.1~bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
{Name: "app", Version: "1.1~bp1", Architecture: "amd64", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
||||||
&Package{Name: "app", Version: "1.1~bp1", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9) | libx (>= 1.5)", "data (>= 1.0) | mail-agent"}}},
|
{Name: "app", Version: "1.1~bp1", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9) | libx (>= 1.5)", "data (>= 1.0) | mail-agent"}}},
|
||||||
&Package{Name: "app", Version: "1.0", Architecture: "s390", deps: &PackageDependencies{PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
{Name: "app", Version: "1.0", Architecture: "s390", deps: &PackageDependencies{PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}}},
|
||||||
&Package{Name: "aa", Version: "2.0-1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
{Name: "aa", Version: "2.0-1", Architecture: "i386", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
||||||
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "libx", Version: "1.5", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
{Name: "libx", Version: "1.5", Architecture: "arm", deps: &PackageDependencies{PreDepends: []string{"dpkg (>= 1.6)"}}},
|
||||||
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "arm", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
{Name: "dpkg", Version: "1.6.1-3", Architecture: "arm", Provides: []string{"package-installer"}, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "dpkg", Version: "1.6.1-3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "dpkg", Version: "1.7", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "dpkg", Version: "1.7", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
}
|
}
|
||||||
for _, p := range s.packages {
|
for _, p := range s.packages {
|
||||||
s.il.Add(p)
|
s.il.Add(p)
|
||||||
}
|
}
|
||||||
s.il.PrepareIndex()
|
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{
|
s.sourcePackages = []*Package{
|
||||||
&Package{Name: "postfix", Version: "1.3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "postfix", Version: "1.3", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "app", Version: "1.1~bp1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "app", Version: "1.1~bp1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "aa", Version: "2.0-1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "aa", Version: "2.0-1", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
&Package{Name: "lib", Version: "0.9", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
{Name: "lib", Version: "0.9", Architecture: "source", SourceArchitecture: "any", IsSource: true, deps: &PackageDependencies{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -196,35 +250,60 @@ func (s *PackageListSuite) TestAppend(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageListSuite) TestSearch(c *C) {
|
func (s *PackageListSuite) TestSearch(c *C) {
|
||||||
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}) }, Panics, "list not indexed, can't search")
|
//allMatches = False
|
||||||
|
c.Check(func() { s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}, false) }, Panics, "list not indexed, can't search")
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}), Equals, s.packages[4])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}, false), DeepEquals, []*Package{s.packages[4]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "puppy"}, false), IsNil)
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}, false), IsNil)
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}, false), IsNil)
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}, false), IsNil)
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}, false), IsNil)
|
||||||
|
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}, false), DeepEquals, []*Package{s.packages[3]})
|
||||||
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}), IsNil)
|
c.Check(s.il.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.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) {
|
func (s *PackageListSuite) TestFilter(c *C) {
|
||||||
c.Check(func() { s.list.Filter([]string{"abcd_0.3_i386"}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
|
c.Check(func() { s.list.Filter([]PackageQuery{&PkgQuery{"abcd", "0.3", "i386"}}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
|
||||||
|
|
||||||
_, err := s.il.Filter([]string{"app >3)"}, false, nil, 0, nil)
|
|
||||||
c.Check(err, ErrorMatches, "unable to parse dependency.*")
|
|
||||||
|
|
||||||
plString := func(l *PackageList) string {
|
plString := func(l *PackageList) string {
|
||||||
list := make([]string, 0, l.Len())
|
list := make([]string, 0, l.Len())
|
||||||
@@ -237,25 +316,69 @@ func (s *PackageListSuite) TestFilter(c *C) {
|
|||||||
return strings.Join(list, " ")
|
return strings.Join(list, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.il.Filter([]string{"app_1.1~bp1_i386"}, false, nil, 0, nil)
|
result, err := s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}}, false, nil, 0, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
|
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
|
||||||
|
|
||||||
result, err = s.il.Filter([]string{"app_1.1~bp1_i386", "dpkg_1.7_source", "dpkg_1.8_amd64"}, false, nil, 0, nil)
|
result, err = s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}, &PkgQuery{"dpkg", "1.7", "source"},
|
||||||
|
&PkgQuery{"dpkg", "1.8", "amd64"}}, false, nil, 0, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
|
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
|
||||||
|
|
||||||
result, err = s.il.Filter([]string{"app", "dpkg (>>1.6.1-3)", "app (>=1.0)", "xyz", "aa (>>3.0)"}, false, nil, 0, nil)
|
result, err = s.il.Filter([]PackageQuery{
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "app"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "xyz"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "aa", Relation: VersionGreater, Version: "3.0"}}}, false, nil, 0, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(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")
|
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
|
||||||
|
|
||||||
result, err = s.il.Filter([]string{"app {i386}"}, true, NewPackageList(), 0, []string{"i386"})
|
result, err = s.il.Filter([]PackageQuery{&DependencyQuery{Dep: Dependency{Pkg: "app", Architecture: "i386"}}}, true, NewPackageList(), 0, []string{"i386"})
|
||||||
c.Check(err, IsNil)
|
c.Check(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")
|
c.Check(plString(result), Equals, "app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
|
||||||
|
|
||||||
result, err = s.il.Filter([]string{"app (>=0.9)", "lib", "data"}, true, NewPackageList(), 0, []string{"i386", "amd64"})
|
result, err = s.il.Filter([]PackageQuery{
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "0.9"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "lib"}},
|
||||||
|
&DependencyQuery{Dep: Dependency{Pkg: "data"}}}, true, NewPackageList(), 0, []string{"i386", "amd64"})
|
||||||
c.Check(err, IsNil)
|
c.Check(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")
|
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) {
|
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
||||||
@@ -265,7 +388,7 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
|||||||
|
|
||||||
missing, err = s.il.VerifyDependencies(0, []string{"i386", "amd64"}, s.il, nil)
|
missing, err = s.il.VerifyDependencies(0, []string{"i386", "amd64"}, s.il, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
|
c.Check(missing, DeepEquals, []Dependency{{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
|
||||||
|
|
||||||
missing, err = s.il.VerifyDependencies(0, []string{"arm"}, s.il, nil)
|
missing, err = s.il.VerifyDependencies(0, []string{"arm"}, s.il, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
@@ -273,8 +396,8 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
|||||||
|
|
||||||
missing, err = s.il.VerifyDependencies(DepFollowAllVariants, []string{"arm"}, s.il, nil)
|
missing, err = s.il.VerifyDependencies(DepFollowAllVariants, []string{"arm"}, s.il, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "arm"},
|
c.Check(missing, DeepEquals, []Dependency{{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "arm"},
|
||||||
Dependency{Pkg: "mail-agent", Relation: VersionDontCare, Version: "", Architecture: "arm"}})
|
{Pkg: "mail-agent", Relation: VersionDontCare, Version: "", Architecture: "arm"}})
|
||||||
|
|
||||||
for _, p := range s.sourcePackages {
|
for _, p := range s.sourcePackages {
|
||||||
s.il.Add(p)
|
s.il.Add(p)
|
||||||
@@ -282,11 +405,11 @@ func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
|||||||
|
|
||||||
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"i386", "amd64"}, s.il, nil)
|
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"i386", "amd64"}, s.il, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
|
c.Check(missing, DeepEquals, []Dependency{{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
|
||||||
|
|
||||||
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"arm"}, s.il, nil)
|
missing, err = s.il.VerifyDependencies(DepFollowSource, []string{"arm"}, s.il, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "libx", Relation: VersionEqual, Version: "1.5", Architecture: "source"}})
|
c.Check(missing, DeepEquals, []Dependency{{Pkg: "libx", Relation: VersionEqual, Version: "1.5", Architecture: "source"}})
|
||||||
|
|
||||||
_, err = s.il.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.il, nil)
|
_, err = s.il.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.il, nil)
|
||||||
c.Check(err, ErrorMatches, "unable to process package app_1.0_s390:.*")
|
c.Check(err, ErrorMatches, "unable to process package app_1.0_s390:.*")
|
||||||
|
|||||||
+6
-3
@@ -7,12 +7,13 @@ import (
|
|||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
"log"
|
"log"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalRepo is a collection of packages created locally
|
// LocalRepo is a collection of packages created locally
|
||||||
type LocalRepo struct {
|
type LocalRepo struct {
|
||||||
// Permanent internal ID
|
// Permanent internal ID
|
||||||
UUID string
|
UUID string `json:"-"`
|
||||||
// User-assigned name
|
// User-assigned name
|
||||||
Name string
|
Name string
|
||||||
// Comment
|
// Comment
|
||||||
@@ -88,6 +89,7 @@ func (repo *LocalRepo) RefKey() []byte {
|
|||||||
|
|
||||||
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
|
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
|
||||||
type LocalRepoCollection struct {
|
type LocalRepoCollection struct {
|
||||||
|
*sync.RWMutex
|
||||||
db database.Storage
|
db database.Storage
|
||||||
list []*LocalRepo
|
list []*LocalRepo
|
||||||
}
|
}
|
||||||
@@ -95,7 +97,8 @@ type LocalRepoCollection struct {
|
|||||||
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
|
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
|
||||||
func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
|
func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
|
||||||
result := &LocalRepoCollection{
|
result := &LocalRepoCollection{
|
||||||
db: db,
|
RWMutex: &sync.RWMutex{},
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
blobs := db.FetchByPrefix([]byte("L"))
|
blobs := db.FetchByPrefix([]byte("L"))
|
||||||
@@ -104,7 +107,7 @@ func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
|
|||||||
for _, blob := range blobs {
|
for _, blob := range blobs {
|
||||||
r := &LocalRepo{}
|
r := &LocalRepo{}
|
||||||
if err := r.Decode(blob); err != nil {
|
if err := r.Decode(blob); err != nil {
|
||||||
log.Printf("Error decoding mirror: %s\n", err)
|
log.Printf("Error decoding repo: %s\n", err)
|
||||||
} else {
|
} else {
|
||||||
result.list = append(result.list, r)
|
result.list = append(result.list, r)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -3,7 +3,8 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LocalRepoSuite struct {
|
type LocalRepoSuite struct {
|
||||||
|
|||||||
+122
-11
@@ -1,6 +1,7 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
@@ -24,6 +25,8 @@ type Package struct {
|
|||||||
Provides []string
|
Provides []string
|
||||||
// Is this source package
|
// Is this source package
|
||||||
IsSource bool
|
IsSource bool
|
||||||
|
// Is this udeb package
|
||||||
|
IsUdeb bool
|
||||||
// Hash of files section
|
// Hash of files section
|
||||||
FilesHash uint64
|
FilesHash uint64
|
||||||
// Is this >= 0.6 package?
|
// Is this >= 0.6 package?
|
||||||
@@ -36,6 +39,11 @@ type Package struct {
|
|||||||
collection *PackageCollection
|
collection *PackageCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var (
|
||||||
|
_ json.Marshaler = &Package{}
|
||||||
|
)
|
||||||
|
|
||||||
// NewPackageFromControlFile creates Package from parsed Debian control file
|
// NewPackageFromControlFile creates Package from parsed Debian control file
|
||||||
func NewPackageFromControlFile(input Stanza) *Package {
|
func NewPackageFromControlFile(input Stanza) *Package {
|
||||||
result := &Package{
|
result := &Package{
|
||||||
@@ -43,7 +51,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
|
|||||||
Version: input["Version"],
|
Version: input["Version"],
|
||||||
Architecture: input["Architecture"],
|
Architecture: input["Architecture"],
|
||||||
Source: input["Source"],
|
Source: input["Source"],
|
||||||
V06Plus: true,
|
V06Plus: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(input, "Package")
|
delete(input, "Package")
|
||||||
@@ -53,12 +61,18 @@ func NewPackageFromControlFile(input Stanza) *Package {
|
|||||||
|
|
||||||
filesize, _ := strconv.ParseInt(input["Size"], 10, 64)
|
filesize, _ := strconv.ParseInt(input["Size"], 10, 64)
|
||||||
|
|
||||||
|
md5, ok := input["MD5sum"]
|
||||||
|
if !ok {
|
||||||
|
// there are some broken repos out there with MD5 in wrong field
|
||||||
|
md5 = input["MD5Sum"]
|
||||||
|
}
|
||||||
|
|
||||||
result.UpdateFiles(PackageFiles{PackageFile{
|
result.UpdateFiles(PackageFiles{PackageFile{
|
||||||
Filename: filepath.Base(input["Filename"]),
|
Filename: filepath.Base(input["Filename"]),
|
||||||
downloadPath: filepath.Dir(input["Filename"]),
|
downloadPath: filepath.Dir(input["Filename"]),
|
||||||
Checksums: utils.ChecksumInfo{
|
Checksums: utils.ChecksumInfo{
|
||||||
Size: filesize,
|
Size: filesize,
|
||||||
MD5: strings.TrimSpace(input["MD5sum"]),
|
MD5: strings.TrimSpace(md5),
|
||||||
SHA1: strings.TrimSpace(input["SHA1"]),
|
SHA1: strings.TrimSpace(input["SHA1"]),
|
||||||
SHA256: strings.TrimSpace(input["SHA256"]),
|
SHA256: strings.TrimSpace(input["SHA256"]),
|
||||||
},
|
},
|
||||||
@@ -66,6 +80,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
|
|||||||
|
|
||||||
delete(input, "Filename")
|
delete(input, "Filename")
|
||||||
delete(input, "MD5sum")
|
delete(input, "MD5sum")
|
||||||
|
delete(input, "MD5Sum")
|
||||||
delete(input, "SHA1")
|
delete(input, "SHA1")
|
||||||
delete(input, "SHA256")
|
delete(input, "SHA256")
|
||||||
delete(input, "Size")
|
delete(input, "Size")
|
||||||
@@ -92,7 +107,7 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
|
|||||||
Version: input["Version"],
|
Version: input["Version"],
|
||||||
Architecture: "source",
|
Architecture: "source",
|
||||||
SourceArchitecture: input["Architecture"],
|
SourceArchitecture: input["Architecture"],
|
||||||
V06Plus: true,
|
V06Plus: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(input, "Package")
|
delete(input, "Package")
|
||||||
@@ -169,6 +184,14 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUdebPackageFromControlFile creates .udeb Package from parsed Debian control file
|
||||||
|
func NewUdebPackageFromControlFile(input Stanza) *Package {
|
||||||
|
p := NewPackageFromControlFile(input)
|
||||||
|
p.IsUdeb = true
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// Key returns unique key identifying package
|
// Key returns unique key identifying package
|
||||||
func (p *Package) Key(prefix string) []byte {
|
func (p *Package) Key(prefix string) []byte {
|
||||||
if p.V06Plus {
|
if p.V06Plus {
|
||||||
@@ -188,6 +211,82 @@ func (p *Package) String() string {
|
|||||||
return fmt.Sprintf("%s_%s_%s", p.Name, p.Version, p.Architecture)
|
return fmt.Sprintf("%s_%s_%s", p.Name, p.Version, p.Architecture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaller interface
|
||||||
|
func (p *Package) MarshalJSON() ([]byte, error) {
|
||||||
|
stanza := p.Stanza()
|
||||||
|
stanza["FilesHash"] = fmt.Sprintf("%08x", p.FilesHash)
|
||||||
|
stanza["Key"] = string(p.Key(""))
|
||||||
|
stanza["ShortKey"] = string(p.ShortKey(""))
|
||||||
|
|
||||||
|
return json.Marshal(stanza)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetField returns fields from package
|
||||||
|
func (p *Package) GetField(name string) string {
|
||||||
|
switch name {
|
||||||
|
// $Version is handled in FieldQuery
|
||||||
|
case "$Source":
|
||||||
|
if p.IsSource {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
source := p.Source
|
||||||
|
if source == "" {
|
||||||
|
return p.Name
|
||||||
|
} else if pos := strings.Index(source, "("); pos != -1 {
|
||||||
|
return strings.TrimSpace(source[:pos])
|
||||||
|
}
|
||||||
|
return source
|
||||||
|
case "$SourceVersion":
|
||||||
|
if p.IsSource {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
source := p.Source
|
||||||
|
if pos := strings.Index(source, "("); pos != -1 {
|
||||||
|
if pos2 := strings.LastIndex(source, ")"); pos2 != -1 && pos2 > pos {
|
||||||
|
return strings.TrimSpace(source[pos+1 : pos2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.Version
|
||||||
|
case "$Architecture":
|
||||||
|
return p.Architecture
|
||||||
|
case "$PackageType":
|
||||||
|
if p.IsSource {
|
||||||
|
return "source"
|
||||||
|
}
|
||||||
|
if p.IsUdeb {
|
||||||
|
return "udeb"
|
||||||
|
}
|
||||||
|
return "deb"
|
||||||
|
case "Name":
|
||||||
|
return p.Name
|
||||||
|
case "Version":
|
||||||
|
return p.Version
|
||||||
|
case "Architecture":
|
||||||
|
if p.IsSource {
|
||||||
|
return p.SourceArchitecture
|
||||||
|
}
|
||||||
|
return p.Architecture
|
||||||
|
case "Source":
|
||||||
|
return p.Source
|
||||||
|
case "Depends":
|
||||||
|
return strings.Join(p.Deps().Depends, ", ")
|
||||||
|
case "Pre-Depends":
|
||||||
|
return strings.Join(p.Deps().PreDepends, ", ")
|
||||||
|
case "Suggests":
|
||||||
|
return strings.Join(p.Deps().Suggests, ", ")
|
||||||
|
case "Recommends":
|
||||||
|
return strings.Join(p.Deps().Recommends, ", ")
|
||||||
|
case "Provides":
|
||||||
|
return strings.Join(p.Provides, ", ")
|
||||||
|
case "Build-Depends":
|
||||||
|
return strings.Join(p.Deps().BuildDepends, ", ")
|
||||||
|
case "Build-Depends-Indep":
|
||||||
|
return strings.Join(p.Deps().BuildDependsInDep, ", ")
|
||||||
|
default:
|
||||||
|
return p.Extra()[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MatchesArchitecture checks whether packages matches specified architecture
|
// MatchesArchitecture checks whether packages matches specified architecture
|
||||||
func (p *Package) MatchesArchitecture(arch string) bool {
|
func (p *Package) MatchesArchitecture(arch string) bool {
|
||||||
if p.Architecture == "all" && arch != "source" {
|
if p.Architecture == "all" && arch != "source" {
|
||||||
@@ -199,19 +298,23 @@ func (p *Package) MatchesArchitecture(arch string) bool {
|
|||||||
|
|
||||||
// MatchesDependency checks whether package matches specified dependency
|
// MatchesDependency checks whether package matches specified dependency
|
||||||
func (p *Package) MatchesDependency(dep Dependency) bool {
|
func (p *Package) MatchesDependency(dep Dependency) bool {
|
||||||
if dep.Pkg != p.Name {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if dep.Architecture != "" && !p.MatchesArchitecture(dep.Architecture) {
|
if dep.Architecture != "" && !p.MatchesArchitecture(dep.Architecture) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if dep.Relation == VersionDontCare {
|
if dep.Relation == VersionDontCare {
|
||||||
return true
|
if utils.StrSliceHasItem(p.Provides, dep.Pkg) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return dep.Pkg == p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if dep.Pkg != p.Name {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
r := CompareVersions(p.Version, dep.Version)
|
r := CompareVersions(p.Version, dep.Version)
|
||||||
|
|
||||||
switch dep.Relation {
|
switch dep.Relation {
|
||||||
case VersionEqual:
|
case VersionEqual:
|
||||||
return r == 0
|
return r == 0
|
||||||
@@ -223,6 +326,11 @@ func (p *Package) MatchesDependency(dep Dependency) bool {
|
|||||||
return r <= 0
|
return r <= 0
|
||||||
case VersionGreaterOrEqual:
|
case VersionGreaterOrEqual:
|
||||||
return r >= 0
|
return r >= 0
|
||||||
|
case VersionPatternMatch:
|
||||||
|
matched, err := filepath.Match(dep.Version, p.Version)
|
||||||
|
return err == nil && matched
|
||||||
|
case VersionRegexp:
|
||||||
|
return dep.Regexp.FindStringIndex(p.Version) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("unknown relation")
|
panic("unknown relation")
|
||||||
@@ -318,7 +426,9 @@ func (p *Package) Stanza() (result Stanza) {
|
|||||||
result["Architecture"] = p.SourceArchitecture
|
result["Architecture"] = p.SourceArchitecture
|
||||||
} else {
|
} else {
|
||||||
result["Architecture"] = p.Architecture
|
result["Architecture"] = p.Architecture
|
||||||
result["Source"] = p.Source
|
if p.Source != "" {
|
||||||
|
result["Source"] = p.Source
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.IsSource {
|
if p.IsSource {
|
||||||
@@ -389,7 +499,8 @@ func (p *Package) Equals(p2 *Package) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LinkFromPool links package file from pool to dist's pool location
|
// LinkFromPool links package file from pool to dist's pool location
|
||||||
func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packagePool aptly.PackagePool, prefix string, component string) error {
|
func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packagePool aptly.PackagePool,
|
||||||
|
prefix, component string, force bool) error {
|
||||||
poolDir, err := p.PoolDirectory()
|
poolDir, err := p.PoolDirectory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -404,7 +515,7 @@ func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packageP
|
|||||||
relPath := filepath.Join("pool", component, poolDir)
|
relPath := filepath.Join("pool", component, poolDir)
|
||||||
publishedDirectory := filepath.Join(prefix, relPath)
|
publishedDirectory := filepath.Join(prefix, relPath)
|
||||||
|
|
||||||
err = publishedStorage.LinkFromPool(publishedDirectory, packagePool, sourcePath)
|
err = publishedStorage.LinkFromPool(publishedDirectory, packagePool, sourcePath, f.Checksums.MD5, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,19 @@ import (
|
|||||||
type PackageCollection struct {
|
type PackageCollection struct {
|
||||||
db database.Storage
|
db database.Storage
|
||||||
encodeBuffer bytes.Buffer
|
encodeBuffer bytes.Buffer
|
||||||
|
codecHandle *codec.MsgpackHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify interface
|
||||||
|
var (
|
||||||
|
_ PackageCatalog = &PackageCollection{}
|
||||||
|
)
|
||||||
|
|
||||||
// NewPackageCollection creates new PackageCollection and binds it to database
|
// NewPackageCollection creates new PackageCollection and binds it to database
|
||||||
func NewPackageCollection(db database.Storage) *PackageCollection {
|
func NewPackageCollection(db database.Storage) *PackageCollection {
|
||||||
return &PackageCollection{
|
return &PackageCollection{
|
||||||
db: db,
|
db: db,
|
||||||
|
codecHandle: &codec.MsgpackHandle{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +60,7 @@ func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
|
|||||||
if len(encoded) > 2 && (encoded[0] != 0xc1 || encoded[1] != 0x1) {
|
if len(encoded) > 2 && (encoded[0] != 0xc1 || encoded[1] != 0x1) {
|
||||||
oldp := &oldPackage{}
|
oldp := &oldPackage{}
|
||||||
|
|
||||||
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
err = decoder.Decode(oldp)
|
err = decoder.Decode(oldp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -88,7 +95,7 @@ func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
decoder := codec.NewDecoderBytes(encoded[2:], &codec.MsgpackHandle{})
|
decoder := codec.NewDecoderBytes(encoded[2:], collection.codecHandle)
|
||||||
err = decoder.Decode(p)
|
err = decoder.Decode(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -109,7 +116,7 @@ func (collection *PackageCollection) loadExtra(p *Package) *Stanza {
|
|||||||
|
|
||||||
stanza := &Stanza{}
|
stanza := &Stanza{}
|
||||||
|
|
||||||
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
err = decoder.Decode(stanza)
|
err = decoder.Decode(stanza)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to decode extra")
|
panic("unable to decode extra")
|
||||||
@@ -127,7 +134,7 @@ func (collection *PackageCollection) loadDependencies(p *Package) *PackageDepend
|
|||||||
|
|
||||||
deps := &PackageDependencies{}
|
deps := &PackageDependencies{}
|
||||||
|
|
||||||
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
err = decoder.Decode(deps)
|
err = decoder.Decode(deps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to decode deps")
|
panic("unable to decode deps")
|
||||||
@@ -145,7 +152,7 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
|
|||||||
|
|
||||||
files := &PackageFiles{}
|
files := &PackageFiles{}
|
||||||
|
|
||||||
decoder := codec.NewDecoderBytes(encoded, &codec.MsgpackHandle{})
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
err = decoder.Decode(files)
|
err = decoder.Decode(files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to decode files")
|
panic("unable to decode files")
|
||||||
@@ -156,7 +163,7 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
|
|||||||
|
|
||||||
// Update adds or updates information about package in DB checking for conficts first
|
// Update adds or updates information about package in DB checking for conficts first
|
||||||
func (collection *PackageCollection) Update(p *Package) error {
|
func (collection *PackageCollection) Update(p *Package) error {
|
||||||
encoder := codec.NewEncoder(&collection.encodeBuffer, &codec.MsgpackHandle{})
|
encoder := codec.NewEncoder(&collection.encodeBuffer, collection.codecHandle)
|
||||||
|
|
||||||
collection.encodeBuffer.Reset()
|
collection.encodeBuffer.Reset()
|
||||||
collection.encodeBuffer.WriteByte(0xc1)
|
collection.encodeBuffer.WriteByte(0xc1)
|
||||||
@@ -235,3 +242,49 @@ func (collection *PackageCollection) DeleteByKey(key []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scan does full scan on all the packages
|
||||||
|
func (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) {
|
||||||
|
result = NewPackageList()
|
||||||
|
|
||||||
|
for _, key := range collection.db.KeysByPrefix([]byte("P")) {
|
||||||
|
pkg, err := collection.ByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unable to load package: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Matches(pkg) {
|
||||||
|
result.Add(pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search is not implemented
|
||||||
|
func (collection *PackageCollection) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchSupported returns false
|
||||||
|
func (collection *PackageCollection) SearchSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchByKey finds package by exact key
|
||||||
|
func (collection *PackageCollection) SearchByKey(arch, name, version string) (result *PackageList) {
|
||||||
|
result = NewPackageList()
|
||||||
|
|
||||||
|
for _, key := range collection.db.KeysByPrefix([]byte(fmt.Sprintf("P%s %s %s", arch, name, version))) {
|
||||||
|
pkg, err := collection.ByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unable to load package: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkg.Architecture == arch && pkg.Name == name && pkg.Version == version {
|
||||||
|
result.Add(pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackageCollectionSuite struct {
|
type PackageCollectionSuite struct {
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/files"
|
"github.com/smira/aptly/files"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackageFilesSuite struct {
|
type PackageFilesSuite struct {
|
||||||
|
|||||||
+131
-10
@@ -4,9 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"github.com/smira/aptly/files"
|
"github.com/smira/aptly/files"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackageSuite struct {
|
type PackageSuite struct {
|
||||||
@@ -27,6 +29,7 @@ func (s *PackageSuite) TestNewFromPara(c *C) {
|
|||||||
p := NewPackageFromControlFile(s.stanza)
|
p := NewPackageFromControlFile(s.stanza)
|
||||||
|
|
||||||
c.Check(p.IsSource, Equals, false)
|
c.Check(p.IsSource, Equals, false)
|
||||||
|
c.Check(p.IsUdeb, Equals, false)
|
||||||
c.Check(p.Name, Equals, "alien-arena-common")
|
c.Check(p.Name, Equals, "alien-arena-common")
|
||||||
c.Check(p.Version, Equals, "7.40-2")
|
c.Check(p.Version, Equals, "7.40-2")
|
||||||
c.Check(p.Architecture, Equals, "i386")
|
c.Check(p.Architecture, Equals, "i386")
|
||||||
@@ -39,11 +42,27 @@ func (s *PackageSuite) TestNewFromPara(c *C) {
|
|||||||
c.Check(p.deps.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"})
|
c.Check(p.deps.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PackageSuite) TestNewUdebFromPara(c *C) {
|
||||||
|
stanza, _ := NewControlFileReader(bytes.NewBufferString(udebPackageMeta)).ReadStanza()
|
||||||
|
p := NewUdebPackageFromControlFile(stanza)
|
||||||
|
|
||||||
|
c.Check(p.IsSource, Equals, false)
|
||||||
|
c.Check(p.IsUdeb, Equals, true)
|
||||||
|
c.Check(p.Name, Equals, "dmidecode-udeb")
|
||||||
|
c.Check(p.Version, Equals, "2.11-9")
|
||||||
|
c.Check(p.Architecture, Equals, "amd64")
|
||||||
|
c.Check(p.Provides, DeepEquals, []string(nil))
|
||||||
|
c.Check(p.Files(), HasLen, 1)
|
||||||
|
c.Check(p.Files()[0].Filename, Equals, "dmidecode-udeb_2.11-9_amd64.udeb")
|
||||||
|
c.Check(p.deps.Depends, DeepEquals, []string{"libc6-udeb (>= 2.13)"})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PackageSuite) TestNewSourceFromPara(c *C) {
|
func (s *PackageSuite) TestNewSourceFromPara(c *C) {
|
||||||
p, err := NewSourcePackageFromControlFile(s.sourceStanza)
|
p, err := NewSourcePackageFromControlFile(s.sourceStanza)
|
||||||
|
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(p.IsSource, Equals, true)
|
c.Check(p.IsSource, Equals, true)
|
||||||
|
c.Check(p.IsUdeb, Equals, false)
|
||||||
c.Check(p.Name, Equals, "access-modifier-checker")
|
c.Check(p.Name, Equals, "access-modifier-checker")
|
||||||
c.Check(p.Version, Equals, "1.0-4")
|
c.Check(p.Version, Equals, "1.0-4")
|
||||||
c.Check(p.Architecture, Equals, "source")
|
c.Check(p.Architecture, Equals, "source")
|
||||||
@@ -90,16 +109,16 @@ func (s *PackageSuite) TestKey(c *C) {
|
|||||||
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
|
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
|
||||||
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
|
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2 c8901eedd79ac51b"))
|
||||||
|
|
||||||
p.V06Plus = false
|
p.V06Plus = false
|
||||||
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
|
c.Check(p.Key(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
|
||||||
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
|
c.Check(p.Key("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageSuite) TestShortKey(c *C) {
|
func (s *PackageSuite) TestShortKey(c *C) {
|
||||||
p := NewPackageFromControlFile(s.stanza)
|
p := NewPackageFromControlFile(s.stanza)
|
||||||
|
|
||||||
c.Check(p.ShortKey(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
|
c.Check(p.ShortKey(""), DeepEquals, []byte("Pi386 alien-arena-common 7.40-2"))
|
||||||
c.Check(p.ShortKey("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
|
c.Check(p.ShortKey("xD"), DeepEquals, []byte("xDPi386 alien-arena-common 7.40-2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageSuite) TestStanza(c *C) {
|
func (s *PackageSuite) TestStanza(c *C) {
|
||||||
@@ -119,6 +138,65 @@ func (s *PackageSuite) TestString(c *C) {
|
|||||||
c.Assert(p.String(), Equals, "alien-arena-common_7.40-2_i386")
|
c.Assert(p.String(), Equals, "alien-arena-common_7.40-2_i386")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PackageSuite) TestGetField(c *C) {
|
||||||
|
p := NewPackageFromControlFile(s.stanza.Copy())
|
||||||
|
|
||||||
|
stanza2 := s.stanza.Copy()
|
||||||
|
delete(stanza2, "Source")
|
||||||
|
stanza2["Provides"] = "app, game"
|
||||||
|
p2 := NewPackageFromControlFile(stanza2)
|
||||||
|
|
||||||
|
stanza3 := s.stanza.Copy()
|
||||||
|
stanza3["Source"] = "alien-arena (3.5)"
|
||||||
|
p3 := NewPackageFromControlFile(stanza3)
|
||||||
|
|
||||||
|
p4, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy())
|
||||||
|
|
||||||
|
stanza5, _ := NewControlFileReader(bytes.NewBufferString(udebPackageMeta)).ReadStanza()
|
||||||
|
p5 := NewUdebPackageFromControlFile(stanza5)
|
||||||
|
|
||||||
|
c.Check(p.GetField("$Source"), Equals, "alien-arena")
|
||||||
|
c.Check(p2.GetField("$Source"), Equals, "alien-arena-common")
|
||||||
|
c.Check(p3.GetField("$Source"), Equals, "alien-arena")
|
||||||
|
c.Check(p4.GetField("$Source"), Equals, "")
|
||||||
|
c.Check(p5.GetField("$Source"), Equals, "dmidecode")
|
||||||
|
|
||||||
|
c.Check(p.GetField("$SourceVersion"), Equals, "7.40-2")
|
||||||
|
c.Check(p2.GetField("$SourceVersion"), Equals, "7.40-2")
|
||||||
|
c.Check(p3.GetField("$SourceVersion"), Equals, "3.5")
|
||||||
|
c.Check(p4.GetField("$SourceVersion"), Equals, "")
|
||||||
|
c.Check(p5.GetField("$SourceVersion"), Equals, "2.11-9")
|
||||||
|
|
||||||
|
c.Check(p.GetField("$Architecture"), Equals, "i386")
|
||||||
|
c.Check(p4.GetField("$Architecture"), Equals, "source")
|
||||||
|
c.Check(p5.GetField("$Architecture"), Equals, "amd64")
|
||||||
|
|
||||||
|
c.Check(p.GetField("$PackageType"), Equals, "deb")
|
||||||
|
c.Check(p4.GetField("$PackageType"), Equals, "source")
|
||||||
|
c.Check(p5.GetField("$PackageType"), Equals, "udeb")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Name"), Equals, "alien-arena-common")
|
||||||
|
c.Check(p4.GetField("Name"), Equals, "access-modifier-checker")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Architecture"), Equals, "i386")
|
||||||
|
c.Check(p4.GetField("Architecture"), Equals, "all")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Version"), Equals, "7.40-2")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Source"), Equals, "alien-arena")
|
||||||
|
c.Check(p2.GetField("Source"), Equals, "")
|
||||||
|
c.Check(p3.GetField("Source"), Equals, "alien-arena (3.5)")
|
||||||
|
c.Check(p4.GetField("Source"), Equals, "")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Depends"), Equals, "libc6 (>= 2.7), alien-arena-data (>= 7.40)")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Provides"), Equals, "")
|
||||||
|
c.Check(p2.GetField("Provides"), Equals, "app, game")
|
||||||
|
|
||||||
|
c.Check(p.GetField("Section"), Equals, "contrib/games")
|
||||||
|
c.Check(p.GetField("Priority"), Equals, "extra")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PackageSuite) TestEquals(c *C) {
|
func (s *PackageSuite) TestEquals(c *C) {
|
||||||
p := NewPackageFromControlFile(s.stanza)
|
p := NewPackageFromControlFile(s.stanza)
|
||||||
|
|
||||||
@@ -174,6 +252,9 @@ func (s *PackageSuite) TestMatchesDependency(c *C) {
|
|||||||
// exact match
|
// exact match
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, true)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, true)
|
||||||
|
|
||||||
|
// exact match, same version, no revision specified
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionEqual, Version: "7.40"}), Equals, false)
|
||||||
|
|
||||||
// different name
|
// different name
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, false)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena", Architecture: "i386", Relation: VersionEqual, Version: "7.40-2"}), Equals, false)
|
||||||
|
|
||||||
@@ -204,6 +285,29 @@ func (s *PackageSuite) TestMatchesDependency(c *C) {
|
|||||||
// <=
|
// <=
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-2"}), Equals, true)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-2"}), Equals, true)
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-1"}), Equals, false)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionLessOrEqual, Version: "7.40-1"}), Equals, false)
|
||||||
|
|
||||||
|
// %
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-*"}), Equals, true)
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2]"}), Equals, true)
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2"}), Equals, false)
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[34]"}), Equals, false)
|
||||||
|
|
||||||
|
// ~
|
||||||
|
c.Check(
|
||||||
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
|
Regexp: regexp.MustCompile("7\\.40-.*")}), Equals, true)
|
||||||
|
c.Check(
|
||||||
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
|
Regexp: regexp.MustCompile("40")}), Equals, true)
|
||||||
|
c.Check(
|
||||||
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
|
Regexp: regexp.MustCompile("39-.*")}), Equals, false)
|
||||||
|
|
||||||
|
// Provides
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, false)
|
||||||
|
p.Provides = []string{"fun", "game"}
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, true)
|
||||||
|
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Architecture: "amd64", Relation: VersionDontCare}), Equals, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageSuite) TestGetDependencies(c *C) {
|
func (s *PackageSuite) TestGetDependencies(c *C) {
|
||||||
@@ -266,13 +370,13 @@ func (s *PackageSuite) TestLinkFromPool(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free")
|
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free", false)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(p.Files()[0].Filename, Equals, "alien-arena-common_7.40-2_i386.deb")
|
c.Check(p.Files()[0].Filename, Equals, "alien-arena-common_7.40-2_i386.deb")
|
||||||
c.Check(p.Files()[0].downloadPath, Equals, "pool/non-free/a/alien-arena")
|
c.Check(p.Files()[0].downloadPath, Equals, "pool/non-free/a/alien-arena")
|
||||||
|
|
||||||
p.IsSource = true
|
p.IsSource = true
|
||||||
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free")
|
err = p.LinkFromPool(publishedStorage, packagePool, "", "non-free", false)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(p.Extra()["Directory"], Equals, "pool/non-free/a/alien-arena")
|
c.Check(p.Extra()["Directory"], Equals, "pool/non-free/a/alien-arena")
|
||||||
}
|
}
|
||||||
@@ -295,7 +399,7 @@ func (s *PackageSuite) TestDownloadList(c *C) {
|
|||||||
list, err := p.DownloadList(packagePool)
|
list, err := p.DownloadList(packagePool)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(list, DeepEquals, []PackageDownloadTask{
|
c.Check(list, DeepEquals, []PackageDownloadTask{
|
||||||
PackageDownloadTask{
|
{
|
||||||
RepoURI: "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb",
|
RepoURI: "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb",
|
||||||
DestinationPath: poolPath,
|
DestinationPath: poolPath,
|
||||||
Checksums: utils.ChecksumInfo{Size: 5,
|
Checksums: utils.ChecksumInfo{Size: 5,
|
||||||
@@ -376,3 +480,20 @@ Directory: pool/main/a/access-modifier-checker
|
|||||||
Priority: source
|
Priority: source
|
||||||
Section: java
|
Section: java
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const udebPackageMeta = `Package: dmidecode-udeb
|
||||||
|
Source: dmidecode
|
||||||
|
Version: 2.11-9
|
||||||
|
Installed-Size: 115
|
||||||
|
Maintainer: Daniel Baumann <daniel.baumann@progress-technologies.net>
|
||||||
|
Architecture: amd64
|
||||||
|
Depends: libc6-udeb (>= 2.13)
|
||||||
|
Description: SMBIOS/DMI table decoder (udeb)
|
||||||
|
Description-md5: bdfb786c6a57097be8c8600b800e749f
|
||||||
|
Section: debian-installer
|
||||||
|
Priority: optional
|
||||||
|
Filename: pool/main/d/dmidecode/dmidecode-udeb_2.11-9_amd64.udeb
|
||||||
|
Size: 29188
|
||||||
|
MD5sum: ae70341c4d96dcded89fa670bcfea31e
|
||||||
|
SHA1: 9532ae4226a85805189a671ee0283f719d48a5ba
|
||||||
|
SHA256: bbb3a2cb07f741c3995b6d4bb08d772d83582b93a0236d4ea7736bc0370fc320`
|
||||||
|
|||||||
+2
-1
@@ -2,7 +2,8 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
. "launchpad.net/gocheck"
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PpaSuite struct {
|
type PpaSuite struct {
|
||||||
|
|||||||
+203
-182
@@ -4,16 +4,19 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"code.google.com/p/go-uuid/uuid"
|
"code.google.com/p/go-uuid/uuid"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +33,8 @@ type repoSourceItem struct {
|
|||||||
type PublishedRepo struct {
|
type PublishedRepo struct {
|
||||||
// Internal unique ID
|
// Internal unique ID
|
||||||
UUID string
|
UUID string
|
||||||
// Prefix & distribution should be unique across all published repositories
|
// Storage & Prefix & distribution should be unique across all published repositories
|
||||||
|
Storage string
|
||||||
Prefix string
|
Prefix string
|
||||||
Distribution string
|
Distribution string
|
||||||
Origin string
|
Origin string
|
||||||
@@ -55,6 +59,21 @@ type PublishedRepo struct {
|
|||||||
rePublishing bool
|
rePublishing bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePrefix splits [storage:]prefix into components
|
||||||
|
func ParsePrefix(param string) (storage, prefix string) {
|
||||||
|
i := strings.LastIndex(param, ":")
|
||||||
|
if i != -1 {
|
||||||
|
storage = param[:i]
|
||||||
|
prefix = param[i+1:]
|
||||||
|
if prefix == "" {
|
||||||
|
prefix = "."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prefix = param
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// walkUpTree goes from source in the tree of source snapshots/mirrors/local repos
|
// walkUpTree goes from source in the tree of source snapshots/mirrors/local repos
|
||||||
// gathering information about declared components and distributions
|
// gathering information about declared components and distributions
|
||||||
func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootDistributions []string, rootComponents []string) {
|
func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootDistributions []string, rootComponents []string) {
|
||||||
@@ -115,13 +134,15 @@ func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootD
|
|||||||
|
|
||||||
// NewPublishedRepo creates new published repository
|
// NewPublishedRepo creates new published repository
|
||||||
//
|
//
|
||||||
|
// storage is PublishedStorage name
|
||||||
// prefix specifies publishing prefix
|
// prefix specifies publishing prefix
|
||||||
// distribution and architectures are user-defined properties
|
// distribution and architectures are user-defined properties
|
||||||
// components & sources are lists of component to source mapping (*Snapshot or *LocalRepo)
|
// components & sources are lists of component to source mapping (*Snapshot or *LocalRepo)
|
||||||
func NewPublishedRepo(prefix string, distribution string, architectures []string,
|
func NewPublishedRepo(storage, prefix, distribution string, architectures []string,
|
||||||
components []string, sources []interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
|
components []string, sources []interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
|
||||||
result := &PublishedRepo{
|
result := &PublishedRepo{
|
||||||
UUID: uuid.New(),
|
UUID: uuid.New(),
|
||||||
|
Storage: storage,
|
||||||
Architectures: architectures,
|
Architectures: architectures,
|
||||||
Sources: make(map[string]string),
|
Sources: make(map[string]string),
|
||||||
sourceItems: make(map[string]repoSourceItem),
|
sourceItems: make(map[string]repoSourceItem),
|
||||||
@@ -165,6 +186,9 @@ func NewPublishedRepo(prefix string, distribution string, architectures []string
|
|||||||
if distribution == "" || component == "" {
|
if distribution == "" || component == "" {
|
||||||
rootDistributions, rootComponents := walkUpTree(source, collectionFactory)
|
rootDistributions, rootComponents := walkUpTree(source, collectionFactory)
|
||||||
if distribution == "" {
|
if distribution == "" {
|
||||||
|
for i := range rootDistributions {
|
||||||
|
rootDistributions[i] = strings.Replace(rootDistributions[i], "/", "-", -1)
|
||||||
|
}
|
||||||
discoveredDistributions = append(discoveredDistributions, rootDistributions...)
|
discoveredDistributions = append(discoveredDistributions, rootDistributions...)
|
||||||
}
|
}
|
||||||
if component == "" {
|
if component == "" {
|
||||||
@@ -224,11 +248,49 @@ func NewPublishedRepo(prefix string, distribution string, architectures []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Index(distribution, "/") != -1 {
|
||||||
|
return nil, fmt.Errorf("invalid distribution %s, '/' is not allowed", distribution)
|
||||||
|
}
|
||||||
|
|
||||||
result.Distribution = distribution
|
result.Distribution = distribution
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON requires object to be "loeaded completely"
|
||||||
|
func (p *PublishedRepo) MarshalJSON() ([]byte, error) {
|
||||||
|
type sourceInfo struct {
|
||||||
|
Component, Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
sources := []sourceInfo{}
|
||||||
|
for component, item := range p.sourceItems {
|
||||||
|
name := ""
|
||||||
|
if item.snapshot != nil {
|
||||||
|
name = item.snapshot.Name
|
||||||
|
} else if item.localRepo != nil {
|
||||||
|
name = item.localRepo.Name
|
||||||
|
} else {
|
||||||
|
panic("no snapshot/local repo")
|
||||||
|
}
|
||||||
|
sources = append(sources, sourceInfo{
|
||||||
|
Component: component,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"Architectures": p.Architectures,
|
||||||
|
"Distribution": p.Distribution,
|
||||||
|
"Label": p.Label,
|
||||||
|
"Origin": p.Origin,
|
||||||
|
"Prefix": p.Prefix,
|
||||||
|
"SourceKind": p.SourceKind,
|
||||||
|
"Sources": sources,
|
||||||
|
"Storage": p.Storage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// String returns human-readable represenation of PublishedRepo
|
// String returns human-readable represenation of PublishedRepo
|
||||||
func (p *PublishedRepo) String() string {
|
func (p *PublishedRepo) String() string {
|
||||||
var sources = []string{}
|
var sources = []string{}
|
||||||
@@ -265,13 +327,22 @@ func (p *PublishedRepo) String() string {
|
|||||||
extra = " (" + extra + ")"
|
extra = " (" + extra + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s%s [%s] publishes %s", p.Prefix, p.Distribution, extra, strings.Join(p.Architectures, ", "),
|
return fmt.Sprintf("%s/%s%s [%s] publishes %s", p.StoragePrefix(), p.Distribution, extra, strings.Join(p.Architectures, ", "),
|
||||||
strings.Join(sources, ", "))
|
strings.Join(sources, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StoragePrefix returns combined storage & prefix for the repo
|
||||||
|
func (p *PublishedRepo) StoragePrefix() string {
|
||||||
|
result := p.Prefix
|
||||||
|
if p.Storage != "" {
|
||||||
|
result = p.Storage + ":" + p.Prefix
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Key returns unique key identifying PublishedRepo
|
// Key returns unique key identifying PublishedRepo
|
||||||
func (p *PublishedRepo) Key() []byte {
|
func (p *PublishedRepo) Key() []byte {
|
||||||
return []byte("U" + p.Prefix + ">>" + p.Distribution)
|
return []byte("U" + p.StoragePrefix() + ">>" + p.Distribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefKey is a unique id for package reference list
|
// RefKey is a unique id for package reference list
|
||||||
@@ -379,8 +450,10 @@ func (p *PublishedRepo) GetLabel() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish publishes snapshot (repository) contents, links package files, generates Packages & Release files, signs them
|
// Publish publishes snapshot (repository) contents, links package files, generates Packages & Release files, signs them
|
||||||
func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage aptly.PublishedStorage,
|
func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorageProvider aptly.PublishedStorageProvider,
|
||||||
collectionFactory *CollectionFactory, signer utils.Signer, progress aptly.Progress) error {
|
collectionFactory *CollectionFactory, signer utils.Signer, progress aptly.Progress, forceOverwrite bool) error {
|
||||||
|
publishedStorage := publishedStorageProvider.GetPublishedStorage(p.Storage)
|
||||||
|
|
||||||
err := publishedStorage.MkDir(filepath.Join(p.Prefix, "pool"))
|
err := publishedStorage.MkDir(filepath.Join(p.Prefix, "pool"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -425,55 +498,64 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
|
|||||||
suffix = ".tmp"
|
suffix = ".tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
generatedFiles := map[string]utils.ChecksumInfo{}
|
|
||||||
renameMap := map[string]string{}
|
|
||||||
|
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
progress.Printf("Generating metadata files and linking package files...\n")
|
progress.Printf("Generating metadata files and linking package files...\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tempDir string
|
||||||
|
tempDir, err = ioutil.TempDir(os.TempDir(), "aptly")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
|
indexes := newIndexFiles(publishedStorage, basePath, tempDir, suffix)
|
||||||
|
|
||||||
for component, list := range lists {
|
for component, list := range lists {
|
||||||
var relativePath string
|
hadUdebs := false
|
||||||
|
|
||||||
// For all architectures, generate packages/sources files
|
// For all architectures, pregenerate packages/sources files
|
||||||
for _, arch := range p.Architectures {
|
for _, arch := range p.Architectures {
|
||||||
|
indexes.PackageIndex(component, arch, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if progress != nil {
|
||||||
|
progress.InitBar(int64(list.Len()), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
list.PrepareIndex()
|
||||||
|
|
||||||
|
err = list.ForEachIndexed(func(pkg *Package) error {
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
progress.InitBar(int64(list.Len()), false)
|
progress.AddBar(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arch == "source" {
|
matches := false
|
||||||
relativePath = filepath.Join(component, "source", "Sources")
|
for _, arch := range p.Architectures {
|
||||||
} else {
|
|
||||||
relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Packages")
|
|
||||||
}
|
|
||||||
err = publishedStorage.MkDir(filepath.Dir(filepath.Join(basePath, relativePath)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var packagesFile *os.File
|
|
||||||
packagesFile, err = publishedStorage.CreateFile(filepath.Join(basePath, relativePath+suffix))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to creates Packages file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if suffix != "" {
|
|
||||||
renameMap[filepath.Join(basePath, relativePath+suffix)] = filepath.Join(basePath, relativePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
bufWriter := bufio.NewWriter(packagesFile)
|
|
||||||
|
|
||||||
err = list.ForEach(func(pkg *Package) error {
|
|
||||||
if progress != nil {
|
|
||||||
progress.AddBar(1)
|
|
||||||
}
|
|
||||||
if pkg.MatchesArchitecture(arch) {
|
if pkg.MatchesArchitecture(arch) {
|
||||||
err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, component)
|
matches = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches {
|
||||||
|
hadUdebs = hadUdebs || pkg.IsUdeb
|
||||||
|
err = pkg.LinkFromPool(publishedStorage, packagePool, p.Prefix, component, forceOverwrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arch := range p.Architectures {
|
||||||
|
if pkg.MatchesArchitecture(arch) {
|
||||||
|
var bufWriter *bufio.Writer
|
||||||
|
|
||||||
|
bufWriter, err = indexes.PackageIndex(component, arch, pkg.IsUdeb).BufWriter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pkg.Stanza().WriteTo(bufWriter)
|
err = pkg.Stanza().WriteTo(bufWriter, pkg.IsSource, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -481,112 +563,68 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg.files = nil
|
|
||||||
pkg.deps = nil
|
|
||||||
pkg.extra = nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to process packages: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bufWriter.Flush()
|
pkg.files = nil
|
||||||
if err != nil {
|
pkg.deps = nil
|
||||||
return fmt.Errorf("unable to write Packages file: %s", err)
|
pkg.extra = nil
|
||||||
}
|
|
||||||
|
|
||||||
err = utils.CompressFile(packagesFile)
|
return nil
|
||||||
if err != nil {
|
})
|
||||||
return fmt.Errorf("unable to compress Packages files: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if suffix != "" {
|
if err != nil {
|
||||||
renameMap[filepath.Join(basePath, relativePath+suffix+".gz")] = filepath.Join(basePath, relativePath+".gz")
|
return fmt.Errorf("unable to process packages: %s", err)
|
||||||
renameMap[filepath.Join(basePath, relativePath+suffix+".bz2")] = filepath.Join(basePath, relativePath+".bz2")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
packagesFile.Close()
|
if progress != nil {
|
||||||
|
progress.ShutdownBar()
|
||||||
|
}
|
||||||
|
|
||||||
var checksumInfo utils.ChecksumInfo
|
udebs := []bool{false}
|
||||||
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix))
|
if hadUdebs {
|
||||||
if err != nil {
|
udebs = append(udebs, true)
|
||||||
return fmt.Errorf("unable to collect checksums: %s", err)
|
|
||||||
}
|
|
||||||
generatedFiles[relativePath] = checksumInfo
|
|
||||||
|
|
||||||
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".gz"))
|
// For all architectures, pregenerate .udeb indexes
|
||||||
if err != nil {
|
for _, arch := range p.Architectures {
|
||||||
return fmt.Errorf("unable to collect checksums: %s", err)
|
indexes.PackageIndex(component, arch, true)
|
||||||
}
|
|
||||||
generatedFiles[relativePath+".gz"] = checksumInfo
|
|
||||||
|
|
||||||
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix+".bz2"))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to collect checksums: %s", err)
|
|
||||||
}
|
|
||||||
generatedFiles[relativePath+".bz2"] = checksumInfo
|
|
||||||
|
|
||||||
if progress != nil {
|
|
||||||
progress.ShutdownBar()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all architectures, generate Release files
|
// For all architectures, generate Release files
|
||||||
for _, arch := range p.Architectures {
|
for _, arch := range p.Architectures {
|
||||||
release := make(Stanza)
|
for _, udeb := range udebs {
|
||||||
release["Archive"] = p.Distribution
|
release := make(Stanza)
|
||||||
release["Architecture"] = arch
|
release["Archive"] = p.Distribution
|
||||||
release["Component"] = component
|
release["Architecture"] = arch
|
||||||
release["Origin"] = p.GetOrigin()
|
release["Component"] = component
|
||||||
release["Label"] = p.GetLabel()
|
release["Origin"] = p.GetOrigin()
|
||||||
|
release["Label"] = p.GetLabel()
|
||||||
|
|
||||||
if arch == "source" {
|
var bufWriter *bufio.Writer
|
||||||
relativePath = filepath.Join(component, "source", "Release")
|
bufWriter, err = indexes.ReleaseIndex(component, arch, udeb).BufWriter()
|
||||||
} else {
|
|
||||||
relativePath = filepath.Join(component, fmt.Sprintf("binary-%s", arch), "Release")
|
err = release.WriteTo(bufWriter, false, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create Release file: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var file *os.File
|
|
||||||
file, err = publishedStorage.CreateFile(filepath.Join(basePath, relativePath+suffix))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if suffix != "" {
|
|
||||||
renameMap[filepath.Join(basePath, relativePath+suffix)] = filepath.Join(basePath, relativePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
bufWriter := bufio.NewWriter(file)
|
|
||||||
|
|
||||||
err = release.WriteTo(bufWriter)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bufWriter.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
var checksumInfo utils.ChecksumInfo
|
|
||||||
checksumInfo, err = publishedStorage.ChecksumsForFile(filepath.Join(basePath, relativePath+suffix))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to collect checksums: %s", err)
|
|
||||||
}
|
|
||||||
generatedFiles[relativePath] = checksumInfo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if progress != nil {
|
||||||
|
progress.Printf("Finalizing metadata files...\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = indexes.FinalizeAll(progress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
release := make(Stanza)
|
release := make(Stanza)
|
||||||
release["Origin"] = p.GetOrigin()
|
release["Origin"] = p.GetOrigin()
|
||||||
release["Label"] = p.GetLabel()
|
release["Label"] = p.GetLabel()
|
||||||
|
release["Suite"] = p.Distribution
|
||||||
release["Codename"] = p.Distribution
|
release["Codename"] = p.Distribution
|
||||||
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
|
release["Date"] = time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST")
|
||||||
release["Architectures"] = strings.Join(utils.StrSlicesSubstract(p.Architectures, []string{"source"}), " ")
|
release["Architectures"] = strings.Join(utils.StrSlicesSubstract(p.Architectures, []string{"source"}), " ")
|
||||||
@@ -597,64 +635,36 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
|
|||||||
|
|
||||||
release["Components"] = strings.Join(p.Components(), " ")
|
release["Components"] = strings.Join(p.Components(), " ")
|
||||||
|
|
||||||
for path, info := range generatedFiles {
|
for path, info := range indexes.generatedFiles {
|
||||||
release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path)
|
release["MD5Sum"] += fmt.Sprintf(" %s %8d %s\n", info.MD5, info.Size, path)
|
||||||
release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path)
|
release["SHA1"] += fmt.Sprintf(" %s %8d %s\n", info.SHA1, info.Size, path)
|
||||||
release["SHA256"] += fmt.Sprintf(" %s %8d %s\n", info.SHA256, info.Size, path)
|
release["SHA256"] += fmt.Sprintf(" %s %8d %s\n", info.SHA256, info.Size, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseFile, err := publishedStorage.CreateFile(filepath.Join(basePath, "Release"+suffix))
|
releaseFile := indexes.ReleaseFile()
|
||||||
|
bufWriter, err := releaseFile.BufWriter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = release.WriteTo(bufWriter, false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
return fmt.Errorf("unable to create Release file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if suffix != "" {
|
|
||||||
renameMap[filepath.Join(basePath, "Release"+suffix)] = filepath.Join(basePath, "Release")
|
|
||||||
}
|
|
||||||
|
|
||||||
bufWriter := bufio.NewWriter(releaseFile)
|
|
||||||
|
|
||||||
err = release.WriteTo(bufWriter)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bufWriter.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
releaseFilename := releaseFile.Name()
|
|
||||||
releaseFile.Close()
|
|
||||||
|
|
||||||
// Signing files might output to console, so flush progress writer first
|
// Signing files might output to console, so flush progress writer first
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
progress.Flush()
|
progress.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
if signer != nil {
|
err = releaseFile.Finalize(signer)
|
||||||
err = signer.DetachedSign(releaseFilename, releaseFilename+".gpg")
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("unable to sign Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = signer.ClearSign(releaseFilename, filepath.Join(filepath.Dir(releaseFilename), "InRelease"+suffix))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to sign Release file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if suffix != "" {
|
|
||||||
renameMap[filepath.Join(basePath, "Release"+suffix+".gpg")] = filepath.Join(basePath, "Release.gpg")
|
|
||||||
renameMap[filepath.Join(basePath, "InRelease"+suffix)] = filepath.Join(basePath, "InRelease")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for oldName, newName := range renameMap {
|
err = indexes.RenameFiles()
|
||||||
err = publishedStorage.RenameFile(oldName, newName)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("unable to rename: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -663,8 +673,10 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorage
|
|||||||
// RemoveFiles removes files that were created by Publish
|
// RemoveFiles removes files that were created by Publish
|
||||||
//
|
//
|
||||||
// It can remove prefix fully, and part of pool (for specific component)
|
// It can remove prefix fully, and part of pool (for specific component)
|
||||||
func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, removePrefix bool,
|
func (p *PublishedRepo) RemoveFiles(publishedStorageProvider aptly.PublishedStorageProvider, removePrefix bool,
|
||||||
removePoolComponents []string, progress aptly.Progress) error {
|
removePoolComponents []string, progress aptly.Progress) error {
|
||||||
|
publishedStorage := publishedStorageProvider.GetPublishedStorage(p.Storage)
|
||||||
|
|
||||||
// I. Easy: remove whole prefix (meta+packages)
|
// I. Easy: remove whole prefix (meta+packages)
|
||||||
if removePrefix {
|
if removePrefix {
|
||||||
err := publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "dists"), progress)
|
err := publishedStorage.RemoveDirs(filepath.Join(p.Prefix, "dists"), progress)
|
||||||
@@ -694,6 +706,7 @@ func (p *PublishedRepo) RemoveFiles(publishedStorage aptly.PublishedStorage, rem
|
|||||||
|
|
||||||
// PublishedRepoCollection does listing, updating/adding/deleting of PublishedRepos
|
// PublishedRepoCollection does listing, updating/adding/deleting of PublishedRepos
|
||||||
type PublishedRepoCollection struct {
|
type PublishedRepoCollection struct {
|
||||||
|
*sync.RWMutex
|
||||||
db database.Storage
|
db database.Storage
|
||||||
list []*PublishedRepo
|
list []*PublishedRepo
|
||||||
}
|
}
|
||||||
@@ -701,7 +714,8 @@ type PublishedRepoCollection struct {
|
|||||||
// NewPublishedRepoCollection loads PublishedRepos from DB and makes up collection
|
// NewPublishedRepoCollection loads PublishedRepos from DB and makes up collection
|
||||||
func NewPublishedRepoCollection(db database.Storage) *PublishedRepoCollection {
|
func NewPublishedRepoCollection(db database.Storage) *PublishedRepoCollection {
|
||||||
result := &PublishedRepoCollection{
|
result := &PublishedRepoCollection{
|
||||||
db: db,
|
RWMutex: &sync.RWMutex{},
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
blobs := db.FetchByPrefix([]byte("U"))
|
blobs := db.FetchByPrefix([]byte("U"))
|
||||||
@@ -722,7 +736,7 @@ func NewPublishedRepoCollection(db database.Storage) *PublishedRepoCollection {
|
|||||||
// Add appends new repo to collection and saves it
|
// Add appends new repo to collection and saves it
|
||||||
func (collection *PublishedRepoCollection) Add(repo *PublishedRepo) error {
|
func (collection *PublishedRepoCollection) Add(repo *PublishedRepo) error {
|
||||||
if collection.CheckDuplicate(repo) != nil {
|
if collection.CheckDuplicate(repo) != nil {
|
||||||
return fmt.Errorf("published repo with prefix/distribution %s/%s already exists", repo.Prefix, repo.Distribution)
|
return fmt.Errorf("published repo with storage/prefix/distribution %s/%s/%s already exists", repo.Storage, repo.Prefix, repo.Distribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := collection.Update(repo)
|
err := collection.Update(repo)
|
||||||
@@ -737,7 +751,7 @@ func (collection *PublishedRepoCollection) Add(repo *PublishedRepo) error {
|
|||||||
// CheckDuplicate verifies that there's no published repo with the same name
|
// CheckDuplicate verifies that there's no published repo with the same name
|
||||||
func (collection *PublishedRepoCollection) CheckDuplicate(repo *PublishedRepo) *PublishedRepo {
|
func (collection *PublishedRepoCollection) CheckDuplicate(repo *PublishedRepo) *PublishedRepo {
|
||||||
for _, r := range collection.list {
|
for _, r := range collection.list {
|
||||||
if r.Prefix == repo.Prefix && r.Distribution == repo.Distribution {
|
if r.Prefix == repo.Prefix && r.Distribution == repo.Distribution && r.Storage == repo.Storage {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -823,14 +837,17 @@ func (collection *PublishedRepoCollection) LoadComplete(repo *PublishedRepo, col
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByPrefixDistribution looks up repository by prefix & distribution
|
// ByStoragePrefixDistribution looks up repository by storage, prefix & distribution
|
||||||
func (collection *PublishedRepoCollection) ByPrefixDistribution(prefix, distribution string) (*PublishedRepo, error) {
|
func (collection *PublishedRepoCollection) ByStoragePrefixDistribution(storage, prefix, distribution string) (*PublishedRepo, error) {
|
||||||
for _, r := range collection.list {
|
for _, r := range collection.list {
|
||||||
if r.Prefix == prefix && r.Distribution == distribution {
|
if r.Prefix == prefix && r.Distribution == distribution && r.Storage == storage {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("published repo with prefix/distribution %s/%s not found", prefix, distribution)
|
if storage != "" {
|
||||||
|
storage += ":"
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("published repo with storage:prefix/distribution %s%s/%s not found", storage, prefix, distribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByUUID looks up repository by uuid
|
// ByUUID looks up repository by uuid
|
||||||
@@ -982,9 +999,10 @@ func (collection *PublishedRepoCollection) CleanupPrefixComponentFiles(prefix st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes published repository, cleaning up directories, files
|
// Remove removes published repository, cleaning up directories, files
|
||||||
func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.PublishedStorage, prefix, distribution string,
|
func (collection *PublishedRepoCollection) Remove(publishedStorageProvider aptly.PublishedStorageProvider,
|
||||||
collectionFactory *CollectionFactory, progress aptly.Progress) error {
|
storage, prefix, distribution string, collectionFactory *CollectionFactory, progress aptly.Progress,
|
||||||
repo, err := collection.ByPrefixDistribution(prefix, distribution)
|
force bool) error {
|
||||||
|
repo, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -999,7 +1017,7 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
|
|||||||
repoPosition = i
|
repoPosition = i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if r.Prefix == repo.Prefix {
|
if r.Storage == repo.Storage && r.Prefix == repo.Prefix {
|
||||||
removePrefix = false
|
removePrefix = false
|
||||||
|
|
||||||
rComponents := r.Components()
|
rComponents := r.Components()
|
||||||
@@ -1012,7 +1030,7 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo.RemoveFiles(publishedStorage, removePrefix, removePoolComponents, progress)
|
err = repo.RemoveFiles(publishedStorageProvider, removePrefix, removePoolComponents, progress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1021,9 +1039,12 @@ func (collection *PublishedRepoCollection) Remove(publishedStorage aptly.Publish
|
|||||||
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
|
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
|
||||||
|
|
||||||
if len(cleanComponents) > 0 {
|
if len(cleanComponents) > 0 {
|
||||||
err = collection.CleanupPrefixComponentFiles(repo.Prefix, cleanComponents, publishedStorage, collectionFactory, progress)
|
err = collection.CleanupPrefixComponentFiles(repo.Prefix, cleanComponents,
|
||||||
|
publishedStorageProvider.GetPublishedStorage(storage), collectionFactory, progress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if !force {
|
||||||
|
return fmt.Errorf("cleanup failed, use -force-drop to override: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+186
-80
@@ -3,13 +3,16 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/smira/aptly/files"
|
"github.com/smira/aptly/files"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
. "launchpad.net/gocheck"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pathExistsChecker struct {
|
type pathExistsChecker struct {
|
||||||
@@ -34,28 +37,47 @@ func (n *NullSigner) Init() error {
|
|||||||
func (n *NullSigner) SetKey(keyRef string) {
|
func (n *NullSigner) SetKey(keyRef string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NullSigner) SetBatch(batch bool) {
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NullSigner) SetKeyRing(keyring, secretKeyring string) {
|
func (n *NullSigner) SetKeyRing(keyring, secretKeyring string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NullSigner) SetPassphrase(passphrase, passphraseFile string) {
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NullSigner) DetachedSign(source string, destination string) error {
|
func (n *NullSigner) DetachedSign(source string, destination string) error {
|
||||||
return nil
|
return ioutil.WriteFile(destination, []byte{}, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NullSigner) ClearSign(source string, destination string) error {
|
func (n *NullSigner) ClearSign(source string, destination string) error {
|
||||||
return nil
|
return ioutil.WriteFile(destination, []byte{}, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FakeStorageProvider struct {
|
||||||
|
storages map[string]aptly.PublishedStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FakeStorageProvider) GetPublishedStorage(name string) aptly.PublishedStorage {
|
||||||
|
storage, ok := p.storages[name]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("unknown storage: %#v", name))
|
||||||
|
}
|
||||||
|
return storage
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublishedRepoSuite struct {
|
type PublishedRepoSuite struct {
|
||||||
PackageListMixinSuite
|
PackageListMixinSuite
|
||||||
repo, repo2, repo3, repo4 *PublishedRepo
|
repo, repo2, repo3, repo4, repo5 *PublishedRepo
|
||||||
root string
|
root, root2 string
|
||||||
publishedStorage aptly.PublishedStorage
|
provider *FakeStorageProvider
|
||||||
packagePool aptly.PackagePool
|
publishedStorage, publishedStorage2 *files.PublishedStorage
|
||||||
localRepo *LocalRepo
|
packagePool aptly.PackagePool
|
||||||
snapshot, snapshot2 *Snapshot
|
localRepo *LocalRepo
|
||||||
db database.Storage
|
snapshot, snapshot2 *Snapshot
|
||||||
factory *CollectionFactory
|
db database.Storage
|
||||||
packageCollection *PackageCollection
|
factory *CollectionFactory
|
||||||
|
packageCollection *PackageCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Suite(&PublishedRepoSuite{})
|
var _ = Suite(&PublishedRepoSuite{})
|
||||||
@@ -68,9 +90,14 @@ func (s *PublishedRepoSuite) SetUpTest(c *C) {
|
|||||||
|
|
||||||
s.root = c.MkDir()
|
s.root = c.MkDir()
|
||||||
s.publishedStorage = files.NewPublishedStorage(s.root)
|
s.publishedStorage = files.NewPublishedStorage(s.root)
|
||||||
|
s.root2 = c.MkDir()
|
||||||
|
s.publishedStorage2 = files.NewPublishedStorage(s.root2)
|
||||||
|
s.provider = &FakeStorageProvider{map[string]aptly.PublishedStorage{
|
||||||
|
"": s.publishedStorage,
|
||||||
|
"files:other": s.publishedStorage2}}
|
||||||
s.packagePool = files.NewPackagePool(s.root)
|
s.packagePool = files.NewPackagePool(s.root)
|
||||||
|
|
||||||
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false)
|
repo, _ := NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{}, false, false)
|
||||||
repo.packageRefs = s.reflist
|
repo.packageRefs = s.reflist
|
||||||
s.factory.RemoteRepoCollection().Add(repo)
|
s.factory.RemoteRepoCollection().Add(repo)
|
||||||
|
|
||||||
@@ -89,13 +116,15 @@ func (s *PublishedRepoSuite) SetUpTest(c *C) {
|
|||||||
s.packageCollection.Update(s.p2)
|
s.packageCollection.Update(s.p2)
|
||||||
s.packageCollection.Update(s.p3)
|
s.packageCollection.Update(s.p3)
|
||||||
|
|
||||||
s.repo, _ = NewPublishedRepo("ppa", "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
s.repo, _ = NewPublishedRepo("", "ppa", "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
||||||
|
|
||||||
s.repo2, _ = NewPublishedRepo("ppa", "maverick", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
s.repo2, _ = NewPublishedRepo("", "ppa", "maverick", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
|
||||||
s.repo3, _ = NewPublishedRepo("linux", "natty", nil, []string{"main", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
s.repo3, _ = NewPublishedRepo("", "linux", "natty", nil, []string{"main", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
||||||
|
|
||||||
s.repo4, _ = NewPublishedRepo("ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
s.repo4, _ = NewPublishedRepo("", "ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
|
||||||
|
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "maverick", []string{"source"}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
|
||||||
poolPath, _ := s.packagePool.Path(s.p1.Files()[0].Filename, s.p1.Files()[0].Checksums.MD5)
|
poolPath, _ := s.packagePool.Path(s.p1.Files()[0].Filename, s.p1.Files()[0].Checksums.MD5)
|
||||||
err := os.MkdirAll(filepath.Dir(poolPath), 0755)
|
err := os.MkdirAll(filepath.Dir(poolPath), 0755)
|
||||||
@@ -132,16 +161,19 @@ func (s *PublishedRepoSuite) TestNewPublishedRepo(c *C) {
|
|||||||
c.Check(s.repo3.RefList("main").Len(), Equals, 3)
|
c.Check(s.repo3.RefList("main").Len(), Equals, 3)
|
||||||
c.Check(s.repo3.RefList("contrib").Len(), Equals, 3)
|
c.Check(s.repo3.RefList("contrib").Len(), Equals, 3)
|
||||||
|
|
||||||
c.Check(func() { NewPublishedRepo(".", "a", nil, nil, nil, s.factory) }, PanicMatches, "publish with empty sources")
|
c.Check(func() { NewPublishedRepo("", ".", "a", nil, nil, nil, s.factory) }, PanicMatches, "publish with empty sources")
|
||||||
c.Check(func() {
|
c.Check(func() {
|
||||||
NewPublishedRepo(".", "a", nil, []string{"main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
NewPublishedRepo("", ".", "a", nil, []string{"main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
||||||
}, PanicMatches, "sources and components should be equal in size")
|
}, PanicMatches, "sources and components should be equal in size")
|
||||||
c.Check(func() {
|
c.Check(func() {
|
||||||
NewPublishedRepo(".", "a", nil, []string{"main", "contrib"}, []interface{}{s.localRepo, s.snapshot2}, s.factory)
|
NewPublishedRepo("", ".", "a", nil, []string{"main", "contrib"}, []interface{}{s.localRepo, s.snapshot2}, s.factory)
|
||||||
}, PanicMatches, "interface conversion:.*")
|
}, PanicMatches, "interface conversion:.*")
|
||||||
|
|
||||||
_, err := NewPublishedRepo(".", "a", nil, []string{"main", "main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
_, err := NewPublishedRepo("", ".", "a", nil, []string{"main", "main"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
||||||
c.Check(err, ErrorMatches, "duplicate component name: main")
|
c.Check(err, ErrorMatches, "duplicate component name: main")
|
||||||
|
|
||||||
|
_, err = NewPublishedRepo("", ".", "wheezy/updates", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
||||||
|
c.Check(err, ErrorMatches, "invalid distribution wheezy/updates, '/' is not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
|
func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
|
||||||
@@ -200,7 +232,7 @@ func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
|
|||||||
errorExpected: "invalid prefix .*",
|
errorExpected: "invalid prefix .*",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
repo, err := NewPublishedRepo(t.prefix, "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
repo, err := NewPublishedRepo("", t.prefix, "squeeze", nil, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
||||||
if t.errorExpected != "" {
|
if t.errorExpected != "" {
|
||||||
c.Check(err, ErrorMatches, t.errorExpected)
|
c.Check(err, ErrorMatches, t.errorExpected)
|
||||||
} else {
|
} else {
|
||||||
@@ -210,49 +242,56 @@ func (s *PublishedRepoSuite) TestPrefixNormalization(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestDistributionComponentGuessing(c *C) {
|
func (s *PublishedRepoSuite) TestDistributionComponentGuessing(c *C) {
|
||||||
repo, err := NewPublishedRepo("ppa", "", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
|
repo, err := NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "squeeze")
|
c.Check(repo.Distribution, Equals, "squeeze")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "wheezy", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "wheezy", nil, []string{""}, []interface{}{s.snapshot}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "wheezy")
|
c.Check(repo.Distribution, Equals, "wheezy")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "", nil, []string{"non-free"}, []interface{}{s.snapshot}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"non-free"}, []interface{}{s.snapshot}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "squeeze")
|
c.Check(repo.Distribution, Equals, "squeeze")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"non-free"})
|
c.Check(repo.Components(), DeepEquals, []string{"non-free"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "squeeze", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "squeeze", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "squeeze")
|
c.Check(repo.Distribution, Equals, "squeeze")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
c.Check(repo.Components(), DeepEquals, []string{"main"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
c.Check(err, ErrorMatches, "unable to guess distribution name, please specify explicitly")
|
c.Check(err, ErrorMatches, "unable to guess distribution name, please specify explicitly")
|
||||||
|
|
||||||
s.localRepo.DefaultDistribution = "precise"
|
s.localRepo.DefaultDistribution = "precise"
|
||||||
s.localRepo.DefaultComponent = "contrib"
|
s.localRepo.DefaultComponent = "contrib"
|
||||||
s.factory.LocalRepoCollection().Update(s.localRepo)
|
s.factory.LocalRepoCollection().Update(s.localRepo)
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "precise")
|
c.Check(repo.Distribution, Equals, "precise")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"contrib"})
|
c.Check(repo.Components(), DeepEquals, []string{"contrib"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "", nil, []string{"", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
s.localRepo.DefaultDistribution = "precise/updates"
|
||||||
|
|
||||||
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{""}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(repo.Distribution, Equals, "precise-updates")
|
||||||
|
c.Check(repo.Components(), DeepEquals, []string{"contrib"})
|
||||||
|
|
||||||
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"", "contrib"}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Distribution, Equals, "squeeze")
|
c.Check(repo.Distribution, Equals, "squeeze")
|
||||||
c.Check(repo.Components(), DeepEquals, []string{"contrib", "main"})
|
c.Check(repo.Components(), DeepEquals, []string{"contrib", "main"})
|
||||||
|
|
||||||
repo, err = NewPublishedRepo("ppa", "", nil, []string{"", ""}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
repo, err = NewPublishedRepo("", "ppa", "", nil, []string{"", ""}, []interface{}{s.snapshot, s.snapshot2}, s.factory)
|
||||||
c.Check(err, ErrorMatches, "duplicate component name: main")
|
c.Check(err, ErrorMatches, "duplicate component name: main")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestPublish(c *C) {
|
func (s *PublishedRepoSuite) TestPublish(c *C) {
|
||||||
err := s.repo.Publish(s.packagePool, s.publishedStorage, s.factory, &NullSigner{}, nil)
|
err := s.repo.Publish(s.packagePool, s.provider, s.factory, &NullSigner{}, nil, false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Check(s.repo.Architectures, DeepEquals, []string{"i386"})
|
c.Check(s.repo.Architectures, DeepEquals, []string{"i386"})
|
||||||
@@ -299,7 +338,7 @@ func (s *PublishedRepoSuite) TestPublish(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestPublishNoSigner(c *C) {
|
func (s *PublishedRepoSuite) TestPublishNoSigner(c *C) {
|
||||||
err := s.repo.Publish(s.packagePool, s.publishedStorage, s.factory, nil, nil)
|
err := s.repo.Publish(s.packagePool, s.provider, s.factory, nil, nil, false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/squeeze/Release"), PathExists)
|
||||||
@@ -307,7 +346,7 @@ func (s *PublishedRepoSuite) TestPublishNoSigner(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
|
func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
|
||||||
err := s.repo2.Publish(s.packagePool, s.publishedStorage, s.factory, nil, nil)
|
err := s.repo2.Publish(s.packagePool, s.provider, s.factory, nil, nil, false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
|
||||||
@@ -315,22 +354,30 @@ func (s *PublishedRepoSuite) TestPublishLocalRepo(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestPublishLocalSourceRepo(c *C) {
|
func (s *PublishedRepoSuite) TestPublishLocalSourceRepo(c *C) {
|
||||||
err := s.repo4.Publish(s.packagePool, s.publishedStorage, s.factory, nil, nil)
|
err := s.repo4.Publish(s.packagePool, s.provider, s.factory, nil, nil, false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/source/Release"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/main/source/Release"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PublishedRepoSuite) TestPublishOtherStorage(c *C) {
|
||||||
|
err := s.repo5.Publish(s.packagePool, s.provider, s.factory, nil, nil, false)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/maverick/Release"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/maverick/Release"), Not(PathExists))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestString(c *C) {
|
func (s *PublishedRepoSuite) TestString(c *C) {
|
||||||
c.Check(s.repo.String(), Equals,
|
c.Check(s.repo.String(), Equals,
|
||||||
"ppa/squeeze [] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
"ppa/squeeze [] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
||||||
c.Check(s.repo2.String(), Equals,
|
c.Check(s.repo2.String(), Equals,
|
||||||
"ppa/maverick [] publishes {main: [local1]: comment1}")
|
"ppa/maverick [] publishes {main: [local1]: comment1}")
|
||||||
repo, _ := NewPublishedRepo("", "squeeze", []string{"s390"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
repo, _ := NewPublishedRepo("", "", "squeeze", []string{"s390"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
||||||
c.Check(repo.String(), Equals,
|
c.Check(repo.String(), Equals,
|
||||||
"./squeeze [s390] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
"./squeeze [s390] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
||||||
repo, _ = NewPublishedRepo("", "squeeze", []string{"i386", "amd64"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
repo, _ = NewPublishedRepo("", "", "squeeze", []string{"i386", "amd64"}, []string{"main"}, []interface{}{s.snapshot}, s.factory)
|
||||||
c.Check(repo.String(), Equals,
|
c.Check(repo.String(), Equals,
|
||||||
"./squeeze [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
"./squeeze [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
||||||
repo.Origin = "myorigin"
|
repo.Origin = "myorigin"
|
||||||
@@ -341,10 +388,13 @@ func (s *PublishedRepoSuite) TestString(c *C) {
|
|||||||
"./squeeze (origin: myorigin, label: mylabel) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
"./squeeze (origin: myorigin, label: mylabel) [i386, amd64] publishes {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
||||||
c.Check(s.repo3.String(), Equals,
|
c.Check(s.repo3.String(), Equals,
|
||||||
"linux/natty [] publishes {contrib: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}, {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
"linux/natty [] publishes {contrib: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}, {main: [snap]: Snapshot from mirror [yandex]: http://mirror.yandex.ru/debian/ squeeze}")
|
||||||
|
c.Check(s.repo5.String(), Equals,
|
||||||
|
"files:other:ppa/maverick [source] publishes {main: [local1]: comment1}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestKey(c *C) {
|
func (s *PublishedRepoSuite) TestKey(c *C) {
|
||||||
c.Check(s.repo.Key(), DeepEquals, []byte("Uppa>>squeeze"))
|
c.Check(s.repo.Key(), DeepEquals, []byte("Uppa>>squeeze"))
|
||||||
|
c.Check(s.repo5.Key(), DeepEquals, []byte("Ufiles:other:ppa>>maverick"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoSuite) TestRefKey(c *C) {
|
func (s *PublishedRepoSuite) TestRefKey(c *C) {
|
||||||
@@ -372,13 +422,13 @@ func (s *PublishedRepoSuite) TestEncodeDecode(c *C) {
|
|||||||
|
|
||||||
type PublishedRepoCollectionSuite struct {
|
type PublishedRepoCollectionSuite struct {
|
||||||
PackageListMixinSuite
|
PackageListMixinSuite
|
||||||
db database.Storage
|
db database.Storage
|
||||||
factory *CollectionFactory
|
factory *CollectionFactory
|
||||||
snapshotCollection *SnapshotCollection
|
snapshotCollection *SnapshotCollection
|
||||||
collection *PublishedRepoCollection
|
collection *PublishedRepoCollection
|
||||||
snap1, snap2 *Snapshot
|
snap1, snap2 *Snapshot
|
||||||
localRepo *LocalRepo
|
localRepo *LocalRepo
|
||||||
repo1, repo2, repo3, repo4 *PublishedRepo
|
repo1, repo2, repo3, repo4, repo5 *PublishedRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Suite(&PublishedRepoCollectionSuite{})
|
var _ = Suite(&PublishedRepoCollectionSuite{})
|
||||||
@@ -398,10 +448,11 @@ func (s *PublishedRepoCollectionSuite) SetUpTest(c *C) {
|
|||||||
s.localRepo = NewLocalRepo("local1", "comment1")
|
s.localRepo = NewLocalRepo("local1", "comment1")
|
||||||
s.factory.LocalRepoCollection().Add(s.localRepo)
|
s.factory.LocalRepoCollection().Add(s.localRepo)
|
||||||
|
|
||||||
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
s.repo1, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
||||||
s.repo2, _ = NewPublishedRepo("", "anaconda", []string{}, []string{"main", "contrib"}, []interface{}{s.snap2, s.snap1}, s.factory)
|
s.repo2, _ = NewPublishedRepo("", "", "anaconda", []string{}, []string{"main", "contrib"}, []interface{}{s.snap2, s.snap1}, s.factory)
|
||||||
s.repo3, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap2}, s.factory)
|
s.repo3, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap2}, s.factory)
|
||||||
s.repo4, _ = NewPublishedRepo("ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
s.repo4, _ = NewPublishedRepo("", "ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "precise", []string{}, []string{"main"}, []interface{}{s.localRepo}, s.factory)
|
||||||
|
|
||||||
s.collection = s.factory.PublishedRepoCollection()
|
s.collection = s.factory.PublishedRepoCollection()
|
||||||
}
|
}
|
||||||
@@ -410,8 +461,8 @@ func (s *PublishedRepoCollectionSuite) TearDownTest(c *C) {
|
|||||||
s.db.Close()
|
s.db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoCollectionSuite) TestAddByPrefixDistribution(c *C) {
|
func (s *PublishedRepoCollectionSuite) TestAddByStoragePrefixDistribution(c *C) {
|
||||||
r, err := s.collection.ByPrefixDistribution("ppa", "anaconda")
|
r, err := s.collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Assert(err, ErrorMatches, "*.not found")
|
c.Assert(err, ErrorMatches, "*.not found")
|
||||||
|
|
||||||
c.Assert(s.collection.Add(s.repo1), IsNil)
|
c.Assert(s.collection.Add(s.repo1), IsNil)
|
||||||
@@ -421,8 +472,9 @@ func (s *PublishedRepoCollectionSuite) TestAddByPrefixDistribution(c *C) {
|
|||||||
c.Assert(s.collection.Add(s.repo3), ErrorMatches, ".*already exists")
|
c.Assert(s.collection.Add(s.repo3), ErrorMatches, ".*already exists")
|
||||||
c.Assert(s.collection.CheckDuplicate(s.repo3), Equals, s.repo1)
|
c.Assert(s.collection.CheckDuplicate(s.repo3), Equals, s.repo1)
|
||||||
c.Assert(s.collection.Add(s.repo4), IsNil)
|
c.Assert(s.collection.Add(s.repo4), IsNil)
|
||||||
|
c.Assert(s.collection.Add(s.repo5), IsNil)
|
||||||
|
|
||||||
r, err = s.collection.ByPrefixDistribution("ppa", "anaconda")
|
r, err = s.collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
err = s.collection.LoadComplete(r, s.factory)
|
err = s.collection.LoadComplete(r, s.factory)
|
||||||
@@ -430,12 +482,15 @@ func (s *PublishedRepoCollectionSuite) TestAddByPrefixDistribution(c *C) {
|
|||||||
c.Assert(r.String(), Equals, s.repo1.String())
|
c.Assert(r.String(), Equals, s.repo1.String())
|
||||||
|
|
||||||
collection := NewPublishedRepoCollection(s.db)
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
r, err = collection.ByPrefixDistribution("ppa", "anaconda")
|
r, err = collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
err = s.collection.LoadComplete(r, s.factory)
|
err = s.collection.LoadComplete(r, s.factory)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(r.String(), Equals, s.repo1.String())
|
c.Assert(r.String(), Equals, s.repo1.String())
|
||||||
|
|
||||||
|
r, err = s.collection.ByStoragePrefixDistribution("files:other", "ppa", "precise")
|
||||||
|
c.Check(r.String(), Equals, s.repo5.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoCollectionSuite) TestByUUID(c *C) {
|
func (s *PublishedRepoCollectionSuite) TestByUUID(c *C) {
|
||||||
@@ -457,14 +512,14 @@ func (s *PublishedRepoCollectionSuite) TestUpdateLoadComplete(c *C) {
|
|||||||
c.Assert(s.collection.Update(s.repo4), IsNil)
|
c.Assert(s.collection.Update(s.repo4), IsNil)
|
||||||
|
|
||||||
collection := NewPublishedRepoCollection(s.db)
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
r, err := collection.ByPrefixDistribution("ppa", "anaconda")
|
r, err := collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(r.sourceItems["main"].snapshot, IsNil)
|
c.Assert(r.sourceItems["main"].snapshot, IsNil)
|
||||||
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
|
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
|
||||||
c.Assert(r.Sources["main"], Equals, s.repo1.sourceItems["main"].snapshot.UUID)
|
c.Assert(r.Sources["main"], Equals, s.repo1.sourceItems["main"].snapshot.UUID)
|
||||||
c.Assert(r.RefList("main").Len(), Equals, 0)
|
c.Assert(r.RefList("main").Len(), Equals, 0)
|
||||||
|
|
||||||
r, err = collection.ByPrefixDistribution("ppa", "precise")
|
r, err = collection.ByStoragePrefixDistribution("", "ppa", "precise")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(r.sourceItems["main"].localRepo, IsNil)
|
c.Assert(r.sourceItems["main"].localRepo, IsNil)
|
||||||
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
|
c.Assert(s.collection.LoadComplete(r, s.factory), IsNil)
|
||||||
@@ -505,7 +560,7 @@ func (s *PublishedRepoCollectionSuite) TestLoadPre0_6(c *C) {
|
|||||||
c.Assert(s.db.Put(s.repo1.RefKey(""), s.localRepo.RefList().Encode()), IsNil)
|
c.Assert(s.db.Put(s.repo1.RefKey(""), s.localRepo.RefList().Encode()), IsNil)
|
||||||
|
|
||||||
collection := NewPublishedRepoCollection(s.db)
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
repo, err := collection.ByPrefixDistribution("ppa", "anaconda")
|
repo, err := collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(repo.Component, Equals, "")
|
c.Check(repo.Component, Equals, "")
|
||||||
c.Check(repo.SourceUUID, Equals, "")
|
c.Check(repo.SourceUUID, Equals, "")
|
||||||
@@ -548,20 +603,22 @@ func (s *PublishedRepoCollectionSuite) TestBySnapshot(c *C) {
|
|||||||
func (s *PublishedRepoCollectionSuite) TestByLocalRepo(c *C) {
|
func (s *PublishedRepoCollectionSuite) TestByLocalRepo(c *C) {
|
||||||
c.Check(s.collection.Add(s.repo1), IsNil)
|
c.Check(s.collection.Add(s.repo1), IsNil)
|
||||||
c.Check(s.collection.Add(s.repo4), IsNil)
|
c.Check(s.collection.Add(s.repo4), IsNil)
|
||||||
|
c.Check(s.collection.Add(s.repo5), IsNil)
|
||||||
|
|
||||||
c.Check(s.collection.ByLocalRepo(s.localRepo), DeepEquals, []*PublishedRepo{s.repo4})
|
c.Check(s.collection.ByLocalRepo(s.localRepo), DeepEquals, []*PublishedRepo{s.repo4, s.repo5})
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublishedRepoRemoveSuite struct {
|
type PublishedRepoRemoveSuite struct {
|
||||||
PackageListMixinSuite
|
PackageListMixinSuite
|
||||||
db database.Storage
|
db database.Storage
|
||||||
factory *CollectionFactory
|
factory *CollectionFactory
|
||||||
snapshotCollection *SnapshotCollection
|
snapshotCollection *SnapshotCollection
|
||||||
collection *PublishedRepoCollection
|
collection *PublishedRepoCollection
|
||||||
root string
|
root, root2 string
|
||||||
publishedStorage aptly.PublishedStorage
|
provider *FakeStorageProvider
|
||||||
snap1 *Snapshot
|
publishedStorage, publishedStorage2 *files.PublishedStorage
|
||||||
repo1, repo2, repo3, repo4 *PublishedRepo
|
snap1 *Snapshot
|
||||||
|
repo1, repo2, repo3, repo4, repo5 *PublishedRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Suite(&PublishedRepoRemoveSuite{})
|
var _ = Suite(&PublishedRepoRemoveSuite{})
|
||||||
@@ -576,16 +633,18 @@ func (s *PublishedRepoRemoveSuite) SetUpTest(c *C) {
|
|||||||
|
|
||||||
s.snapshotCollection.Add(s.snap1)
|
s.snapshotCollection.Add(s.snap1)
|
||||||
|
|
||||||
s.repo1, _ = NewPublishedRepo("ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
s.repo1, _ = NewPublishedRepo("", "ppa", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
||||||
s.repo2, _ = NewPublishedRepo("", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
s.repo2, _ = NewPublishedRepo("", "", "anaconda", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
||||||
s.repo3, _ = NewPublishedRepo("ppa", "meduza", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
s.repo3, _ = NewPublishedRepo("", "ppa", "meduza", []string{}, []string{"main"}, []interface{}{s.snap1}, s.factory)
|
||||||
s.repo4, _ = NewPublishedRepo("ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
|
s.repo4, _ = NewPublishedRepo("", "ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
|
||||||
|
s.repo5, _ = NewPublishedRepo("files:other", "ppa", "osminog", []string{}, []string{"contrib"}, []interface{}{s.snap1}, s.factory)
|
||||||
|
|
||||||
s.collection = s.factory.PublishedRepoCollection()
|
s.collection = s.factory.PublishedRepoCollection()
|
||||||
s.collection.Add(s.repo1)
|
s.collection.Add(s.repo1)
|
||||||
s.collection.Add(s.repo2)
|
s.collection.Add(s.repo2)
|
||||||
s.collection.Add(s.repo3)
|
s.collection.Add(s.repo3)
|
||||||
s.collection.Add(s.repo4)
|
s.collection.Add(s.repo4)
|
||||||
|
s.collection.Add(s.repo5)
|
||||||
|
|
||||||
s.root = c.MkDir()
|
s.root = c.MkDir()
|
||||||
s.publishedStorage = files.NewPublishedStorage(s.root)
|
s.publishedStorage = files.NewPublishedStorage(s.root)
|
||||||
@@ -596,6 +655,15 @@ func (s *PublishedRepoRemoveSuite) SetUpTest(c *C) {
|
|||||||
s.publishedStorage.MkDir("ppa/pool/contrib")
|
s.publishedStorage.MkDir("ppa/pool/contrib")
|
||||||
s.publishedStorage.MkDir("dists/anaconda")
|
s.publishedStorage.MkDir("dists/anaconda")
|
||||||
s.publishedStorage.MkDir("pool/main")
|
s.publishedStorage.MkDir("pool/main")
|
||||||
|
|
||||||
|
s.root2 = c.MkDir()
|
||||||
|
s.publishedStorage2 = files.NewPublishedStorage(s.root2)
|
||||||
|
s.publishedStorage2.MkDir("ppa/dists/osminog")
|
||||||
|
s.publishedStorage2.MkDir("ppa/pool/contrib")
|
||||||
|
|
||||||
|
s.provider = &FakeStorageProvider{map[string]aptly.PublishedStorage{
|
||||||
|
"": s.publishedStorage,
|
||||||
|
"files:other": s.publishedStorage2}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
|
func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
|
||||||
@@ -603,7 +671,7 @@ func (s *PublishedRepoRemoveSuite) TearDownTest(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
|
||||||
s.repo1.RemoveFiles(s.publishedStorage, false, []string{}, nil)
|
s.repo1.RemoveFiles(s.provider, false, []string{}, nil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
||||||
@@ -612,10 +680,12 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesOnlyDist(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
|
||||||
s.repo1.RemoveFiles(s.publishedStorage, false, []string{"main"}, nil)
|
s.repo1.RemoveFiles(s.provider, false, []string{"main"}, nil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
||||||
@@ -624,10 +694,12 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPool(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithTwoPools(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithTwoPools(c *C) {
|
||||||
s.repo1.RemoveFiles(s.publishedStorage, false, []string{"main", "contrib"}, nil)
|
s.repo1.RemoveFiles(s.provider, false, []string{"main", "contrib"}, nil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
||||||
@@ -636,10 +708,12 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithTwoPools(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
|
||||||
s.repo1.RemoveFiles(s.publishedStorage, true, []string{"main"}, nil)
|
s.repo1.RemoveFiles(s.provider, true, []string{"main"}, nil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), Not(PathExists))
|
||||||
@@ -648,10 +722,12 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefix(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
|
||||||
s.repo2.RemoveFiles(s.publishedStorage, true, []string{"main"}, nil)
|
s.repo2.RemoveFiles(s.provider, true, []string{"main"}, nil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
||||||
@@ -659,17 +735,19 @@ func (s *PublishedRepoRemoveSuite) TestRemoveFilesWithPrefixRoot(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), Not(PathExists))
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
|
||||||
err := s.collection.Remove(s.publishedStorage, "ppa", "anaconda", s.factory, nil)
|
err := s.collection.Remove(s.provider, "", "ppa", "anaconda", s.factory, nil, false)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
_, err = s.collection.ByPrefixDistribution("ppa", "anaconda")
|
_, err = s.collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Check(err, ErrorMatches, ".*not found")
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
collection := NewPublishedRepoCollection(s.db)
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
_, err = collection.ByPrefixDistribution("ppa", "anaconda")
|
_, err = collection.ByStoragePrefixDistribution("", "ppa", "anaconda")
|
||||||
c.Check(err, ErrorMatches, ".*not found")
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
@@ -679,11 +757,13 @@ func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
|
|
||||||
err = s.collection.Remove(s.publishedStorage, "ppa", "anaconda", s.factory, nil)
|
err = s.collection.Remove(s.provider, "", "ppa", "anaconda", s.factory, nil, false)
|
||||||
c.Check(err, ErrorMatches, ".*not found")
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
err = s.collection.Remove(s.publishedStorage, "ppa", "meduza", s.factory, nil)
|
err = s.collection.Remove(s.provider, "", "ppa", "meduza", s.factory, nil, false)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), Not(PathExists))
|
||||||
@@ -693,17 +773,19 @@ func (s *PublishedRepoRemoveSuite) TestRemoveRepo1and2(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/anaconda"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishedRepoRemoveSuite) TestRemoveRepo3(c *C) {
|
func (s *PublishedRepoRemoveSuite) TestRemoveRepo3(c *C) {
|
||||||
err := s.collection.Remove(s.publishedStorage, ".", "anaconda", s.factory, nil)
|
err := s.collection.Remove(s.provider, "", ".", "anaconda", s.factory, nil, false)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
_, err = s.collection.ByPrefixDistribution(".", "anaconda")
|
_, err = s.collection.ByStoragePrefixDistribution("", ".", "anaconda")
|
||||||
c.Check(err, ErrorMatches, ".*not found")
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
collection := NewPublishedRepoCollection(s.db)
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
_, err = collection.ByPrefixDistribution(".", "anaconda")
|
_, err = collection.ByStoragePrefixDistribution("", ".", "anaconda")
|
||||||
c.Check(err, ErrorMatches, ".*not found")
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
|
||||||
@@ -713,4 +795,28 @@ func (s *PublishedRepoRemoveSuite) TestRemoveRepo3(c *C) {
|
|||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/"), Not(PathExists))
|
||||||
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/"), Not(PathExists))
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/"), Not(PathExists))
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PublishedRepoRemoveSuite) TestRemoveRepo5(c *C) {
|
||||||
|
err := s.collection.Remove(s.provider, "files:other", "ppa", "osminog", s.factory, nil, false)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
|
_, err = s.collection.ByStoragePrefixDistribution("files:other", "ppa", "osminog")
|
||||||
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
|
collection := NewPublishedRepoCollection(s.db)
|
||||||
|
_, err = collection.ByStoragePrefixDistribution("files:other", "ppa", "osminog")
|
||||||
|
c.Check(err, ErrorMatches, ".*not found")
|
||||||
|
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/anaconda"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/meduza"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/dists/osminog"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/main"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "ppa/pool/contrib"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "dists/"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage.PublicPath(), "pool/"), PathExists)
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/dists/osminog"), Not(PathExists))
|
||||||
|
c.Check(filepath.Join(s.publishedStorage2.PublicPath(), "ppa/pool/contrib"), Not(PathExists))
|
||||||
}
|
}
|
||||||
|
|||||||
+267
@@ -0,0 +1,267 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PackageCatalog is abstraction on top of PackageCollection and PackageList
|
||||||
|
type PackageCatalog interface {
|
||||||
|
Scan(q PackageQuery) (result *PackageList)
|
||||||
|
Search(dep Dependency, allMatches bool) (searchResults []*Package)
|
||||||
|
SearchSupported() bool
|
||||||
|
SearchByKey(arch, name, version string) (result *PackageList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageQuery is interface of predicate on Package
|
||||||
|
type PackageQuery interface {
|
||||||
|
// Matches calculates match of condition against package
|
||||||
|
Matches(pkg *Package) bool
|
||||||
|
// Fast returns if search strategy is possible for this query
|
||||||
|
Fast(list PackageCatalog) bool
|
||||||
|
// Query performs search on package list
|
||||||
|
Query(list PackageCatalog) *PackageList
|
||||||
|
// String interface
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrQuery is L | R
|
||||||
|
type OrQuery struct {
|
||||||
|
L, R PackageQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// AndQuery is L , R
|
||||||
|
type AndQuery struct {
|
||||||
|
L, R PackageQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotQuery is ! Q
|
||||||
|
type NotQuery struct {
|
||||||
|
Q PackageQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldQuery is generic request against field
|
||||||
|
type FieldQuery struct {
|
||||||
|
Field string
|
||||||
|
Relation int
|
||||||
|
Value string
|
||||||
|
Regexp *regexp.Regexp `codec:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PkgQuery is search request against specific package
|
||||||
|
type PkgQuery struct {
|
||||||
|
Pkg string
|
||||||
|
Version string
|
||||||
|
Arch string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DependencyQuery is generic Debian-dependency like query
|
||||||
|
type DependencyQuery struct {
|
||||||
|
Dep Dependency
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches if any of L, R matches
|
||||||
|
func (q *OrQuery) Matches(pkg *Package) bool {
|
||||||
|
return q.L.Matches(pkg) || q.R.Matches(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast is true only if both parts are fast
|
||||||
|
func (q *OrQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return q.L.Fast(list) && q.R.Fast(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query strategy depends on nodes
|
||||||
|
func (q *OrQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
if q.Fast(list) {
|
||||||
|
result = q.L.Query(list)
|
||||||
|
result.Append(q.R.Query(list))
|
||||||
|
} else {
|
||||||
|
result = list.Scan(q)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *OrQuery) String() string {
|
||||||
|
return fmt.Sprintf("(%s) | (%s)", q.L, q.R)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches if both of L, R matches
|
||||||
|
func (q *AndQuery) Matches(pkg *Package) bool {
|
||||||
|
return q.L.Matches(pkg) && q.R.Matches(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast is true if any of the parts are fast
|
||||||
|
func (q *AndQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return q.L.Fast(list) || q.R.Fast(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query strategy depends on nodes
|
||||||
|
func (q *AndQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
if !q.Fast(list) {
|
||||||
|
result = list.Scan(q)
|
||||||
|
} else {
|
||||||
|
if q.L.Fast(list) {
|
||||||
|
result = q.L.Query(list)
|
||||||
|
result = result.Scan(q.R)
|
||||||
|
} else {
|
||||||
|
result = q.R.Query(list)
|
||||||
|
result = result.Scan(q.L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *AndQuery) String() string {
|
||||||
|
return fmt.Sprintf("(%s), (%s)", q.L, q.R)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches if not matches
|
||||||
|
func (q *NotQuery) Matches(pkg *Package) bool {
|
||||||
|
return !q.Q.Matches(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast is false
|
||||||
|
func (q *NotQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query strategy is scan always
|
||||||
|
func (q *NotQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
result = list.Scan(q)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *NotQuery) String() string {
|
||||||
|
return fmt.Sprintf("!(%s)", q.Q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches on generic field
|
||||||
|
func (q *FieldQuery) Matches(pkg *Package) bool {
|
||||||
|
if q.Field == "$Version" {
|
||||||
|
return pkg.MatchesDependency(Dependency{Pkg: pkg.Name, Relation: q.Relation, Version: q.Value, Regexp: q.Regexp})
|
||||||
|
}
|
||||||
|
if q.Field == "$Architecture" && q.Relation == VersionEqual {
|
||||||
|
return pkg.MatchesArchitecture(q.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
field := pkg.GetField(q.Field)
|
||||||
|
|
||||||
|
switch q.Relation {
|
||||||
|
case VersionDontCare:
|
||||||
|
return field != ""
|
||||||
|
case VersionEqual:
|
||||||
|
return field == q.Value
|
||||||
|
case VersionGreater:
|
||||||
|
return field > q.Value
|
||||||
|
case VersionGreaterOrEqual:
|
||||||
|
return field >= q.Value
|
||||||
|
case VersionLess:
|
||||||
|
return field < q.Value
|
||||||
|
case VersionLessOrEqual:
|
||||||
|
return field <= q.Value
|
||||||
|
case VersionPatternMatch:
|
||||||
|
matched, err := filepath.Match(q.Value, field)
|
||||||
|
return err == nil && matched
|
||||||
|
case VersionRegexp:
|
||||||
|
if q.Regexp == nil {
|
||||||
|
q.Regexp = regexp.MustCompile(q.Value)
|
||||||
|
}
|
||||||
|
return q.Regexp.FindStringIndex(field) != nil
|
||||||
|
|
||||||
|
}
|
||||||
|
panic("unknown relation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query runs iteration through list
|
||||||
|
func (q *FieldQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
result = list.Scan(q)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast depends on the query
|
||||||
|
func (q *FieldQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *FieldQuery) String() string {
|
||||||
|
escape := func(val string) string {
|
||||||
|
if strings.IndexAny(val, "()|,!{} \t\n") != -1 {
|
||||||
|
return "'" + strings.Replace(strings.Replace(val, "\\", "\\\\", -1), "'", "\\'", -1) + "'"
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
var op string
|
||||||
|
switch q.Relation {
|
||||||
|
case VersionEqual:
|
||||||
|
op = "="
|
||||||
|
case VersionGreater:
|
||||||
|
op = ">>"
|
||||||
|
case VersionLess:
|
||||||
|
op = "<<"
|
||||||
|
case VersionRegexp:
|
||||||
|
op = "~"
|
||||||
|
case VersionPatternMatch:
|
||||||
|
op = "%"
|
||||||
|
case VersionGreaterOrEqual:
|
||||||
|
op = ">="
|
||||||
|
case VersionLessOrEqual:
|
||||||
|
op = "<="
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (%s %s)", escape(q.Field), op, escape(q.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches on dependency condition
|
||||||
|
func (q *DependencyQuery) Matches(pkg *Package) bool {
|
||||||
|
return pkg.MatchesDependency(q.Dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast is always true for dependency query
|
||||||
|
func (q *DependencyQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return list.SearchSupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query runs PackageList.Search
|
||||||
|
func (q *DependencyQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
if q.Fast(list) {
|
||||||
|
result = NewPackageList()
|
||||||
|
for _, pkg := range list.Search(q.Dep, true) {
|
||||||
|
result.Add(pkg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = list.Scan(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *DependencyQuery) String() string {
|
||||||
|
return q.Dep.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches on specific properties
|
||||||
|
func (q *PkgQuery) Matches(pkg *Package) bool {
|
||||||
|
return pkg.Name == q.Pkg && pkg.Version == q.Version && pkg.Architecture == q.Arch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast is always true for package query
|
||||||
|
func (q *PkgQuery) Fast(list PackageCatalog) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query looks up specific package
|
||||||
|
func (q *PkgQuery) Query(list PackageCatalog) (result *PackageList) {
|
||||||
|
return list.SearchByKey(q.Arch, q.Pkg, q.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String interface
|
||||||
|
func (q *PkgQuery) String() string {
|
||||||
|
return fmt.Sprintf("%s_%s_%s", q.Pkg, q.Version, q.Arch)
|
||||||
|
}
|
||||||
+70
-13
@@ -2,6 +2,8 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/AlekSi/pointer"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
@@ -84,6 +86,29 @@ func (l *PackageRefList) ForEach(handler func([]byte) error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has checks whether package is part of reflist
|
||||||
|
func (l *PackageRefList) Has(p *Package) bool {
|
||||||
|
key := p.Key("")
|
||||||
|
|
||||||
|
i := sort.Search(len(l.Refs), func(j int) bool { return bytes.Compare(l.Refs[j], key) >= 0 })
|
||||||
|
return i < len(l.Refs) && bytes.Compare(l.Refs[i], key) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings builds list of strings with package keys
|
||||||
|
func (l *PackageRefList) Strings() []string {
|
||||||
|
if l == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, l.Len())
|
||||||
|
|
||||||
|
for i := 0; i < l.Len(); i++ {
|
||||||
|
result[i] = string(l.Refs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Substract returns all packages in l that are not in r
|
// Substract returns all packages in l that are not in r
|
||||||
func (l *PackageRefList) Substract(r *PackageRefList) *PackageRefList {
|
func (l *PackageRefList) Substract(r *PackageRefList) *PackageRefList {
|
||||||
result := &PackageRefList{Refs: make([][]byte, 0, 128)}
|
result := &PackageRefList{Refs: make([][]byte, 0, 128)}
|
||||||
@@ -131,6 +156,27 @@ type PackageDiff struct {
|
|||||||
Left, Right *Package
|
Left, Right *Package
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var (
|
||||||
|
_ json.Marshaler = PackageDiff{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface
|
||||||
|
func (d PackageDiff) MarshalJSON() ([]byte, error) {
|
||||||
|
serialized := struct {
|
||||||
|
Left, Right *string
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if d.Left != nil {
|
||||||
|
serialized.Left = pointer.ToString(string(d.Left.Key("")))
|
||||||
|
}
|
||||||
|
if d.Right != nil {
|
||||||
|
serialized.Right = pointer.ToString(string(d.Right.Key("")))
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(serialized)
|
||||||
|
}
|
||||||
|
|
||||||
// PackageDiffs is a list of PackageDiff records
|
// PackageDiffs is a list of PackageDiff records
|
||||||
type PackageDiffs []PackageDiff
|
type PackageDiffs []PackageDiff
|
||||||
|
|
||||||
@@ -224,8 +270,9 @@ func (l *PackageRefList) Diff(r *PackageRefList, packageCollection *PackageColle
|
|||||||
|
|
||||||
// Merge merges reflist r into current reflist. If overrideMatching, merge
|
// Merge merges reflist r into current reflist. If overrideMatching, merge
|
||||||
// replaces matching packages (by architecture/name) with reference from r.
|
// replaces matching packages (by architecture/name) with reference from r.
|
||||||
// Otherwise, all packages are saved.
|
// If ignoreConflicting is set, all packages are preserved, otherwise conflciting
|
||||||
func (l *PackageRefList) Merge(r *PackageRefList, overrideMatching bool) (result *PackageRefList) {
|
// packages are overwritten with packages from "right" snapshot.
|
||||||
|
func (l *PackageRefList) Merge(r *PackageRefList, overrideMatching, ignoreConflicting bool) (result *PackageRefList) {
|
||||||
var overriddenArch, overridenName []byte
|
var overriddenArch, overridenName []byte
|
||||||
|
|
||||||
// pointer to left and right reflists
|
// pointer to left and right reflists
|
||||||
@@ -262,13 +309,23 @@ func (l *PackageRefList) Merge(r *PackageRefList, overrideMatching bool) (result
|
|||||||
overridenName = nil
|
overridenName = nil
|
||||||
overriddenArch = nil
|
overriddenArch = nil
|
||||||
} else {
|
} else {
|
||||||
|
partsL := bytes.Split(rl, []byte(" "))
|
||||||
|
archL, nameL, versionL := partsL[0][1:], partsL[1], partsL[2]
|
||||||
|
|
||||||
|
partsR := bytes.Split(rr, []byte(" "))
|
||||||
|
archR, nameR, versionR := partsR[0][1:], partsR[1], partsR[2]
|
||||||
|
|
||||||
|
if !ignoreConflicting && bytes.Equal(archL, archR) && bytes.Equal(nameL, nameR) && bytes.Equal(versionL, versionR) {
|
||||||
|
// conflicting duplicates with same arch, name, version, but different file hash
|
||||||
|
result.Refs = append(result.Refs, r.Refs[ir])
|
||||||
|
il++
|
||||||
|
ir++
|
||||||
|
overridenName = nil
|
||||||
|
overriddenArch = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if overrideMatching {
|
if overrideMatching {
|
||||||
partsL := bytes.Split(rl, []byte(" "))
|
|
||||||
archL, nameL := partsL[0][1:], partsL[1]
|
|
||||||
|
|
||||||
partsR := bytes.Split(rr, []byte(" "))
|
|
||||||
archR, nameR := partsR[0][1:], partsR[1]
|
|
||||||
|
|
||||||
if bytes.Equal(archL, overriddenArch) && bytes.Equal(nameL, overridenName) {
|
if bytes.Equal(archL, overriddenArch) && bytes.Equal(nameL, overridenName) {
|
||||||
// this package has already been overriden on the right
|
// this package has already been overriden on the right
|
||||||
il++
|
il++
|
||||||
@@ -306,15 +363,15 @@ func (l *PackageRefList) Merge(r *PackageRefList, overrideMatching bool) (result
|
|||||||
// packages and reduces it to only the latest of each package. The operations
|
// packages and reduces it to only the latest of each package. The operations
|
||||||
// are done in-place. This implements a "latest wins" approach which can be used
|
// are done in-place. This implements a "latest wins" approach which can be used
|
||||||
// while merging two or more snapshots together.
|
// while merging two or more snapshots together.
|
||||||
func FilterLatestRefs(r *PackageRefList) {
|
func (l *PackageRefList) FilterLatestRefs() {
|
||||||
var (
|
var (
|
||||||
lastArch, lastName, lastVer []byte
|
lastArch, lastName, lastVer []byte
|
||||||
arch, name, ver []byte
|
arch, name, ver []byte
|
||||||
parts [][]byte
|
parts [][]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < len(r.Refs); i++ {
|
for i := 0; i < len(l.Refs); i++ {
|
||||||
parts = bytes.Split(r.Refs[i][1:], []byte(" "))
|
parts = bytes.Split(l.Refs[i][1:], []byte(" "))
|
||||||
arch, name, ver = parts[0], parts[1], parts[2]
|
arch, name, ver = parts[0], parts[1], parts[2]
|
||||||
|
|
||||||
if bytes.Equal(arch, lastArch) && bytes.Equal(name, lastName) {
|
if bytes.Equal(arch, lastArch) && bytes.Equal(name, lastName) {
|
||||||
@@ -324,10 +381,10 @@ func FilterLatestRefs(r *PackageRefList) {
|
|||||||
// Remove the older refs from the result
|
// Remove the older refs from the result
|
||||||
if vres > 0 {
|
if vres > 0 {
|
||||||
// ver[i] > ver[i-1], remove element i-1
|
// ver[i] > ver[i-1], remove element i-1
|
||||||
r.Refs = append(r.Refs[:i-1], r.Refs[i:]...)
|
l.Refs = append(l.Refs[:i-1], l.Refs[i:]...)
|
||||||
} else {
|
} else {
|
||||||
// ver[i] < ver[i-1], remove element i
|
// ver[i] < ver[i-1], remove element i
|
||||||
r.Refs = append(r.Refs[:i], r.Refs[i+1:]...)
|
l.Refs = append(l.Refs[:i], l.Refs[i+1:]...)
|
||||||
arch, name, ver = lastArch, lastName, lastVer
|
arch, name, ver = lastArch, lastName, lastVer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user