mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-01 04:40:38 +00:00
Compare commits
481 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35e2253944 | |||
| a584b2e058 | |||
| 587bfd742f | |||
| 84ef963d7d | |||
| e70ef0a518 | |||
| e05768737f | |||
| a626e4693b | |||
| 4d9b4298d8 | |||
| 4cca7272ce | |||
| e9b2c18e2f | |||
| cbb576cbcc | |||
| bcc83bff31 | |||
| 68da8a674a | |||
| cafa82f018 | |||
| 83a9c394f3 | |||
| 2c0a1b836c | |||
| 28ae18792d | |||
| 2811ad02d5 | |||
| ab20c2d329 | |||
| d137bcf8d4 | |||
| 3674e1adee | |||
| 05a5e69483 | |||
| 5e9515a912 | |||
| 84a6d573f8 | |||
| 6228a399cf | |||
| 0e9f966dd1 | |||
| 07fde3177b | |||
| f9377b2aa6 | |||
| 499ab35012 | |||
| 3c95f92b95 | |||
| d7a7aa93a4 | |||
| 58ab4e8902 | |||
| fcd453118b | |||
| 7d179dd405 | |||
| 20b874f81f | |||
| e3f1880ad4 | |||
| 39293d7faf | |||
| c13eb99925 | |||
| 211ac0501f | |||
| af2f7baf63 | |||
| 3c25db3ffb | |||
| 12a6b0ceb8 | |||
| 0d041898ca | |||
| 982c093fbf | |||
| f54e798eac | |||
| cafb89f30f | |||
| f0360cf2d3 | |||
| 1be8d39105 | |||
| c026106352 | |||
| c507d0620b | |||
| f84672239a | |||
| c9bd7b4b5d | |||
| 470165a419 | |||
| 9de9fbe6bd | |||
| e396a2e6c3 | |||
| 829ea2e65c | |||
| 39d2d273dc | |||
| 5a3e660c0d | |||
| 589dc93380 | |||
| 33357c1fe4 | |||
| 5ce6bf8718 | |||
| d7bcf372c4 | |||
| 3aa044d722 | |||
| a9a5a73dfd | |||
| 66b44e68a9 | |||
| 51213899b7 | |||
| 7a7c9cd26c | |||
| 1b9ab46c5f | |||
| 2cbed28446 | |||
| 39aa0fdbfe | |||
| c983810e2d | |||
| c798db8056 | |||
| 1e4a80252e | |||
| bae3f949b4 | |||
| 7a7b981d4f | |||
| 2ffefeb1e0 | |||
| 1941418c10 | |||
| 186bb2dff0 | |||
| 2308632683 | |||
| ee21b69402 | |||
| 01512df853 | |||
| 7dcc0d597d | |||
| 154ef7fe65 | |||
| 4601f07349 | |||
| b7b9f12c88 | |||
| b48e8425ec | |||
| 3ce8227122 | |||
| 0bc3f71d27 | |||
| c1d4c0fb88 | |||
| 8078f3b588 | |||
| 5dd11a2ec2 | |||
| cc34a021ce | |||
| 10c096fbb6 | |||
| a85d8b6f90 | |||
| 5566111a7b | |||
| 6994e35119 | |||
| 4eedb62418 | |||
| 1f3cb2db5d | |||
| c40025a335 | |||
| 4171a73995 | |||
| 29e5f4ca10 | |||
| 05f6c75743 | |||
| 45d187bc14 | |||
| bc7903f86e | |||
| 72d233b587 | |||
| 2535367c3c | |||
| f4ff8d957f | |||
| 7bad358408 | |||
| 94b49818a1 | |||
| a245b722a8 | |||
| 8dc6a14766 | |||
| d66185ca03 | |||
| c3acabe303 | |||
| 4697d8eaf8 | |||
| 8bf71a5561 | |||
| 898cbd2c83 | |||
| 62762f1616 | |||
| 4d38e0bc87 | |||
| 25f9c29f00 | |||
| 096b30b5e8 | |||
| ac475c0a10 | |||
| 60800b5f25 | |||
| 36a4d78162 | |||
| 50cf2b49bd | |||
| 675d35c7a1 | |||
| bc469eecfb | |||
| bc01d9ed5b | |||
| 7a5be6736d | |||
| eb48460b7b | |||
| 85b4a8b1ae | |||
| e6bad637fd | |||
| 47b5cc27c8 | |||
| ca16841223 | |||
| 800c5c1e06 | |||
| 7fd8bd0171 | |||
| 4707efe4d6 | |||
| 8ae61f9448 | |||
| a138d0111d | |||
| af1adb44ce | |||
| 4ddf85bbc1 | |||
| 9978595c59 | |||
| 2943422d5d | |||
| 91219e3a0a | |||
| 7f8db9087a | |||
| aa16899c60 | |||
| 16a0d0d428 | |||
| 66f51d2b17 | |||
| 92c844b8ac | |||
| 2b56a3937b | |||
| 9cea9b6470 | |||
| e3e68b9f22 | |||
| d56839664d | |||
| 516dd7b044 | |||
| 53e59d3765 | |||
| 11d828b3b1 | |||
| 07472bec50 | |||
| f737787c01 | |||
| c6c1012330 | |||
| 070347295e | |||
| acd8d4a6ea | |||
| c9768416ed | |||
| bfb9ffad1d | |||
| 9cfe1307e3 | |||
| db8595711b | |||
| ce0001f94c | |||
| b102562478 | |||
| 69cbe10690 | |||
| e3e4ea91bd | |||
| 02c582e227 | |||
| 6e96cd29dc | |||
| 17044f43dc | |||
| 5d3b170ffc | |||
| a0f7b2242d | |||
| b8e7ad9022 | |||
| 1b80d55ea4 | |||
| a0832adfa5 | |||
| f17d398e8f | |||
| bc3b2ed5a8 | |||
| 07cf8925f9 | |||
| 564ebf3130 | |||
| dbee214259 | |||
| 6267c5cb25 | |||
| 4c06e26d85 | |||
| f2dc4eeec9 | |||
| f86e6ebf1f | |||
| 0d208c93bc | |||
| 485f311498 | |||
| 46b0d637e2 | |||
| 5a71847b7f | |||
| 38a9917815 | |||
| 4456f8da57 | |||
| 970b1a424a | |||
| edffa24658 | |||
| 3040e7360a | |||
| b948180b4e | |||
| f58d2627c1 | |||
| ab0d77f6f9 | |||
| 33d6cd8c0a | |||
| 4eef4f1803 | |||
| c75d4c749c | |||
| c8a1b9a1f0 | |||
| d8d8973ad5 | |||
| d1ded5c224 | |||
| 155a801bc1 | |||
| 6212b39264 | |||
| 92116072c2 | |||
| b0ab39e07f | |||
| 4bf27d1dae | |||
| 207ebffbb8 | |||
| b0dd83335f | |||
| 8df6457931 | |||
| 7d2a396b27 | |||
| d5df049630 | |||
| 7c62a706c4 | |||
| 96948d6f18 | |||
| 43e6498713 | |||
| 91561b40f6 | |||
| 0e8ea6363a | |||
| 345fa02fdc | |||
| 064adbae57 | |||
| ab458f4dfc | |||
| 0fdee9cbf6 | |||
| 50e3e93166 | |||
| 570835227b | |||
| 781c22e256 | |||
| babccfa21f | |||
| 891113717e | |||
| bfb9045fa9 | |||
| 1c6b174b8a | |||
| fb27fb01ea | |||
| b6327ecc43 | |||
| af71b9541c | |||
| f31b5ec3f8 | |||
| 6becd5a3aa | |||
| 653255c728 | |||
| 5f0ce38161 | |||
| d100033b46 | |||
| d008cabf07 | |||
| f3214144a4 | |||
| d41841b84a | |||
| 2a95e0eb1b | |||
| 81f8ab2691 | |||
| 4e61db8d0f | |||
| 273d4cfa1b | |||
| d290950d4f | |||
| 2ade5b8a7f | |||
| fcd4429370 | |||
| 8e62620880 | |||
| 511fb439ac | |||
| 34ea7e8d61 | |||
| 543c986885 | |||
| f939532461 | |||
| aa4e225455 | |||
| 65541a1df2 | |||
| 0a74b50a12 | |||
| 902c6487da | |||
| 1d5b7f59cf | |||
| 1c45c79cc1 | |||
| 85c5aeddae | |||
| a95e409f52 | |||
| 53b571d6fc | |||
| 7a8af044ee | |||
| a667744502 | |||
| aa53b8da15 | |||
| 52f7c83f95 | |||
| d7665119e4 | |||
| 587086beb4 | |||
| 644d24d1cc | |||
| 2fe8cfdc12 | |||
| 2ecd933d50 | |||
| 90ea1111e2 | |||
| 165a1c53b5 | |||
| 876935050a | |||
| d9a1299f6b | |||
| ff52d2655a | |||
| bc438ff694 | |||
| 0db3cac281 | |||
| 9ed6e8dbbd | |||
| 7294241c08 | |||
| 60cca0245b | |||
| 75b860e0b1 | |||
| 7f5a7323a6 | |||
| 1069458aee | |||
| 76edf9649b | |||
| 8b0d293c6a | |||
| 281d0dd68d | |||
| cfaa8f3881 | |||
| f1b6841757 | |||
| b966b2eabf | |||
| a4e573bb07 | |||
| 067d197dac | |||
| 18d04c7977 | |||
| a29453805c | |||
| 05b1296144 | |||
| 29e33069aa | |||
| ee05bb23c9 | |||
| 505da096e6 | |||
| 77be7b9e3b | |||
| ffafed472c | |||
| 8c9cc41099 | |||
| f50e008763 | |||
| 64b04c2764 | |||
| d6c7a9a89c | |||
| 0339f0fe23 | |||
| fcedaa3fc5 | |||
| 7acfc84c9d | |||
| 02b937ad17 | |||
| 7ad1c1ad17 | |||
| 640bd2b530 | |||
| 06149ef2bb | |||
| b271e8fe31 | |||
| efc6ab27db | |||
| 05c063839d | |||
| fd30b37a0e | |||
| 9738687116 | |||
| 219315c01d | |||
| 62f44e53fd | |||
| b25f8e438c | |||
| f14fce01e9 | |||
| a790770a19 | |||
| 7bb052ac37 | |||
| 631fe44c6b | |||
| ca319c804e | |||
| 3e368690fd | |||
| 339bf0a90b | |||
| c5b48f0362 | |||
| d5f50732c1 | |||
| 9d973aeceb | |||
| 7caeac7515 | |||
| 7f6a52019f | |||
| 16101b56fe | |||
| a294a91685 | |||
| cf644289a3 | |||
| 33c905ce02 | |||
| 8fdc222196 | |||
| 1e4d825d36 | |||
| 76bf7cba04 | |||
| 08bc5ac934 | |||
| c160cbccc7 | |||
| c473a5cba8 | |||
| 84801bce78 | |||
| b95b3473bf | |||
| f1d5caab8b | |||
| 6a973554ad | |||
| 698e239f45 | |||
| 205297d0b8 | |||
| ba4669a9c4 | |||
| 8bda799545 | |||
| 6c28e3aca8 | |||
| 901babe500 | |||
| 0c6f38ab08 | |||
| a131d6093c | |||
| 974cec3e73 | |||
| 442c5f090f | |||
| d04f08c1cf | |||
| 767c7ca0db | |||
| dd27aad751 | |||
| ddfdeaf2d0 | |||
| 4c51350517 | |||
| 40e48c963a | |||
| c44d347540 | |||
| 4a54bff225 | |||
| e39736153d | |||
| f032196d70 | |||
| a030e24b96 | |||
| c2993c6691 | |||
| 7d4a70ba25 | |||
| 38dfe3435a | |||
| 313c71dff6 | |||
| a88d92436f | |||
| 9d298dee51 | |||
| 9abc772b16 | |||
| 2f1df39204 | |||
| 0f328ec1fe | |||
| 78b6d6ca7b | |||
| 5cd3c33854 | |||
| 9af76843b5 | |||
| 53506124a4 | |||
| 9bbf9c7b13 | |||
| 82e6e8242e | |||
| 2bf11a556c | |||
| c62828bf14 | |||
| b53cf7e710 | |||
| 780277d0a6 | |||
| a6f5631542 | |||
| 52b1501ec0 | |||
| c9339f5cca | |||
| a9c23fb4aa | |||
| 72e3eaebfe | |||
| f3bcaa6cfb | |||
| 1c8f1517f8 | |||
| 50ae34cc19 | |||
| 8cc7d1345b | |||
| 0791c88a02 | |||
| ba08ffe38b | |||
| 1bec1e4dc4 | |||
| bcf8074f31 | |||
| 6a2d564eee | |||
| 709e14ecc1 | |||
| 5b1f446a6b | |||
| f41146c750 | |||
| d56ac81fd6 | |||
| fb213ef6eb | |||
| 933b019f71 | |||
| 6293ca3206 | |||
| d46d8de5f7 | |||
| 4e3284cd98 | |||
| 10876b99f5 | |||
| 61d31ce7c0 | |||
| e0f284d68f | |||
| df887d871b | |||
| 99f6ffe1ca | |||
| 138f9f7994 | |||
| 3886db9d4f | |||
| b877e06a02 | |||
| 38f4fc209b | |||
| b223acdecb | |||
| cc8a87b448 | |||
| ee3d414ed5 | |||
| d791aa0f15 | |||
| 393ae8adbd | |||
| 7037c6be7e | |||
| c10645f4f2 | |||
| 27da1015af | |||
| 78b0fe0e90 | |||
| 4651e41247 | |||
| a6c40f3193 | |||
| 3e138fd6db | |||
| 3c20b5472e | |||
| 8b782ce370 | |||
| a160a39d53 | |||
| 1c4b44e772 | |||
| b4b03f2752 | |||
| 1d21d3cfeb | |||
| d2ce33e66a | |||
| f0fbb8259b | |||
| 962c4a842d | |||
| 54e21afee7 | |||
| cc3f5149c6 | |||
| c8713aa412 | |||
| 02a82f3545 | |||
| c573746896 | |||
| 813b9593fa | |||
| bc68513708 | |||
| c4692bec3d | |||
| c53060d95a | |||
| 22c656d18e | |||
| 4d622e467c | |||
| 36326788b0 | |||
| 782ac1a36a | |||
| 8ca07d9acd | |||
| 4a57fe3c39 | |||
| 7579f1998c | |||
| 67a31d5eaa | |||
| 5b9d287b62 | |||
| 775670181c | |||
| 2a3bd5546a | |||
| 197e230ef1 | |||
| c6eeac11a4 | |||
| 90d3b623b4 | |||
| a59c2ac859 | |||
| 103fa5310f | |||
| 71b7de7a63 | |||
| a937ebc744 | |||
| 925882b253 | |||
| 615a5ee3f9 | |||
| 4a6d6a85f7 | |||
| 2937435960 | |||
| 2f3b5f5a51 | |||
| 5b4563f250 | |||
| 5da4bde428 | |||
| 42c4644be3 | |||
| 1845c493f4 | |||
| 8a0f754fe2 | |||
| 77bb4d423d | |||
| 1d483dc817 | |||
| a7103623af | |||
| 903e999cdc | |||
| 69eff97b34 | |||
| 8e20daa927 | |||
| 9e39dbf81e |
@@ -0,0 +1,14 @@
|
|||||||
|
<!--- Provide a general summary of the issue in the Title above -->
|
||||||
|
|
||||||
|
## Detailed Description
|
||||||
|
<!--- Provide a detailed description of the change or addition you are proposing -->
|
||||||
|
|
||||||
|
## Context
|
||||||
|
<!--- Why is this change important to you? How would you use it? -->
|
||||||
|
<!--- How can it benefit other users? -->
|
||||||
|
|
||||||
|
## Possible Implementation
|
||||||
|
<!--- Not obligatory, but suggest an idea for implementing addition or change -->
|
||||||
|
|
||||||
|
## Your Environment
|
||||||
|
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Fixes #
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
All new code should be covered with tests, documentation should be updated. CI should pass.
|
||||||
|
|
||||||
|
## Description of the Change
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Why this change is important?
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] unit-test added (if change is algorithm)
|
||||||
|
- [ ] functional test added/updated (if change is functional)
|
||||||
|
- [ ] man page updated (if applicable)
|
||||||
|
- [ ] bash completion updated (if applicable)
|
||||||
|
- [ ] documentation updated
|
||||||
|
- [ ] author name in `AUTHORS`
|
||||||
+6
-2
@@ -27,8 +27,12 @@ coverage*.out
|
|||||||
|
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
_vendor/
|
xc-out/
|
||||||
|
root/
|
||||||
|
|
||||||
gen
|
|
||||||
man/aptly.1.html
|
man/aptly.1.html
|
||||||
man/aptly.1.ronn
|
man/aptly.1.ronn
|
||||||
|
|
||||||
|
.goxc.local.json
|
||||||
|
|
||||||
|
system/env/
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"AppName": "aptly",
|
||||||
|
"ArtifactsDest": "xc-out/",
|
||||||
|
"TasksExclude": [
|
||||||
|
"rmbin",
|
||||||
|
"go-test",
|
||||||
|
"go-vet"
|
||||||
|
],
|
||||||
|
"TasksAppend": [
|
||||||
|
"bintray"
|
||||||
|
],
|
||||||
|
"TaskSettings": {
|
||||||
|
"debs": {
|
||||||
|
"metadata": {
|
||||||
|
"maintainer": "Andrey Smirnov",
|
||||||
|
"maintainer-email": "me@smira.ru",
|
||||||
|
"description": "Debian repository management tool"
|
||||||
|
},
|
||||||
|
"metadata-deb": {
|
||||||
|
"License": "MIT",
|
||||||
|
"Homepage": "https://www.aptly.info/",
|
||||||
|
"Depends": "bzip2, xz-utils, gnupg, gpgv",
|
||||||
|
"Suggests": "graphviz"
|
||||||
|
},
|
||||||
|
"other-mapped-files": {
|
||||||
|
"/": "root/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bintray": {
|
||||||
|
"repository": "aptly",
|
||||||
|
"subject": "smira",
|
||||||
|
"package": "aptly",
|
||||||
|
"downloadspage": "bintray.md"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResourcesInclude": "README.rst,LICENSE,AUTHORS,man/aptly.1",
|
||||||
|
"Arch": "386 amd64",
|
||||||
|
"Os": "linux darwin freebsd",
|
||||||
|
"MainDirsExclude": "_man,vendor",
|
||||||
|
"BuildSettings": {
|
||||||
|
"LdFlagsXVars": {
|
||||||
|
"Version": "main.Version"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConfigVersion": "0.9"
|
||||||
|
}
|
||||||
+22
-10
@@ -1,8 +1,21 @@
|
|||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.3.3
|
- 1.6.x
|
||||||
- tip
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- master
|
||||||
|
|
||||||
|
go_import_path: github.com/smira/aptly
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- python-virtualenv
|
||||||
|
- graphviz
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
@@ -10,21 +23,20 @@ env:
|
|||||||
- secure: "V7OjWrfQ8UbktgT036jYQPb/7GJT3Ol9LObDr8FYlzsQ+F1uj2wLac6ePuxcOS4FwWOJinWGM1h+JiFkbxbyFqfRNJ0jj0O2p93QyDojxFVOn1mXqqvV66KFqAWR2Vzkny/gDvj8LTvdB1cgAIm2FNOkQc6E1BFnyWS2sN9ea5E="
|
- secure: "V7OjWrfQ8UbktgT036jYQPb/7GJT3Ol9LObDr8FYlzsQ+F1uj2wLac6ePuxcOS4FwWOJinWGM1h+JiFkbxbyFqfRNJ0jj0O2p93QyDojxFVOn1mXqqvV66KFqAWR2Vzkny/gDvj8LTvdB1cgAIm2FNOkQc6E1BFnyWS2sN9ea5E="
|
||||||
- secure: "OxiVNmre2JzUszwPNNilKDgIqtfX2gnRSsVz6nuySB1uO2yQsOQmKWJ9cVYgH2IB5H8eWXKOhexcSE28kz6TPLRuEcU9fnqKY3uEkdwm7rJfz9lf+7C4bJEUdA1OIzJppjnWUiXxD7CEPL1DlnMZM24eDQYqa/4WKACAgkK53gE="
|
- secure: "OxiVNmre2JzUszwPNNilKDgIqtfX2gnRSsVz6nuySB1uO2yQsOQmKWJ9cVYgH2IB5H8eWXKOhexcSE28kz6TPLRuEcU9fnqKY3uEkdwm7rJfz9lf+7C4bJEUdA1OIzJppjnWUiXxD7CEPL1DlnMZM24eDQYqa/4WKACAgkK53gE="
|
||||||
before_install:
|
before_install:
|
||||||
- sudo apt-get update -qq
|
- virtualenv system/env
|
||||||
- sudo apt-get install -y python-virtualenv graphviz
|
- . system/env/bin/activate
|
||||||
- virtualenv env
|
- pip install six packaging appdirs
|
||||||
- . env/bin/activate
|
- pip install -U pip setuptools
|
||||||
- pip install boto requests python-swiftclient
|
- pip install -r system/requirements.txt
|
||||||
|
- make version
|
||||||
install:
|
install:
|
||||||
- make prepare
|
- make prepare
|
||||||
|
|
||||||
|
|
||||||
script: make travis
|
script: make travis
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: master
|
||||||
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
webhooks:
|
webhooks:
|
||||||
|
|||||||
@@ -15,3 +15,16 @@ List of contributors, in chronological order:
|
|||||||
* Michael Koval (https://github.com/mkoval)
|
* Michael Koval (https://github.com/mkoval)
|
||||||
* Alexander Guy (https://github.com/alexanderguy)
|
* Alexander Guy (https://github.com/alexanderguy)
|
||||||
* Sebastien Badia (https://github.com/sbadia)
|
* Sebastien Badia (https://github.com/sbadia)
|
||||||
|
* Szymon Sobik (https://github.com/sobczyk)
|
||||||
|
* Paul Krohn (https://github.com/paul-krohn)
|
||||||
|
* Vincent Bernat (https://github.com/vincentbernat)
|
||||||
|
* x539 (https://github.com/x539)
|
||||||
|
* Phil Frost (https://github.com/bitglue)
|
||||||
|
* Benoit Foucher (https://github.com/bentoi)
|
||||||
|
* Geoffrey Thomas (https://github.com/geofft)
|
||||||
|
* Oliver Sauder (https://github.com/sliverc)
|
||||||
|
* Harald Sitter (https://github.com/apachelogger)
|
||||||
|
* Johannes Layher (https://github.com/jola5)
|
||||||
|
* Charles Hsu (https://github.com/charz)
|
||||||
|
* Clemens Rabe (https://github.com/seeraven)
|
||||||
|
* TJ Merritt (https://github.com/tjmerritt)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity and
|
||||||
|
orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at [team@aptly.info](mailto:team@aptly.info). All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
+239
@@ -0,0 +1,239 @@
|
|||||||
|
# Contributing to aptly
|
||||||
|
|
||||||
|
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||||
|
|
||||||
|
The following is a set of guidelines for contributing to [aptly](https://github.com/smira/aplty) and related repositories, which are hosted in the [aptly-dev Organization](https://github.com/aptly-dev) on GitHub.
|
||||||
|
These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||||
|
|
||||||
|
## What should I know before I get started?
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md).
|
||||||
|
By participating, you are expected to uphold this code.
|
||||||
|
Please report unacceptable behavior to [team@aptly.info](mailto:team@aptly.info).
|
||||||
|
|
||||||
|
### List of Repositories
|
||||||
|
|
||||||
|
* [smira/aptly](https://github.com/smira/aptly) - aptly source code, functional tests, man page
|
||||||
|
* [apty-dev/aptly-dev.github.io](https://github.com/aptly-dev/aptly-dev.github.io) - aptly website (https://www.aptly.info/)
|
||||||
|
* [aptly-dev/aptly-fixture-db](https://github.com/aptly-dev/aptly-fixture-db) & [aptly-dev/aptly-fixture-pool](https://github.com/aptly-dev/aptly-fixture-pool) provide
|
||||||
|
fixtures for aptly functional tests
|
||||||
|
|
||||||
|
## How Can I Contribute?
|
||||||
|
|
||||||
|
### Reporting Bugs
|
||||||
|
|
||||||
|
1. Please search for similar bug report in [issue tracker](https://github.com/smira/aptly/issues)
|
||||||
|
2. Please verify that bug is not fixed in latest aptly nightly ([download information](https://www.aptly.info/download/))
|
||||||
|
3. Steps to reproduce increases chances for bug to be fixed quickly. If possible, submit PR with new functional test which fails.
|
||||||
|
4. If bug is reproducible with specific package, please provide link to package file.
|
||||||
|
5. Open issue at [GitHub](https://github.com/smira/aptly/issues)
|
||||||
|
|
||||||
|
### Suggesting Enhancements
|
||||||
|
|
||||||
|
1. Please search [issue tracker](https://github.com/smira/aptly/issues) for similar feature requests.
|
||||||
|
2. Describe why enhancement is important to you.
|
||||||
|
3. Include any additional details or implementation details.
|
||||||
|
|
||||||
|
### Improving Documentation
|
||||||
|
|
||||||
|
There are two kinds of documentation:
|
||||||
|
|
||||||
|
* [aptly website](https://www.aptly/info)
|
||||||
|
* aptly `man` page
|
||||||
|
|
||||||
|
Core content is mostly the same, but website contains more information, tutorials, examples.
|
||||||
|
|
||||||
|
If you want to update `man` page, please open PR to [main aptly repo](https://github.com/smira/aptly),
|
||||||
|
details in [man page](#man-page) section.
|
||||||
|
|
||||||
|
If you want to update website, please follow steps below:
|
||||||
|
|
||||||
|
1. Install [hugo](http://gohugo.io/)
|
||||||
|
2. Fork [website source](https://github.com/aptly-dev/aptly-dev.github.io) and clone it
|
||||||
|
3. Launch hugo in development mode: `hugo -w server`
|
||||||
|
4. Navigate to `http://localhost:1313/`: you should see aptly website
|
||||||
|
5. Update documentation, most of the time editing Markdown is all you need.
|
||||||
|
6. Page in browser should reload automatically as you make changes to source files.
|
||||||
|
|
||||||
|
We're always looking for new contributions to [FAQ](https://www.aptly.info/doc/faq/), [tutorials](https://www.aptly.info/tutorial/),
|
||||||
|
general fixes, clarifications, misspellings, grammar mistakes!
|
||||||
|
|
||||||
|
### Your Fist Code Contribution
|
||||||
|
|
||||||
|
Please follow [next section](#development-setup) on development process. When change is ready, please submit PR
|
||||||
|
following [PR template](.github/PULL_REQUEST_TEMPLATE.md).
|
||||||
|
|
||||||
|
Make sure that purpose of your change is clear, all the tests and checks pass, and all new code is covered with tests
|
||||||
|
if that is possible.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
This section describes local setup to start contributing to aptly source.
|
||||||
|
|
||||||
|
### Go & Python
|
||||||
|
|
||||||
|
You would need `Go` (latest version is recommended) and `Python` 2.7.x (3.x is not supported yet).
|
||||||
|
|
||||||
|
If you're new to Go, follow [getting started guide](https://golang.org/doc/install) to install it and perform
|
||||||
|
initial setup. With Go 1.8+, default `$GOPATH` is `$HOME/go`, so rest of this document assumes that.
|
||||||
|
|
||||||
|
Usually `$GOPATH/bin` is appended to your `$PATH` to make it easier to run built binaries, but you might choose
|
||||||
|
to prepend it or to skip this test if you're security conscious.
|
||||||
|
|
||||||
|
### Forking and Cloning
|
||||||
|
|
||||||
|
As Go is using repository path in import paths, it's better to clone aptly repo (not your fork) at default location:
|
||||||
|
|
||||||
|
mkdir -p ~/go/src/github.com/smira
|
||||||
|
cd ~/go/src/github.com/smira
|
||||||
|
git clone git@github.com:smira/aptly.git
|
||||||
|
cd aptly
|
||||||
|
|
||||||
|
For main repo under your GitHub user and add it as another Git remote:
|
||||||
|
|
||||||
|
git remote add <user> git@github.com:<user>/aptly.git
|
||||||
|
|
||||||
|
That way you can continue to build project as is (you don't need to adjust import paths), but you would need
|
||||||
|
to specify your remote name when pushing branches:
|
||||||
|
|
||||||
|
git push <user> <your-branch>
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
You would need some additional tools and Python virtual environment to run tests and checks, install them with:
|
||||||
|
|
||||||
|
make prepare dev system/env
|
||||||
|
|
||||||
|
This is usually one-time action.
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
If you want to build aptly binary from your current source tree, run:
|
||||||
|
|
||||||
|
make install
|
||||||
|
|
||||||
|
This would build `aptly` in `$GOPATH/bin`, so depending on your `$PATH`, you should be able to run it immediately with:
|
||||||
|
|
||||||
|
aptly
|
||||||
|
|
||||||
|
Or, if it's not on your path:
|
||||||
|
|
||||||
|
~/go/bin/aptly
|
||||||
|
|
||||||
|
### Unit-tests
|
||||||
|
|
||||||
|
aptly has two kinds of tests: unit-tests and functional (system) tests. Functional tests are preferred way to test any
|
||||||
|
feature, but some features are much easier to test with unit-tests (e.g. algorithms, failure scenarios, ...)
|
||||||
|
|
||||||
|
aptly is using standard Go unit-test infrastructure plus [gocheck](http://labix.org/gocheck). Run the unit-tests with:
|
||||||
|
|
||||||
|
make test
|
||||||
|
|
||||||
|
### Functional Tests
|
||||||
|
|
||||||
|
Functional tests are implemented in Python, and they use custom test runner which is similar to Python unit-test
|
||||||
|
runner. Most of the tests start with clean aptly state, run some aptly commands to prepare environment, and finally
|
||||||
|
run some aptly commands capturing output, exit code, checking any additional files being created and so on. API tests
|
||||||
|
are a bit different, as they re-use same aptly process serving API requests.
|
||||||
|
|
||||||
|
The easiest way to run functional tests is to use `make`:
|
||||||
|
|
||||||
|
make system-test
|
||||||
|
|
||||||
|
This would check all the dependencies and run all the tests. Some tests (S3, Swift) require access credentials to
|
||||||
|
be set up in the environment. For example, it needs AWS credentials to run S3 tests (they would be used to publish to S3).
|
||||||
|
If credentials are missing, tests would be skipped.
|
||||||
|
|
||||||
|
You can also run subset of tests manually:
|
||||||
|
|
||||||
|
system/run.py t04_mirror
|
||||||
|
|
||||||
|
This would run all the mirroring tests under `system/t04_mirror` folder.
|
||||||
|
|
||||||
|
Or you can run tests by test name mask:
|
||||||
|
|
||||||
|
system/run.py UpdateMirror*
|
||||||
|
|
||||||
|
Or, you can run specific test by name:
|
||||||
|
|
||||||
|
system/run.py UpdateMirror7Test
|
||||||
|
|
||||||
|
Test runner can update expected output instead of failing on mismatch (this is especially useful while
|
||||||
|
working on new tests):
|
||||||
|
|
||||||
|
system/run.py --capture <test>
|
||||||
|
|
||||||
|
Output for some tests might contain environment-specific things, e.g. your home directory. In that case
|
||||||
|
you can use `${HOME}` and similar variable expansion in expected output files.
|
||||||
|
|
||||||
|
Some tests depend on fixtures, for example pre-populated GPG trusted keys. There are also test fixtures
|
||||||
|
captured after mirror update which contain pre-build aptly database and pool contents. They're useful if you
|
||||||
|
don't want to waste time in the test on populating aptly database while you need some packages to work with.
|
||||||
|
There are some packages available under `system/files/` directory which are used to build contents of local repos.
|
||||||
|
|
||||||
|
*WARNING*: tests are running under current `$HOME` directory with aptly default settings, so they clear completely
|
||||||
|
`~/.aptly.conf` and `~/.aptly` subdirectory between the runs. So it's not wise to have non-dev aptly being used with
|
||||||
|
this default location. You can run aptly under different user or by using non-default config location with non-default
|
||||||
|
aptly root directory.
|
||||||
|
|
||||||
|
### Style Checks
|
||||||
|
|
||||||
|
Style checks could be run with:
|
||||||
|
|
||||||
|
make check
|
||||||
|
|
||||||
|
aptly is using [gometalinter](https://github.com/alecthomas/gometalinter) to run style checks on Go code. Configuration
|
||||||
|
for the linter could be found in [linter.json](linter.json) file. Running linters might take considerable amount of time
|
||||||
|
unfortunately, but usually warning reported by linters hint at real code issues.
|
||||||
|
|
||||||
|
Python code (system tests) are linted with [flake8 tool](https://pypi.python.org/pypi/flake8).
|
||||||
|
|
||||||
|
### Vendored Code
|
||||||
|
|
||||||
|
aptly is using Go vendoring for all the libraries aptly depends upon. `vendor/` directory is checked into the source
|
||||||
|
repository to avoid any problems if source repositories go away. Go build process will automatically prefer vendored
|
||||||
|
packages over packages in `$GOPATH`.
|
||||||
|
|
||||||
|
If you want to update vendored dependencies or to introduce new dependency, use [dep tool](https://github.com/golang/dep).
|
||||||
|
Usually all you need is `dep ensure` or `dep ensure -update`.
|
||||||
|
|
||||||
|
### man Page
|
||||||
|
|
||||||
|
aptly is using combination of [Go templates](http://godoc.org/text/template) and automatically generated text to build `aptly.1` man page. If either source
|
||||||
|
template [man/aptly.1.ronn.tmpl](man/aptly.1.ronn.tmpl) is changed or any command help is changed, run `make man` to regenerate
|
||||||
|
final rendered man page [man/aptly.1](man/aptly.1). In the end of the build, new man page is displayed for visual
|
||||||
|
verification.
|
||||||
|
|
||||||
|
Man page is built with small helper [_man/gen.go](man/gen.go) which pulls in template, command-line help from [cmd/](cmd/) folder
|
||||||
|
and runs that through [forked copy](https://github.com/smira/ronn) of [ronn](https://github.com/rtomayko/ronn).
|
||||||
|
|
||||||
|
### Bash Completion
|
||||||
|
|
||||||
|
Bash completion for aptly resides in the same repo under in [bash_completion.d/aptly](bash_completion.d/aptly). It's all hand-crafted.
|
||||||
|
When new option or command is introduced, bash completion should be updated to reflect that change.
|
||||||
|
|
||||||
|
When aptly package is being built, it automatically pulls bash completion and man page into the package.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
This section requires future work.
|
||||||
|
|
||||||
|
*TBD*
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
### Package Pool
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
### PackageList, PackageRefList
|
||||||
|
|
||||||
|
### LocalRepo, RemoteRepo, Snapshot
|
||||||
|
|
||||||
|
### PublishedRepository
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
### Collections, CollectionFactory
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
|
|
||||||
gom 'code.google.com/p/gographviz', :commit => '454bc64fdfa2'
|
|
||||||
gom 'code.google.com/p/mxk/go1/flowcontrol', :commit => '5ff2502e2556'
|
|
||||||
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
|
|
||||||
gom 'github.com/AlekSi/pointer', :commit => '5f6d527dae3d678b46fbb20331ddf44e2b841943'
|
|
||||||
gom 'github.com/cheggaaa/pb', :commit => '2c1b74620cc58a81ac152ee2d322e28c806d81ed'
|
|
||||||
gom 'github.com/gin-gonic/gin', :commit => 'b1758d3bfa09e61ddbc1c9a627e936eec6a170de'
|
|
||||||
gom 'github.com/jlaffaye/ftp', :commit => 'fec71e62e457557fbe85cefc847a048d57815d76'
|
|
||||||
gom 'github.com/julienschmidt/httprouter', :commit => '46807412fe50aaceb73bb57061c2230fd26a1640'
|
|
||||||
gom 'github.com/mattn/go-shellwords', :commit => 'c7ca6f94add751566a61cf2199e1de78d4c3eee4'
|
|
||||||
gom 'github.com/mitchellh/goamz/s3', :commit => 'e7664b32019f31fd1bdf33f9e85f28722f700405'
|
|
||||||
gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
|
|
||||||
gom 'github.com/ncw/swift', :commit => '384ef27c70645e285f8bb9d02276bf654d06027e'
|
|
||||||
gom 'github.com/smira/commander', :commit => 'f408b00e68d5d6e21b9f18bd310978dafc604e47'
|
|
||||||
gom 'github.com/smira/flag', :commit => '357ed3e599ffcbd4aeaa828e1d10da2df3ea5107'
|
|
||||||
gom 'github.com/smira/go-ftp-protocol/protocol', :commit => '066b75c2b70dca7ae10b1b88b47534a3c31ccfaa'
|
|
||||||
gom 'github.com/syndtr/gosnappy/snappy', :commit => 'ce8acff4829e0c2458a67ead32390ac0a381c862'
|
|
||||||
gom 'github.com/syndtr/goleveldb/leveldb', :commit => '97e257099d2ab9578151ba85e2641e2cd14d3ca8'
|
|
||||||
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
|
|
||||||
gom 'github.com/vaughan0/go-ini', :commit => 'a98ad7ee00ec53921f08832bc06ecf7fd600e6a1'
|
|
||||||
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
|
|
||||||
gom 'golang.org/x/crypto/ssh/terminal', :commit => 'a7ead6ddf06233883deca151dffaef2effbf498f'
|
|
||||||
|
|
||||||
group :test do
|
|
||||||
gom 'gopkg.in/check.v1'
|
|
||||||
end
|
|
||||||
|
|
||||||
group :development do
|
|
||||||
gom 'github.com/golang/lint/golint'
|
|
||||||
gom 'github.com/mattn/goveralls'
|
|
||||||
gom 'github.com/axw/gocov/gocov'
|
|
||||||
gom 'golang.org/x/tools/cmd/cover'
|
|
||||||
end
|
|
||||||
Generated
+191
@@ -0,0 +1,191 @@
|
|||||||
|
memo = "becdf010a814559719c990c1bd645c737cee332ad52004c440605c13de100d45"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/AlekSi/pointer"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "08a25bac605b3fcb6cc27f3917b2c2c87451963d"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/DisposaBoy/JsonConfigReader"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/awalterschulze/gographviz"
|
||||||
|
packages = [".","ast","parser","scanner","token"]
|
||||||
|
revision = "761fd5fbb34e4c2c138c280395b65b48e4ff5a53"
|
||||||
|
version = "v1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/aws/aws-sdk-go"
|
||||||
|
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"]
|
||||||
|
revision = "2db5849d2939d93075d911138309a83235032bea"
|
||||||
|
version = "v1.8.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/cheggaaa/pb"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "cdf719fac0dd208251aa828e687c2d5802053b51"
|
||||||
|
version = "v1.0.10"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gin-gonic/gin"
|
||||||
|
packages = [".","binding","render"]
|
||||||
|
revision = "b1758d3bfa09e61ddbc1c9a627e936eec6a170de"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-ini/ini"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "1730955e3146956d6a087861380f9b4667ed5071"
|
||||||
|
version = "v1.26.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/golang/snappy"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/h2non/filetype"
|
||||||
|
packages = ["matchers"]
|
||||||
|
revision = "0df83c38d14ff5f653d419d480eaac286ccbc823"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/jlaffaye/ftp"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "7b85eb4638a2c0473acefcfb929a98f879c15c86"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/jmespath/go-jmespath"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9"
|
||||||
|
version = "0.2.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/julienschmidt/httprouter"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "8c199fb6259ffc1af525cc3ad52ee60ba8359669"
|
||||||
|
version = "v1.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/mattn/go-runewidth"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||||
|
version = "v0.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/mattn/go-shellwords"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "005a0944d84452842197c2108bd9168ced206f78"
|
||||||
|
version = "v1.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mkrautz/goar"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "282caa8bd9daba480b51f1d5a988714913b97aad"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mxk/go-flowrate"
|
||||||
|
packages = ["flowrate"]
|
||||||
|
revision = "cca7078d478f8520f85629ad7c68962d31ed7682"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/ncw/swift"
|
||||||
|
packages = [".","swifttest"]
|
||||||
|
revision = "8e9b10220613abdbc2896808ee6b43e411a4fa6c"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pkg/errors"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||||
|
version = "v0.8.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/commander"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "f408b00e68d5d6e21b9f18bd310978dafc604e47"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/flag"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "357ed3e599ffcbd4aeaa828e1d10da2df3ea5107"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-aws-auth"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0070896e9d7f4f9f2d558532b2d896ce2239992a"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-ftp-protocol"
|
||||||
|
packages = ["protocol"]
|
||||||
|
revision = "066b75c2b70dca7ae10b1b88b47534a3c31ccfaa"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-uuid"
|
||||||
|
packages = ["uuid"]
|
||||||
|
revision = "ed3ca8a15a931b141440a7e98e4f716eec255f7d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-xz"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0c531f070014e218b21f3cfca801cc992d52726d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/lzma"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "7f0af6269940baa2c938fabe73e0d7ba41205683"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/syndtr/goleveldb"
|
||||||
|
packages = ["leveldb","leveldb/cache","leveldb/comparer","leveldb/errors","leveldb/filter","leveldb/iterator","leveldb/journal","leveldb/memdb","leveldb/opt","leveldb/storage","leveldb/table","leveldb/util"]
|
||||||
|
revision = "3c5717caf1475fd25964109a0fc640bd150fce43"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/ugorji/go"
|
||||||
|
packages = ["codec"]
|
||||||
|
revision = "71c2886f5a673a35f909803f38ece5810165097b"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/wsxiaoys/terminal"
|
||||||
|
packages = ["color"]
|
||||||
|
revision = "0940f3fc43a0ed42d04916b1c04578462c650b09"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
packages = ["cast5","openpgp","openpgp/armor","openpgp/clearsign","openpgp/elgamal","openpgp/errors","openpgp/packet","openpgp/s2k","ssh/terminal"]
|
||||||
|
revision = "459e26527287adbc2adcc5d0d49abff9a5f315a7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
revision = "99f16d856c9836c42d24e7ab64ea72916925fa97"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v1"
|
||||||
|
name = "gopkg.in/check.v1"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/h2non/filetype.v1"
|
||||||
|
packages = ["types"]
|
||||||
|
revision = "3093b8ebec6efb56ac813238b8beab4ed4eaac6a"
|
||||||
|
version = "v1.0.1"
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
name = "github.com/gin-gonic/gin"
|
||||||
|
revision = "b1758d3bfa09e61ddbc1c9a627e936eec6a170de"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mkrautz/goar"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-uuid"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/smira/go-xz"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
name = "github.com/ugorji/go"
|
||||||
|
revision = "71c2886f5a673a35f909803f38ece5810165097b"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
|
||||||
|
[[dependencies]]
|
||||||
|
branch = "v1"
|
||||||
|
name = "gopkg.in/check.v1"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2013-2014 Andrey Smirnov. All rights reserved.
|
Copyright 2013-2015 aptly authors. All rights reserved.
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
|||||||
@@ -1,86 +1,85 @@
|
|||||||
GOVERSION=$(shell go version | awk '{print $$3;}')
|
GOVERSION=$(shell go version | awk '{print $$3;}')
|
||||||
PACKAGES=context database deb files http query swift s3 utils
|
VERSION=$(shell git describe --tags | sed 's@^v@@' | sed 's@-@+@g')
|
||||||
ALL_PACKAGES=api aptly context cmd console database deb files http query swift s3 utils
|
PACKAGES=context database deb files gpg http query swift s3 utils
|
||||||
BINPATH=$(abspath ./_vendor/bin)
|
|
||||||
GOM_ENVIRONMENT=-test
|
|
||||||
PYTHON?=python
|
PYTHON?=python
|
||||||
|
TESTS?=
|
||||||
|
BINPATH?=$(GOPATH)/bin
|
||||||
|
|
||||||
ifeq ($(GOVERSION), devel)
|
ifeq ($(GOVERSION), devel)
|
||||||
TRAVIS_TARGET=coveralls
|
TRAVIS_TARGET=coveralls
|
||||||
GOM_ENVIRONMENT+=-development
|
|
||||||
else
|
else
|
||||||
TRAVIS_TARGET=test
|
TRAVIS_TARGET=test
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(TRAVIS), true)
|
|
||||||
GOM=$(HOME)/gopath/bin/gom
|
|
||||||
else
|
|
||||||
GOM=gom
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: test check system-test
|
all: test check system-test
|
||||||
|
|
||||||
prepare:
|
prepare:
|
||||||
go get -u github.com/mattn/gom
|
go get -u github.com/mattn/goveralls
|
||||||
$(GOM) $(GOM_ENVIRONMENT) install
|
go get -u github.com/axw/gocov/gocov
|
||||||
|
go get -u golang.org/x/tools/cmd/cover
|
||||||
|
go get -u github.com/alecthomas/gometalinter
|
||||||
|
gometalinter --install
|
||||||
|
|
||||||
|
dev:
|
||||||
|
go get -u github.com/golang/dep/...
|
||||||
|
go get -u github.com/laher/goxc
|
||||||
|
|
||||||
coverage.out:
|
coverage.out:
|
||||||
rm -f coverage.*.out
|
rm -f coverage.*.out
|
||||||
for i in $(PACKAGES); do $(GOM) test -coverprofile=coverage.$$i.out -covermode=count ./$$i; done
|
for i in $(PACKAGES); do go test -coverprofile=coverage.$$i.out -covermode=count ./$$i; done
|
||||||
echo "mode: count" > coverage.out
|
echo "mode: count" > coverage.out
|
||||||
grep -v -h "mode: count" coverage.*.out >> coverage.out
|
grep -v -h "mode: count" coverage.*.out >> coverage.out
|
||||||
rm -f coverage.*.out
|
rm -f coverage.*.out
|
||||||
|
|
||||||
coverage: coverage.out
|
coverage: coverage.out
|
||||||
$(GOM) exec go tool cover -html=coverage.out
|
go tool cover -html=coverage.out
|
||||||
rm -f coverage.out
|
rm -f coverage.out
|
||||||
|
|
||||||
check:
|
check: system/env
|
||||||
$(GOM) exec go tool vet -all=true $(ALL_PACKAGES:%=./%)
|
if [ -x travis_wait ]; then \
|
||||||
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
|
travis_wait gometalinter --config=linter.json ./...; \
|
||||||
|
else \
|
||||||
|
gometalinter --config=linter.json ./...; \
|
||||||
|
fi
|
||||||
|
. system/env/bin/activate && flake8 --max-line-length=200 --exclude=system/env/ system/
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(GOM) build -o $(BINPATH)/aptly
|
go install -v -ldflags "-X main.Version=$(VERSION)"
|
||||||
|
|
||||||
system-test: install
|
system/env: system/requirements.txt
|
||||||
|
rm -rf system/env
|
||||||
|
virtualenv system/env
|
||||||
|
system/env/bin/pip install -r system/requirements.txt
|
||||||
|
|
||||||
|
system-test: install system/env
|
||||||
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
|
||||||
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
|
. system/env/bin/activate && APTLY_VERSION=$(VERSION) PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long $(TESTS)
|
||||||
|
|
||||||
travis: $(TRAVIS_TARGET) system-test
|
travis: $(TRAVIS_TARGET) check system-test
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GOM) test -v ./... -gocheck.v=true
|
go test -v `go list ./... | grep -v vendor/` -gocheck.v=true
|
||||||
|
|
||||||
coveralls: coverage.out
|
coveralls: coverage.out
|
||||||
$(GOM) exec $(BINPATH)/goveralls -service travis-ci.org -coverprofile=coverage.out -repotoken=$(COVERALLS_TOKEN)
|
$(BINPATH)/goveralls -service travis-ci.org -coverprofile=coverage.out -repotoken=$(COVERALLS_TOKEN)
|
||||||
|
|
||||||
mem.png: mem.dat mem.gp
|
mem.png: mem.dat mem.gp
|
||||||
gnuplot mem.gp
|
gnuplot mem.gp
|
||||||
open mem.png
|
open mem.png
|
||||||
|
|
||||||
package:
|
goxc:
|
||||||
rm -rf root/
|
rm -rf root/
|
||||||
mkdir -p root/usr/bin/ root/usr/share/man/man1/ root/etc/bash_completion.d
|
mkdir -p root/usr/share/man/man1/ root/etc/bash_completion.d
|
||||||
cp $(BINPATH)/aptly root/usr/bin
|
|
||||||
cp man/aptly.1 root/usr/share/man/man1
|
cp man/aptly.1 root/usr/share/man/man1
|
||||||
(cd root/etc/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/master/aptly)
|
cp bash_completion.d/aptly root/etc/bash_completion.d
|
||||||
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>" \
|
goxc -pv=$(VERSION) -max-processors=4 $(GOXC_OPTS)
|
||||||
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" --deb-recommends bzip2 --deb-recommends graphviz -C root/ .
|
|
||||||
mv aptly_$(VERSION)_*.deb ~
|
|
||||||
|
|
||||||
src-package:
|
man:
|
||||||
rm -rf aptly-$(VERSION)
|
make -C man
|
||||||
mkdir -p aptly-$(VERSION)/src/github.com/smira/aptly/
|
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/ && git clone https://github.com/smira/aptly && cd aptly && git checkout v$(VERSION)
|
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/aptly && gom -production install
|
|
||||||
cd aptly-$(VERSION)/src/github.com/smira/aptly && find . \( -name .git -o -name .bzr -o -name .hg \) -print | xargs rm -rf
|
|
||||||
rm -rf aptly-$(VERSION)/src/github.com/smira/aptly/_vendor/{pkg,bin}
|
|
||||||
mkdir -p aptly-$(VERSION)/bash_completion.d
|
|
||||||
(cd aptly-$(VERSION)/bash_completion.d && wget https://raw.github.com/aptly-dev/aptly-bash-completion/$(VERSION)/aptly)
|
|
||||||
tar cyf aptly-$(VERSION)-src.tar.bz2 aptly-$(VERSION)
|
|
||||||
rm -rf aptly-$(VERSION)
|
|
||||||
curl -T aptly-$(VERSION)-src.tar.bz2 -usmira:$(BINTRAY_KEY) https://api.bintray.com/content/smira/aptly/aptly/$(VERSION)/$(VERSION)/aptly-$(VERSION)-src.tar.bz2
|
|
||||||
|
|
||||||
.PHONY: coverage.out
|
version:
|
||||||
|
@echo $(VERSION)
|
||||||
|
|
||||||
|
.PHONY: coverage.out man version
|
||||||
|
|||||||
+48
-15
@@ -2,24 +2,24 @@
|
|||||||
aptly
|
aptly
|
||||||
=====
|
=====
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/smira/aptly.png?branch=master
|
.. image:: https://api.travis-ci.org/smira/aptly.svg?branch=master
|
||||||
:target: https://travis-ci.org/smira/aptly
|
:target: https://travis-ci.org/smira/aptly
|
||||||
|
|
||||||
.. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD
|
.. image:: https://coveralls.io/repos/smira/aptly/badge.svg?branch=master
|
||||||
:target: https://coveralls.io/r/smira/aptly?branch=HEAD
|
:target: https://coveralls.io/r/smira/aptly?branch=master
|
||||||
|
|
||||||
.. image:: https://badges.gitter.im/Join Chat.svg
|
.. 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
|
: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
|
.. image:: http://goreportcard.com/badge/smira/aptly
|
||||||
:target: http://goreportcard.com/report/gojp/goreportcard
|
:target: http://goreportcard.com/report/smira/aptly
|
||||||
|
|
||||||
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
|
.. image:: http://www.aptly.info/img/aptly_logo.png
|
||||||
:target: http://www.aptly.info/
|
: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 please 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>`_.
|
||||||
|
|
||||||
Aptly features: ("+" means planned features)
|
Aptly features: ("+" means planned features)
|
||||||
@@ -42,13 +42,13 @@ Current limitations:
|
|||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
|
||||||
To install aptly on Debian/Ubuntu, add new repository to /etc/apt/sources.list::
|
To install aptly on Debian/Ubuntu, add new repository to ``/etc/apt/sources.list``::
|
||||||
|
|
||||||
deb http://repo.aptly.info/ squeeze main
|
deb http://repo.aptly.info/ squeeze main
|
||||||
|
|
||||||
And import key that is used to sign the release::
|
And import key that is used to sign the release::
|
||||||
|
|
||||||
$ apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
|
$ apt-key adv --keyserver keys.gnupg.net --recv-keys 9E3E53F19C7DE460
|
||||||
|
|
||||||
After that you can install aptly as any other software package::
|
After that you can install aptly as any other software package::
|
||||||
|
|
||||||
@@ -64,18 +64,51 @@ If you would like to use nightly builds (unstable), please use following reposit
|
|||||||
|
|
||||||
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.3+ required)::
|
If you have Go environment set up, you can build aptly from source by running (go 1.6+ required)::
|
||||||
|
|
||||||
go get -u github.com/mattn/gom
|
|
||||||
mkdir -p $GOPATH/src/github.com/smira/aptly
|
mkdir -p $GOPATH/src/github.com/smira/aptly
|
||||||
git clone https://github.com/smira/aptly $GOPATH/src/github.com/smira/aptly
|
git clone https://github.com/smira/aptly $GOPATH/src/github.com/smira/aptly
|
||||||
cd $GOPATH/src/github.com/smira/aptly
|
cd $GOPATH/src/github.com/smira/aptly
|
||||||
gom -production install
|
make install
|
||||||
gom build -o $GOPATH/bin/aptly
|
|
||||||
|
|
||||||
Aptly is using `gom <https://github.com/mattn/gom>`_ to fix external dependencies, so regular ``go get github.com/smira/aptly``
|
Binary would be installed to ```$GOPATH/bin/aptly``.
|
||||||
should work as well, but might fail or produce different result (if external libraries got updated).
|
|
||||||
|
|
||||||
If you don't have Go installed (or older version), you can easily install Go using `gvm <https://github.com/moovweb/gvm/>`_.
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
Please follow detailed documentation in `CONTRIBUTING.md <CONTRIBUTING.md>`_.
|
||||||
|
|
||||||
|
Integrations
|
||||||
|
------------
|
||||||
|
|
||||||
|
Vagrant:
|
||||||
|
|
||||||
|
- `Vagrant configuration <https://github.com/sepulworld/aptly-vagrant>`_ by
|
||||||
|
Zane Williamson, allowing to bring two virtual servers, one with aptly installed
|
||||||
|
and another one set up to install packages from repository published by aptly
|
||||||
|
|
||||||
|
Docker:
|
||||||
|
|
||||||
|
- `Docker container <https://github.com/mikepurvis/aptly-docker>`_ with aptly inside by Mike Purvis
|
||||||
|
- `Docker container <https://github.com/bryanhong/docker-aptly>`_ with aptly and nginx by Bryan Hong
|
||||||
|
|
||||||
|
With configuration management systems:
|
||||||
|
|
||||||
|
- `Chef cookbook <https://github.com/hw-cookbooks/aptly>`_ by Aaron Baer
|
||||||
|
(Heavy Water Operations, LLC)
|
||||||
|
- `Puppet module <https://github.com/alphagov/puppet-aptly>`_ by
|
||||||
|
Government Digital Services
|
||||||
|
- `Puppet module <https://github.com/tubemogul/puppet-aptly>`_ by
|
||||||
|
TubeMogul
|
||||||
|
- `SaltStack Formula <https://github.com/saltstack-formulas/aptly-formula>`_ by
|
||||||
|
Forrest Alvarez and Brian Jackson
|
||||||
|
- `Ansible role <https://github.com/aioue/ansible-role-aptly>`_ by Tom Paine
|
||||||
|
|
||||||
|
CLI for aptly API:
|
||||||
|
|
||||||
|
- `Ruby aptly CLI/library <https://github.com/sepulworld/aptly_cli>`_ by Zane Williamson
|
||||||
|
- `Python aptly CLI (good for CI) <https://github.com/TimSusa/aptly_api_cli>`_ by Tim Susa
|
||||||
|
|
||||||
|
Scala sbt:
|
||||||
|
|
||||||
|
- `sbt aptly plugin <https://github.com/amalakar/sbt-aptly>`_ by Arup Malakar
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/cmd"
|
"io/ioutil"
|
||||||
"github.com/smira/commander"
|
|
||||||
"github.com/smira/flag"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/cmd"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func allFlags(flags *flag.FlagSet) []*flag.Flag {
|
func allFlags(flags *flag.FlagSet) []*flag.Flag {
|
||||||
@@ -43,22 +44,42 @@ func capitalize(s string) string {
|
|||||||
return strings.Join(parts, " ")
|
return strings.Join(parts, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var authorsS string
|
||||||
|
|
||||||
|
func authors() string {
|
||||||
|
return authorsS
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
command := cmd.RootCommand()
|
command := cmd.RootCommand()
|
||||||
command.UsageLine = "aptly"
|
command.UsageLine = "aptly"
|
||||||
command.Dispatch(nil)
|
command.Dispatch(nil)
|
||||||
|
|
||||||
_, _File, _, _ := runtime.Caller(0)
|
_File, _ := filepath.Abs("./man")
|
||||||
_File, _ = filepath.Abs(_File)
|
|
||||||
|
|
||||||
templ := template.New("man").Funcs(template.FuncMap{
|
templ := template.New("man").Funcs(template.FuncMap{
|
||||||
"allFlags": allFlags,
|
"allFlags": allFlags,
|
||||||
"findCommand": findCommand,
|
"findCommand": findCommand,
|
||||||
"toUpper": strings.ToUpper,
|
"toUpper": strings.ToUpper,
|
||||||
"capitalize": capitalize,
|
"capitalize": capitalize,
|
||||||
|
"authors": authors,
|
||||||
})
|
})
|
||||||
template.Must(templ.ParseFiles(filepath.Join(filepath.Dir(_File), "aptly.1.ronn.tmpl")))
|
template.Must(templ.ParseFiles(filepath.Join(filepath.Dir(_File), "aptly.1.ronn.tmpl")))
|
||||||
|
|
||||||
|
authorsF, err := os.Open(filepath.Join(filepath.Dir(_File), "..", "AUTHORS"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authorsB, err := ioutil.ReadAll(authorsF)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authorsF.Close()
|
||||||
|
|
||||||
|
authorsS = string(authorsB)
|
||||||
|
|
||||||
output, err := os.Create(filepath.Join(filepath.Dir(_File), "aptly.1.ronn"))
|
output, err := os.Create(filepath.Join(filepath.Dir(_File), "aptly.1.ronn"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
+62
-10
@@ -3,12 +3,13 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lock order acquisition (canonical):
|
// Lock order acquisition (canonical):
|
||||||
@@ -22,16 +23,20 @@ func apiVersion(c *gin.Context) {
|
|||||||
c.JSON(200, gin.H{"Version": aptly.Version})
|
c.JSON(200, gin.H{"Version": aptly.Version})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periodically flushes CollectionFactory to free up memory used by collections,
|
type dbRequestKind int
|
||||||
// flushing caches.
|
|
||||||
//
|
|
||||||
// Should be run in goroutine!
|
|
||||||
func cacheFlusher() {
|
|
||||||
ticker := time.Tick(15 * time.Minute)
|
|
||||||
|
|
||||||
for {
|
const (
|
||||||
<-ticker
|
acquiredb dbRequestKind = iota
|
||||||
|
releasedb
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbRequest struct {
|
||||||
|
kind dbRequestKind
|
||||||
|
err chan<- error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flushes all collections which cache in-memory objects
|
||||||
|
func flushColections() {
|
||||||
// lock everything to eliminate in-progress calls
|
// lock everything to eliminate in-progress calls
|
||||||
r := context.CollectionFactory().RemoteRepoCollection()
|
r := context.CollectionFactory().RemoteRepoCollection()
|
||||||
r.Lock()
|
r.Lock()
|
||||||
@@ -51,6 +56,52 @@ func cacheFlusher() {
|
|||||||
|
|
||||||
// all collections locked, flush them
|
// all collections locked, flush them
|
||||||
context.CollectionFactory().Flush()
|
context.CollectionFactory().Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
flushColections()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire database lock and release it when not needed anymore.
|
||||||
|
//
|
||||||
|
// Should be run in a goroutine!
|
||||||
|
func acquireDatabase(requests <-chan dbRequest) {
|
||||||
|
clients := 0
|
||||||
|
for request := range requests {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch request.kind {
|
||||||
|
case acquiredb:
|
||||||
|
if clients == 0 {
|
||||||
|
err = context.ReOpenDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
request.err <- err
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
clients++
|
||||||
|
}
|
||||||
|
case releasedb:
|
||||||
|
clients--
|
||||||
|
if clients == 0 {
|
||||||
|
flushColections()
|
||||||
|
err = context.CloseDatabase()
|
||||||
|
} else {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
request.err <- err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +148,7 @@ func showPackages(c *gin.Context, reflist *deb.PackageRefList) {
|
|||||||
nil, context.DependencyOptions(), architecturesList)
|
nil, context.DependencyOptions(), architecturesList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, fmt.Errorf("unable to search: %s", err))
|
c.Fail(500, fmt.Errorf("unable to search: %s", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -2,11 +2,12 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func verifyPath(path string) bool {
|
func verifyPath(path string) bool {
|
||||||
|
|||||||
+13
-4
@@ -3,15 +3,16 @@ package api
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/smira/aptly/deb"
|
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GET /api/graph.:ext
|
// GET /api/graph.:ext?layout=[vertical|horizontal(default)]
|
||||||
func apiGraph(c *gin.Context) {
|
func apiGraph(c *gin.Context) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@@ -19,6 +20,7 @@ func apiGraph(c *gin.Context) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
ext := c.Params.ByName("ext")
|
ext := c.Params.ByName("ext")
|
||||||
|
layout := c.Request.URL.Query().Get("layout")
|
||||||
|
|
||||||
factory := context.CollectionFactory()
|
factory := context.CollectionFactory()
|
||||||
|
|
||||||
@@ -31,7 +33,7 @@ func apiGraph(c *gin.Context) {
|
|||||||
factory.PublishedRepoCollection().RLock()
|
factory.PublishedRepoCollection().RLock()
|
||||||
defer factory.PublishedRepoCollection().RUnlock()
|
defer factory.PublishedRepoCollection().RUnlock()
|
||||||
|
|
||||||
graph, err := deb.BuildGraph(factory)
|
graph, err := deb.BuildGraph(factory, layout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, err)
|
c.JSON(500, err)
|
||||||
return
|
return
|
||||||
@@ -39,6 +41,13 @@ func apiGraph(c *gin.Context) {
|
|||||||
|
|
||||||
buf := bytes.NewBufferString(graph.String())
|
buf := bytes.NewBufferString(graph.String())
|
||||||
|
|
||||||
|
if ext == "dot" || ext == "gv" {
|
||||||
|
// If the raw dot data is requested, return it as string.
|
||||||
|
// This allows client-side rendering rather than server-side.
|
||||||
|
c.String(200, buf.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
command := exec.Command("dot", "-T"+ext)
|
command := exec.Command("dot", "-T"+ext)
|
||||||
command.Stderr = os.Stderr
|
command.Stderr = os.Stderr
|
||||||
|
|
||||||
|
|||||||
+38
-10
@@ -2,10 +2,12 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SigningOptions is a shared between publish API GPG options structure
|
// SigningOptions is a shared between publish API GPG options structure
|
||||||
@@ -19,12 +21,12 @@ type SigningOptions struct {
|
|||||||
PassphraseFile string
|
PassphraseFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSigner(options *SigningOptions) (utils.Signer, error) {
|
func getSigner(options *SigningOptions) (pgp.Signer, error) {
|
||||||
if options.Skip {
|
if options.Skip {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := &utils.GpgSigner{}
|
signer := context.GetSigner()
|
||||||
signer.SetKey(options.GpgKey)
|
signer.SetKey(options.GpgKey)
|
||||||
signer.SetKeyRing(options.Keyring, options.SecretKeyring)
|
signer.SetKeyRing(options.Keyring, options.SecretKeyring)
|
||||||
signer.SetPassphrase(options.Passphrase, options.PassphraseFile)
|
signer.SetPassphrase(options.Passphrase, options.PassphraseFile)
|
||||||
@@ -96,7 +98,10 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
Distribution string
|
Distribution string
|
||||||
Label string
|
Label string
|
||||||
Origin string
|
Origin string
|
||||||
|
NotAutomatic string
|
||||||
|
ButAutomaticUpgrades string
|
||||||
ForceOverwrite bool
|
ForceOverwrite bool
|
||||||
|
SkipContents *bool
|
||||||
Architectures []string
|
Architectures []string
|
||||||
Signing SigningOptions
|
Signing SigningOptions
|
||||||
}
|
}
|
||||||
@@ -143,7 +148,7 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
|
|
||||||
sources = append(sources, snapshot)
|
sources = append(sources, snapshot)
|
||||||
}
|
}
|
||||||
} else if b.SourceKind == "local" {
|
} else if b.SourceKind == deb.SourceLocalRepo {
|
||||||
var localRepo *deb.LocalRepo
|
var localRepo *deb.LocalRepo
|
||||||
|
|
||||||
localCollection := context.CollectionFactory().LocalRepoCollection()
|
localCollection := context.CollectionFactory().LocalRepoCollection()
|
||||||
@@ -180,9 +185,22 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
|
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if b.Origin != "" {
|
||||||
published.Origin = b.Origin
|
published.Origin = b.Origin
|
||||||
|
}
|
||||||
|
if b.NotAutomatic != "" {
|
||||||
|
published.NotAutomatic = b.NotAutomatic
|
||||||
|
}
|
||||||
|
if b.ButAutomaticUpgrades != "" {
|
||||||
|
published.ButAutomaticUpgrades = b.ButAutomaticUpgrades
|
||||||
|
}
|
||||||
published.Label = b.Label
|
published.Label = b.Label
|
||||||
|
|
||||||
|
published.SkipContents = context.Config().SkipContentsPublishing
|
||||||
|
if b.SkipContents != nil {
|
||||||
|
published.SkipContents = *b.SkipContents
|
||||||
|
}
|
||||||
|
|
||||||
duplicate := collection.CheckDuplicate(published)
|
duplicate := collection.CheckDuplicate(published)
|
||||||
if duplicate != nil {
|
if duplicate != nil {
|
||||||
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
|
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
|
||||||
@@ -199,6 +217,7 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
err = collection.Add(published)
|
err = collection.Add(published)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
|
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(201, published)
|
c.JSON(201, published)
|
||||||
@@ -213,6 +232,7 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
var b struct {
|
var b struct {
|
||||||
ForceOverwrite bool
|
ForceOverwrite bool
|
||||||
Signing SigningOptions
|
Signing SigningOptions
|
||||||
|
SkipContents *bool
|
||||||
Snapshots []struct {
|
Snapshots []struct {
|
||||||
Component string `binding:"required"`
|
Component string `binding:"required"`
|
||||||
Name string `binding:"required"`
|
Name string `binding:"required"`
|
||||||
@@ -255,7 +275,7 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
|
|
||||||
var updatedComponents []string
|
var updatedComponents []string
|
||||||
|
|
||||||
if published.SourceKind == "local" {
|
if published.SourceKind == deb.SourceLocalRepo {
|
||||||
if len(b.Snapshots) > 0 {
|
if len(b.Snapshots) > 0 {
|
||||||
c.Fail(400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
|
c.Fail(400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
|
||||||
return
|
return
|
||||||
@@ -272,15 +292,15 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot, err := snapshotCollection.ByName(snapshotInfo.Name)
|
snapshot, err2 := snapshotCollection.ByName(snapshotInfo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(404, err)
|
c.Fail(404, err2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = snapshotCollection.LoadComplete(snapshot)
|
err2 = snapshotCollection.LoadComplete(snapshot)
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
c.Fail(500, err)
|
c.Fail(500, err2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,22 +309,30 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Fail(500, fmt.Errorf("unknown published repository type"))
|
c.Fail(500, fmt.Errorf("unknown published repository type"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.SkipContents != nil {
|
||||||
|
published.SkipContents = *b.SkipContents
|
||||||
}
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, nil, b.ForceOverwrite)
|
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, nil, b.ForceOverwrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, fmt.Errorf("unable to update: %s", err))
|
c.Fail(500, fmt.Errorf("unable to update: %s", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.Update(published)
|
err = collection.Update(published)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
|
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
|
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
|
||||||
context.GetPublishedStorage(storage), context.CollectionFactory(), nil)
|
context.GetPublishedStorage(storage), context.CollectionFactory(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(500, fmt.Errorf("unable to update: %s", err))
|
c.Fail(500, fmt.Errorf("unable to update: %s", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, published)
|
c.JSON(200, published)
|
||||||
|
|||||||
+15
-19
@@ -2,13 +2,14 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GET /api/repos
|
// GET /api/repos
|
||||||
@@ -60,9 +61,9 @@ func apiReposCreate(c *gin.Context) {
|
|||||||
// PUT /api/repos/:name
|
// PUT /api/repos/:name
|
||||||
func apiReposEdit(c *gin.Context) {
|
func apiReposEdit(c *gin.Context) {
|
||||||
var b struct {
|
var b struct {
|
||||||
Comment string
|
Comment *string
|
||||||
DefaultDistribution string
|
DefaultDistribution *string
|
||||||
DefaultComponent string
|
DefaultComponent *string
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.Bind(&b) {
|
if !c.Bind(&b) {
|
||||||
@@ -79,14 +80,14 @@ func apiReposEdit(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Comment != "" {
|
if b.Comment != nil {
|
||||||
repo.Comment = b.Comment
|
repo.Comment = *b.Comment
|
||||||
}
|
}
|
||||||
if b.DefaultDistribution != "" {
|
if b.DefaultDistribution != nil {
|
||||||
repo.DefaultDistribution = b.DefaultDistribution
|
repo.DefaultDistribution = *b.DefaultDistribution
|
||||||
}
|
}
|
||||||
if b.DefaultComponent != "" {
|
if b.DefaultComponent != nil {
|
||||||
repo.DefaultComponent = b.DefaultComponent
|
repo.DefaultComponent = *b.DefaultComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.Update(repo)
|
err = collection.Update(repo)
|
||||||
@@ -295,7 +296,7 @@ func apiReposPackageFromDir(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
verifier := &utils.GpgVerifier{}
|
verifier := context.GetVerifier()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sources []string
|
sources []string
|
||||||
@@ -315,12 +316,7 @@ func apiReposPackageFromDir(c *gin.Context) {
|
|||||||
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))}
|
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))}
|
||||||
}
|
}
|
||||||
|
|
||||||
packageFiles, failedFiles, err = deb.CollectPackageFiles(sources, reporter)
|
packageFiles, failedFiles = 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)
|
list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -329,7 +325,7 @@ func apiReposPackageFromDir(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
||||||
context.CollectionFactory().PackageCollection(), reporter)
|
context.CollectionFactory().PackageCollection(), reporter, nil, context.CollectionFactory().ChecksumCollection())
|
||||||
failedFiles = append(failedFiles, failedFiles2...)
|
failedFiles = append(failedFiles, failedFiles2...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+37
-3
@@ -1,9 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
ctx "github.com/smira/aptly/context"
|
ctx "github.com/smira/aptly/context"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var context *ctx.AptlyContext
|
var context *ctx.AptlyContext
|
||||||
@@ -12,11 +13,44 @@ var context *ctx.AptlyContext
|
|||||||
func Router(c *ctx.AptlyContext) http.Handler {
|
func Router(c *ctx.AptlyContext) http.Handler {
|
||||||
context = c
|
context = c
|
||||||
|
|
||||||
go cacheFlusher()
|
|
||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
router.Use(gin.ErrorLogger())
|
router.Use(gin.ErrorLogger())
|
||||||
|
|
||||||
|
if context.Flags().Lookup("no-lock").Value.Get().(bool) {
|
||||||
|
// We use a goroutine to count the number of
|
||||||
|
// concurrent requests. When no more requests are
|
||||||
|
// running, we close the database to free the lock.
|
||||||
|
requests := make(chan dbRequest)
|
||||||
|
|
||||||
|
go acquireDatabase(requests)
|
||||||
|
|
||||||
|
router.Use(func(c *gin.Context) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
errCh := make(chan error)
|
||||||
|
requests <- dbRequest{acquiredb, errCh}
|
||||||
|
|
||||||
|
err = <-errCh
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
requests <- dbRequest{releasedb, errCh}
|
||||||
|
err = <-errCh
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(500, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
go cacheFlusher()
|
||||||
|
}
|
||||||
|
|
||||||
root := router.Group("/api")
|
root := router.Group("/api")
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
|||||||
+56
-21
@@ -3,24 +3,58 @@
|
|||||||
package aptly
|
package aptly
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ReadSeekerCloser = ReadSeeker + Closer
|
||||||
|
type ReadSeekerCloser interface {
|
||||||
|
io.ReadSeeker
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
// PackagePool is asbtraction of package pool storage.
|
// PackagePool is asbtraction of package pool storage.
|
||||||
//
|
//
|
||||||
// PackagePool stores all the package files, deduplicating them.
|
// PackagePool stores all the package files, deduplicating them.
|
||||||
type PackagePool interface {
|
type PackagePool interface {
|
||||||
// Path returns full path to package file in pool given any name and hash of file contents
|
// Verify checks whether file exists in the pool and fills back checksum info
|
||||||
Path(filename string, hashMD5 string) (string, error)
|
//
|
||||||
// RelativePath returns path relative to pool's root for package files given MD5 and original filename
|
// if poolPath is empty, poolPath is generated automatically based on checksum info (if available)
|
||||||
RelativePath(filename string, hashMD5 string) (string, error)
|
// in any case, if function returns true, it also fills back checksums with complete information about the file in the pool
|
||||||
|
Verify(poolPath, basename string, checksums *utils.ChecksumInfo, checksumStorage ChecksumStorage) (string, bool, error)
|
||||||
|
// Import copies file into package pool
|
||||||
|
//
|
||||||
|
// - srcPath is full path to source file as it is now
|
||||||
|
// - basename is desired human-readable name (canonical filename)
|
||||||
|
// - checksums are used to calculate file placement
|
||||||
|
// - move indicates whether srcPath can be removed
|
||||||
|
Import(srcPath, basename string, checksums *utils.ChecksumInfo, move bool, storage ChecksumStorage) (path string, err error)
|
||||||
|
// LegacyPath returns legacy (pre 1.1) path to package file (relative to root)
|
||||||
|
LegacyPath(filename string, checksums *utils.ChecksumInfo) (string, error)
|
||||||
|
// Stat returns Unix stat(2) info
|
||||||
|
Stat(path string) (os.FileInfo, error)
|
||||||
|
// Open returns ReadSeekerCloser to access the file
|
||||||
|
Open(path string) (ReadSeekerCloser, error)
|
||||||
// FilepathList returns file paths of all the files in the pool
|
// FilepathList returns file paths of all the files in the pool
|
||||||
FilepathList(progress Progress) ([]string, error)
|
FilepathList(progress Progress) ([]string, error)
|
||||||
// Remove deletes file in package pool returns its size
|
// Remove deletes file in package pool returns its size
|
||||||
Remove(path string) (size int64, err error)
|
Remove(path string) (size int64, err error)
|
||||||
// Import copies file into package pool
|
}
|
||||||
Import(path string, hashMD5 string) error
|
|
||||||
|
// LocalPackagePool is implemented by PackagePools residing on the same filesystem
|
||||||
|
type LocalPackagePool interface {
|
||||||
|
// GenerateTempPath generates temporary path for download (which is fast to import into package pool later on)
|
||||||
|
GenerateTempPath(filename string) (string, error)
|
||||||
|
// Link generates hardlink to destination path
|
||||||
|
Link(path, dstPath string) error
|
||||||
|
// Symlink generates symlink to destination path
|
||||||
|
Symlink(path, dstPath string) error
|
||||||
|
// FullPath generates full path to the file in pool
|
||||||
|
//
|
||||||
|
// Please use with care: it's not supposed to be used to access files
|
||||||
|
FullPath(path string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublishedStorage is abstraction of filesystem storing all published repositories
|
// PublishedStorage is abstraction of filesystem storing all published repositories
|
||||||
@@ -34,15 +68,15 @@ type PublishedStorage interface {
|
|||||||
// 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, sourceMD5 string, force bool) error
|
LinkFromPool(publishedDirectory, baseName string, sourcePool PackagePool, sourcePath string, sourceChecksums utils.ChecksumInfo, 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)
|
||||||
// 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
|
// FileSystemPublishedStorage is published storage on filesystem
|
||||||
type LocalPublishedStorage interface {
|
type FileSystemPublishedStorage interface {
|
||||||
// PublicPath returns root of public part
|
// PublicPath returns root of public part
|
||||||
PublicPath() string
|
PublicPath() string
|
||||||
}
|
}
|
||||||
@@ -75,23 +109,24 @@ type Progress interface {
|
|||||||
Printf(msg string, a ...interface{})
|
Printf(msg string, a ...interface{})
|
||||||
// ColoredPrintf does printf in colored way + newline
|
// ColoredPrintf does printf in colored way + newline
|
||||||
ColoredPrintf(msg string, a ...interface{})
|
ColoredPrintf(msg string, a ...interface{})
|
||||||
|
// PrintfStdErr does printf but in safe manner to stderr
|
||||||
|
PrintfStdErr(msg string, a ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downloader is parallel HTTP fetcher
|
// Downloader is parallel HTTP fetcher
|
||||||
type Downloader interface {
|
type Downloader interface {
|
||||||
// Download starts new download task
|
// Download starts new download task
|
||||||
Download(url string, destination string, result chan<- error)
|
Download(url string, destination string) error
|
||||||
// DownloadWithChecksum starts new download task with checksum verification
|
// DownloadWithChecksum starts new download task with checksum verification
|
||||||
DownloadWithChecksum(url string, destination string, result chan<- error, expected utils.ChecksumInfo, ignoreMismatch bool)
|
DownloadWithChecksum(url string, destination string, expected *utils.ChecksumInfo, ignoreMismatch bool, maxTries int) error
|
||||||
// Pause pauses task processing
|
|
||||||
Pause()
|
|
||||||
// Resume resumes task processing
|
|
||||||
Resume()
|
|
||||||
// Shutdown stops downloader after current tasks are finished,
|
|
||||||
// but doesn't process rest of queue
|
|
||||||
Shutdown()
|
|
||||||
// Abort stops downloader without waiting for shutdown
|
|
||||||
Abort()
|
|
||||||
// GetProgress returns Progress object
|
// GetProgress returns Progress object
|
||||||
GetProgress() Progress
|
GetProgress() Progress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChecksumStorage is stores checksums in some (persistent) storage
|
||||||
|
type ChecksumStorage interface {
|
||||||
|
// Get finds checksums in DB by path
|
||||||
|
Get(path string) (*utils.ChecksumInfo, error)
|
||||||
|
// Update adds or updates information about checksum in DB
|
||||||
|
Update(path string, c *utils.ChecksumInfo) error
|
||||||
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
package aptly
|
package aptly
|
||||||
|
|
||||||
// Version of aptly
|
// Version of aptly (filled in at link time)
|
||||||
const Version = "0.9.5"
|
var Version string
|
||||||
|
|
||||||
// Enable debugging features?
|
// EnableDebug triggers some debugging features
|
||||||
const EnableDebug = false
|
const EnableDebug = false
|
||||||
|
|||||||
@@ -0,0 +1,633 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# (The MIT License)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 Andrey Smirnov
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the 'Software'), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
__aptly_mirror_list()
|
||||||
|
{
|
||||||
|
aptly mirror list -raw
|
||||||
|
}
|
||||||
|
|
||||||
|
__aptly_repo_list()
|
||||||
|
{
|
||||||
|
aptly repo list -raw
|
||||||
|
}
|
||||||
|
|
||||||
|
__aptly_snapshot_list()
|
||||||
|
{
|
||||||
|
aptly snapshot list -raw
|
||||||
|
}
|
||||||
|
|
||||||
|
__aptly_published_distributions()
|
||||||
|
{
|
||||||
|
aptly publish list -raw | cut -d ' ' -f 2 | sort | uniq
|
||||||
|
}
|
||||||
|
|
||||||
|
__aptly_published_prefixes()
|
||||||
|
{
|
||||||
|
aptly publish list -raw | cut -d ' ' -f 1 | sort | uniq
|
||||||
|
}
|
||||||
|
|
||||||
|
__aptly_prefixes_for_distribution()
|
||||||
|
{
|
||||||
|
aptly publish list -raw | awk -v dist="$1" '{ if (dist == $2) print $1 }' | sort | uniq
|
||||||
|
}
|
||||||
|
|
||||||
|
_aptly()
|
||||||
|
{
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
commands="api config db graph mirror package publish repo serve snapshot task version"
|
||||||
|
options="-architectures= -config= -db-open-attempts= -dep-follow-all-variants -dep-follow-recommends -dep-follow-source -dep-follow-suggests -dep-verbose-resolve -gpg-provider="
|
||||||
|
db_subcommands="cleanup recover"
|
||||||
|
mirror_subcommands="create drop edit show list rename search update"
|
||||||
|
publish_subcommands="drop list repo snapshot switch update"
|
||||||
|
snapshot_subcommands="create diff drop filter list merge pull rename search show verify"
|
||||||
|
repo_subcommands="add copy create drop edit import include list move remove rename search show"
|
||||||
|
package_subcommands="search show"
|
||||||
|
task_subcommands="run"
|
||||||
|
config_subcommands="show"
|
||||||
|
api_subcommands="serve"
|
||||||
|
|
||||||
|
local cmd subcmd numargs numoptions i
|
||||||
|
|
||||||
|
numargs=0
|
||||||
|
numoptions=0
|
||||||
|
|
||||||
|
for (( i=1; i < $COMP_CWORD; i++ )); do
|
||||||
|
if [[ -n "$cmd" ]]; then
|
||||||
|
if [[ ! -n "$subcmd" ]]; then
|
||||||
|
subcmd=${COMP_WORDS[i]}
|
||||||
|
numargs=$(( COMP_CWORD - i - 1 ))
|
||||||
|
else
|
||||||
|
if [[ "${COMP_WORDS[i]}" == -* ]]; then
|
||||||
|
numoptions=$(( numoptions + 1 ))
|
||||||
|
numargs=$(( numargs - 1 ))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ! "${COMP_WORDS[i]}" == -* ]]; then
|
||||||
|
cmd=${COMP_WORDS[i]}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -n "$cmd" ]];
|
||||||
|
then
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "${options}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W "${commands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -n "$subcmd" ]];
|
||||||
|
then
|
||||||
|
case "$prev" in
|
||||||
|
"db")
|
||||||
|
COMPREPLY=($(compgen -W "${db_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"mirror")
|
||||||
|
COMPREPLY=($(compgen -W "${mirror_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"repo")
|
||||||
|
COMPREPLY=($(compgen -W "${repo_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"snapshot")
|
||||||
|
COMPREPLY=($(compgen -W "${snapshot_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"publish")
|
||||||
|
COMPREPLY=($(compgen -W "${publish_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"package")
|
||||||
|
COMPREPLY=($(compgen -W "${package_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"task")
|
||||||
|
COMPREPLY=($(compgen -W "${task_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"config")
|
||||||
|
COMPREPLY=($(compgen -W "${config_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"api")
|
||||||
|
COMPREPLY=($(compgen -W "${api_subcommands}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
"mirror")
|
||||||
|
case "$subcmd" in
|
||||||
|
"create")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-filter= -filter-with-deps -force-components -ignore-signatures -keyring= -with-sources -with-udebs" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"edit")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-filter= -filter-with-deps -with-sources -with-udebs" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"show")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-packages" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"search")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-format= -with-deps" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"rename")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"drop")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"list")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-raw" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"update")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force -download-limit= -ignore-checksums -ignore-signatures -keyring= -skip-existing-packages" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"repo")
|
||||||
|
case "$subcmd" in
|
||||||
|
"add")
|
||||||
|
case $numargs in
|
||||||
|
0)
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force-replace -remove-files" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
_filedir '@(deb|dsc|udeb)'
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"copy"|"move")
|
||||||
|
case $numargs in
|
||||||
|
0)
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-deps -dry-run" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"create")
|
||||||
|
case $numargs in
|
||||||
|
0)
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-comment= -distribution= -component= -uploaders-file=" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
COMPREPLY=($(compgen -W "from" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
COMPREPLY=($(compgen -W "snapshot" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"drop")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"edit")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-comment= -distribution= -component= -uploaders-file=" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"search")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-format= -with-deps" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"list")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-raw" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"include")
|
||||||
|
case $numargs in
|
||||||
|
0)
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-accept-unsigned -force-replace -ignore-signatures -keyring= -no-remove-files -repo= -uploaders-file=" -- ${cur}))
|
||||||
|
else
|
||||||
|
comptopt -o filenames 2>/dev/null
|
||||||
|
COMPREPLY=($(compgen -f -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"import")
|
||||||
|
case $numargs in
|
||||||
|
0)
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-deps -dry-run" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"remove")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-dry-run" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"show")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-packages" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"rename")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"snapshot")
|
||||||
|
case "$subcmd" in
|
||||||
|
"create")
|
||||||
|
case $numargs in
|
||||||
|
1)
|
||||||
|
COMPREPLY=($(compgen -W "from empty" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [[ "$prev" == "from" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "mirror repo" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
if [[ "$prev" == "mirror" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$prev" == "repo" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"diff")
|
||||||
|
if [[ $numargs -eq 0 ]] && [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-only-matching" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -lt 2 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"drop")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"list")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-raw -sort=" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"merge")
|
||||||
|
if [[ $numargs -gt 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-latest" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"pull")
|
||||||
|
if [[ $numargs -eq 0 ]] && [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-all-matches -dry-run -no-deps -no-remove" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -lt 2 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"filter")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-deps" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"show")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-packages" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"search")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-format= -with-deps" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"rename")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"verify")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"publish")
|
||||||
|
case "$subcmd" in
|
||||||
|
"snapshot"|"repo")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-batch -force-overwrite -distribution= -component= -gpg-key= -keyring= -label= -origin= -notautomatic= -butautomaticupgrades= -passphrase= -passphrase-file= -secret-keyring= -skip-contents -skip-signing" -- ${cur}))
|
||||||
|
else
|
||||||
|
if [[ "$subcmd" == "snapshot" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_repo_list)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_published_prefixes)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"list")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-raw" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"update")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-batch -force-overwrite -gpg-key= -keyring= -passphrase= -passphrase-file= -secret-keyring= -skip-contents -skip-signing" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_published_distributions)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_prefixes_for_distribution $prev)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"switch")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-batch -force-overwrite -component= -gpg-key= -keyring= -passphrase= -passphrase-file= -secret-keyring= -skip-contents -skip-signing" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_published_distributions)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_prefixes_for_distribution $prev)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -ge 2 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_snapshot_list)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"drop")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-force-drop" -- ${cur}))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_published_distributions)" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $numargs -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$(__aptly_prefixes_for_distribution $prev)" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"package")
|
||||||
|
case "$subcmd" in
|
||||||
|
"search")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-format=" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"show")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-with-files -with-references" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"serve")
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-listen=" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"graph")
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-format= -output=" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"api")
|
||||||
|
case "$subcmd" in
|
||||||
|
"serve")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-listen=" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"db")
|
||||||
|
case "$subcmd" in
|
||||||
|
"cleanup")
|
||||||
|
if [[ $numargs -eq 0 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "-dry-run -verbose" -- ${cur}))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
} && complete -F _aptly aptly
|
||||||
+61
-4
@@ -2,10 +2,16 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/smira/aptly/api"
|
"github.com/smira/aptly/api"
|
||||||
|
"github.com/smira/aptly/systemd/activation"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyAPIServe(cmd *commander.Command, args []string) error {
|
func aptlyAPIServe(cmd *commander.Command, args []string) error {
|
||||||
@@ -18,10 +24,56 @@ func aptlyAPIServe(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
listen := context.Flags().Lookup("listen").Value.String()
|
// There are only two working options for aptly's rootDir:
|
||||||
|
// 1. rootDir does not exist, then we'll create it
|
||||||
|
// 2. rootDir exists and is writable
|
||||||
|
// anything else must fail.
|
||||||
|
// E.g.: Running the service under a different user may lead to a rootDir
|
||||||
|
// that exists but is not usable due to access permissions.
|
||||||
|
err = utils.DirIsAccessible(context.Config().RootDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to recycle systemd fds for listening
|
||||||
|
listeners, err := activation.Listeners(true)
|
||||||
|
if len(listeners) > 1 {
|
||||||
|
panic("Got more than 1 listener from systemd. This is currently not supported!")
|
||||||
|
}
|
||||||
|
if err == nil && len(listeners) == 1 {
|
||||||
|
listener := listeners[0]
|
||||||
|
defer listener.Close()
|
||||||
|
fmt.Printf("\nTaking over web server at: %s (press Ctrl+C to quit)...\n", listener.Addr().String())
|
||||||
|
err = http.Serve(listener, api.Router(context))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to serve: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are none: use the listen argument.
|
||||||
|
listen := context.Flags().Lookup("listen").Value.String()
|
||||||
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)
|
||||||
|
|
||||||
|
listenURL, err := url.Parse(listen)
|
||||||
|
if err == nil && listenURL.Scheme == "unix" {
|
||||||
|
file := listenURL.Path
|
||||||
|
os.Remove(file)
|
||||||
|
|
||||||
|
var listener net.Listener
|
||||||
|
listener, err = net.Listen("unix", file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to listen on: %s\n%s", file, err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
err = http.Serve(listener, api.Router(context))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to serve: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = http.ListenAndServe(listen, api.Router(context))
|
err = http.ListenAndServe(listen, api.Router(context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to serve: %s", err)
|
return fmt.Errorf("unable to serve: %s", err)
|
||||||
@@ -36,16 +88,21 @@ func makeCmdAPIServe() *commander.Command {
|
|||||||
UsageLine: "serve",
|
UsageLine: "serve",
|
||||||
Short: "start API HTTP service",
|
Short: "start API HTTP service",
|
||||||
Long: `
|
Long: `
|
||||||
Stat HTTP server with aptly REST API.
|
Start HTTP server with aptly REST API. The server can listen to either a port
|
||||||
|
or Unix domain socket. When using a socket, Aptly will fully manage the socket
|
||||||
|
file. This command also supports taking over from a systemd file descriptors to
|
||||||
|
enable systemd socket activation.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly api serve -listen=:8080
|
$ aptly api serve -listen=:8080
|
||||||
|
$ aptly api serve -listen=unix:///tmp/aptly.sock
|
||||||
`,
|
`,
|
||||||
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening")
|
cmd.Flag.String("listen", ":8080", "host:port for HTTP listening or unix://path to listen on a Unix domain socket")
|
||||||
|
cmd.Flag.Bool("no-lock", false, "don't lock the database")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
|||||||
+46
-12
@@ -2,13 +2,22 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"os"
|
)
|
||||||
"time"
|
|
||||||
|
// Various command flags/UI things
|
||||||
|
const (
|
||||||
|
Yes = "yes"
|
||||||
|
No = "no"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListPackagesRefList shows list of packages in PackageRefList
|
// ListPackagesRefList shows list of packages in PackageRefList
|
||||||
@@ -19,19 +28,41 @@ func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = reflist.ForEach(func(key []byte) error {
|
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
|
||||||
p, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
|
|
||||||
if err2 != nil {
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
fmt.Printf(" %s\n", p)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to load packages: %s", err)
|
return fmt.Errorf("unable to load packages: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return PrintPackageList(list, "", " ")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintPackageList shows package list with specified format or default representation
|
||||||
|
func PrintPackageList(result *deb.PackageList, format, prefix string) error {
|
||||||
|
result.PrepareIndex()
|
||||||
|
|
||||||
|
if format == "" {
|
||||||
|
return result.ForEachIndexed(func(p *deb.Package) error {
|
||||||
|
context.Progress().Printf(prefix+"%s\n", p)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
formatTemplate, err := template.New("format").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing -format template: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ForEachIndexed(func(p *deb.Package) error {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err = formatTemplate.Execute(b, p.ExtendedStanza())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error applying template: %s", err)
|
||||||
|
}
|
||||||
|
context.Progress().Printf(prefix+"%s\n", b.String())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupOption checks boolean flag with default (usually config) and command-line
|
// LookupOption checks boolean flag with default (usually config) and command-line
|
||||||
@@ -80,12 +111,15 @@ package environment to new version.`,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flag.Int("db-open-attempts", 10, "number of attempts to open DB if it's locked by other instance")
|
||||||
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
|
cmd.Flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
|
||||||
cmd.Flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
|
cmd.Flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
|
||||||
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
|
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
|
||||||
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
|
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if dependency is 'a|b'")
|
||||||
|
cmd.Flag.Bool("dep-verbose-resolve", false, "when processing dependencies, print detailed logs")
|
||||||
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
|
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
|
||||||
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
|
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
|
||||||
|
cmd.Flag.String("gpg-provider", "", "PGP implementation (\"gpg\" for external gpg or \"internal\" for Go internal implementation)")
|
||||||
|
|
||||||
if aptly.EnableDebug {
|
if aptly.EnableDebug {
|
||||||
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
|
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+17
-16
@@ -2,11 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"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"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// aptly db cleanup
|
// aptly db cleanup
|
||||||
@@ -36,9 +37,9 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
|
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
e := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
|
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
|
||||||
@@ -66,9 +67,9 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
|
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
@@ -97,9 +98,9 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
context.Progress().ColoredPrintf("- @{g}%s@|", snapshot.Name)
|
context.Progress().ColoredPrintf("- @{g}%s@|", snapshot.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
e := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false, true)
|
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false, true)
|
||||||
@@ -124,12 +125,12 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
if verbose {
|
if verbose {
|
||||||
context.Progress().ColoredPrintf("- @{g}%s:%s/%s{|}", published.Storage, published.Prefix, published.Distribution)
|
context.Progress().ColoredPrintf("- @{g}%s:%s/%s{|}", published.Storage, published.Prefix, published.Distribution)
|
||||||
}
|
}
|
||||||
if published.SourceKind != "local" {
|
if published.SourceKind != deb.SourceLocalRepo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
|
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, component := range published.Components() {
|
for _, component := range published.Components() {
|
||||||
@@ -153,7 +154,7 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
|
|||||||
context.Progress().ColoredPrintf("@{w!}Loading list of all packages...@|")
|
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.Subtract(existingPackageRefs)
|
||||||
|
|
||||||
// delete packages that are no longer referenced
|
// delete packages that are no longer referenced
|
||||||
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced packages (%d)...@|", toDelete.Len())
|
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced packages (%d)...@|", toDelete.Len())
|
||||||
|
|||||||
+64
-8
@@ -3,12 +3,18 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
|
||||||
"github.com/smira/commander"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyGraph(cmd *commander.Command, args []string) error {
|
func aptlyGraph(cmd *commander.Command, args []string) error {
|
||||||
@@ -19,8 +25,11 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layout := context.Flags().Lookup("layout").Value.String()
|
||||||
|
|
||||||
fmt.Printf("Generating graph...\n")
|
fmt.Printf("Generating graph...\n")
|
||||||
graph, err := deb.BuildGraph(context.CollectionFactory())
|
graph, err := deb.BuildGraph(context.CollectionFactory(), layout)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -34,9 +43,16 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
|
|||||||
tempfile.Close()
|
tempfile.Close()
|
||||||
os.Remove(tempfile.Name())
|
os.Remove(tempfile.Name())
|
||||||
|
|
||||||
tempfilename := tempfile.Name() + ".png"
|
format := context.Flags().Lookup("format").Value.String()
|
||||||
|
output := context.Flags().Lookup("output").Value.String()
|
||||||
|
|
||||||
command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
|
if filepath.Ext(output) != "" {
|
||||||
|
format = filepath.Ext(output)[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
tempfilename := tempfile.Name() + "." + format
|
||||||
|
|
||||||
|
command := exec.Command("dot", "-T"+format, "-o"+tempfilename)
|
||||||
command.Stderr = os.Stderr
|
command.Stderr = os.Stderr
|
||||||
|
|
||||||
stdin, err := command.StdinPipe()
|
stdin, err := command.StdinPipe()
|
||||||
@@ -64,15 +80,51 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = exec.Command("open", tempfilename).Run()
|
defer func() {
|
||||||
|
_ = os.Remove(tempfilename)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if output != "" {
|
||||||
|
err = utils.CopyFile(tempfilename, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
|
return fmt.Errorf("unable to copy %s -> %s: %s", tempfilename, output, err)
|
||||||
err = nil
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Output saved to %s\n", output)
|
||||||
|
} else {
|
||||||
|
command := getOpenCommand()
|
||||||
|
fmt.Printf("Rendered to %s file: %s, trying to open it with: %s %s...\n", format, tempfilename, command, tempfilename)
|
||||||
|
|
||||||
|
args := strings.Split(command, " ")
|
||||||
|
|
||||||
|
viewer := exec.Command(args[0], append(args[1:], tempfilename)...)
|
||||||
|
viewer.Stderr = os.Stderr
|
||||||
|
if err = viewer.Start(); err == nil {
|
||||||
|
// Wait for a second so that the visualizer has a chance to
|
||||||
|
// open the input file. This needs to be done even if we're
|
||||||
|
// waiting for the visualizer as it can be just a wrapper that
|
||||||
|
// spawns a browser tab and returns right away.
|
||||||
|
defer func(t <-chan time.Time) {
|
||||||
|
<-t
|
||||||
|
}(time.After(time.Second))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOpenCommand tries to guess command to open image for OS
|
||||||
|
func getOpenCommand() string {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
return "/usr/bin/open"
|
||||||
|
case "windows":
|
||||||
|
return "cmd /c start"
|
||||||
|
default:
|
||||||
|
return "xdg-open"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeCmdGraph() *commander.Command {
|
func makeCmdGraph() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyGraph,
|
Run: aptlyGraph,
|
||||||
@@ -89,5 +141,9 @@ Example:
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flag.String("format", "png", "render graph to specified format (png, svg, pdf, etc.)")
|
||||||
|
cmd.Flag.String("output", "", "specify output filename, default is to open result in viewer")
|
||||||
|
cmd.Flag.String("layout", "horizontal", "create a more 'vertical' or a more 'horizontal' graph layout")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -1,20 +1,21 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
|
func getVerifier(flags *flag.FlagSet) (pgp.Verifier, error) {
|
||||||
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
|
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
keyRings := flags.Lookup("keyring").Value.Get().([]string)
|
keyRings := flags.Lookup("keyring").Value.Get().([]string)
|
||||||
|
|
||||||
verifier := &utils.GpgVerifier{}
|
verifier := context.GetVerifier()
|
||||||
for _, keyRing := range keyRings {
|
for _, keyRing := range keyRings {
|
||||||
verifier.AddKeyring(keyRing)
|
verifier.AddKeyring(keyRing)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
|
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
|
||||||
@@ -43,6 +44,7 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
|
|||||||
repo.Filter = context.Flags().Lookup("filter").Value.String()
|
repo.Filter = context.Flags().Lookup("filter").Value.String()
|
||||||
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
|
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
|
||||||
repo.SkipComponentCheck = context.Flags().Lookup("force-components").Value.Get().(bool)
|
repo.SkipComponentCheck = context.Flags().Lookup("force-components").Value.Get().(bool)
|
||||||
|
repo.SkipArchitectureCheck = context.Flags().Lookup("force-architectures").Value.Get().(bool)
|
||||||
|
|
||||||
if repo.Filter != "" {
|
if repo.Filter != "" {
|
||||||
_, err = query.Parse(repo.Filter)
|
_, err = query.Parse(repo.Filter)
|
||||||
@@ -97,6 +99,7 @@ Example:
|
|||||||
cmd.Flag.String("filter", "", "filter packages in mirror")
|
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("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.Bool("force-components", false, "(only with component list) skip check that requested components are listed in Release file")
|
||||||
|
cmd.Flag.Bool("force-architectures", false, "(only with architecture list) skip check that requested architectures 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
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
|
|||||||
+2
-1
@@ -2,9 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyMirrorList(cmd *commander.Command, args []string) error {
|
func aptlyMirrorList(cmd *commander.Command, args []string) error {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import (
|
|||||||
func makeCmdMirrorSearch() *commander.Command {
|
func makeCmdMirrorSearch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlySnapshotMirrorRepoSearch,
|
Run: aptlySnapshotMirrorRepoSearch,
|
||||||
UsageLine: "search <name> <package-query>",
|
UsageLine: "search <name> [<package-query>]",
|
||||||
Short: "search mirror for packages matching query",
|
Short: "search mirror for packages matching query",
|
||||||
Long: `
|
Long: `
|
||||||
Command search displays list of packages in mirror that match package query
|
Command search displays list of packages in mirror that match package query
|
||||||
|
|
||||||
|
If query is not specified, all the packages are displayed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly mirror search wheezy-main '$Architecture (i386), Name (% *-dev)'
|
$ aptly mirror search wheezy-main '$Architecture (i386), Name (% *-dev)'
|
||||||
@@ -21,6 +23,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
||||||
|
cmd.Flag.String("format", "", "custom format for result printing")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -2,11 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"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"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
||||||
@@ -36,21 +37,21 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
|
|||||||
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, ", "))
|
||||||
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
|
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
|
||||||
downloadSources := "no"
|
downloadSources := No
|
||||||
if repo.DownloadSources {
|
if repo.DownloadSources {
|
||||||
downloadSources = "yes"
|
downloadSources = Yes
|
||||||
}
|
}
|
||||||
fmt.Printf("Download Sources: %s\n", downloadSources)
|
fmt.Printf("Download Sources: %s\n", downloadSources)
|
||||||
downloadUdebs := "no"
|
downloadUdebs := No
|
||||||
if repo.DownloadUdebs {
|
if repo.DownloadUdebs {
|
||||||
downloadUdebs = "yes"
|
downloadUdebs = Yes
|
||||||
}
|
}
|
||||||
fmt.Printf("Download .udebs: %s\n", downloadUdebs)
|
fmt.Printf("Download .udebs: %s\n", downloadUdebs)
|
||||||
if repo.Filter != "" {
|
if repo.Filter != "" {
|
||||||
fmt.Printf("Filter: %s\n", repo.Filter)
|
fmt.Printf("Filter: %s\n", repo.Filter)
|
||||||
filterWithDeps := "no"
|
filterWithDeps := No
|
||||||
if repo.FilterWithDeps {
|
if repo.FilterWithDeps {
|
||||||
filterWithDeps = "yes"
|
filterWithDeps = Yes
|
||||||
}
|
}
|
||||||
fmt.Printf("Filter With Deps: %s\n", filterWithDeps)
|
fmt.Printf("Filter With Deps: %s\n", filterWithDeps)
|
||||||
}
|
}
|
||||||
|
|||||||
+122
-29
@@ -2,14 +2,17 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"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/signal"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
||||||
@@ -40,6 +43,7 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ignoreMismatch := context.Flags().Lookup("ignore-checksums").Value.Get().(bool)
|
ignoreMismatch := context.Flags().Lookup("ignore-checksums").Value.Get().(bool)
|
||||||
|
maxTries := context.Flags().Lookup("max-tries").Value.Get().(int)
|
||||||
|
|
||||||
verifier, err := getVerifier(context.Flags())
|
verifier, err := getVerifier(context.Flags())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -52,7 +56,7 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().Printf("Downloading & parsing package files...\n")
|
context.Progress().Printf("Downloading & parsing package files...\n")
|
||||||
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch)
|
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch, maxTries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -67,7 +71,7 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var oldLen, newLen int
|
var oldLen, newLen int
|
||||||
oldLen, newLen, err = repo.ApplyFilter(context.DependencyOptions(), filterQuery)
|
oldLen, newLen, err = repo.ApplyFilter(context.DependencyOptions(), filterQuery, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
@@ -79,15 +83,19 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
queue []deb.PackageDownloadTask
|
queue []deb.PackageDownloadTask
|
||||||
)
|
)
|
||||||
|
|
||||||
|
skipExistingPackages := context.Flags().Lookup("skip-existing-packages").Value.Get().(bool)
|
||||||
|
|
||||||
context.Progress().Printf("Building download queue...\n")
|
context.Progress().Printf("Building download queue...\n")
|
||||||
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool())
|
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool(), context.CollectionFactory().PackageCollection(),
|
||||||
|
context.CollectionFactory().ChecksumCollection(), skipExistingPackages)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// on any interruption, unlock the mirror
|
// on any interruption, unlock the mirror
|
||||||
err := context.ReOpenDatabase()
|
err = context.ReOpenDatabase()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
repo.MarkAsIdle()
|
repo.MarkAsIdle()
|
||||||
context.CollectionFactory().RemoteRepoCollection().Update(repo)
|
context.CollectionFactory().RemoteRepoCollection().Update(repo)
|
||||||
@@ -108,6 +116,14 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
// Catch ^C
|
// Catch ^C
|
||||||
sigch := make(chan os.Signal)
|
sigch := make(chan os.Signal)
|
||||||
signal.Notify(sigch, os.Interrupt)
|
signal.Notify(sigch, os.Interrupt)
|
||||||
|
defer signal.Stop(sigch)
|
||||||
|
|
||||||
|
abort := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
<-sigch
|
||||||
|
signal.Stop(sigch)
|
||||||
|
close(abort)
|
||||||
|
}()
|
||||||
|
|
||||||
count := len(queue)
|
count := len(queue)
|
||||||
context.Progress().Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
|
context.Progress().Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
|
||||||
@@ -115,40 +131,85 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
// Download from the queue
|
// Download from the queue
|
||||||
context.Progress().InitBar(downloadSize, true)
|
context.Progress().InitBar(downloadSize, true)
|
||||||
|
|
||||||
// Download all package files
|
downloadQueue := make(chan int)
|
||||||
ch := make(chan error, count)
|
|
||||||
|
|
||||||
// In separate goroutine (to avoid blocking main), push queue to downloader
|
var (
|
||||||
go func() {
|
errors []string
|
||||||
for _, task := range queue {
|
errLock sync.Mutex
|
||||||
context.Downloader().DownloadWithChecksum(repo.PackageURL(task.RepoURI).String(), task.DestinationPath, ch, task.Checksums, ignoreMismatch)
|
)
|
||||||
|
|
||||||
|
pushError := func(err error) {
|
||||||
|
errLock.Lock()
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
errLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't need queue after this point
|
go func() {
|
||||||
queue = nil
|
for idx := range queue {
|
||||||
|
select {
|
||||||
|
case downloadQueue <- idx:
|
||||||
|
case <-abort:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(downloadQueue)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Wait for all downloads to finish
|
var wg sync.WaitGroup
|
||||||
errors := make([]string, 0)
|
|
||||||
|
|
||||||
for count > 0 {
|
for i := 0; i < context.Config().DownloadConcurrency; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sigch:
|
case idx, ok := <-downloadQueue:
|
||||||
signal.Stop(sigch)
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
task := &queue[idx]
|
||||||
|
|
||||||
|
var e error
|
||||||
|
|
||||||
|
// provision download location
|
||||||
|
task.TempDownPath, e = context.PackagePool().(aptly.LocalPackagePool).GenerateTempPath(task.File.Filename)
|
||||||
|
if e != nil {
|
||||||
|
pushError(e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// download file...
|
||||||
|
e = context.Downloader().DownloadWithChecksum(
|
||||||
|
repo.PackageURL(task.File.DownloadURL()).String(),
|
||||||
|
task.TempDownPath,
|
||||||
|
&task.File.Checksums,
|
||||||
|
ignoreMismatch,
|
||||||
|
maxTries)
|
||||||
|
if e != nil {
|
||||||
|
pushError(e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case <-abort:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all downloads to finish
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-abort:
|
||||||
return fmt.Errorf("unable to update: interrupted")
|
return fmt.Errorf("unable to update: interrupted")
|
||||||
case err = <-ch:
|
default:
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
}
|
|
||||||
count--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().ShutdownBar()
|
context.Progress().ShutdownBar()
|
||||||
signal.Stop(sigch)
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
return fmt.Errorf("unable to update: download errors:\n %s\n", strings.Join(errors, "\n "))
|
return fmt.Errorf("unable to update: download errors:\n %s", strings.Join(errors, "\n "))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = context.ReOpenDatabase()
|
err = context.ReOpenDatabase()
|
||||||
@@ -156,7 +217,37 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.FinalizeDownload()
|
// Import downloaded files
|
||||||
|
context.Progress().InitBar(int64(len(queue)), false)
|
||||||
|
|
||||||
|
for idx := range queue {
|
||||||
|
|
||||||
|
context.Progress().AddBar(1)
|
||||||
|
|
||||||
|
task := &queue[idx]
|
||||||
|
|
||||||
|
// and import it back to the pool
|
||||||
|
task.File.PoolPath, err = context.PackagePool().Import(task.TempDownPath, task.File.Filename, &task.File.Checksums, true, context.CollectionFactory().ChecksumCollection())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to import file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update "attached" files if any
|
||||||
|
for _, additionalTask := range task.Additional {
|
||||||
|
additionalTask.File.PoolPath = task.File.PoolPath
|
||||||
|
additionalTask.File.Checksums = task.File.Checksums
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-abort:
|
||||||
|
return fmt.Errorf("unable to update: interrupted")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Progress().ShutdownBar()
|
||||||
|
|
||||||
|
repo.FinalizeDownload(context.CollectionFactory(), context.Progress())
|
||||||
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)
|
||||||
@@ -186,7 +277,9 @@ Example:
|
|||||||
cmd.Flag.Bool("force", false, "force update mirror even if it is locked by another process")
|
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.Bool("skip-existing-packages", false, "do not check file existence for packages listed in the internal database of the mirror")
|
||||||
cmd.Flag.Int64("download-limit", 0, "limit download speed (kbytes/sec)")
|
cmd.Flag.Int64("download-limit", 0, "limit download speed (kbytes/sec)")
|
||||||
|
cmd.Flag.Int("max-tries", 1, "max download tries till process fails with download error")
|
||||||
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
|
||||||
|
|||||||
+20
-9
@@ -2,6 +2,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/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
@@ -9,26 +10,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func aptlyPackageSearch(cmd *commander.Command, args []string) error {
|
func aptlyPackageSearch(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var (
|
||||||
if len(args) != 1 {
|
err error
|
||||||
|
q deb.PackageQuery
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
q, err := query.Parse(args[0])
|
if len(args) == 1 {
|
||||||
|
q, err = query.Parse(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
q = &deb.MatchAllQuery{}
|
||||||
|
}
|
||||||
|
|
||||||
result := q.Query(context.CollectionFactory().PackageCollection())
|
result := q.Query(context.CollectionFactory().PackageCollection())
|
||||||
if result.Len() == 0 {
|
if result.Len() == 0 {
|
||||||
return fmt.Errorf("no results")
|
return fmt.Errorf("no results")
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ForEach(func(p *deb.Package) error {
|
format := context.Flags().Lookup("format").Value.String()
|
||||||
context.Progress().Printf("%s\n", p)
|
PrintPackageList(result, format, "")
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -36,10 +43,12 @@ func aptlyPackageSearch(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPackageSearch() *commander.Command {
|
func makeCmdPackageSearch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPackageSearch,
|
Run: aptlyPackageSearch,
|
||||||
UsageLine: "search <package-query>",
|
UsageLine: "search [<package-query>]",
|
||||||
Short: "search for packages matching query",
|
Short: "search for packages matching query",
|
||||||
Long: `
|
Long: `
|
||||||
Command search displays list of packages in whole DB that match package query
|
Command search displays list of packages in whole DB that match package query.
|
||||||
|
|
||||||
|
If query is not specified, all the packages are displayed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -48,5 +57,7 @@ Example:
|
|||||||
Flag: *flag.NewFlagSet("aptly-package-search", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly-package-search", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flag.String("format", "", "custom format for result printing")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-16
@@ -3,18 +3,20 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func printReferencesTo(p *deb.Package) (err error) {
|
func printReferencesTo(p *deb.Package) (err error) {
|
||||||
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
|
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
|
||||||
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
e := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
if repo.RefList().Has(p) {
|
if repo.RefList().Has(p) {
|
||||||
@@ -28,9 +30,9 @@ func printReferencesTo(p *deb.Package) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
|
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
|
||||||
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if repo.RefList() != nil {
|
if repo.RefList() != nil {
|
||||||
if repo.RefList().Has(p) {
|
if repo.RefList().Has(p) {
|
||||||
@@ -44,20 +46,17 @@ func printReferencesTo(p *deb.Package) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
|
||||||
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
e := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if snapshot.RefList().Has(p) {
|
if snapshot.RefList().Has(p) {
|
||||||
fmt.Printf(" snapshot %s\n", snapshot)
|
fmt.Printf(" snapshot %s\n", snapshot)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func aptlyPackageShow(cmd *commander.Command, args []string) error {
|
func aptlyPackageShow(cmd *commander.Command, args []string) error {
|
||||||
@@ -86,11 +85,18 @@ func aptlyPackageShow(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
if withFiles {
|
if withFiles {
|
||||||
fmt.Printf("Files in the pool:\n")
|
fmt.Printf("Files in the pool:\n")
|
||||||
|
packagePool := context.PackagePool()
|
||||||
for _, f := range p.Files() {
|
for _, f := range p.Files() {
|
||||||
path, err := context.PackagePool().Path(f.Filename, f.Checksums.MD5)
|
var path string
|
||||||
|
path, err = f.GetPoolPath(packagePool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pp, ok := packagePool.(aptly.LocalPackagePool); ok {
|
||||||
|
path = pp.FullPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf(" %s\n", path)
|
fmt.Printf(" %s\n", path)
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
@@ -125,7 +131,7 @@ inclusion into mirrors/snapshots/local repos is shown.
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly package show nginx-light_1.2.1-2.2+wheezy2_i386'
|
$ aptly package show 'nginx-light_1.2.1-2.2+wheezy2_i386'
|
||||||
`,
|
`,
|
||||||
Flag: *flag.NewFlagSet("aptly-package-show", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly-package-show", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -1,17 +1,17 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/pgp"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
|
func getSigner(flags *flag.FlagSet) (pgp.Signer, error) {
|
||||||
if LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
|
if LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := &utils.GpgSigner{}
|
signer := context.GetSigner()
|
||||||
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.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").Value.String())
|
||||||
@@ -37,6 +37,7 @@ func makeCmdPublish() *commander.Command {
|
|||||||
makeCmdPublishSnapshot(),
|
makeCmdPublishSnapshot(),
|
||||||
makeCmdPublishSwitch(),
|
makeCmdPublishSwitch(),
|
||||||
makeCmdPublishUpdate(),
|
makeCmdPublishUpdate(),
|
||||||
|
makeCmdPublishShow(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-4
@@ -2,9 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyPublishList(cmd *commander.Command, args []string) error {
|
func aptlyPublishList(cmd *commander.Command, args []string) error {
|
||||||
@@ -19,9 +20,9 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
|
|||||||
published := make([]string, 0, context.CollectionFactory().PublishedRepoCollection().Len())
|
published := make([]string, 0, context.CollectionFactory().PublishedRepoCollection().Len())
|
||||||
|
|
||||||
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
|
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
|
||||||
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
|
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ Example:
|
|||||||
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file 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("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("skip-contents", false, "don't generate Contents indexes")
|
||||||
cmd.Flag.String("origin", "", "origin name to publish")
|
cmd.Flag.String("origin", "", "origin name to publish")
|
||||||
|
cmd.Flag.String("notautomatic", "", "set value for NotAutomatic field")
|
||||||
|
cmd.Flag.String("butautomaticupgrades", "", "set value for ButAutomaticUpgrades field")
|
||||||
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")
|
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishShow(cmd *commander.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
if len(args) < 1 || len(args) > 2 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
distribution := args[0]
|
||||||
|
param := "."
|
||||||
|
|
||||||
|
if len(args) == 2 {
|
||||||
|
param = args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
|
||||||
|
repo, err := context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to show: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.Storage != "" {
|
||||||
|
fmt.Printf("Storage: %s\n", repo.Storage)
|
||||||
|
}
|
||||||
|
fmt.Printf("Prefix: %s\n", repo.Prefix)
|
||||||
|
if repo.Distribution != "" {
|
||||||
|
fmt.Printf("Distribution: %s\n", repo.Distribution)
|
||||||
|
}
|
||||||
|
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, " "))
|
||||||
|
|
||||||
|
fmt.Printf("Sources:\n")
|
||||||
|
for component, sourceID := range repo.Sources {
|
||||||
|
var name string
|
||||||
|
if repo.SourceKind == deb.SourceSnapshot {
|
||||||
|
source, e := context.CollectionFactory().SnapshotCollection().ByUUID(sourceID)
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = source.Name
|
||||||
|
} else if repo.SourceKind == deb.SourceLocalRepo {
|
||||||
|
source, e := context.CollectionFactory().LocalRepoCollection().ByUUID(sourceID)
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = source.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
fmt.Printf(" %s: %s [%s]\n", component, name, repo.SourceKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishShow() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishShow,
|
||||||
|
UsageLine: "show <distribution> [[<endpoint>:]<prefix>]",
|
||||||
|
Short: "shows details of published repository",
|
||||||
|
Long: `
|
||||||
|
Command show displays full information of a published repository.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish show wheezy
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
+29
-8
@@ -2,12 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/aptly"
|
"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"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
||||||
@@ -34,7 +35,7 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
message string
|
message string
|
||||||
)
|
)
|
||||||
|
|
||||||
if cmd.Name() == "snapshot" {
|
if cmd.Name() == "snapshot" { // nolint: goconst
|
||||||
var (
|
var (
|
||||||
snapshot *deb.Snapshot
|
snapshot *deb.Snapshot
|
||||||
emptyWarning = false
|
emptyWarning = false
|
||||||
@@ -70,7 +71,7 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
if emptyWarning {
|
if emptyWarning {
|
||||||
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
|
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
|
||||||
}
|
}
|
||||||
} else if cmd.Name() == "repo" {
|
} else if cmd.Name() == "repo" { // nolint: goconst
|
||||||
var (
|
var (
|
||||||
localRepo *deb.LocalRepo
|
localRepo *deb.LocalRepo
|
||||||
emptyWarning = false
|
emptyWarning = false
|
||||||
@@ -111,13 +112,30 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distribution := context.Flags().Lookup("distribution").Value.String()
|
distribution := context.Flags().Lookup("distribution").Value.String()
|
||||||
|
origin := context.Flags().Lookup("origin").Value.String()
|
||||||
|
notAutomatic := context.Flags().Lookup("notautomatic").Value.String()
|
||||||
|
butAutomaticUpgrades := context.Flags().Lookup("butautomaticupgrades").Value.String()
|
||||||
|
|
||||||
published, err := deb.NewPublishedRepo(storage, 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)
|
||||||
}
|
}
|
||||||
published.Origin = cmd.Flag.Lookup("origin").Value.String()
|
if origin != "" {
|
||||||
published.Label = cmd.Flag.Lookup("label").Value.String()
|
published.Origin = origin
|
||||||
|
}
|
||||||
|
if notAutomatic != "" {
|
||||||
|
published.NotAutomatic = notAutomatic
|
||||||
|
}
|
||||||
|
if butAutomaticUpgrades != "" {
|
||||||
|
published.ButAutomaticUpgrades = butAutomaticUpgrades
|
||||||
|
}
|
||||||
|
published.Label = context.Flags().Lookup("label").Value.String()
|
||||||
|
|
||||||
|
published.SkipContents = context.Config().SkipContentsPublishing
|
||||||
|
|
||||||
|
if context.Flags().IsSet("skip-contents") {
|
||||||
|
published.SkipContents = context.Flags().Lookup("skip-contents").Value.Get().(bool)
|
||||||
|
}
|
||||||
|
|
||||||
duplicate := context.CollectionFactory().PublishedRepoCollection().CheckDuplicate(published)
|
duplicate := context.CollectionFactory().PublishedRepoCollection().CheckDuplicate(published)
|
||||||
if duplicate != nil {
|
if duplicate != nil {
|
||||||
@@ -156,14 +174,14 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
context.Progress().Printf("\n%s been successfully published.\n", message)
|
context.Progress().Printf("\n%s been successfully published.\n", message)
|
||||||
|
|
||||||
if localStorage, ok := context.GetPublishedStorage(storage).(aptly.LocalPublishedStorage); ok {
|
if localStorage, ok := context.GetPublishedStorage(storage).(aptly.FileSystemPublishedStorage); ok {
|
||||||
context.Progress().Printf("Please setup your webserver to serve directory '%s' with autoindexing.\n",
|
context.Progress().Printf("Please setup your webserver to serve directory '%s' with autoindexing.\n",
|
||||||
localStorage.PublicPath())
|
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, deb.ArchitectureSource) {
|
||||||
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
|
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
|
||||||
}
|
}
|
||||||
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
|
context.Progress().Printf("Don't forget to add your GPG key to apt with apt-key.\n")
|
||||||
@@ -203,7 +221,10 @@ Example:
|
|||||||
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file 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("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.Bool("skip-contents", false, "don't generate Contents indexes")
|
||||||
|
cmd.Flag.String("origin", "", "overwrite origin name to publish")
|
||||||
|
cmd.Flag.String("notautomatic", "", "overwrite value for NotAutomatic field")
|
||||||
|
cmd.Flag.String("butautomaticupgrades", "", "overwrite value for ButAutomaticUpgrades field")
|
||||||
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")
|
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"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"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
||||||
@@ -43,7 +44,7 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if published.SourceKind != "snapshot" {
|
if published.SourceKind != deb.SourceSnapshot {
|
||||||
return fmt.Errorf("unable to update: not a snapshot publish")
|
return fmt.Errorf("unable to update: not a snapshot publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +91,10 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
"the same package pool.\n")
|
"the same package pool.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.Flags().IsSet("skip-contents") {
|
||||||
|
published.SkipContents = context.Flags().Lookup("skip-contents").Value.Get().(bool)
|
||||||
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
|
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)
|
||||||
@@ -143,6 +148,7 @@ This command would switch published repository (with one component) named ppa/wh
|
|||||||
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file 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("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("skip-contents", false, "don't generate Contents indexes")
|
||||||
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")
|
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
@@ -29,7 +30,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if published.SourceKind != "local" {
|
if published.SourceKind != deb.SourceLocalRepo {
|
||||||
return fmt.Errorf("unable to update: not a local repository publish")
|
return fmt.Errorf("unable to update: not a local repository publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,10 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
"the same package pool.\n")
|
"the same package pool.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.Flags().IsSet("skip-contents") {
|
||||||
|
published.SkipContents = context.Flags().Lookup("skip-contents").Value.Get().(bool)
|
||||||
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
|
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)
|
||||||
@@ -102,6 +107,7 @@ Example:
|
|||||||
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file 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("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("skip-contents", false, "don't generate Contents indexes")
|
||||||
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func makeCmdRepo() *commander.Command {
|
|||||||
makeCmdRepoShow(),
|
makeCmdRepoShow(),
|
||||||
makeCmdRepoRename(),
|
makeCmdRepoRename(),
|
||||||
makeCmdRepoSearch(),
|
makeCmdRepoSearch(),
|
||||||
|
makeCmdRepoInclude(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -2,12 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/smira/aptly/aptly"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
||||||
@@ -19,7 +20,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
verifier := &utils.GpgVerifier{}
|
verifier := context.GetVerifier()
|
||||||
|
|
||||||
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
|
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -42,15 +43,13 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
var packageFiles, failedFiles []string
|
var packageFiles, failedFiles []string
|
||||||
|
|
||||||
packageFiles, failedFiles, err = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()})
|
packageFiles, failedFiles = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()})
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to collect package files: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var processedFiles, failedFiles2 []string
|
var processedFiles, failedFiles2 []string
|
||||||
|
|
||||||
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
||||||
context.CollectionFactory().PackageCollection(), &aptly.ConsoleResultReporter{Progress: context.Progress()})
|
context.CollectionFactory().PackageCollection(), &aptly.ConsoleResultReporter{Progress: context.Progress()}, nil,
|
||||||
|
context.CollectionFactory().ChecksumCollection())
|
||||||
failedFiles = append(failedFiles, failedFiles2...)
|
failedFiles = append(failedFiles, failedFiles2...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to import package files: %s", err)
|
return fmt.Errorf("unable to import package files: %s", err)
|
||||||
@@ -67,7 +66,7 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
|
|||||||
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
||||||
|
|
||||||
for _, file := range processedFiles {
|
for _, file := range processedFiles {
|
||||||
err := os.Remove(file)
|
err = os.Remove(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to remove file: %s", err)
|
return fmt.Errorf("unable to remove file: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-2
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
@@ -9,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func aptlyRepoCreate(cmd *commander.Command, args []string) error {
|
func aptlyRepoCreate(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
if len(args) != 1 {
|
if !(len(args) == 1 || (len(args) == 4 && args[1] == "from" && args[2] == "snapshot")) { // nolint: goconst
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
@@ -18,6 +19,30 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
|
|||||||
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()
|
||||||
|
|
||||||
|
uploadersFile := context.Flags().Lookup("uploaders-file").Value.Get().(string)
|
||||||
|
if uploadersFile != "" {
|
||||||
|
repo.Uploaders, err = deb.NewUploadersFromFile(uploadersFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 4 {
|
||||||
|
var snapshot *deb.Snapshot
|
||||||
|
|
||||||
|
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(args[3])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load source snapshot: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load source snapshot: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.UpdateRefList(snapshot.RefList())
|
||||||
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
|
err = context.CollectionFactory().LocalRepoCollection().Add(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to add local repo: %s", err)
|
return fmt.Errorf("unable to add local repo: %s", err)
|
||||||
@@ -30,16 +55,21 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdRepoCreate() *commander.Command {
|
func makeCmdRepoCreate() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyRepoCreate,
|
Run: aptlyRepoCreate,
|
||||||
UsageLine: "create <name>",
|
UsageLine: "create <name> [ from snapshot <snapshot> ]",
|
||||||
Short: "create local repository",
|
Short: "create local repository",
|
||||||
Long: `
|
Long: `
|
||||||
Create local package repository. Repository would be empty when
|
Create local package repository. Repository would be empty when
|
||||||
created, packages could be added from files, copied or moved from
|
created, packages could be added from files, copied or moved from
|
||||||
another local repository or imported from the mirror.
|
another local repository or imported from the mirror.
|
||||||
|
|
||||||
|
If local package repository is created from snapshot, repo initial
|
||||||
|
contents are copied from snapsot contents.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly repo create testing
|
$ aptly repo create testing
|
||||||
|
|
||||||
|
$ aptly repo create mysql35 from snapshot mysql-35-2017
|
||||||
`,
|
`,
|
||||||
Flag: *flag.NewFlagSet("aptly-repo-create", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("aptly-repo-create", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
@@ -47,6 +77,7 @@ Example:
|
|||||||
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
|
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
|
||||||
cmd.Flag.String("distribution", "", "default distribution when publishing")
|
cmd.Flag.String("distribution", "", "default distribution when publishing")
|
||||||
cmd.Flag.String("component", "main", "default component when publishing")
|
cmd.Flag.String("component", "main", "default component when publishing")
|
||||||
|
cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|||||||
+25
-7
@@ -2,6 +2,9 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/AlekSi/pointer"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
@@ -23,16 +26,30 @@ 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() != "" {
|
var uploadersFile *string
|
||||||
repo.Comment = context.Flags().Lookup("comment").Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Flags().Lookup("distribution").Value.String() != "" {
|
context.Flags().Visit(func(flag *flag.Flag) {
|
||||||
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
|
switch flag.Name {
|
||||||
|
case "comment":
|
||||||
|
repo.Comment = flag.Value.String()
|
||||||
|
case "distribution":
|
||||||
|
repo.DefaultDistribution = flag.Value.String()
|
||||||
|
case "component":
|
||||||
|
repo.DefaultComponent = flag.Value.String()
|
||||||
|
case "uploaders-file":
|
||||||
|
uploadersFile = pointer.ToString(flag.Value.String())
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if context.Flags().Lookup("component").Value.String() != "" {
|
if uploadersFile != nil {
|
||||||
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
|
if *uploadersFile != "" {
|
||||||
|
repo.Uploaders, err = deb.NewUploadersFromFile(*uploadersFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repo.Uploaders = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
||||||
@@ -63,6 +80,7 @@ Example:
|
|||||||
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
|
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
|
||||||
cmd.Flag.String("distribution", "", "default distribution when publishing")
|
cmd.Flag.String("distribution", "", "default distribution when publishing")
|
||||||
cmd.Flag.String("component", "", "default component when publishing")
|
cmd.Flag.String("component", "", "default component when publishing")
|
||||||
|
cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/query"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyRepoInclude(cmd *commander.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
verifier, err := getVerifier(context.Flags())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if verifier == nil {
|
||||||
|
verifier = context.GetVerifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool)
|
||||||
|
acceptUnsigned := context.Flags().Lookup("accept-unsigned").Value.Get().(bool)
|
||||||
|
ignoreSignatures := context.Flags().Lookup("ignore-signatures").Value.Get().(bool)
|
||||||
|
noRemoveFiles := context.Flags().Lookup("no-remove-files").Value.Get().(bool)
|
||||||
|
|
||||||
|
repoTemplate, err := template.New("repo").Parse(context.Flags().Lookup("repo").Value.Get().(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing -repo template: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploaders := (*deb.Uploaders)(nil)
|
||||||
|
uploadersFile := context.Flags().Lookup("uploaders-file").Value.Get().(string)
|
||||||
|
if uploadersFile != "" {
|
||||||
|
uploaders, err = deb.NewUploadersFromFile(uploadersFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range uploaders.Rules {
|
||||||
|
uploaders.Rules[i].CompiledCondition, err = query.Parse(uploaders.Rules[i].Condition)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing query %s: %s", uploaders.Rules[i].Condition, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reporter := &aptly.ConsoleResultReporter{Progress: context.Progress()}
|
||||||
|
|
||||||
|
var changesFiles, failedFiles, processedFiles []string
|
||||||
|
|
||||||
|
changesFiles, failedFiles = deb.CollectChangesFiles(args, reporter)
|
||||||
|
|
||||||
|
for _, path := range changesFiles {
|
||||||
|
var changes *deb.Changes
|
||||||
|
|
||||||
|
changes, err = deb.NewChanges(path)
|
||||||
|
if err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("unable to process file %s: %s", path, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = changes.VerifyAndParse(acceptUnsigned, ignoreSignatures, verifier)
|
||||||
|
if err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("unable to process file %s: %s", changes.ChangesName, err)
|
||||||
|
changes.Cleanup()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = changes.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("unable to process file %s: %s", changes.ChangesName, err)
|
||||||
|
changes.Cleanup()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
repoName := &bytes.Buffer{}
|
||||||
|
err = repoTemplate.Execute(repoName, changes.Stanza)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error applying template to repo: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Progress().Printf("Loading repository %s for changes file %s...\n", repoName.String(), changes.ChangesName)
|
||||||
|
|
||||||
|
var repo *deb.LocalRepo
|
||||||
|
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(repoName.String())
|
||||||
|
if err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("unable to process file %s: %s", changes.ChangesName, err)
|
||||||
|
changes.Cleanup()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUploaders := uploaders
|
||||||
|
if repo.Uploaders != nil {
|
||||||
|
currentUploaders = repo.Uploaders
|
||||||
|
for i := range currentUploaders.Rules {
|
||||||
|
currentUploaders.Rules[i].CompiledCondition, err = query.Parse(currentUploaders.Rules[i].Condition)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing query %s: %s", currentUploaders.Rules[i].Condition, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentUploaders != nil {
|
||||||
|
if err = currentUploaders.IsAllowed(changes); err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("changes file skipped due to uploaders config: %s, keys %#v: %s",
|
||||||
|
changes.ChangesName, changes.SignatureKeys, err)
|
||||||
|
changes.Cleanup()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load repo: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var list *deb.PackageList
|
||||||
|
list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load packages: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
packageFiles, _ := deb.CollectPackageFiles([]string{changes.TempDir}, reporter)
|
||||||
|
|
||||||
|
var restriction deb.PackageQuery
|
||||||
|
|
||||||
|
restriction, err = changes.PackageQuery()
|
||||||
|
if err != nil {
|
||||||
|
failedFiles = append(failedFiles, path)
|
||||||
|
reporter.Warning("unable to process file %s: %s", changes.ChangesName, err)
|
||||||
|
changes.Cleanup()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var processedFiles2, failedFiles2 []string
|
||||||
|
|
||||||
|
processedFiles2, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
||||||
|
context.CollectionFactory().PackageCollection(), reporter, restriction, context.CollectionFactory().ChecksumCollection())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to import package files: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
||||||
|
|
||||||
|
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to save: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = changes.Cleanup()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range failedFiles2 {
|
||||||
|
failedFiles = append(failedFiles, filepath.Join(changes.BasePath, filepath.Base(file)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range processedFiles2 {
|
||||||
|
processedFiles = append(processedFiles, filepath.Join(changes.BasePath, filepath.Base(file)))
|
||||||
|
}
|
||||||
|
|
||||||
|
processedFiles = append(processedFiles, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !noRemoveFiles {
|
||||||
|
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
||||||
|
|
||||||
|
for _, file := range processedFiles {
|
||||||
|
err = os.Remove(file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to remove file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(failedFiles) > 0 {
|
||||||
|
context.Progress().ColoredPrintf("@y[!]@| @!Some files were skipped due to errors:@|")
|
||||||
|
for _, file := range failedFiles {
|
||||||
|
context.Progress().ColoredPrintf(" %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("some files failed to be added")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdRepoInclude() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyRepoInclude,
|
||||||
|
UsageLine: "include <file.changes>|<directory> ...",
|
||||||
|
Short: "add packages to local repositories based on .changes files",
|
||||||
|
Long: `
|
||||||
|
Command include looks for .changes files in list of arguments or specified directories. Each
|
||||||
|
.changes file is verified, parsed, referenced files are put into separate temporary directory
|
||||||
|
and added into local repository. Successfully imported files are removed by default.
|
||||||
|
|
||||||
|
Additionally uploads could be restricted with <uploaders.json> file. Rules in this file control
|
||||||
|
uploads based on GPG key ID of .changes file signature and queries on .changes file fields.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly repo include -repo=foo-release incoming/
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-repo-include", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flag.Bool("no-remove-files", false, "don't 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")
|
||||||
|
cmd.Flag.String("repo", "{{.Distribution}}", "which repo should files go to, defaults to Distribution field of .changes file")
|
||||||
|
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "gpg keyring to use when verifying Release file (could be specified multiple times)")
|
||||||
|
cmd.Flag.Bool("ignore-signatures", false, "disable verification of .changes file signature")
|
||||||
|
cmd.Flag.Bool("accept-unsigned", false, "accept unsigned .changes files")
|
||||||
|
cmd.Flag.String("uploaders-file", "", "path to uploaders.json file")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
+5
-4
@@ -2,9 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyRepoList(cmd *commander.Command, args []string) error {
|
func aptlyRepoList(cmd *commander.Command, args []string) error {
|
||||||
@@ -22,9 +23,9 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
|
|||||||
if raw {
|
if raw {
|
||||||
repos[i] = repo.Name
|
repos[i] = repo.Name
|
||||||
} else {
|
} else {
|
||||||
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
|
repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
|
||||||
|
|||||||
+10
-9
@@ -2,11 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
||||||
@@ -33,7 +34,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
srcRepo *deb.LocalRepo
|
srcRepo *deb.LocalRepo
|
||||||
)
|
)
|
||||||
|
|
||||||
if command == "copy" || command == "move" {
|
if command == "copy" || command == "move" { // nolint: goconst
|
||||||
srcRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(args[0])
|
srcRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to %s: %s", command, err)
|
return fmt.Errorf("unable to %s: %s", command, err)
|
||||||
@@ -49,7 +50,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
srcRefList = srcRepo.RefList()
|
srcRefList = srcRepo.RefList()
|
||||||
} else if command == "import" {
|
} else if command == "import" { // nolint: goconst
|
||||||
var srcRemoteRepo *deb.RemoteRepo
|
var srcRemoteRepo *deb.RemoteRepo
|
||||||
|
|
||||||
srcRemoteRepo, err = context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
|
srcRemoteRepo, err = context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
|
||||||
@@ -114,18 +115,18 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toProcess, err := srcList.Filter(queries, withDeps, dstList, context.DependencyOptions(), architecturesList)
|
toProcess, err := srcList.FilterWithProgress(queries, withDeps, dstList, context.DependencyOptions(), architecturesList, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to %s: %s", command, err)
|
return fmt.Errorf("unable to %s: %s", command, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var verb string
|
var verb string
|
||||||
|
|
||||||
if command == "move" {
|
if command == "move" { // nolint: goconst
|
||||||
verb = "moved"
|
verb = "moved"
|
||||||
} else if command == "copy" {
|
} else if command == "copy" { // nolint: goconst
|
||||||
verb = "copied"
|
verb = "copied"
|
||||||
} else if command == "import" {
|
} else if command == "import" { // nolint: goconst
|
||||||
verb = "imported"
|
verb = "imported"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if command == "move" {
|
if command == "move" { // nolint: goconst
|
||||||
srcList.Remove(p)
|
srcList.Remove(p)
|
||||||
}
|
}
|
||||||
context.Progress().ColoredPrintf("@g[o]@| %s %s", p, verb)
|
context.Progress().ColoredPrintf("@g[o]@| %s %s", p, verb)
|
||||||
@@ -155,7 +156,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to save: %s", err)
|
return fmt.Errorf("unable to save: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if command == "move" {
|
if command == "move" { // nolint: goconst
|
||||||
srcRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(srcList))
|
srcRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(srcList))
|
||||||
|
|
||||||
err = context.CollectionFactory().LocalRepoCollection().Update(srcRepo)
|
err = context.CollectionFactory().LocalRepoCollection().Update(srcRepo)
|
||||||
|
|||||||
@@ -2,6 +2,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/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
+4
-1
@@ -8,11 +8,13 @@ import (
|
|||||||
func makeCmdRepoSearch() *commander.Command {
|
func makeCmdRepoSearch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlySnapshotMirrorRepoSearch,
|
Run: aptlySnapshotMirrorRepoSearch,
|
||||||
UsageLine: "search <name> <package-query>",
|
UsageLine: "search <name> [<package-query>]",
|
||||||
Short: "search repo for packages matching query",
|
Short: "search repo for packages matching query",
|
||||||
Long: `
|
Long: `
|
||||||
Command search displays list of packages in local repository that match package query
|
Command search displays list of packages in local repository that match package query
|
||||||
|
|
||||||
|
If query is not specified, all the packages are displayed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly repo search my-software '$Architecture (i386), Name (% *-dev)'
|
$ aptly repo search my-software '$Architecture (i386), Name (% *-dev)'
|
||||||
@@ -21,6 +23,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
||||||
|
cmd.Flag.String("format", "", "custom format for result printing")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
@@ -29,6 +30,9 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("Comment: %s\n", repo.Comment)
|
fmt.Printf("Comment: %s\n", repo.Comment)
|
||||||
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
|
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
|
||||||
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
|
fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
|
||||||
|
if repo.Uploaders != nil {
|
||||||
|
fmt.Printf("Uploaders: %s\n", repo.Uploaders)
|
||||||
|
}
|
||||||
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)
|
||||||
|
|||||||
+3
-1
@@ -2,6 +2,8 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
ctx "github.com/smira/aptly/context"
|
ctx "github.com/smira/aptly/context"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
@@ -14,7 +16,7 @@ func Run(cmd *commander.Command, cmdArgs []string, initContext bool) (returnCode
|
|||||||
if !ok {
|
if !ok {
|
||||||
panic(r)
|
panic(r)
|
||||||
}
|
}
|
||||||
fmt.Println("ERROR:", fatal.Message)
|
fmt.Fprintln(os.Stderr, "ERROR:", fatal.Message)
|
||||||
returnCode = fatal.ReturnCode
|
returnCode = fatal.ReturnCode
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
+22
-10
@@ -2,16 +2,17 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
|
||||||
"github.com/smira/aptly/deb"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"github.com/smira/commander"
|
|
||||||
"github.com/smira/flag"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyServe(cmd *commander.Command, args []string) error {
|
func aptlyServe(cmd *commander.Command, args []string) error {
|
||||||
@@ -22,6 +23,17 @@ func aptlyServe(cmd *commander.Command, args []string) error {
|
|||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are only two working options for aptly's rootDir:
|
||||||
|
// 1. rootDir does not exist, then we'll create it
|
||||||
|
// 2. rootDir exists and is writable
|
||||||
|
// anything else must fail.
|
||||||
|
// E.g.: Running the service under a different user may lead to a rootDir
|
||||||
|
// that exists but is not usable due to access permissions.
|
||||||
|
err = utils.DirIsAccessible(context.Config().RootDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
|
if context.CollectionFactory().PublishedRepoCollection().Len() == 0 {
|
||||||
fmt.Printf("No published repositories, unable to serve.\n")
|
fmt.Printf("No published repositories, unable to serve.\n")
|
||||||
return nil
|
return nil
|
||||||
@@ -48,9 +60,9 @@ func aptlyServe(cmd *commander.Command, args []string) error {
|
|||||||
published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
|
published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
|
||||||
|
|
||||||
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
|
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
|
||||||
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
|
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
sources = append(sources, repo.String())
|
sources = append(sources, repo.String())
|
||||||
@@ -78,13 +90,13 @@ func aptlyServe(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
|
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
|
||||||
repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
|
repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
|
||||||
|
|
||||||
if utils.StrSliceHasItem(repo.Architectures, "source") {
|
if utils.StrSliceHasItem(repo.Architectures, deb.ArchitectureSource) {
|
||||||
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
|
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
|
||||||
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
|
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publicPath := context.GetPublishedStorage("").(aptly.LocalPublishedStorage).PublicPath()
|
publicPath := context.GetPublishedStorage("").(aptly.FileSystemPublishedStorage).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)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
@@ -12,7 +13,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
|
|||||||
snapshot *deb.Snapshot
|
snapshot *deb.Snapshot
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
|
if len(args) == 4 && args[1] == "from" && args[2] == "mirror" { // nolint: goconst
|
||||||
// aptly snapshot create snap from mirror mirror
|
// aptly snapshot create snap from mirror mirror
|
||||||
var repo *deb.RemoteRepo
|
var repo *deb.RemoteRepo
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create snapshot: %s", err)
|
return fmt.Errorf("unable to create snapshot: %s", err)
|
||||||
}
|
}
|
||||||
} else if len(args) == 4 && args[1] == "from" && args[2] == "repo" {
|
} else if len(args) == 4 && args[1] == "from" && args[2] == "repo" { // nolint: goconst
|
||||||
// aptly snapshot create snap from repo repo
|
// aptly snapshot create snap from repo repo
|
||||||
var repo *deb.LocalRepo
|
var repo *deb.LocalRepo
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotFilter(cmd *commander.Command, args []string) error {
|
func aptlySnapshotFilter(cmd *commander.Command, args []string) error {
|
||||||
@@ -65,7 +66,7 @@ func aptlySnapshotFilter(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter with dependencies as requested
|
// Filter with dependencies as requested
|
||||||
result, err := packageList.Filter(queries, withDeps, nil, context.DependencyOptions(), architecturesList)
|
result, err := packageList.FilterWithProgress(queries, withDeps, nil, context.DependencyOptions(), architecturesList, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to filter: %s", err)
|
return fmt.Errorf("unable to filter: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
|
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
||||||
@@ -95,7 +96,7 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter with dependencies as requested
|
// Filter with dependencies as requested
|
||||||
result, err := sourcePackageList.Filter(queries, !noDeps, packageList, context.DependencyOptions(), architecturesList)
|
result, err := sourcePackageList.FilterWithProgress(queries, !noDeps, packageList, context.DependencyOptions(), architecturesList, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to pull: %s", err)
|
return fmt.Errorf("unable to pull: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
+29
-16
@@ -2,16 +2,21 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/aptly/query"
|
"github.com/smira/aptly/query"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error {
|
func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var (
|
||||||
if len(args) != 2 {
|
err error
|
||||||
|
q deb.PackageQuery
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(args) < 1 || len(args) > 2 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return commander.ErrCommandError
|
return commander.ErrCommandError
|
||||||
}
|
}
|
||||||
@@ -21,8 +26,9 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
|
|
||||||
var reflist *deb.PackageRefList
|
var reflist *deb.PackageRefList
|
||||||
|
|
||||||
if command == "snapshot" {
|
if command == "snapshot" { // nolint: goconst
|
||||||
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(name)
|
var snapshot *deb.Snapshot
|
||||||
|
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
@@ -34,7 +40,8 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
|
|
||||||
reflist = snapshot.RefList()
|
reflist = snapshot.RefList()
|
||||||
} else if command == "mirror" {
|
} else if command == "mirror" {
|
||||||
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(name)
|
var repo *deb.RemoteRepo
|
||||||
|
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
@@ -45,8 +52,9 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
reflist = repo.RefList()
|
reflist = repo.RefList()
|
||||||
} else if command == "repo" {
|
} else if command == "repo" { // nolint: goconst
|
||||||
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
|
var repo *deb.LocalRepo
|
||||||
|
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
@@ -68,10 +76,14 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
|
|
||||||
list.PrepareIndex()
|
list.PrepareIndex()
|
||||||
|
|
||||||
q, err := query.Parse(args[1])
|
if len(args) == 2 {
|
||||||
|
q, err = query.Parse(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
q = &deb.MatchAllQuery{}
|
||||||
|
}
|
||||||
|
|
||||||
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
|
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
|
||||||
architecturesList := []string{}
|
architecturesList := []string{}
|
||||||
@@ -90,8 +102,8 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := list.Filter([]deb.PackageQuery{q}, withDeps,
|
result, err := list.FilterWithProgress([]deb.PackageQuery{q}, withDeps,
|
||||||
nil, context.DependencyOptions(), architecturesList)
|
nil, context.DependencyOptions(), architecturesList, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to search: %s", err)
|
return fmt.Errorf("unable to search: %s", err)
|
||||||
}
|
}
|
||||||
@@ -100,10 +112,8 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
return fmt.Errorf("no results")
|
return fmt.Errorf("no results")
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ForEach(func(p *deb.Package) error {
|
format := context.Flags().Lookup("format").Value.String()
|
||||||
context.Progress().Printf("%s\n", p)
|
PrintPackageList(result, format, "")
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -111,11 +121,13 @@ func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error
|
|||||||
func makeCmdSnapshotSearch() *commander.Command {
|
func makeCmdSnapshotSearch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlySnapshotMirrorRepoSearch,
|
Run: aptlySnapshotMirrorRepoSearch,
|
||||||
UsageLine: "search <name> <package-query>",
|
UsageLine: "search <name> [<package-query>]",
|
||||||
Short: "search snapshot for packages matching query",
|
Short: "search snapshot for packages matching query",
|
||||||
Long: `
|
Long: `
|
||||||
Command search displays list of packages in snapshot that match package query
|
Command search displays list of packages in snapshot that match package query
|
||||||
|
|
||||||
|
If query is not specified, all the packages are displayed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ aptly snapshot search wheezy-main '$Architecture (i386), Name (% *-dev)'
|
$ aptly snapshot search wheezy-main '$Architecture (i386), Name (% *-dev)'
|
||||||
@@ -124,6 +136,7 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
|
||||||
|
cmd.Flag.String("format", "", "custom format for result printing")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
@@ -29,6 +31,38 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
|
|||||||
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
|
fmt.Printf("Created At: %s\n", snapshot.CreatedAt.Format("2006-01-02 15:04:05 MST"))
|
||||||
fmt.Printf("Description: %s\n", snapshot.Description)
|
fmt.Printf("Description: %s\n", snapshot.Description)
|
||||||
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
|
fmt.Printf("Number of packages: %d\n", snapshot.NumPackages())
|
||||||
|
if len(snapshot.SourceIDs) > 0 {
|
||||||
|
fmt.Printf("Sources:\n")
|
||||||
|
for _, sourceID := range snapshot.SourceIDs {
|
||||||
|
var name string
|
||||||
|
if snapshot.SourceKind == deb.SourceSnapshot {
|
||||||
|
var source *deb.Snapshot
|
||||||
|
source, err = context.CollectionFactory().SnapshotCollection().ByUUID(sourceID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = source.Name
|
||||||
|
} else if snapshot.SourceKind == deb.SourceLocalRepo {
|
||||||
|
var source *deb.LocalRepo
|
||||||
|
source, err = context.CollectionFactory().LocalRepoCollection().ByUUID(sourceID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = source.Name
|
||||||
|
} else if snapshot.SourceKind == deb.SourceRemoteRepo {
|
||||||
|
var source *deb.RemoteRepo
|
||||||
|
source, err = context.CollectionFactory().RemoteRepoCollection().ByUUID(sourceID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = source.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
fmt.Printf(" %s [%s]\n", name, snapshot.SourceKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
|
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
|
||||||
if withPackages {
|
if withPackages {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
|
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
|
||||||
@@ -31,25 +32,25 @@ func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
packageList, err := deb.NewPackageListFromRefList(snapshots[0].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
|
packageList, err := deb.NewPackageListFromRefList(snapshots[0].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("unable to load packages: %s", err)
|
return fmt.Errorf("unable to load packages: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourcePackageList := deb.NewPackageList()
|
sourcePackageList := deb.NewPackageList()
|
||||||
err = sourcePackageList.Append(packageList)
|
err = sourcePackageList.Append(packageList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("unable to merge sources: %s", err)
|
return fmt.Errorf("unable to merge sources: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pL *deb.PackageList
|
var pL *deb.PackageList
|
||||||
for i := 1; i < len(snapshots); i++ {
|
for i := 1; i < len(snapshots); i++ {
|
||||||
pL, err = deb.NewPackageListFromRefList(snapshots[i].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
|
pL, err = deb.NewPackageListFromRefList(snapshots[i].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("unable to load packages: %s", err)
|
return fmt.Errorf("unable to load packages: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sourcePackageList.Append(pL)
|
err = sourcePackageList.Append(pL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("unable to merge sources: %s", err)
|
return fmt.Errorf("unable to merge sources: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@ func aptlyTaskRun(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
var finfo os.FileInfo
|
var finfo os.FileInfo
|
||||||
if finfo, err = os.Stat(filename); os.IsNotExist(err) || finfo.IsDir() {
|
if finfo, err = os.Stat(filename); os.IsNotExist(err) || finfo.IsDir() {
|
||||||
return fmt.Errorf("no such file, %s\n", filename)
|
return fmt.Errorf("no such file, %s", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print("Reading file...\n\n")
|
fmt.Print("Reading file...\n\n")
|
||||||
@@ -82,7 +82,7 @@ func aptlyTaskRun(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
for i, command := range cmdList {
|
for i, command := range cmdList {
|
||||||
if !commandErrored {
|
if !commandErrored {
|
||||||
err := context.ReOpenDatabase()
|
err = context.ReOpenDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to reopen DB: %s", err)
|
return fmt.Errorf("failed to reopen DB: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
)
|
)
|
||||||
|
|||||||
+15
-2
@@ -2,14 +2,17 @@ package console
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/cheggaaa/pb"
|
"github.com/cheggaaa/pb"
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/wsxiaoys/terminal/color"
|
"github.com/wsxiaoys/terminal/color"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
codePrint = iota
|
codePrint = iota
|
||||||
|
codePrintStdErr
|
||||||
codeProgress
|
codeProgress
|
||||||
codeHideProgress
|
codeHideProgress
|
||||||
codeStop
|
codeStop
|
||||||
@@ -27,7 +30,6 @@ type printTask struct {
|
|||||||
// Progress is a progress displaying subroutine, it allows to show download and other operations progress
|
// Progress is a progress displaying subroutine, it allows to show download and other operations progress
|
||||||
// mixed with progress bar
|
// mixed with progress bar
|
||||||
type Progress struct {
|
type Progress struct {
|
||||||
stop chan bool
|
|
||||||
stopped chan bool
|
stopped chan bool
|
||||||
queue chan printTask
|
queue chan printTask
|
||||||
bar *pb.ProgressBar
|
bar *pb.ProgressBar
|
||||||
@@ -127,6 +129,11 @@ func (p *Progress) Printf(msg string, a ...interface{}) {
|
|||||||
p.queue <- printTask{code: codePrint, message: fmt.Sprintf(msg, a...)}
|
p.queue <- printTask{code: codePrint, message: fmt.Sprintf(msg, a...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrintfStdErr does printf but in safe manner to stderr
|
||||||
|
func (p *Progress) PrintfStdErr(msg string, a ...interface{}) {
|
||||||
|
p.queue <- printTask{code: codePrintStdErr, message: fmt.Sprintf(msg, a...)}
|
||||||
|
}
|
||||||
|
|
||||||
// ColoredPrintf does printf in colored way + newline
|
// ColoredPrintf does printf in colored way + newline
|
||||||
func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
|
func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
|
||||||
if RunningOnTerminal() {
|
if RunningOnTerminal() {
|
||||||
@@ -182,6 +189,12 @@ func (p *Progress) worker() {
|
|||||||
p.barShown = false
|
p.barShown = false
|
||||||
}
|
}
|
||||||
fmt.Print(task.message)
|
fmt.Print(task.message)
|
||||||
|
case codePrintStdErr:
|
||||||
|
if p.barShown {
|
||||||
|
fmt.Print("\r\033[2K")
|
||||||
|
p.barShown = false
|
||||||
|
}
|
||||||
|
fmt.Fprint(os.Stderr, task.message)
|
||||||
case codeProgress:
|
case codeProgress:
|
||||||
if hasBar {
|
if hasBar {
|
||||||
fmt.Print("\r" + task.message)
|
fmt.Print("\r" + task.message)
|
||||||
|
|||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunningOnTerminal checks whether stdout is terminal
|
// RunningOnTerminal checks whether stdout is terminal
|
||||||
|
|||||||
+102
-43
@@ -3,17 +3,7 @@ package context
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
"math/rand"
|
||||||
"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"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -21,6 +11,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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/pgp"
|
||||||
|
"github.com/smira/aptly/s3"
|
||||||
|
"github.com/smira/aptly/swift"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AptlyContext is a common context shared by all commands
|
// AptlyContext is a common context shared by all commands
|
||||||
@@ -99,7 +102,10 @@ func (context *AptlyContext) config() *utils.ConfigStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
|
fmt.Fprintf(os.Stderr, "Config file not found, creating default config at %s\n\n", configLocations[0])
|
||||||
|
|
||||||
|
// as this is fresh aptly installation, we don't need to support legacy pool locations
|
||||||
|
utils.Config.SkipLegacyPool = true
|
||||||
utils.SaveConfig(configLocations[0], &utils.Config)
|
utils.SaveConfig(configLocations[0], &utils.Config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,6 +154,9 @@ func (context *AptlyContext) DependencyOptions() int {
|
|||||||
if context.lookupOption(context.config().DepFollowSource, "dep-follow-source") {
|
if context.lookupOption(context.config().DepFollowSource, "dep-follow-source") {
|
||||||
context.dependencyOptions |= deb.DepFollowSource
|
context.dependencyOptions |= deb.DepFollowSource
|
||||||
}
|
}
|
||||||
|
if context.lookupOption(context.config().DepVerboseResolve, "dep-verbose-resolve") {
|
||||||
|
context.dependencyOptions |= deb.DepVerboseResolve
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.dependencyOptions
|
return context.dependencyOptions
|
||||||
@@ -200,8 +209,7 @@ func (context *AptlyContext) Downloader() aptly.Downloader {
|
|||||||
if downloadLimit == 0 {
|
if downloadLimit == 0 {
|
||||||
downloadLimit = context.config().DownloadLimit
|
downloadLimit = context.config().DownloadLimit
|
||||||
}
|
}
|
||||||
context.downloader = http.NewDownloader(context.config().DownloadConcurrency,
|
context.downloader = http.NewDownloader(downloadLimit*1024, context._progress())
|
||||||
downloadLimit*1024, context._progress())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.downloader
|
return context.downloader
|
||||||
@@ -232,13 +240,34 @@ func (context *AptlyContext) _database() (database.Storage, error) {
|
|||||||
if context.database == nil {
|
if context.database == nil {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
context.database, err = database.OpenDB(context.dbPath())
|
context.database, err = database.NewDB(context.dbPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't open database: %s", err)
|
return nil, fmt.Errorf("can't instantiate database: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.database, nil
|
tries := context.flags.Lookup("db-open-attempts").Value.Get().(int)
|
||||||
|
const BaseDelay = 10 * time.Second
|
||||||
|
const Jitter = 1 * time.Second
|
||||||
|
|
||||||
|
for ; tries >= 0; tries-- {
|
||||||
|
err := context.database.Open()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "resource temporarily unavailable") {
|
||||||
|
return context.database, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tries > 0 {
|
||||||
|
delay := time.Duration(rand.NormFloat64()*float64(Jitter) + float64(BaseDelay))
|
||||||
|
if delay < 0 {
|
||||||
|
delay = time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
context._progress().PrintfStdErr("Unable to open database, sleeping %s, attempts left %d...\n", delay, tries)
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unable to reopen the DB, maximum number of retries reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseDatabase closes the db temporarily
|
// CloseDatabase closes the db temporarily
|
||||||
@@ -255,26 +284,9 @@ func (context *AptlyContext) CloseDatabase() error {
|
|||||||
|
|
||||||
// ReOpenDatabase reopens the db after close
|
// ReOpenDatabase reopens the db after close
|
||||||
func (context *AptlyContext) ReOpenDatabase() error {
|
func (context *AptlyContext) ReOpenDatabase() error {
|
||||||
context.Lock()
|
_, err := context.Database()
|
||||||
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
|
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
|
// CollectionFactory builds factory producing all kinds of collections
|
||||||
@@ -299,7 +311,7 @@ func (context *AptlyContext) PackagePool() aptly.PackagePool {
|
|||||||
defer context.Unlock()
|
defer context.Unlock()
|
||||||
|
|
||||||
if context.packagePool == nil {
|
if context.packagePool == nil {
|
||||||
context.packagePool = files.NewPackagePool(context.config().RootDir)
|
context.packagePool = files.NewPackagePool(context.config().RootDir, !context.config().SkipLegacyPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.packagePool
|
return context.packagePool
|
||||||
@@ -313,7 +325,14 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto
|
|||||||
publishedStorage, ok := context.publishedStorages[name]
|
publishedStorage, ok := context.publishedStorages[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
publishedStorage = files.NewPublishedStorage(context.config().RootDir)
|
publishedStorage = files.NewPublishedStorage(filepath.Join(context.config().RootDir, "public"), "hardlink", "")
|
||||||
|
} else if strings.HasPrefix(name, "filesystem:") {
|
||||||
|
params, ok := context.config().FileSystemPublishRoots[name[11:]]
|
||||||
|
if !ok {
|
||||||
|
Fatal(fmt.Errorf("published local storage %v not configured", name[6:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
publishedStorage = files.NewPublishedStorage(params.RootDir, params.LinkMethod, params.VerifyMethod)
|
||||||
} else if strings.HasPrefix(name, "s3:") {
|
} else if strings.HasPrefix(name, "s3:") {
|
||||||
params, ok := context.config().S3PublishRoots[name[3:]]
|
params, ok := context.config().S3PublishRoots[name[3:]]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -321,9 +340,11 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
publishedStorage, err = s3.NewPublishedStorage(params.AccessKeyID, params.SecretAccessKey,
|
publishedStorage, err = s3.NewPublishedStorage(
|
||||||
params.Region, params.Bucket, params.ACL, params.Prefix, params.StorageClass,
|
params.AccessKeyID, params.SecretAccessKey, params.SessionToken,
|
||||||
params.EncryptionMethod, params.PlusWorkaround)
|
params.Region, params.Endpoint, params.Bucket, params.ACL, params.Prefix, params.StorageClass,
|
||||||
|
params.EncryptionMethod, params.PlusWorkaround, params.DisableMultiDel,
|
||||||
|
params.ForceSigV2, params.Debug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal(err)
|
Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -335,7 +356,7 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
|
publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
|
||||||
params.AuthURL, params.Tenant, params.TenantID, params.Container, params.Prefix)
|
params.AuthURL, params.Tenant, params.TenantID, params.Domain, params.DomainID, params.TenantDomain, params.TenantDomainID, params.Container, params.Prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal(err)
|
Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -353,6 +374,46 @@ func (context *AptlyContext) UploadPath() string {
|
|||||||
return filepath.Join(context.Config().RootDir, "upload")
|
return filepath.Join(context.Config().RootDir, "upload")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (context *AptlyContext) pgpProvider() string {
|
||||||
|
var provider string
|
||||||
|
|
||||||
|
if context.globalFlags.IsSet("gpg-provider") {
|
||||||
|
provider = context.globalFlags.Lookup("gpg-provider").Value.String()
|
||||||
|
} else {
|
||||||
|
provider = context.config().GpgProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(provider == "gpg" || provider == "internal") { // nolint: goconst
|
||||||
|
Fatal(fmt.Errorf("unknown gpg provider: %v", provider))
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSigner returns Signer with respect to provider
|
||||||
|
func (context *AptlyContext) GetSigner() pgp.Signer {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.pgpProvider() == "gpg" { // nolint: goconst
|
||||||
|
return &pgp.GpgSigner{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pgp.GoSigner{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVerifier returns Verifier with respect to provider
|
||||||
|
func (context *AptlyContext) GetVerifier() pgp.Verifier {
|
||||||
|
context.Lock()
|
||||||
|
defer context.Unlock()
|
||||||
|
|
||||||
|
if context.pgpProvider() == "gpg" { // nolint: goconst
|
||||||
|
return &pgp.GpgVerifier{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pgp.GoVerifier{}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateFlags sets internal copy of flags in the context
|
// UpdateFlags sets internal copy of flags in the context
|
||||||
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
|
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
|
||||||
context.Lock()
|
context.Lock()
|
||||||
@@ -403,7 +464,6 @@ func (context *AptlyContext) Shutdown() {
|
|||||||
context.database = nil
|
context.database = nil
|
||||||
}
|
}
|
||||||
if context.downloader != nil {
|
if context.downloader != nil {
|
||||||
context.downloader.Abort()
|
|
||||||
context.downloader = nil
|
context.downloader = nil
|
||||||
}
|
}
|
||||||
if context.progress != nil {
|
if context.progress != nil {
|
||||||
@@ -418,7 +478,6 @@ func (context *AptlyContext) Cleanup() {
|
|||||||
defer context.Unlock()
|
defer context.Unlock()
|
||||||
|
|
||||||
if context.downloader != nil {
|
if context.downloader != nil {
|
||||||
context.downloader.Shutdown()
|
|
||||||
context.downloader = nil
|
context.downloader = nil
|
||||||
}
|
}
|
||||||
if context.progress != nil {
|
if context.progress != nil {
|
||||||
|
|||||||
+79
-10
@@ -4,6 +4,9 @@ package database
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
@@ -16,18 +19,25 @@ var (
|
|||||||
ErrNotFound = errors.New("key not found")
|
ErrNotFound = errors.New("key not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StorageProcessor is a function to process one single storage entry
|
||||||
|
type StorageProcessor func(key []byte, value []byte) error
|
||||||
|
|
||||||
// Storage is an interface to KV storage
|
// Storage is an interface to KV storage
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
|
CreateTemporary() (Storage, error)
|
||||||
Get(key []byte) ([]byte, error)
|
Get(key []byte) ([]byte, error)
|
||||||
Put(key []byte, value []byte) error
|
Put(key []byte, value []byte) error
|
||||||
Delete(key []byte) error
|
Delete(key []byte) error
|
||||||
|
HasPrefix(prefix []byte) bool
|
||||||
|
ProcessByPrefix(prefix []byte, proc StorageProcessor) error
|
||||||
KeysByPrefix(prefix []byte) [][]byte
|
KeysByPrefix(prefix []byte) [][]byte
|
||||||
FetchByPrefix(prefix []byte) [][]byte
|
FetchByPrefix(prefix []byte) [][]byte
|
||||||
|
Open() error
|
||||||
Close() error
|
Close() error
|
||||||
ReOpen() error
|
|
||||||
StartBatch()
|
StartBatch()
|
||||||
FinishBatch() error
|
FinishBatch() error
|
||||||
CompactDB() error
|
CompactDB() error
|
||||||
|
Drop() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type levelDB struct {
|
type levelDB struct {
|
||||||
@@ -41,26 +51,39 @@ var (
|
|||||||
_ Storage = &levelDB{}
|
_ Storage = &levelDB{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func internalOpen(path string) (*leveldb.DB, error) {
|
func internalOpen(path string, throttleCompaction bool) (*leveldb.DB, error) {
|
||||||
o := &opt.Options{
|
o := &opt.Options{
|
||||||
Filter: filter.NewBloomFilter(10),
|
Filter: filter.NewBloomFilter(10),
|
||||||
|
OpenFilesCacheCapacity: 256,
|
||||||
|
}
|
||||||
|
|
||||||
|
if throttleCompaction {
|
||||||
|
o.CompactionL0Trigger = 32
|
||||||
|
o.WriteL0PauseTrigger = 96
|
||||||
|
o.WriteL0SlowdownTrigger = 64
|
||||||
}
|
}
|
||||||
|
|
||||||
return leveldb.OpenFile(path, o)
|
return leveldb.OpenFile(path, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDB opens (creates) LevelDB database
|
// NewDB creates new instance of DB, but doesn't open it (yet)
|
||||||
func OpenDB(path string) (Storage, error) {
|
func NewDB(path string) (Storage, error) {
|
||||||
db, err := internalOpen(path)
|
return &levelDB{path: path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOpenDB creates new instance of DB and opens it
|
||||||
|
func NewOpenDB(path string) (Storage, error) {
|
||||||
|
db, err := NewDB(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &levelDB{db: db, path: path}, nil
|
|
||||||
|
return db, db.Open()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecoverDB recovers LevelDB database from corruption
|
// RecoverDB recovers LevelDB database from corruption
|
||||||
func RecoverDB(path string) error {
|
func RecoverDB(path string) error {
|
||||||
stor, err := storage.OpenFile(path)
|
stor, err := storage.OpenFile(path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -76,6 +99,20 @@ func RecoverDB(path string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTemporary creates new DB of the same type in temp dir
|
||||||
|
func (l *levelDB) CreateTemporary() (Storage, error) {
|
||||||
|
tempdir, err := ioutil.TempDir("", "aptly")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := internalOpen(tempdir, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &levelDB{db: db, path: tempdir}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get key value from database
|
// Get key value from database
|
||||||
func (l *levelDB) Get(key []byte) ([]byte, error) {
|
func (l *levelDB) Get(key []byte) ([]byte, error) {
|
||||||
value, err := l.db.Get(key, nil)
|
value, err := l.db.Get(key, nil)
|
||||||
@@ -151,6 +188,29 @@ func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPrefix checks whether it can find any key with given prefix and returns true if one exists
|
||||||
|
func (l *levelDB) HasPrefix(prefix []byte) bool {
|
||||||
|
iterator := l.db.NewIterator(nil, nil)
|
||||||
|
defer iterator.Release()
|
||||||
|
return iterator.Seek(prefix) && bytes.HasPrefix(iterator.Key(), prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessByPrefix iterates through all entries where key starts with prefix and calls
|
||||||
|
// StorageProcessor on key value pair
|
||||||
|
func (l *levelDB) ProcessByPrefix(prefix []byte, proc StorageProcessor) error {
|
||||||
|
iterator := l.db.NewIterator(nil, nil)
|
||||||
|
defer iterator.Release()
|
||||||
|
|
||||||
|
for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
|
||||||
|
err := proc(iterator.Key(), iterator.Value())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Close finishes DB work
|
// Close finishes DB work
|
||||||
func (l *levelDB) Close() error {
|
func (l *levelDB) Close() error {
|
||||||
if l.db == nil {
|
if l.db == nil {
|
||||||
@@ -161,14 +221,14 @@ func (l *levelDB) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reopen tries to re-open the database
|
// Reopen tries to open (re-open) the database
|
||||||
func (l *levelDB) ReOpen() error {
|
func (l *levelDB) Open() error {
|
||||||
if l.db != nil {
|
if l.db != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
l.db, err = internalOpen(l.path)
|
l.db, err = internalOpen(l.path, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,3 +256,12 @@ func (l *levelDB) FinishBatch() error {
|
|||||||
func (l *levelDB) CompactDB() error {
|
func (l *levelDB) CompactDB() error {
|
||||||
return l.db.CompactRange(util.Range{})
|
return l.db.CompactRange(util.Range{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop removes all the DB files (DANGEROUS!)
|
||||||
|
func (l *levelDB) Drop() error {
|
||||||
|
if l.db != nil {
|
||||||
|
return errors.New("DB is still open")
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.RemoveAll(l.path)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (s *LevelDBSuite) SetUpTest(c *C) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
s.path = c.MkDir()
|
s.path = c.MkDir()
|
||||||
s.db, err = OpenDB(s.path)
|
s.db, err = NewOpenDB(s.path)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ func (s *LevelDBSuite) TestRecoverDB(c *C) {
|
|||||||
err = RecoverDB(s.path)
|
err = RecoverDB(s.path)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
s.db, err = OpenDB(s.path)
|
s.db, err = NewOpenDB(s.path)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
result, err := s.db.Get(key)
|
result, err := s.db.Get(key)
|
||||||
@@ -71,6 +71,29 @@ func (s *LevelDBSuite) TestGetPut(c *C) {
|
|||||||
c.Assert(result, DeepEquals, value)
|
c.Assert(result, DeepEquals, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LevelDBSuite) TestTemporaryDelete(c *C) {
|
||||||
|
var (
|
||||||
|
key = []byte("key")
|
||||||
|
value = []byte("value")
|
||||||
|
)
|
||||||
|
|
||||||
|
err := s.db.Put(key, value)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
temp, err := s.db.CreateTemporary()
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
c.Check(s.db.HasPrefix([]byte(nil)), Equals, true)
|
||||||
|
c.Check(temp.HasPrefix([]byte(nil)), Equals, false)
|
||||||
|
|
||||||
|
err = temp.Put(key, value)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(temp.HasPrefix([]byte(nil)), Equals, true)
|
||||||
|
|
||||||
|
c.Assert(temp.Close(), IsNil)
|
||||||
|
c.Assert(temp.Drop(), IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *LevelDBSuite) TestDelete(c *C) {
|
func (s *LevelDBSuite) TestDelete(c *C) {
|
||||||
var (
|
var (
|
||||||
key = []byte("key")
|
key = []byte("key")
|
||||||
@@ -107,10 +130,41 @@ func (s *LevelDBSuite) TestByPrefix(c *C) {
|
|||||||
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
|
c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
|
||||||
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
|
c.Check(s.db.KeysByPrefix([]byte{0x80}), DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
|
||||||
|
|
||||||
|
keys := [][]byte{}
|
||||||
|
values := [][]byte{}
|
||||||
|
|
||||||
|
c.Check(s.db.ProcessByPrefix([]byte{0x80}, func(k, v []byte) error {
|
||||||
|
keys = append(keys, append([]byte(nil), k...))
|
||||||
|
values = append(values, append([]byte(nil), v...))
|
||||||
|
return nil
|
||||||
|
}), IsNil)
|
||||||
|
|
||||||
|
c.Check(values, DeepEquals, [][]byte{{0x01}, {0x02}, {0x03}})
|
||||||
|
c.Check(keys, DeepEquals, [][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}})
|
||||||
|
|
||||||
|
c.Check(s.db.ProcessByPrefix([]byte{0x80}, func(k, v []byte) error {
|
||||||
|
return ErrNotFound
|
||||||
|
}), Equals, ErrNotFound)
|
||||||
|
|
||||||
|
c.Check(s.db.ProcessByPrefix([]byte{0xa0}, func(k, v []byte) error {
|
||||||
|
return ErrNotFound
|
||||||
|
}), IsNil)
|
||||||
|
|
||||||
c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
|
c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
|
||||||
c.Check(s.db.KeysByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
|
c.Check(s.db.KeysByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LevelDBSuite) TestHasPrefix(c *C) {
|
||||||
|
c.Check(s.db.HasPrefix([]byte(nil)), Equals, false)
|
||||||
|
c.Check(s.db.HasPrefix([]byte{0x80}), Equals, false)
|
||||||
|
|
||||||
|
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
|
||||||
|
|
||||||
|
c.Check(s.db.HasPrefix([]byte(nil)), Equals, true)
|
||||||
|
c.Check(s.db.HasPrefix([]byte{0x80}), Equals, true)
|
||||||
|
c.Check(s.db.HasPrefix([]byte{0x79}), Equals, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *LevelDBSuite) TestBatch(c *C) {
|
func (s *LevelDBSuite) TestBatch(c *C) {
|
||||||
var (
|
var (
|
||||||
key = []byte("key")
|
key = []byte("key")
|
||||||
@@ -169,7 +223,7 @@ func (s *LevelDBSuite) TestReOpen(c *C) {
|
|||||||
err = s.db.Close()
|
err = s.db.Close()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
err = s.db.ReOpen()
|
err = s.db.Open()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
result, err := s.db.Get(key)
|
result, err := s.db.Get(key)
|
||||||
|
|||||||
+290
@@ -0,0 +1,290 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Changes is a result of .changes file parsing
|
||||||
|
type Changes struct {
|
||||||
|
Changes string
|
||||||
|
Distribution string
|
||||||
|
Files PackageFiles
|
||||||
|
BasePath, ChangesName string
|
||||||
|
TempDir string
|
||||||
|
Source string
|
||||||
|
Binary []string
|
||||||
|
Architectures []string
|
||||||
|
Stanza Stanza
|
||||||
|
SignatureKeys []pgp.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChanges moves .changes file into temporary directory and creates Changes structure
|
||||||
|
func NewChanges(path string) (*Changes, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
c := &Changes{
|
||||||
|
BasePath: filepath.Dir(path),
|
||||||
|
ChangesName: filepath.Base(path),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.TempDir, err = ioutil.TempDir(os.TempDir(), "aptly")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy .changes file into temporary directory
|
||||||
|
err = utils.CopyFile(filepath.Join(c.BasePath, c.ChangesName), filepath.Join(c.TempDir, c.ChangesName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyAndParse does optional signature verification and parses changes files
|
||||||
|
func (c *Changes) VerifyAndParse(acceptUnsigned, ignoreSignature bool, verifier pgp.Verifier) error {
|
||||||
|
input, err := os.Open(filepath.Join(c.TempDir, c.ChangesName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer input.Close()
|
||||||
|
|
||||||
|
isClearSigned, err := verifier.IsClearSigned(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Seek(0, 0)
|
||||||
|
|
||||||
|
if !isClearSigned && !acceptUnsigned {
|
||||||
|
return fmt.Errorf(".changes file is not signed and unsigned processing hasn't been enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isClearSigned && !ignoreSignature {
|
||||||
|
var keyInfo *pgp.KeyInfo
|
||||||
|
keyInfo, err = verifier.VerifyClearsigned(input, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
input.Seek(0, 0)
|
||||||
|
|
||||||
|
c.SignatureKeys = keyInfo.GoodKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
var text io.ReadCloser
|
||||||
|
|
||||||
|
if isClearSigned {
|
||||||
|
text, err = verifier.ExtractClearsigned(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer text.Close()
|
||||||
|
} else {
|
||||||
|
text = input
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := NewControlFileReader(text)
|
||||||
|
c.Stanza, err = reader.ReadStanza(false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Distribution = c.Stanza["Distribution"]
|
||||||
|
c.Changes = c.Stanza["Changes"]
|
||||||
|
c.Source = c.Stanza["Source"]
|
||||||
|
c.Binary = strings.Fields(c.Stanza["Binary"])
|
||||||
|
c.Architectures = strings.Fields(c.Stanza["Architecture"])
|
||||||
|
|
||||||
|
c.Files, err = c.Files.ParseSumFields(c.Stanza)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare creates temporary directory, copies file there and verifies checksums
|
||||||
|
func (c *Changes) Prepare() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, file := range c.Files {
|
||||||
|
if filepath.Dir(file.Filename) != "." {
|
||||||
|
return fmt.Errorf("file is not in the same folder as .changes file: %s", file.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Filename = filepath.Base(file.Filename)
|
||||||
|
|
||||||
|
err = utils.CopyFile(filepath.Join(c.BasePath, file.Filename), filepath.Join(c.TempDir, file.Filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range c.Files {
|
||||||
|
var info utils.ChecksumInfo
|
||||||
|
|
||||||
|
info, err = utils.ChecksumsForFile(filepath.Join(c.TempDir, file.Filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Size != file.Checksums.Size {
|
||||||
|
return fmt.Errorf("size mismatch: expected %v != obtained %v", file.Checksums.Size, info.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.MD5 != file.Checksums.MD5 {
|
||||||
|
return fmt.Errorf("checksum mismatch MD5: expected %v != obtained %v", file.Checksums.MD5, info.MD5)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.SHA1 != file.Checksums.SHA1 {
|
||||||
|
return fmt.Errorf("checksum mismatch SHA1: expected %v != obtained %v", file.Checksums.SHA1, info.SHA1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.SHA256 != file.Checksums.SHA256 {
|
||||||
|
return fmt.Errorf("checksum mismatch SHA256 expected %v != obtained %v", file.Checksums.SHA256, info.SHA256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup removes all temporary files
|
||||||
|
func (c *Changes) Cleanup() error {
|
||||||
|
if c.TempDir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.RemoveAll(c.TempDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageQuery returns query that every package should match to be included
|
||||||
|
func (c *Changes) PackageQuery() (PackageQuery, error) {
|
||||||
|
var archQuery PackageQuery = &FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: ""}
|
||||||
|
for _, arch := range c.Architectures {
|
||||||
|
archQuery = &OrQuery{L: &FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: arch}, R: archQuery}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if c.Source is empty, this would never match
|
||||||
|
sourceQuery := &AndQuery{
|
||||||
|
L: &FieldQuery{Field: "$PackageType", Relation: VersionEqual, Value: ArchitectureSource},
|
||||||
|
R: &FieldQuery{Field: "Name", Relation: VersionEqual, Value: c.Source},
|
||||||
|
}
|
||||||
|
|
||||||
|
var binaryQuery PackageQuery
|
||||||
|
if len(c.Binary) > 0 {
|
||||||
|
binaryQuery = &FieldQuery{Field: "Name", Relation: VersionEqual, Value: c.Binary[0]}
|
||||||
|
// matching debug ddeb packages, they're not present in the Binary field
|
||||||
|
var ddebQuery PackageQuery = &FieldQuery{Field: "Name", Relation: VersionEqual, Value: fmt.Sprintf("%s-dbgsym", c.Binary[0])}
|
||||||
|
|
||||||
|
for _, binary := range c.Binary[1:] {
|
||||||
|
binaryQuery = &OrQuery{
|
||||||
|
L: &FieldQuery{Field: "Name", Relation: VersionEqual, Value: binary},
|
||||||
|
R: binaryQuery,
|
||||||
|
}
|
||||||
|
ddebQuery = &OrQuery{
|
||||||
|
L: &FieldQuery{Field: "Name", Relation: VersionEqual, Value: fmt.Sprintf("%s-dbgsym", binary)},
|
||||||
|
R: ddebQuery,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ddebQuery = &AndQuery{
|
||||||
|
L: &FieldQuery{Field: "Source", Relation: VersionEqual, Value: c.Source},
|
||||||
|
R: ddebQuery,
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryQuery = &OrQuery{
|
||||||
|
L: binaryQuery,
|
||||||
|
R: ddebQuery,
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryQuery = &AndQuery{
|
||||||
|
L: &NotQuery{Q: &FieldQuery{Field: "$PackageType", Relation: VersionEqual, Value: ArchitectureSource}},
|
||||||
|
R: binaryQuery}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameQuery PackageQuery
|
||||||
|
if binaryQuery == nil {
|
||||||
|
nameQuery = sourceQuery
|
||||||
|
} else {
|
||||||
|
nameQuery = &OrQuery{L: sourceQuery, R: binaryQuery}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AndQuery{L: archQuery, R: nameQuery}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetField implements PackageLike interface
|
||||||
|
func (c *Changes) GetField(field string) string {
|
||||||
|
return c.Stanza[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesDependency implements PackageLike interface
|
||||||
|
func (c *Changes) MatchesDependency(d Dependency) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesArchitecture implements PackageLike interface
|
||||||
|
func (c *Changes) MatchesArchitecture(arch string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName implements PackageLike interface
|
||||||
|
func (c *Changes) GetName() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion implements PackageLike interface
|
||||||
|
func (c *Changes) GetVersion() string {
|
||||||
|
return ""
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArchitecture implements PackageLike interface
|
||||||
|
func (c *Changes) GetArchitecture() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectChangesFiles walks filesystem collecting all .changes files
|
||||||
|
func CollectChangesFiles(locations []string, reporter aptly.ResultReporter) (changesFiles, failedFiles []string) {
|
||||||
|
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(), ".changes") {
|
||||||
|
changesFiles = append(changesFiles, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err2 != nil {
|
||||||
|
reporter.Warning("Unable to process %s: %s", location, err2)
|
||||||
|
failedFiles = append(failedFiles, location)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if strings.HasSuffix(info.Name(), ".changes") {
|
||||||
|
changesFiles = append(changesFiles, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(changesFiles)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChangesSuite struct {
|
||||||
|
Dir, Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&ChangesSuite{})
|
||||||
|
|
||||||
|
func (s *ChangesSuite) SetUpTest(c *C) {
|
||||||
|
s.Dir = c.MkDir()
|
||||||
|
s.Path = filepath.Join(s.Dir, "calamares.changes")
|
||||||
|
|
||||||
|
f, err := os.Create(s.Path)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
f.WriteString(changesFile)
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChangesSuite) TestParseAndVerify(c *C) {
|
||||||
|
changes, err := NewChanges(s.Path)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
err = changes.VerifyAndParse(true, true, &NullVerifier{})
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
|
c.Check(changes.Distribution, Equals, "sid")
|
||||||
|
c.Check(changes.Files, HasLen, 4)
|
||||||
|
c.Check(changes.Files[0].Filename, Equals, "calamares_0+git20141127.99.dsc")
|
||||||
|
c.Check(changes.Files[0].Checksums.Size, Equals, int64(1106))
|
||||||
|
c.Check(changes.Files[0].Checksums.MD5, Equals, "05fd8f3ffe8f362c5ef9bad2f936a56e")
|
||||||
|
c.Check(changes.Files[0].Checksums.SHA1, Equals, "79f10e955dab6eb25b7f7bae18213f367a3a0396")
|
||||||
|
c.Check(changes.Files[0].Checksums.SHA256, Equals, "35b3280a7b1ffe159a276128cb5c408d687318f60ecbb8ab6dedb2e49c4e82dc")
|
||||||
|
c.Check(changes.BasePath, Equals, s.Dir)
|
||||||
|
c.Check(changes.Architectures, DeepEquals, []string{"source", "amd64"})
|
||||||
|
c.Check(changes.Source, Equals, "calamares")
|
||||||
|
c.Check(changes.Binary, DeepEquals, []string{"calamares", "calamares-dbg"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChangesSuite) TestPackageQuery(c *C) {
|
||||||
|
changes, err := NewChanges(s.Path)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
err = changes.VerifyAndParse(true, true, &NullVerifier{})
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
|
q, err := changes.PackageQuery()
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
|
c.Check(q.String(), Equals,
|
||||||
|
"(($Architecture (= amd64)) | (($Architecture (= source)) | ($Architecture (= )))), ((($PackageType (= source)), (Name (= calamares))) | ((!($PackageType (= source))), (((Name (= calamares-dbg)) | (Name (= calamares))) | ((Source (= calamares)), ((Name (= calamares-dbg-dbgsym)) | (Name (= calamares-dbgsym)))))))")
|
||||||
|
}
|
||||||
|
|
||||||
|
var changesFile = `Format: 1.8
|
||||||
|
Date: Thu, 27 Nov 2014 13:24:53 +0000
|
||||||
|
Source: calamares
|
||||||
|
Binary: calamares calamares-dbg
|
||||||
|
Architecture: source amd64
|
||||||
|
Version: 0+git20141127.99
|
||||||
|
Distribution: sid
|
||||||
|
Urgency: medium
|
||||||
|
Maintainer: Rohan Garg <rohan@kde.org>
|
||||||
|
Changed-By: Rohan <rohan@kde.org>
|
||||||
|
Description:
|
||||||
|
calamares - distribution-independent installer framework
|
||||||
|
calamares-dbg - distribution-independent installer framework -- debug symbols
|
||||||
|
Changes:
|
||||||
|
calamares (0+git20141127.99) sid; urgency=medium
|
||||||
|
.
|
||||||
|
* Update from git
|
||||||
|
Checksums-Sha1:
|
||||||
|
79f10e955dab6eb25b7f7bae18213f367a3a0396 1106 calamares_0+git20141127.99.dsc
|
||||||
|
294c28e2c8e34e72ca9ee0d9da5c14f3bf4188db 2694800 calamares_0+git20141127.99.tar.xz
|
||||||
|
d6c26c04b5407c7511f61cb3e3de60c4a1d6c4ff 1698924 calamares_0+git20141127.99_amd64.deb
|
||||||
|
a3da632d193007b0d4a1aff73159fde1b532d7a8 12835902 calamares-dbg_0+git20141127.99_amd64.deb
|
||||||
|
Checksums-Sha256:
|
||||||
|
35b3280a7b1ffe159a276128cb5c408d687318f60ecbb8ab6dedb2e49c4e82dc 1106 calamares_0+git20141127.99.dsc
|
||||||
|
5576b9caaf814564830f95561227e4f04ee87b31da22c1371aab155cbf7ce395 2694800 calamares_0+git20141127.99.tar.xz
|
||||||
|
2e6e2f232ed7ffe52369928ebdf5436d90feb37840286ffba79e87d57a43a2e9 1698924 calamares_0+git20141127.99_amd64.deb
|
||||||
|
8dd926080ed7bad2e2439e37e49ce12d5f1357c5041b7da4d860a1041f878a8a 12835902 calamares-dbg_0+git20141127.99_amd64.deb
|
||||||
|
Files:
|
||||||
|
05fd8f3ffe8f362c5ef9bad2f936a56e 1106 devel optional calamares_0+git20141127.99.dsc
|
||||||
|
097e55c81abd8e5f30bb2eed90c2c1e9 2694800 devel optional calamares_0+git20141127.99.tar.xz
|
||||||
|
827fb3b12534241e119815d331e8197b 1698924 devel optional calamares_0+git20141127.99_amd64.deb
|
||||||
|
e6f8ce70f564d1f68cb57758b15b13e3 12835902 debug optional calamares-dbg_0+git20141127.99_amd64.deb`
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChecksumCollection does management of ChecksumInfo in DB
|
||||||
|
type ChecksumCollection struct {
|
||||||
|
db database.Storage
|
||||||
|
codecHandle *codec.MsgpackHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChecksumCollection creates new ChecksumCollection and binds it to database
|
||||||
|
func NewChecksumCollection(db database.Storage) *ChecksumCollection {
|
||||||
|
return &ChecksumCollection{
|
||||||
|
db: db,
|
||||||
|
codecHandle: &codec.MsgpackHandle{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (collection *ChecksumCollection) dbKey(path string) []byte {
|
||||||
|
return []byte("C" + path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get finds checksums in DB by path
|
||||||
|
func (collection *ChecksumCollection) Get(path string) (*utils.ChecksumInfo, error) {
|
||||||
|
encoded, err := collection.db.Get(collection.dbKey(path))
|
||||||
|
if err != nil {
|
||||||
|
if err == database.ErrNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &utils.ChecksumInfo{}
|
||||||
|
|
||||||
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
|
err = decoder.Decode(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update adds or updates information about checksum in DB
|
||||||
|
func (collection *ChecksumCollection) Update(path string, c *utils.ChecksumInfo) error {
|
||||||
|
var encodeBuffer bytes.Buffer
|
||||||
|
|
||||||
|
encoder := codec.NewEncoder(&encodeBuffer, collection.codecHandle)
|
||||||
|
err := encoder.Encode(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection.db.Put(collection.dbKey(path), encodeBuffer.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check interface
|
||||||
|
var (
|
||||||
|
_ aptly.ChecksumStorage = &ChecksumCollection{}
|
||||||
|
)
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChecksumCollectionSuite struct {
|
||||||
|
collection *ChecksumCollection
|
||||||
|
c utils.ChecksumInfo
|
||||||
|
db database.Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&ChecksumCollectionSuite{})
|
||||||
|
|
||||||
|
func (s *ChecksumCollectionSuite) SetUpTest(c *C) {
|
||||||
|
s.c = utils.ChecksumInfo{
|
||||||
|
Size: 124,
|
||||||
|
MD5: "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||||
|
SHA1: "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||||
|
SHA256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
}
|
||||||
|
s.db, _ = database.NewOpenDB(c.MkDir())
|
||||||
|
s.collection = NewChecksumCollection(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChecksumCollectionSuite) TearDownTest(c *C) {
|
||||||
|
s.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChecksumCollectionSuite) TestFlow(c *C) {
|
||||||
|
// checksum not stored
|
||||||
|
checksum, err := s.collection.Get("some/path")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(checksum, IsNil)
|
||||||
|
|
||||||
|
// store checksum
|
||||||
|
err = s.collection.Update("some/path", &s.c)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
// load it back
|
||||||
|
checksum, err = s.collection.Get("some/path")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(*checksum, DeepEquals, s.c)
|
||||||
|
}
|
||||||
+23
-1
@@ -1,8 +1,9 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/database"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CollectionFactory is a single place to generate all desired collections
|
// CollectionFactory is a single place to generate all desired collections
|
||||||
@@ -14,6 +15,7 @@ type CollectionFactory struct {
|
|||||||
snapshots *SnapshotCollection
|
snapshots *SnapshotCollection
|
||||||
localRepos *LocalRepoCollection
|
localRepos *LocalRepoCollection
|
||||||
publishedRepos *PublishedRepoCollection
|
publishedRepos *PublishedRepoCollection
|
||||||
|
checksums *ChecksumCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollectionFactory creates new factory
|
// NewCollectionFactory creates new factory
|
||||||
@@ -21,6 +23,13 @@ func NewCollectionFactory(db database.Storage) *CollectionFactory {
|
|||||||
return &CollectionFactory{Mutex: &sync.Mutex{}, db: db}
|
return &CollectionFactory{Mutex: &sync.Mutex{}, db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TemporaryDB creates new temporary DB
|
||||||
|
//
|
||||||
|
// DB should be closed/droped after being used
|
||||||
|
func (factory *CollectionFactory) TemporaryDB() (database.Storage, error) {
|
||||||
|
return factory.db.CreateTemporary()
|
||||||
|
}
|
||||||
|
|
||||||
// PackageCollection returns (or creates) new PackageCollection
|
// PackageCollection returns (or creates) new PackageCollection
|
||||||
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
|
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
|
||||||
factory.Lock()
|
factory.Lock()
|
||||||
@@ -81,6 +90,18 @@ func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollec
|
|||||||
return factory.publishedRepos
|
return factory.publishedRepos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChecksumCollection returns (or creates) new ChecksumCollection
|
||||||
|
func (factory *CollectionFactory) ChecksumCollection() *ChecksumCollection {
|
||||||
|
factory.Lock()
|
||||||
|
defer factory.Unlock()
|
||||||
|
|
||||||
|
if factory.checksums == nil {
|
||||||
|
factory.checksums = NewChecksumCollection(factory.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory.checksums
|
||||||
|
}
|
||||||
|
|
||||||
// Flush removes all references to collections, so that memory could be reclaimed
|
// Flush removes all references to collections, so that memory could be reclaimed
|
||||||
func (factory *CollectionFactory) Flush() {
|
func (factory *CollectionFactory) Flush() {
|
||||||
factory.Lock()
|
factory.Lock()
|
||||||
@@ -91,4 +112,5 @@ func (factory *CollectionFactory) Flush() {
|
|||||||
factory.remoteRepos = nil
|
factory.remoteRepos = nil
|
||||||
factory.publishedRepos = nil
|
factory.publishedRepos = nil
|
||||||
factory.packages = nil
|
factory.packages = nil
|
||||||
|
factory.checksums = nil
|
||||||
}
|
}
|
||||||
|
|||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/go-uuid/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContentsIndex calculates mapping from files to packages, with sorting and aggregation
|
||||||
|
type ContentsIndex struct {
|
||||||
|
db database.Storage
|
||||||
|
prefix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContentsIndex creates empty ContentsIndex
|
||||||
|
func NewContentsIndex(db database.Storage) *ContentsIndex {
|
||||||
|
return &ContentsIndex{
|
||||||
|
db: db,
|
||||||
|
prefix: []byte(uuid.New()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds package to contents index, calculating package contents as required
|
||||||
|
func (index *ContentsIndex) Push(p *Package, packagePool aptly.PackagePool, progress aptly.Progress) error {
|
||||||
|
contents := p.Contents(packagePool, progress)
|
||||||
|
qualifiedName := []byte(p.QualifiedName())
|
||||||
|
|
||||||
|
for _, path := range contents {
|
||||||
|
// for performance reasons we only write to leveldb during push.
|
||||||
|
// merging of qualified names per path will be done in WriteTo
|
||||||
|
err := index.db.Put(append(append(append(index.prefix, []byte(path)...), byte(0)), qualifiedName...), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty checks whether index contains no packages
|
||||||
|
func (index *ContentsIndex) Empty() bool {
|
||||||
|
return !index.db.HasPrefix(index.prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo dumps sorted mapping of files to qualified package names
|
||||||
|
func (index *ContentsIndex) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
// For performance reasons push method wrote on key per path and package
|
||||||
|
// in this method we now need to merge all packages which have the same path
|
||||||
|
// and write it to contents index file
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
|
||||||
|
nn, err := fmt.Fprintf(w, "%s %s\n", "FILE", "LOCATION")
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixLen := len(index.prefix)
|
||||||
|
|
||||||
|
var (
|
||||||
|
currentPath []byte
|
||||||
|
currentPkgs [][]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
err = index.db.ProcessByPrefix(index.prefix, func(key []byte, value []byte) error {
|
||||||
|
// cut prefix
|
||||||
|
key = key[prefixLen:]
|
||||||
|
|
||||||
|
i := bytes.Index(key, []byte{0})
|
||||||
|
if i == -1 {
|
||||||
|
return errors.New("corrupted index entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := key[:i]
|
||||||
|
pkg := key[i+1:]
|
||||||
|
|
||||||
|
if !bytes.Equal(path, currentPath) {
|
||||||
|
if currentPath != nil {
|
||||||
|
nn, err = w.Write(append(currentPath, ' '))
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = w.Write(bytes.Join(currentPkgs, []byte{','}))
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = w.Write([]byte{'\n'})
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath = append([]byte(nil), path...)
|
||||||
|
currentPkgs = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPkgs = append(currentPkgs, append([]byte(nil), pkg...))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentPath != nil {
|
||||||
|
nn, err = w.Write(append(currentPath, ' '))
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = w.Write(bytes.Join(currentPkgs, []byte{','}))
|
||||||
|
n += int64(nn)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = w.Write([]byte{'\n'})
|
||||||
|
n += int64(nn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
+105
-15
@@ -3,13 +3,27 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"compress/bzip2"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mkrautz/goar"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/h2non/filetype/matchers"
|
||||||
|
"github.com/mkrautz/goar"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
|
"github.com/smira/go-xz"
|
||||||
|
"github.com/smira/lzma"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Source kinds
|
||||||
|
const (
|
||||||
|
SourceSnapshot = "snapshot"
|
||||||
|
SourceLocalRepo = "local"
|
||||||
|
SourceRemoteRepo = "repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetControlFileFromDeb reads control file from deb package
|
// GetControlFileFromDeb reads control file from deb package
|
||||||
@@ -24,16 +38,16 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
|
|||||||
for {
|
for {
|
||||||
header, err := library.Next()
|
header, err := library.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil, fmt.Errorf("unable to find control.tar.gz part")
|
return nil, fmt.Errorf("unable to find control.tar.gz part in package %s", packageFile)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to read .deb archive: %s", err)
|
return nil, fmt.Errorf("unable to read .deb archive %s: %s", packageFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if header.Name == "control.tar.gz" {
|
if header.Name == "control.tar.gz" {
|
||||||
ungzip, err := gzip.NewReader(library)
|
ungzip, err := gzip.NewReader(library)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to ungzip: %s", err)
|
return nil, fmt.Errorf("unable to ungzip control file from %s. Error: %s", packageFile, err)
|
||||||
}
|
}
|
||||||
defer ungzip.Close()
|
defer ungzip.Close()
|
||||||
|
|
||||||
@@ -41,15 +55,15 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
|
|||||||
for {
|
for {
|
||||||
tarHeader, err := untar.Next()
|
tarHeader, err := untar.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil, fmt.Errorf("unable to find control file")
|
return nil, fmt.Errorf("unable to find control file in %s", packageFile)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to read .tar archive: %s", err)
|
return nil, fmt.Errorf("unable to read .tar archive from %s. Error: %s", packageFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
|
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
|
||||||
reader := NewControlFileReader(untar)
|
reader := NewControlFileReader(untar)
|
||||||
stanza, err := reader.ReadStanza()
|
stanza, err := reader.ReadStanza(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -62,23 +76,23 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetControlFileFromDsc reads control file from dsc package
|
// GetControlFileFromDsc reads control file from dsc package
|
||||||
func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, error) {
|
func GetControlFileFromDsc(dscFile string, verifier pgp.Verifier) (Stanza, error) {
|
||||||
file, err := os.Open(dscFile)
|
file, err := os.Open(dscFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
line, err := bufio.NewReader(file).ReadString('\n')
|
isClearSigned, err := verifier.IsClearSigned(file)
|
||||||
|
file.Seek(0, 0)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Seek(0, 0)
|
var text io.ReadCloser
|
||||||
|
|
||||||
var text *os.File
|
if isClearSigned {
|
||||||
|
|
||||||
if strings.Index(line, "BEGIN PGP SIGN") != -1 {
|
|
||||||
text, err = verifier.ExtractClearsigned(file)
|
text, err = verifier.ExtractClearsigned(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -89,7 +103,7 @@ func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader := NewControlFileReader(text)
|
reader := NewControlFileReader(text)
|
||||||
stanza, err := reader.ReadStanza()
|
stanza, err := reader.ReadStanza(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,3 +111,79 @@ func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, err
|
|||||||
return stanza, nil
|
return stanza, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContentsFromDeb returns list of files installed by .deb package
|
||||||
|
func GetContentsFromDeb(file io.Reader, packageFile string) ([]string, error) {
|
||||||
|
library := ar.NewReader(file)
|
||||||
|
for {
|
||||||
|
header, err := library.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, fmt.Errorf("unable to find data.tar.* part in %s", packageFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to read .deb archive from %s", packageFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(header.Name, "data.tar") {
|
||||||
|
bufReader := bufio.NewReader(library)
|
||||||
|
signature, err := bufReader.Peek(270)
|
||||||
|
|
||||||
|
var isTar bool
|
||||||
|
if err == nil {
|
||||||
|
isTar = matchers.Tar(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tarInput io.Reader
|
||||||
|
|
||||||
|
switch header.Name {
|
||||||
|
case "data.tar":
|
||||||
|
tarInput = bufReader
|
||||||
|
case "data.tar.gz":
|
||||||
|
if isTar {
|
||||||
|
tarInput = bufReader
|
||||||
|
} else {
|
||||||
|
ungzip, err := gzip.NewReader(bufReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to ungzip data.tar.gz from %s", packageFile)
|
||||||
|
}
|
||||||
|
defer ungzip.Close()
|
||||||
|
tarInput = ungzip
|
||||||
|
}
|
||||||
|
case "data.tar.bz2":
|
||||||
|
tarInput = bzip2.NewReader(bufReader)
|
||||||
|
case "data.tar.xz":
|
||||||
|
unxz, err := xz.NewReader(bufReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to unxz data.tar.xz from %s", packageFile)
|
||||||
|
}
|
||||||
|
defer unxz.Close()
|
||||||
|
tarInput = unxz
|
||||||
|
case "data.tar.lzma":
|
||||||
|
unlzma := lzma.NewReader(bufReader)
|
||||||
|
defer unlzma.Close()
|
||||||
|
tarInput = unlzma
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported tar compression in %s: %s", packageFile, header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
untar := tar.NewReader(tarInput)
|
||||||
|
var results []string
|
||||||
|
for {
|
||||||
|
tarHeader, err := untar.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to read .tar archive from %s", packageFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tarHeader.Typeflag == tar.TypeDir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tarHeader.Name = strings.TrimPrefix(tarHeader.Name[2:], "./")
|
||||||
|
results = append(results, tarHeader.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+25
-4
@@ -1,15 +1,17 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/utils"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DebSuite struct {
|
type DebSuite struct {
|
||||||
debFile, dscFile, dscFileNoSign string
|
debFile, debFile2, dscFile, dscFileNoSign string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Suite(&DebSuite{})
|
var _ = Suite(&DebSuite{})
|
||||||
@@ -17,6 +19,7 @@ var _ = Suite(&DebSuite{})
|
|||||||
func (s *DebSuite) SetUpSuite(c *C) {
|
func (s *DebSuite) SetUpSuite(c *C) {
|
||||||
_, _File, _, _ := runtime.Caller(0)
|
_, _File, _, _ := runtime.Caller(0)
|
||||||
s.debFile = filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
|
s.debFile = filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||||
|
s.debFile2 = filepath.Join(filepath.Dir(_File), "../system/changes/hardlink_0.2.1_amd64.deb")
|
||||||
s.dscFile = filepath.Join(filepath.Dir(_File), "../system/files/pyspi_0.6.1-1.3.dsc")
|
s.dscFile = filepath.Join(filepath.Dir(_File), "../system/files/pyspi_0.6.1-1.3.dsc")
|
||||||
s.dscFileNoSign = filepath.Join(filepath.Dir(_File), "../system/files/pyspi-0.6.1-1.3.stripped.dsc")
|
s.dscFileNoSign = filepath.Join(filepath.Dir(_File), "../system/files/pyspi-0.6.1-1.3.stripped.dsc")
|
||||||
}
|
}
|
||||||
@@ -27,7 +30,7 @@ func (s *DebSuite) TestGetControlFileFromDeb(c *C) {
|
|||||||
|
|
||||||
_, _File, _, _ := runtime.Caller(0)
|
_, _File, _, _ := runtime.Caller(0)
|
||||||
_, err = GetControlFileFromDeb(_File)
|
_, err = GetControlFileFromDeb(_File)
|
||||||
c.Check(err, ErrorMatches, "unable to read .deb archive: ar: missing global header")
|
c.Check(err, ErrorMatches, "^.+ar: missing global header")
|
||||||
|
|
||||||
st, err := GetControlFileFromDeb(s.debFile)
|
st, err := GetControlFileFromDeb(s.debFile)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
@@ -36,7 +39,7 @@ func (s *DebSuite) TestGetControlFileFromDeb(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DebSuite) TestGetControlFileFromDsc(c *C) {
|
func (s *DebSuite) TestGetControlFileFromDsc(c *C) {
|
||||||
verifier := &utils.GpgVerifier{}
|
verifier := &pgp.GoVerifier{}
|
||||||
|
|
||||||
_, err := GetControlFileFromDsc("/no/such/file", verifier)
|
_, err := GetControlFileFromDsc("/no/such/file", verifier)
|
||||||
c.Check(err, ErrorMatches, ".*no such file or directory")
|
c.Check(err, ErrorMatches, ".*no such file or directory")
|
||||||
@@ -55,3 +58,21 @@ func (s *DebSuite) TestGetControlFileFromDsc(c *C) {
|
|||||||
c.Check(st["Version"], Equals, "0.6.1-1.4")
|
c.Check(st["Version"], Equals, "0.6.1-1.4")
|
||||||
c.Check(st["Source"], Equals, "pyspi")
|
c.Check(st["Source"], Equals, "pyspi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DebSuite) TestGetContentsFromDeb(c *C) {
|
||||||
|
f, err := os.Open(s.debFile)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
contents, err := GetContentsFromDeb(f, s.debFile)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(contents, DeepEquals, []string{"usr/share/doc/libboost-program-options-dev/changelog.gz",
|
||||||
|
"usr/share/doc/libboost-program-options-dev/copyright"})
|
||||||
|
c.Assert(f.Close(), IsNil)
|
||||||
|
|
||||||
|
f, err = os.Open(s.debFile2)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
contents, err = GetContentsFromDeb(f, s.debFile2)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(contents, DeepEquals, []string{"usr/bin/hardlink", "usr/share/man/man1/hardlink.1.gz",
|
||||||
|
"usr/share/doc/hardlink/changelog.gz", "usr/share/doc/hardlink/copyright", "usr/share/doc/hardlink/NEWS.Debian.gz"})
|
||||||
|
c.Assert(f.Close(), IsNil)
|
||||||
|
}
|
||||||
|
|||||||
+43
-23
@@ -22,6 +22,8 @@ var (
|
|||||||
"Version",
|
"Version",
|
||||||
"Codename",
|
"Codename",
|
||||||
"Date",
|
"Date",
|
||||||
|
"NotAutomatic",
|
||||||
|
"ButAutomaticUpgrades",
|
||||||
"Architectures",
|
"Architectures",
|
||||||
"Architecture",
|
"Architecture",
|
||||||
"Components",
|
"Components",
|
||||||
@@ -30,6 +32,7 @@ var (
|
|||||||
"MD5Sum",
|
"MD5Sum",
|
||||||
"SHA1",
|
"SHA1",
|
||||||
"SHA256",
|
"SHA256",
|
||||||
|
"SHA512",
|
||||||
}
|
}
|
||||||
|
|
||||||
canonicalOrderBinary = []string{
|
canonicalOrderBinary = []string{
|
||||||
@@ -59,6 +62,7 @@ var (
|
|||||||
"MD5sum",
|
"MD5sum",
|
||||||
"SHA1",
|
"SHA1",
|
||||||
"SHA256",
|
"SHA256",
|
||||||
|
"SHA512",
|
||||||
"Description",
|
"Description",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,16 +96,46 @@ func (s Stanza) Copy() (result Stanza) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write single field from Stanza to writer
|
func isMultilineField(field string, isRelease bool) bool {
|
||||||
func writeField(w *bufio.Writer, field, value string) (err error) {
|
switch field {
|
||||||
_, multiline := multilineFields[field]
|
case "Description":
|
||||||
|
return true
|
||||||
|
case "Files":
|
||||||
|
return true
|
||||||
|
case "Changes":
|
||||||
|
return true
|
||||||
|
case "Checksums-Sha1":
|
||||||
|
return true
|
||||||
|
case "Checksums-Sha256":
|
||||||
|
return true
|
||||||
|
case "Checksums-Sha512":
|
||||||
|
return true
|
||||||
|
case "Package-List":
|
||||||
|
return true
|
||||||
|
case "MD5Sum":
|
||||||
|
return isRelease
|
||||||
|
case "SHA1":
|
||||||
|
return isRelease
|
||||||
|
case "SHA256":
|
||||||
|
return isRelease
|
||||||
|
case "SHA512":
|
||||||
|
return isRelease
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if !multiline {
|
// Write single field from Stanza to writer
|
||||||
|
func writeField(w *bufio.Writer, field, value string, isRelease bool) (err error) {
|
||||||
|
if !isMultilineField(field, isRelease) {
|
||||||
_, err = w.WriteString(field + ": " + value + "\n")
|
_, err = w.WriteString(field + ": " + value + "\n")
|
||||||
} else {
|
} else {
|
||||||
if !strings.HasSuffix(value, "\n") {
|
if !strings.HasSuffix(value, "\n") {
|
||||||
value = value + "\n"
|
value = value + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if field != "Description" {
|
||||||
|
value = "\n" + value
|
||||||
|
}
|
||||||
_, err = w.WriteString(field + ":" + value)
|
_, err = w.WriteString(field + ":" + value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +156,7 @@ func (s Stanza) WriteTo(w *bufio.Writer, isSource, isRelease bool) error {
|
|||||||
value, ok := s[field]
|
value, ok := s[field]
|
||||||
if ok {
|
if ok {
|
||||||
delete(s, field)
|
delete(s, field)
|
||||||
err := writeField(w, field, value)
|
err := writeField(w, field, value, isRelease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -130,7 +164,7 @@ func (s Stanza) WriteTo(w *bufio.Writer, isSource, isRelease bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for field, value := range s {
|
for field, value := range s {
|
||||||
err := writeField(w, field, value)
|
err := writeField(w, field, value, isRelease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -144,20 +178,6 @@ var (
|
|||||||
ErrMalformedStanza = errors.New("malformed stanza syntax")
|
ErrMalformedStanza = errors.New("malformed stanza syntax")
|
||||||
)
|
)
|
||||||
|
|
||||||
var multilineFields = make(map[string]bool)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
multilineFields["Description"] = true
|
|
||||||
multilineFields["Files"] = true
|
|
||||||
multilineFields["Changes"] = true
|
|
||||||
multilineFields["Checksums-Sha1"] = true
|
|
||||||
multilineFields["Checksums-Sha256"] = true
|
|
||||||
multilineFields["Package-List"] = true
|
|
||||||
multilineFields["SHA256"] = true
|
|
||||||
multilineFields["SHA1"] = true
|
|
||||||
multilineFields["MD5Sum"] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func canonicalCase(field string) string {
|
func canonicalCase(field string) string {
|
||||||
upper := strings.ToUpper(field)
|
upper := strings.ToUpper(field)
|
||||||
switch upper {
|
switch upper {
|
||||||
@@ -198,7 +218,7 @@ func NewControlFileReader(r io.Reader) *ControlFileReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadStanza reeads one stanza from control file
|
// ReadStanza reeads one stanza from control file
|
||||||
func (c *ControlFileReader) ReadStanza() (Stanza, error) {
|
func (c *ControlFileReader) ReadStanza(isRelease bool) (Stanza, error) {
|
||||||
stanza := make(Stanza, 32)
|
stanza := make(Stanza, 32)
|
||||||
lastField := ""
|
lastField := ""
|
||||||
lastFieldMultiline := false
|
lastFieldMultiline := false
|
||||||
@@ -218,7 +238,7 @@ func (c *ControlFileReader) ReadStanza() (Stanza, error) {
|
|||||||
if lastFieldMultiline {
|
if lastFieldMultiline {
|
||||||
stanza[lastField] += line + "\n"
|
stanza[lastField] += line + "\n"
|
||||||
} else {
|
} else {
|
||||||
stanza[lastField] += strings.TrimSpace(line)
|
stanza[lastField] += " " + strings.TrimSpace(line)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parts := strings.SplitN(line, ":", 2)
|
parts := strings.SplitN(line, ":", 2)
|
||||||
@@ -226,7 +246,7 @@ func (c *ControlFileReader) ReadStanza() (Stanza, error) {
|
|||||||
return nil, ErrMalformedStanza
|
return nil, ErrMalformedStanza
|
||||||
}
|
}
|
||||||
lastField = canonicalCase(parts[0])
|
lastField = canonicalCase(parts[0])
|
||||||
_, lastFieldMultiline = multilineFields[lastField]
|
lastFieldMultiline = isMultilineField(lastField, isRelease)
|
||||||
if lastFieldMultiline {
|
if lastFieldMultiline {
|
||||||
stanza[lastField] = parts[1]
|
stanza[lastField] = parts[1]
|
||||||
if parts[1] != "" {
|
if parts[1] != "" {
|
||||||
|
|||||||
+8
-8
@@ -84,18 +84,18 @@ func (s *ControlFileSuite) SetUpTest(c *C) {
|
|||||||
func (s *ControlFileSuite) TestReadStanza(c *C) {
|
func (s *ControlFileSuite) TestReadStanza(c *C) {
|
||||||
r := NewControlFileReader(s.reader)
|
r := NewControlFileReader(s.reader)
|
||||||
|
|
||||||
stanza1, err := r.ReadStanza()
|
stanza1, err := r.ReadStanza(false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
stanza2, err := r.ReadStanza()
|
stanza2, err := r.ReadStanza(false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
stanza3, err := r.ReadStanza()
|
stanza3, err := r.ReadStanza(false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(stanza3, IsNil)
|
c.Assert(stanza3, IsNil)
|
||||||
|
|
||||||
c.Check(stanza1["Format"], Equals, "3.0 (quilt)")
|
c.Check(stanza1["Format"], Equals, "3.0 (quilt)")
|
||||||
c.Check(stanza1["Build-Depends"], Equals, "debhelper (>= 8),bash-completion (>= 1:1.1-3),libcurl4-nss-dev, libreadline-dev, libxml2-dev, libpcre3-dev, liboauth-dev, xsltproc, docbook-xsl, docbook-xml, dh-autoreconf")
|
c.Check(stanza1["Build-Depends"], Equals, "debhelper (>= 8), bash-completion (>= 1:1.1-3), libcurl4-nss-dev, libreadline-dev, libxml2-dev, libpcre3-dev, liboauth-dev, xsltproc, docbook-xsl, docbook-xml, dh-autoreconf")
|
||||||
c.Check(stanza1["Files"], Equals, " 3d5f65778bf3f89be03c313b0024b62c 1980 bti_032-1.dsc\n"+
|
c.Check(stanza1["Files"], Equals, " 3d5f65778bf3f89be03c313b0024b62c 1980 bti_032-1.dsc\n"+
|
||||||
" 1e0d0b693fdeebec268004ba41701baf 59773 bti_032.orig.tar.gz\n"+" ac1229a6d685023aeb8fcb0806324aa8 5065 bti_032-1.debian.tar.gz\n")
|
" 1e0d0b693fdeebec268004ba41701baf 59773 bti_032.orig.tar.gz\n"+" ac1229a6d685023aeb8fcb0806324aa8 5065 bti_032-1.debian.tar.gz\n")
|
||||||
c.Check(len(stanza2), Equals, 20)
|
c.Check(len(stanza2), Equals, 20)
|
||||||
@@ -103,12 +103,12 @@ func (s *ControlFileSuite) TestReadStanza(c *C) {
|
|||||||
|
|
||||||
func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
|
func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
|
||||||
r := NewControlFileReader(s.reader)
|
r := NewControlFileReader(s.reader)
|
||||||
stanza, err := r.ReadStanza()
|
stanza, err := r.ReadStanza(false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
w := bufio.NewWriter(buf)
|
w := bufio.NewWriter(buf)
|
||||||
err = stanza.Copy().WriteTo(w, false, false)
|
err = stanza.Copy().WriteTo(w, true, false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
err = w.Flush()
|
err = w.Flush()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
@@ -116,7 +116,7 @@ func (s *ControlFileSuite) TestReadWriteStanza(c *C) {
|
|||||||
str := buf.String()
|
str := buf.String()
|
||||||
|
|
||||||
r = NewControlFileReader(buf)
|
r = NewControlFileReader(buf)
|
||||||
stanza2, err := r.ReadStanza()
|
stanza2, err := r.ReadStanza(false)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Assert(stanza2, DeepEquals, stanza)
|
c.Assert(stanza2, DeepEquals, stanza)
|
||||||
@@ -140,7 +140,7 @@ func (s *ControlFileSuite) BenchmarkReadStanza(c *C) {
|
|||||||
reader := bytes.NewBufferString(controlFile)
|
reader := bytes.NewBufferString(controlFile)
|
||||||
r := NewControlFileReader(reader)
|
r := NewControlFileReader(reader)
|
||||||
for {
|
for {
|
||||||
s, e := r.ReadStanza()
|
s, e := r.ReadStanza(false)
|
||||||
if s == nil && e == nil {
|
if s == nil && e == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-21
@@ -1,34 +1,50 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/gographviz"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awalterschulze/gographviz"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildGraph generates graph contents from aptly object database
|
// BuildGraph generates graph contents from aptly object database
|
||||||
func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, error) {
|
func BuildGraph(collectionFactory *CollectionFactory, layout string) (gographviz.Interface, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
graph := gographviz.NewEscape()
|
graph := gographviz.NewEscape()
|
||||||
graph.SetDir(true)
|
graph.SetDir(true)
|
||||||
graph.SetName("aptly")
|
graph.SetName("aptly")
|
||||||
|
|
||||||
|
var labelStart string
|
||||||
|
var labelEnd string
|
||||||
|
|
||||||
|
switch layout {
|
||||||
|
case "vertical":
|
||||||
|
graph.AddAttr("aptly", "rankdir", "LR")
|
||||||
|
labelStart = ""
|
||||||
|
labelEnd = ""
|
||||||
|
case "horizontal":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
labelStart = "{"
|
||||||
|
labelEnd = "}"
|
||||||
|
}
|
||||||
|
|
||||||
existingNodes := map[string]bool{}
|
existingNodes := map[string]bool{}
|
||||||
|
|
||||||
err = collectionFactory.RemoteRepoCollection().ForEach(func(repo *RemoteRepo) error {
|
err = collectionFactory.RemoteRepoCollection().ForEach(func(repo *RemoteRepo) error {
|
||||||
err := collectionFactory.RemoteRepoCollection().LoadComplete(repo)
|
e := collectionFactory.RemoteRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.AddNode("aptly", repo.UUID, map[string]string{
|
graph.AddNode("aptly", repo.UUID, map[string]string{
|
||||||
"shape": "Mrecord",
|
"shape": "Mrecord",
|
||||||
"style": "filled",
|
"style": "filled",
|
||||||
"fillcolor": "darkgoldenrod1",
|
"fillcolor": "darkgoldenrod1",
|
||||||
"label": fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
|
"label": fmt.Sprintf("%sMirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d%s", labelStart, repo.Name, repo.ArchiveRoot,
|
||||||
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
|
repo.Distribution, strings.Join(repo.Components, ", "),
|
||||||
strings.Join(repo.Architectures, ", "), repo.NumPackages()),
|
strings.Join(repo.Architectures, ", "), repo.NumPackages(), labelEnd),
|
||||||
})
|
})
|
||||||
existingNodes[repo.UUID] = true
|
existingNodes[repo.UUID] = true
|
||||||
return nil
|
return nil
|
||||||
@@ -39,17 +55,17 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = collectionFactory.LocalRepoCollection().ForEach(func(repo *LocalRepo) error {
|
err = collectionFactory.LocalRepoCollection().ForEach(func(repo *LocalRepo) error {
|
||||||
err := collectionFactory.LocalRepoCollection().LoadComplete(repo)
|
e := collectionFactory.LocalRepoCollection().LoadComplete(repo)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.AddNode("aptly", repo.UUID, map[string]string{
|
graph.AddNode("aptly", repo.UUID, map[string]string{
|
||||||
"shape": "Mrecord",
|
"shape": "Mrecord",
|
||||||
"style": "filled",
|
"style": "filled",
|
||||||
"fillcolor": "mediumseagreen",
|
"fillcolor": "mediumseagreen",
|
||||||
"label": fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
|
"label": fmt.Sprintf("%sRepo %s|comment: %s|pkgs: %d%s", labelStart,
|
||||||
repo.Name, repo.Comment, repo.NumPackages()),
|
repo.Name, repo.Comment, repo.NumPackages(), labelEnd),
|
||||||
})
|
})
|
||||||
existingNodes[repo.UUID] = true
|
existingNodes[repo.UUID] = true
|
||||||
return nil
|
return nil
|
||||||
@@ -65,13 +81,13 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
|
|||||||
})
|
})
|
||||||
|
|
||||||
err = collectionFactory.SnapshotCollection().ForEach(func(snapshot *Snapshot) error {
|
err = collectionFactory.SnapshotCollection().ForEach(func(snapshot *Snapshot) error {
|
||||||
err := collectionFactory.SnapshotCollection().LoadComplete(snapshot)
|
e := collectionFactory.SnapshotCollection().LoadComplete(snapshot)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
description := snapshot.Description
|
description := snapshot.Description
|
||||||
if snapshot.SourceKind == "repo" {
|
if snapshot.SourceKind == SourceRemoteRepo {
|
||||||
description = "Snapshot from repo"
|
description = "Snapshot from repo"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,10 +95,11 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
|
|||||||
"shape": "Mrecord",
|
"shape": "Mrecord",
|
||||||
"style": "filled",
|
"style": "filled",
|
||||||
"fillcolor": "cadetblue1",
|
"fillcolor": "cadetblue1",
|
||||||
"label": fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages()),
|
"label": fmt.Sprintf("%sSnapshot %s|%s|pkgs: %d%s", labelStart,
|
||||||
|
snapshot.Name, description, snapshot.NumPackages(), labelEnd),
|
||||||
})
|
})
|
||||||
|
|
||||||
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
|
if snapshot.SourceKind == SourceRemoteRepo || snapshot.SourceKind == SourceLocalRepo || snapshot.SourceKind == SourceSnapshot {
|
||||||
for _, uuid := range snapshot.SourceIDs {
|
for _, uuid := range snapshot.SourceIDs {
|
||||||
_, exists := existingNodes[uuid]
|
_, exists := existingNodes[uuid]
|
||||||
if exists {
|
if exists {
|
||||||
@@ -102,8 +119,9 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
|
|||||||
"shape": "Mrecord",
|
"shape": "Mrecord",
|
||||||
"style": "filled",
|
"style": "filled",
|
||||||
"fillcolor": "darkolivegreen1",
|
"fillcolor": "darkolivegreen1",
|
||||||
"label": fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution,
|
"label": fmt.Sprintf("%sPublished %s/%s|comp: %s|arch: %s%s", labelStart,
|
||||||
strings.Join(repo.Components(), " "), strings.Join(repo.Architectures, ", ")),
|
repo.Prefix, repo.Distribution, strings.Join(repo.Components(), " "),
|
||||||
|
strings.Join(repo.Architectures, ", "), labelEnd),
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, uuid := range repo.Sources {
|
for _, uuid := range repo.Sources {
|
||||||
|
|||||||
+38
-19
@@ -1,16 +1,18 @@
|
|||||||
package deb
|
package deb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/aptly"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CollectPackageFiles walks filesystem collecting all candidates for package files
|
// CollectPackageFiles walks filesystem collecting all candidates for package files
|
||||||
func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, failedFiles []string, err error) {
|
func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, failedFiles []string) {
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
info, err2 := os.Stat(location)
|
info, err2 := os.Stat(location)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
@@ -28,15 +30,21 @@ func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (pac
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
|
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
|
||||||
strings.HasSuffix(info.Name(), ".dsc") {
|
strings.HasSuffix(info.Name(), ".dsc") || strings.HasSuffix(info.Name(), ".ddeb") {
|
||||||
packageFiles = append(packageFiles, path)
|
packageFiles = append(packageFiles, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err2 != nil {
|
||||||
|
reporter.Warning("Unable to process %s: %s", location, err2)
|
||||||
|
failedFiles = append(failedFiles, location)
|
||||||
|
continue
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
|
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
|
||||||
strings.HasSuffix(info.Name(), ".dsc") {
|
strings.HasSuffix(info.Name(), ".dsc") || strings.HasSuffix(info.Name(), ".ddeb") {
|
||||||
packageFiles = append(packageFiles, location)
|
packageFiles = append(packageFiles, location)
|
||||||
} else {
|
} else {
|
||||||
reporter.Warning("Unknown file extension: %s", location)
|
reporter.Warning("Unknown file extension: %s", location)
|
||||||
@@ -52,8 +60,9 @@ func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (pac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ImportPackageFiles imports files into local repository
|
// ImportPackageFiles imports files into local repository
|
||||||
func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace bool, verifier utils.Verifier,
|
func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace bool, verifier pgp.Verifier,
|
||||||
pool aptly.PackagePool, collection *PackageCollection, reporter aptly.ResultReporter) (processedFiles []string, failedFiles []string, err error) {
|
pool aptly.PackagePool, collection *PackageCollection, reporter aptly.ResultReporter, restriction PackageQuery,
|
||||||
|
checksumStorage aptly.ChecksumStorage) (processedFiles []string, failedFiles []string, err error) {
|
||||||
if forceReplace {
|
if forceReplace {
|
||||||
list.PrepareIndex()
|
list.PrepareIndex()
|
||||||
}
|
}
|
||||||
@@ -109,19 +118,24 @@ func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace b
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var files PackageFiles
|
||||||
|
|
||||||
|
if isSourcePackage {
|
||||||
|
files = p.Files()
|
||||||
|
}
|
||||||
|
|
||||||
var checksums utils.ChecksumInfo
|
var checksums utils.ChecksumInfo
|
||||||
checksums, err = utils.ChecksumsForFile(file)
|
checksums, err = utils.ChecksumsForFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSourcePackage {
|
mainPackageFile := PackageFile{
|
||||||
p.UpdateFiles(append(p.Files(), PackageFile{Filename: filepath.Base(file), Checksums: checksums}))
|
Filename: filepath.Base(file),
|
||||||
} else {
|
Checksums: checksums,
|
||||||
p.UpdateFiles([]PackageFile{{Filename: filepath.Base(file), Checksums: checksums}})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pool.Import(file, checksums.MD5)
|
mainPackageFile.PoolPath, err = pool.Import(file, mainPackageFile.Filename, &mainPackageFile.Checksums, false, checksumStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reporter.Warning("Unable to import file %s into pool: %s", file, err)
|
reporter.Warning("Unable to import file %s into pool: %s", file, err)
|
||||||
failedFiles = append(failedFiles, file)
|
failedFiles = append(failedFiles, file)
|
||||||
@@ -130,13 +144,10 @@ func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace b
|
|||||||
|
|
||||||
candidateProcessedFiles = append(candidateProcessedFiles, file)
|
candidateProcessedFiles = append(candidateProcessedFiles, file)
|
||||||
|
|
||||||
// go over all files, except for the last one (.dsc/.deb itself)
|
// go over all the other files
|
||||||
for _, f := range p.Files() {
|
for i := range files {
|
||||||
if filepath.Base(f.Filename) == filepath.Base(file) {
|
sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(files[i].Filename))
|
||||||
continue
|
files[i].PoolPath, err = pool.Import(sourceFile, files[i].Filename, &files[i].Checksums, false, checksumStorage)
|
||||||
}
|
|
||||||
sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(f.Filename))
|
|
||||||
err = pool.Import(sourceFile, f.Checksums.MD5)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reporter.Warning("Unable to import file %s into pool: %s", sourceFile, err)
|
reporter.Warning("Unable to import file %s into pool: %s", sourceFile, err)
|
||||||
failedFiles = append(failedFiles, file)
|
failedFiles = append(failedFiles, file)
|
||||||
@@ -150,6 +161,14 @@ func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace b
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.UpdateFiles(append(files, mainPackageFile))
|
||||||
|
|
||||||
|
if restriction != nil && !restriction.Matches(p) {
|
||||||
|
reporter.Warning("%s has been ignored as it doesn't match restriction", p)
|
||||||
|
failedFiles = append(failedFiles, file)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
err = collection.Update(p)
|
err = collection.Update(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reporter.Warning("Unable to save package %s: %s", p, err)
|
reporter.Warning("Unable to save package %s: %s", p, err)
|
||||||
|
|||||||
+44
-8
@@ -3,11 +3,13 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/pgp"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type indexFiles struct {
|
type indexFiles struct {
|
||||||
@@ -24,6 +26,7 @@ type indexFile struct {
|
|||||||
parent *indexFiles
|
parent *indexFiles
|
||||||
discardable bool
|
discardable bool
|
||||||
compressable bool
|
compressable bool
|
||||||
|
onlyGzip bool
|
||||||
signable bool
|
signable bool
|
||||||
relativePath string
|
relativePath string
|
||||||
tempFilename string
|
tempFilename string
|
||||||
@@ -46,7 +49,7 @@ func (file *indexFile) BufWriter() (*bufio.Writer, error) {
|
|||||||
return file.w, nil
|
return file.w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (file *indexFile) Finalize(signer utils.Signer) error {
|
func (file *indexFile) Finalize(signer pgp.Signer) error {
|
||||||
if file.w == nil {
|
if file.w == nil {
|
||||||
if file.discardable {
|
if file.discardable {
|
||||||
return nil
|
return nil
|
||||||
@@ -61,7 +64,7 @@ func (file *indexFile) Finalize(signer utils.Signer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if file.compressable {
|
if file.compressable {
|
||||||
err = utils.CompressFile(file.tempFile)
|
err = utils.CompressFile(file.tempFile, file.onlyGzip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
file.tempFile.Close()
|
file.tempFile.Close()
|
||||||
return fmt.Errorf("unable to compress index file: %s", err)
|
return fmt.Errorf("unable to compress index file: %s", err)
|
||||||
@@ -73,6 +76,9 @@ func (file *indexFile) Finalize(signer utils.Signer) error {
|
|||||||
exts := []string{""}
|
exts := []string{""}
|
||||||
if file.compressable {
|
if file.compressable {
|
||||||
exts = append(exts, ".gz", ".bz2")
|
exts = append(exts, ".gz", ".bz2")
|
||||||
|
if file.onlyGzip {
|
||||||
|
exts = []string{".gz"}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ext := range exts {
|
for _, ext := range exts {
|
||||||
@@ -150,7 +156,7 @@ func newIndexFiles(publishedStorage aptly.PublishedStorage, basePath, tempDir, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (files *indexFiles) PackageIndex(component, arch string, udeb bool) *indexFile {
|
func (files *indexFiles) PackageIndex(component, arch string, udeb bool) *indexFile {
|
||||||
if arch == "source" {
|
if arch == ArchitectureSource {
|
||||||
udeb = false
|
udeb = false
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf("pi-%s-%s-%v", component, arch, udeb)
|
key := fmt.Sprintf("pi-%s-%s-%v", component, arch, udeb)
|
||||||
@@ -158,7 +164,7 @@ func (files *indexFiles) PackageIndex(component, arch string, udeb bool) *indexF
|
|||||||
if !ok {
|
if !ok {
|
||||||
var relativePath string
|
var relativePath string
|
||||||
|
|
||||||
if arch == "source" {
|
if arch == ArchitectureSource {
|
||||||
relativePath = filepath.Join(component, "source", "Sources")
|
relativePath = filepath.Join(component, "source", "Sources")
|
||||||
} else {
|
} else {
|
||||||
if udeb {
|
if udeb {
|
||||||
@@ -183,7 +189,7 @@ func (files *indexFiles) PackageIndex(component, arch string, udeb bool) *indexF
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (files *indexFiles) ReleaseIndex(component, arch string, udeb bool) *indexFile {
|
func (files *indexFiles) ReleaseIndex(component, arch string, udeb bool) *indexFile {
|
||||||
if arch == "source" {
|
if arch == ArchitectureSource {
|
||||||
udeb = false
|
udeb = false
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf("ri-%s-%s-%v", component, arch, udeb)
|
key := fmt.Sprintf("ri-%s-%s-%v", component, arch, udeb)
|
||||||
@@ -191,7 +197,7 @@ func (files *indexFiles) ReleaseIndex(component, arch string, udeb bool) *indexF
|
|||||||
if !ok {
|
if !ok {
|
||||||
var relativePath string
|
var relativePath string
|
||||||
|
|
||||||
if arch == "source" {
|
if arch == ArchitectureSource {
|
||||||
relativePath = filepath.Join(component, "source", "Release")
|
relativePath = filepath.Join(component, "source", "Release")
|
||||||
} else {
|
} else {
|
||||||
if udeb {
|
if udeb {
|
||||||
@@ -215,6 +221,36 @@ func (files *indexFiles) ReleaseIndex(component, arch string, udeb bool) *indexF
|
|||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (files *indexFiles) ContentsIndex(component, arch string, udeb bool) *indexFile {
|
||||||
|
if arch == ArchitectureSource {
|
||||||
|
udeb = false
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf("ci-%s-%s-%v", component, arch, udeb)
|
||||||
|
file, ok := files.indexes[key]
|
||||||
|
if !ok {
|
||||||
|
var relativePath string
|
||||||
|
|
||||||
|
if udeb {
|
||||||
|
relativePath = filepath.Join(component, fmt.Sprintf("Contents-udeb-%s", arch))
|
||||||
|
} else {
|
||||||
|
relativePath = filepath.Join(component, fmt.Sprintf("Contents-%s", arch))
|
||||||
|
}
|
||||||
|
|
||||||
|
file = &indexFile{
|
||||||
|
parent: files,
|
||||||
|
discardable: true,
|
||||||
|
compressable: true,
|
||||||
|
onlyGzip: true,
|
||||||
|
signable: false,
|
||||||
|
relativePath: relativePath,
|
||||||
|
}
|
||||||
|
|
||||||
|
files.indexes[key] = file
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
func (files *indexFiles) ReleaseFile() *indexFile {
|
func (files *indexFiles) ReleaseFile() *indexFile {
|
||||||
return &indexFile{
|
return &indexFile{
|
||||||
parent: files,
|
parent: files,
|
||||||
|
|||||||
+71
-15
@@ -2,9 +2,11 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/smira/aptly/aptly"
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dependency options
|
// Dependency options
|
||||||
@@ -19,6 +21,8 @@ const (
|
|||||||
DepFollowAllVariants
|
DepFollowAllVariants
|
||||||
// DepFollowBuild pulls build dependencies
|
// DepFollowBuild pulls build dependencies
|
||||||
DepFollowBuild
|
DepFollowBuild
|
||||||
|
// DepVerboseResolve emits additional logs while dependencies are being resolved
|
||||||
|
DepVerboseResolve
|
||||||
)
|
)
|
||||||
|
|
||||||
// PackageList is list of unique (by key) packages
|
// PackageList is list of unique (by key) packages
|
||||||
@@ -30,12 +34,16 @@ const (
|
|||||||
type PackageList struct {
|
type PackageList struct {
|
||||||
// Straight list of packages as map
|
// Straight list of packages as map
|
||||||
packages map[string]*Package
|
packages map[string]*Package
|
||||||
// Has index been prepared?
|
|
||||||
indexed bool
|
|
||||||
// Indexed list of packages, sorted by name internally
|
// Indexed list of packages, sorted by name internally
|
||||||
packagesIndex []*Package
|
packagesIndex []*Package
|
||||||
// Map of packages for each virtual package (provides)
|
// Map of packages for each virtual package (provides)
|
||||||
providesIndex map[string][]*Package
|
providesIndex map[string][]*Package
|
||||||
|
// Package key generation function
|
||||||
|
keyFunc func(p *Package) string
|
||||||
|
// Allow duplicates?
|
||||||
|
duplicatesAllowed bool
|
||||||
|
// Has index been prepared?
|
||||||
|
indexed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageConflictError means that package can't be added to the list due to error
|
// PackageConflictError means that package can't be added to the list due to error
|
||||||
@@ -49,9 +57,36 @@ var (
|
|||||||
_ PackageCatalog = &PackageList{}
|
_ PackageCatalog = &PackageList{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPackageList creates empty package list
|
func packageShortKey(p *Package) string {
|
||||||
|
return string(p.ShortKey(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageFullKey(p *Package) string {
|
||||||
|
return string(p.Key(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPackageList creates empty package list without duplicate package
|
||||||
func NewPackageList() *PackageList {
|
func NewPackageList() *PackageList {
|
||||||
return &PackageList{packages: make(map[string]*Package, 1000)}
|
return NewPackageListWithDuplicates(false, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPackageListWithDuplicates creates empty package list which might allow or block duplicate packages
|
||||||
|
func NewPackageListWithDuplicates(duplicates bool, capacity int) *PackageList {
|
||||||
|
if capacity == 0 {
|
||||||
|
capacity = 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &PackageList{
|
||||||
|
packages: make(map[string]*Package, capacity),
|
||||||
|
duplicatesAllowed: duplicates,
|
||||||
|
keyFunc: packageShortKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
if duplicates {
|
||||||
|
result.keyFunc = packageFullKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPackageListFromRefList loads packages list from PackageRefList
|
// NewPackageListFromRefList loads packages list from PackageRefList
|
||||||
@@ -61,7 +96,7 @@ func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageColle
|
|||||||
return NewPackageList(), nil
|
return NewPackageList(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &PackageList{packages: make(map[string]*Package, reflist.Len())}
|
result := NewPackageListWithDuplicates(false, reflist.Len())
|
||||||
|
|
||||||
if progress != nil {
|
if progress != nil {
|
||||||
progress.InitBar(int64(reflist.Len()), false)
|
progress.InitBar(int64(reflist.Len()), false)
|
||||||
@@ -91,7 +126,7 @@ func NewPackageListFromRefList(reflist *PackageRefList, collection *PackageColle
|
|||||||
|
|
||||||
// Add appends package to package list, additionally checking for uniqueness
|
// Add appends package to package list, additionally checking for uniqueness
|
||||||
func (l *PackageList) Add(p *Package) error {
|
func (l *PackageList) Add(p *Package) error {
|
||||||
key := string(p.ShortKey(""))
|
key := l.keyFunc(p)
|
||||||
existing, ok := l.packages[key]
|
existing, ok := l.packages[key]
|
||||||
if ok {
|
if ok {
|
||||||
if !existing.Equals(p) {
|
if !existing.Equals(p) {
|
||||||
@@ -170,7 +205,7 @@ func (l *PackageList) Append(pl *PackageList) error {
|
|||||||
|
|
||||||
// Remove removes package from the list, and updates index when required
|
// Remove removes package from the list, and updates index when required
|
||||||
func (l *PackageList) Remove(p *Package) {
|
func (l *PackageList) Remove(p *Package) {
|
||||||
delete(l.packages, string(p.ShortKey("")))
|
delete(l.packages, l.keyFunc(p))
|
||||||
if l.indexed {
|
if l.indexed {
|
||||||
for _, provides := range p.Provides {
|
for _, provides := range p.Provides {
|
||||||
for i, pkg := range l.providesIndex[provides] {
|
for i, pkg := range l.providesIndex[provides] {
|
||||||
@@ -203,7 +238,7 @@ func (l *PackageList) Remove(p *Package) {
|
|||||||
func (l *PackageList) Architectures(includeSource bool) (result []string) {
|
func (l *PackageList) Architectures(includeSource bool) (result []string) {
|
||||||
result = make([]string, 0, 10)
|
result = make([]string, 0, 10)
|
||||||
for _, pkg := range l.packages {
|
for _, pkg := range l.packages {
|
||||||
if pkg.Architecture != "all" && (pkg.Architecture != "source" || includeSource) && !utils.StrSliceHasItem(result, pkg.Architecture) {
|
if pkg.Architecture != ArchitectureAll && (pkg.Architecture != ArchitectureSource || includeSource) && !utils.StrSliceHasItem(result, pkg.Architecture) {
|
||||||
result = append(result, pkg.Architecture)
|
result = append(result, pkg.Architecture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +252,7 @@ func (l *PackageList) Strings() []string {
|
|||||||
|
|
||||||
for _, p := range l.packages {
|
for _, p := range l.packages {
|
||||||
result[i] = string(p.Key(""))
|
result[i] = string(p.Key(""))
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -252,7 +287,7 @@ func depSliceDeduplicate(s []Dependency) []Dependency {
|
|||||||
|
|
||||||
// VerifyDependencies looks for missing dependencies in package list.
|
// VerifyDependencies looks for missing dependencies in package list.
|
||||||
//
|
//
|
||||||
// Analysis would be peformed for each architecture, in specified sources
|
// Analysis would be performed 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()
|
l.PrepareIndex()
|
||||||
missing := make([]Dependency, 0, 128)
|
missing := make([]Dependency, 0, 128)
|
||||||
@@ -314,6 +349,14 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
|
|||||||
progress.ShutdownBar()
|
progress.ShutdownBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options&DepVerboseResolve == DepVerboseResolve && progress != nil {
|
||||||
|
missingStr := make([]string, len(missing))
|
||||||
|
for i := range missing {
|
||||||
|
missingStr[i] = missing[i].String()
|
||||||
|
}
|
||||||
|
progress.ColoredPrintf("@{y}Missing dependencies:@| %s", strings.Join(missingStr, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
return missing, nil
|
return missing, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +408,7 @@ func (l *PackageList) PrepareIndex() {
|
|||||||
|
|
||||||
// Scan searches package index using full scan
|
// Scan searches package index using full scan
|
||||||
func (l *PackageList) Scan(q PackageQuery) (result *PackageList) {
|
func (l *PackageList) Scan(q PackageQuery) (result *PackageList) {
|
||||||
result = NewPackageList()
|
result = NewPackageListWithDuplicates(l.duplicatesAllowed, 0)
|
||||||
for _, pkg := range l.packages {
|
for _, pkg := range l.packages {
|
||||||
if q.Matches(pkg) {
|
if q.Matches(pkg) {
|
||||||
result.Add(pkg)
|
result.Add(pkg)
|
||||||
@@ -382,7 +425,7 @@ func (l *PackageList) SearchSupported() bool {
|
|||||||
|
|
||||||
// SearchByKey looks up package by exact key reference
|
// SearchByKey looks up package by exact key reference
|
||||||
func (l *PackageList) SearchByKey(arch, name, version string) (result *PackageList) {
|
func (l *PackageList) SearchByKey(arch, name, version string) (result *PackageList) {
|
||||||
result = NewPackageList()
|
result = NewPackageListWithDuplicates(l.duplicatesAllowed, 0)
|
||||||
|
|
||||||
pkg := l.packages["P"+arch+" "+name+" "+version]
|
pkg := l.packages["P"+arch+" "+name+" "+version]
|
||||||
if pkg != nil {
|
if pkg != nil {
|
||||||
@@ -430,6 +473,11 @@ func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*
|
|||||||
|
|
||||||
// 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 []PackageQuery, 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) {
|
||||||
|
return l.FilterWithProgress(queries, withDependencies, source, dependencyOptions, architecturesList, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterWithProgress filters package index by specified queries (ORed together), possibly pulling dependencies and displays progress
|
||||||
|
func (l *PackageList) FilterWithProgress(queries []PackageQuery, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string, progress aptly.Progress) (*PackageList, error) {
|
||||||
if !l.indexed {
|
if !l.indexed {
|
||||||
panic("list not indexed, can't filter")
|
panic("list not indexed, can't filter")
|
||||||
}
|
}
|
||||||
@@ -456,7 +504,7 @@ func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, sour
|
|||||||
added = 0
|
added = 0
|
||||||
|
|
||||||
// find missing dependencies
|
// find missing dependencies
|
||||||
missing, err := result.VerifyDependencies(dependencyOptions, architecturesList, dependencySource, nil)
|
missing, err := result.VerifyDependencies(dependencyOptions, architecturesList, dependencySource, progress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -469,9 +517,12 @@ func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, sour
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResults := l.Search(dep, false)
|
searchResults := l.Search(dep, true)
|
||||||
if searchResults != nil {
|
if searchResults != nil {
|
||||||
for _, p := range searchResults {
|
for _, p := range searchResults {
|
||||||
|
if dependencyOptions&DepVerboseResolve == DepVerboseResolve && progress != nil {
|
||||||
|
progress.ColoredPrintf("@{g}Injecting package@|: %s", p)
|
||||||
|
}
|
||||||
result.Add(p)
|
result.Add(p)
|
||||||
dependencySource.Add(p)
|
dependencySource.Add(p)
|
||||||
added++
|
added++
|
||||||
@@ -479,6 +530,11 @@ func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, sour
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if dependencyOptions&DepVerboseResolve == DepVerboseResolve && progress != nil {
|
||||||
|
progress.ColoredPrintf("@{r}Unsatisfied dependency@|: %s", dep.String())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,6 +379,20 @@ func (s *PackageListSuite) TestFilter(c *C) {
|
|||||||
&FieldQuery{Field: "$Architecture", Relation: VersionRegexp, Value: "i.*6", Regexp: regexp.MustCompile("i.*6")}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
|
&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(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([]PackageQuery{&AndQuery{
|
||||||
|
&FieldQuery{Field: "Name", Relation: VersionRegexp, Value: "a", Regexp: regexp.MustCompile("a")},
|
||||||
|
&NotQuery{Q: &FieldQuery{Field: "Name", Relation: VersionEqual, Value: "data"}},
|
||||||
|
}}, false, nil, 0, nil)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(plString(result), Equals, "aa_2.0-1_i386 app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 mailer_3.5.8_i386")
|
||||||
|
|
||||||
|
result, err = s.il.Filter([]PackageQuery{&AndQuery{
|
||||||
|
&NotQuery{Q: &FieldQuery{Field: "Name", Relation: VersionEqual, Value: "data"}},
|
||||||
|
&FieldQuery{Field: "Name", Relation: VersionRegexp, Value: "a", Regexp: regexp.MustCompile("a")},
|
||||||
|
}}, false, nil, 0, nil)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(plString(result), Equals, "aa_2.0-1_i386 app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 mailer_3.5.8_i386")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
||||||
|
|||||||
+6
-3
@@ -2,12 +2,13 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"code.google.com/p/go-uuid/uuid"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/database"
|
|
||||||
"github.com/ugorji/go/codec"
|
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/database"
|
||||||
|
"github.com/smira/go-uuid/uuid"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalRepo is a collection of packages created locally
|
// LocalRepo is a collection of packages created locally
|
||||||
@@ -22,6 +23,8 @@ type LocalRepo struct {
|
|||||||
DefaultDistribution string `codec:",omitempty"`
|
DefaultDistribution string `codec:",omitempty"`
|
||||||
// DefaultComponent
|
// DefaultComponent
|
||||||
DefaultComponent string `codec:",omitempty"`
|
DefaultComponent string `codec:",omitempty"`
|
||||||
|
// Uploaders configuration
|
||||||
|
Uploaders *Uploaders `code:",omitempty" json:"-"`
|
||||||
// "Snapshot" of current list of packages
|
// "Snapshot" of current list of packages
|
||||||
packageRefs *PackageRefList
|
packageRefs *PackageRefList
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -2,6 +2,7 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
@@ -17,7 +18,7 @@ type LocalRepoSuite struct {
|
|||||||
var _ = Suite(&LocalRepoSuite{})
|
var _ = Suite(&LocalRepoSuite{})
|
||||||
|
|
||||||
func (s *LocalRepoSuite) SetUpTest(c *C) {
|
func (s *LocalRepoSuite) SetUpTest(c *C) {
|
||||||
s.db, _ = database.OpenDB(c.MkDir())
|
s.db, _ = database.NewOpenDB(c.MkDir())
|
||||||
s.list = NewPackageList()
|
s.list = NewPackageList()
|
||||||
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
|
s.list.Add(&Package{Name: "lib", Version: "1.7", Architecture: "i386"})
|
||||||
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
|
s.list.Add(&Package{Name: "app", Version: "1.9", Architecture: "amd64"})
|
||||||
@@ -82,7 +83,7 @@ type LocalRepoCollectionSuite struct {
|
|||||||
var _ = Suite(&LocalRepoCollectionSuite{})
|
var _ = Suite(&LocalRepoCollectionSuite{})
|
||||||
|
|
||||||
func (s *LocalRepoCollectionSuite) SetUpTest(c *C) {
|
func (s *LocalRepoCollectionSuite) SetUpTest(c *C) {
|
||||||
s.db, _ = database.OpenDB(c.MkDir())
|
s.db, _ = database.NewOpenDB(c.MkDir())
|
||||||
s.collection = NewLocalRepoCollection(s.db)
|
s.collection = NewLocalRepoCollection(s.db)
|
||||||
|
|
||||||
s.list = NewPackageList()
|
s.list = NewPackageList()
|
||||||
@@ -97,14 +98,14 @@ func (s *LocalRepoCollectionSuite) TearDownTest(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalRepoCollectionSuite) TestAddByName(c *C) {
|
func (s *LocalRepoCollectionSuite) TestAddByName(c *C) {
|
||||||
r, err := s.collection.ByName("local1")
|
_, err := s.collection.ByName("local1")
|
||||||
c.Assert(err, ErrorMatches, "*.not found")
|
c.Assert(err, ErrorMatches, "*.not found")
|
||||||
|
|
||||||
repo := NewLocalRepo("local1", "Comment 1")
|
repo := NewLocalRepo("local1", "Comment 1")
|
||||||
c.Assert(s.collection.Add(repo), IsNil)
|
c.Assert(s.collection.Add(repo), IsNil)
|
||||||
c.Assert(s.collection.Add(repo), ErrorMatches, ".*already exists")
|
c.Assert(s.collection.Add(repo), ErrorMatches, ".*already exists")
|
||||||
|
|
||||||
r, err = s.collection.ByName("local1")
|
r, err := s.collection.ByName("local1")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(r.String(), Equals, repo.String())
|
c.Assert(r.String(), Equals, repo.String())
|
||||||
|
|
||||||
@@ -115,13 +116,13 @@ func (s *LocalRepoCollectionSuite) TestAddByName(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalRepoCollectionSuite) TestByUUID(c *C) {
|
func (s *LocalRepoCollectionSuite) TestByUUID(c *C) {
|
||||||
r, err := s.collection.ByUUID("some-uuid")
|
_, err := s.collection.ByUUID("some-uuid")
|
||||||
c.Assert(err, ErrorMatches, "*.not found")
|
c.Assert(err, ErrorMatches, "*.not found")
|
||||||
|
|
||||||
repo := NewLocalRepo("local1", "Comment 1")
|
repo := NewLocalRepo("local1", "Comment 1")
|
||||||
c.Assert(s.collection.Add(repo), IsNil)
|
c.Assert(s.collection.Add(repo), IsNil)
|
||||||
|
|
||||||
r, err = s.collection.ByUUID(repo.UUID)
|
r, err := s.collection.ByUUID(repo.UUID)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(r.String(), Equals, repo.String())
|
c.Assert(r.String(), Equals, repo.String())
|
||||||
}
|
}
|
||||||
|
|||||||
+144
-85
@@ -3,11 +3,12 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/aptly"
|
|
||||||
"github.com/smira/aptly/utils"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package is single instance of Debian package
|
// Package is single instance of Debian package
|
||||||
@@ -23,22 +24,37 @@ type Package struct {
|
|||||||
Source string
|
Source string
|
||||||
// List of virtual packages this package provides
|
// List of virtual packages this package provides
|
||||||
Provides []string
|
Provides []string
|
||||||
|
// Hash of files section
|
||||||
|
FilesHash uint64
|
||||||
// Is this source package
|
// Is this source package
|
||||||
IsSource bool
|
IsSource bool
|
||||||
// Is this udeb package
|
// Is this udeb package
|
||||||
IsUdeb bool
|
IsUdeb bool
|
||||||
// Hash of files section
|
|
||||||
FilesHash uint64
|
|
||||||
// Is this >= 0.6 package?
|
// Is this >= 0.6 package?
|
||||||
V06Plus bool
|
V06Plus bool
|
||||||
// Offload fields
|
// Offload fields
|
||||||
deps *PackageDependencies
|
deps *PackageDependencies
|
||||||
extra *Stanza
|
extra *Stanza
|
||||||
files *PackageFiles
|
files *PackageFiles
|
||||||
|
contents []string
|
||||||
// Mother collection
|
// Mother collection
|
||||||
collection *PackageCollection
|
collection *PackageCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Package types
|
||||||
|
const (
|
||||||
|
PackageTypeBinary = "deb"
|
||||||
|
PackageTypeUdeb = "udeb"
|
||||||
|
PackageTypeSource = "source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Special arhictectures
|
||||||
|
const (
|
||||||
|
ArchitectureAll = "all"
|
||||||
|
ArhictectureAny = "any"
|
||||||
|
ArchitectureSource = "source"
|
||||||
|
)
|
||||||
|
|
||||||
// Check interface
|
// Check interface
|
||||||
var (
|
var (
|
||||||
_ json.Marshaler = &Package{}
|
_ json.Marshaler = &Package{}
|
||||||
@@ -75,6 +91,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
|
|||||||
MD5: strings.TrimSpace(md5),
|
MD5: strings.TrimSpace(md5),
|
||||||
SHA1: strings.TrimSpace(input["SHA1"]),
|
SHA1: strings.TrimSpace(input["SHA1"]),
|
||||||
SHA256: strings.TrimSpace(input["SHA256"]),
|
SHA256: strings.TrimSpace(input["SHA256"]),
|
||||||
|
SHA512: strings.TrimSpace(input["SHA512"]),
|
||||||
},
|
},
|
||||||
}})
|
}})
|
||||||
|
|
||||||
@@ -83,6 +100,7 @@ func NewPackageFromControlFile(input Stanza) *Package {
|
|||||||
delete(input, "MD5Sum")
|
delete(input, "MD5Sum")
|
||||||
delete(input, "SHA1")
|
delete(input, "SHA1")
|
||||||
delete(input, "SHA256")
|
delete(input, "SHA256")
|
||||||
|
delete(input, "SHA512")
|
||||||
delete(input, "Size")
|
delete(input, "Size")
|
||||||
|
|
||||||
depends := &PackageDependencies{}
|
depends := &PackageDependencies{}
|
||||||
@@ -114,62 +132,20 @@ func NewSourcePackageFromControlFile(input Stanza) (*Package, error) {
|
|||||||
delete(input, "Version")
|
delete(input, "Version")
|
||||||
delete(input, "Architecture")
|
delete(input, "Architecture")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
files := make(PackageFiles, 0, 3)
|
files := make(PackageFiles, 0, 3)
|
||||||
|
files, err = files.ParseSumFields(input)
|
||||||
parseSums := func(field string, setter func(sum *utils.ChecksumInfo, data string)) error {
|
|
||||||
for _, line := range strings.Split(input[field], "\n") {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if line == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts := strings.Fields(line)
|
|
||||||
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return fmt.Errorf("unparseable hash sum line: %#v", line)
|
|
||||||
}
|
|
||||||
|
|
||||||
size, err := strconv.ParseInt(parts[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse size: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Base(parts[2])
|
|
||||||
|
|
||||||
found := false
|
|
||||||
pos := 0
|
|
||||||
for i, file := range files {
|
|
||||||
if file.Filename == filename {
|
|
||||||
found = true
|
|
||||||
pos = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
files = append(files, PackageFile{Filename: filename, downloadPath: input["Directory"]})
|
|
||||||
pos = len(files) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
files[pos].Checksums.Size = size
|
|
||||||
setter(&files[pos].Checksums, parts[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(input, field)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := parseSums("Files", func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data })
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = parseSums("Checksums-Sha1", func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data })
|
|
||||||
if err != nil {
|
delete(input, "Files")
|
||||||
return nil, err
|
delete(input, "Checksums-Sha1")
|
||||||
}
|
delete(input, "Checksums-Sha256")
|
||||||
err = parseSums("Checksums-Sha256", func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data })
|
|
||||||
if err != nil {
|
for i := range files {
|
||||||
return nil, err
|
files[i].downloadPath = input["Directory"]
|
||||||
}
|
}
|
||||||
|
|
||||||
result.UpdateFiles(files)
|
result.UpdateFiles(files)
|
||||||
@@ -211,14 +187,19 @@ 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
|
// ExtendedStanza returns package stanza enhanced with aptly-specific fields
|
||||||
func (p *Package) MarshalJSON() ([]byte, error) {
|
func (p *Package) ExtendedStanza() Stanza {
|
||||||
stanza := p.Stanza()
|
stanza := p.Stanza()
|
||||||
stanza["FilesHash"] = fmt.Sprintf("%08x", p.FilesHash)
|
stanza["FilesHash"] = fmt.Sprintf("%08x", p.FilesHash)
|
||||||
stanza["Key"] = string(p.Key(""))
|
stanza["Key"] = string(p.Key(""))
|
||||||
stanza["ShortKey"] = string(p.ShortKey(""))
|
stanza["ShortKey"] = string(p.ShortKey(""))
|
||||||
|
|
||||||
return json.Marshal(stanza)
|
return stanza
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaller interface
|
||||||
|
func (p *Package) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(p.ExtendedStanza())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetField returns fields from package
|
// GetField returns fields from package
|
||||||
@@ -251,12 +232,12 @@ func (p *Package) GetField(name string) string {
|
|||||||
return p.Architecture
|
return p.Architecture
|
||||||
case "$PackageType":
|
case "$PackageType":
|
||||||
if p.IsSource {
|
if p.IsSource {
|
||||||
return "source"
|
return PackageTypeSource
|
||||||
}
|
}
|
||||||
if p.IsUdeb {
|
if p.IsUdeb {
|
||||||
return "udeb"
|
return PackageTypeUdeb
|
||||||
}
|
}
|
||||||
return "deb"
|
return PackageTypeBinary
|
||||||
case "Name":
|
case "Name":
|
||||||
return p.Name
|
return p.Name
|
||||||
case "Version":
|
case "Version":
|
||||||
@@ -289,7 +270,7 @@ func (p *Package) GetField(name string) string {
|
|||||||
|
|
||||||
// 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 == ArchitectureAll && arch != ArchitectureSource {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +317,22 @@ func (p *Package) MatchesDependency(dep Dependency) bool {
|
|||||||
panic("unknown relation")
|
panic("unknown relation")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDependencies compiles list of dependenices by flags from options
|
// GetName returns package name
|
||||||
|
func (p *Package) GetName() string {
|
||||||
|
return p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns package version
|
||||||
|
func (p *Package) GetVersion() string {
|
||||||
|
return p.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArchitecture returns package arch
|
||||||
|
func (p *Package) GetArchitecture() string {
|
||||||
|
return p.Architecture
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDependencies compiles list of dependncies by flags from options
|
||||||
func (p *Package) GetDependencies(options int) (dependencies []string) {
|
func (p *Package) GetDependencies(options int) (dependencies []string) {
|
||||||
deps := p.Deps()
|
deps := p.Deps()
|
||||||
|
|
||||||
@@ -362,7 +358,7 @@ func (p *Package) GetDependencies(options int) (dependencies []string) {
|
|||||||
if source == "" {
|
if source == "" {
|
||||||
source = p.Name
|
source = p.Name
|
||||||
}
|
}
|
||||||
if strings.Index(source, ")") != -1 {
|
if strings.Contains(source, ")") {
|
||||||
dependencies = append(dependencies, fmt.Sprintf("%s {source}", source))
|
dependencies = append(dependencies, fmt.Sprintf("%s {source}", source))
|
||||||
} else {
|
} else {
|
||||||
dependencies = append(dependencies, fmt.Sprintf("%s (= %s) {source}", source, p.Version))
|
dependencies = append(dependencies, fmt.Sprintf("%s (= %s) {source}", source, p.Version))
|
||||||
@@ -372,6 +368,16 @@ func (p *Package) GetDependencies(options int) (dependencies []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QualifiedName returns [$SECTION/]$NAME
|
||||||
|
func (p *Package) QualifiedName() string {
|
||||||
|
section := p.Extra()["Section"]
|
||||||
|
if section != "" {
|
||||||
|
return section + "/" + p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Name
|
||||||
|
}
|
||||||
|
|
||||||
// Extra returns Stanza of extra fields (it may load it from collection)
|
// Extra returns Stanza of extra fields (it may load it from collection)
|
||||||
func (p *Package) Extra() Stanza {
|
func (p *Package) Extra() Stanza {
|
||||||
if p.extra == nil {
|
if p.extra == nil {
|
||||||
@@ -410,6 +416,50 @@ func (p *Package) Files() PackageFiles {
|
|||||||
return *p.files
|
return *p.files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Contents returns cached package contents
|
||||||
|
func (p *Package) Contents(packagePool aptly.PackagePool, progress aptly.Progress) []string {
|
||||||
|
if p.IsSource {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.collection.loadContents(p, packagePool, progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateContents looks up contents in package file
|
||||||
|
func (p *Package) CalculateContents(packagePool aptly.PackagePool, progress aptly.Progress) ([]string, error) {
|
||||||
|
if p.IsSource {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file := p.Files()[0]
|
||||||
|
poolPath, err := file.GetPoolPath(packagePool)
|
||||||
|
if err != nil {
|
||||||
|
if progress != nil {
|
||||||
|
progress.ColoredPrintf("@y[!]@| @!Failed to build pool path: @| %s", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := packagePool.Open(poolPath)
|
||||||
|
if err != nil {
|
||||||
|
if progress != nil {
|
||||||
|
progress.ColoredPrintf("@y[!]@| @!Failed to open package in pool: @| %s", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
contents, err := GetContentsFromDeb(reader, file.Filename)
|
||||||
|
if err != nil {
|
||||||
|
if progress != nil {
|
||||||
|
progress.ColoredPrintf("@y[!]@| @!Failed to generate package contents: @| %s", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateFiles saves new state of files
|
// UpdateFiles saves new state of files
|
||||||
func (p *Package) UpdateFiles(files PackageFiles) {
|
func (p *Package) UpdateFiles(files PackageFiles) {
|
||||||
p.files = &files
|
p.files = &files
|
||||||
@@ -432,7 +482,7 @@ func (p *Package) Stanza() (result Stanza) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.IsSource {
|
if p.IsSource {
|
||||||
md5, sha1, sha256 := make([]string, 0), make([]string, 0), make([]string, 0)
|
md5, sha1, sha256, sha512 := []string{}, []string{}, []string{}, []string{}
|
||||||
|
|
||||||
for _, f := range p.Files() {
|
for _, f := range p.Files() {
|
||||||
if f.Checksums.MD5 != "" {
|
if f.Checksums.MD5 != "" {
|
||||||
@@ -444,11 +494,21 @@ func (p *Package) Stanza() (result Stanza) {
|
|||||||
if f.Checksums.SHA256 != "" {
|
if f.Checksums.SHA256 != "" {
|
||||||
sha256 = append(sha256, fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA256, f.Checksums.Size, f.Filename))
|
sha256 = append(sha256, fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA256, f.Checksums.Size, f.Filename))
|
||||||
}
|
}
|
||||||
|
if f.Checksums.SHA512 != "" {
|
||||||
|
sha512 = append(sha512, fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA512, f.Checksums.Size, f.Filename))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result["Files"] = strings.Join(md5, "")
|
result["Files"] = strings.Join(md5, "")
|
||||||
|
if len(sha1) > 0 {
|
||||||
result["Checksums-Sha1"] = strings.Join(sha1, "")
|
result["Checksums-Sha1"] = strings.Join(sha1, "")
|
||||||
|
}
|
||||||
|
if len(sha256) > 0 {
|
||||||
result["Checksums-Sha256"] = strings.Join(sha256, "")
|
result["Checksums-Sha256"] = strings.Join(sha256, "")
|
||||||
|
}
|
||||||
|
if len(sha512) > 0 {
|
||||||
|
result["Checksums-Sha512"] = strings.Join(sha512, "")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
f := p.Files()[0]
|
f := p.Files()[0]
|
||||||
result["Filename"] = f.DownloadURL()
|
result["Filename"] = f.DownloadURL()
|
||||||
@@ -456,10 +516,13 @@ func (p *Package) Stanza() (result Stanza) {
|
|||||||
result["MD5sum"] = f.Checksums.MD5
|
result["MD5sum"] = f.Checksums.MD5
|
||||||
}
|
}
|
||||||
if f.Checksums.SHA1 != "" {
|
if f.Checksums.SHA1 != "" {
|
||||||
result["SHA1"] = " " + f.Checksums.SHA1
|
result["SHA1"] = f.Checksums.SHA1
|
||||||
}
|
}
|
||||||
if f.Checksums.SHA256 != "" {
|
if f.Checksums.SHA256 != "" {
|
||||||
result["SHA256"] = " " + f.Checksums.SHA256
|
result["SHA256"] = f.Checksums.SHA256
|
||||||
|
}
|
||||||
|
if f.Checksums.SHA512 != "" {
|
||||||
|
result["SHA512"] = f.Checksums.SHA512
|
||||||
}
|
}
|
||||||
result["Size"] = fmt.Sprintf("%d", f.Checksums.Size)
|
result["Size"] = fmt.Sprintf("%d", f.Checksums.Size)
|
||||||
}
|
}
|
||||||
@@ -507,7 +570,7 @@ func (p *Package) LinkFromPool(publishedStorage aptly.PublishedStorage, packageP
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, f := range p.Files() {
|
for i, f := range p.Files() {
|
||||||
sourcePath, err := packagePool.Path(f.Filename, f.Checksums.MD5)
|
sourcePoolPath, err := f.GetPoolPath(packagePool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -515,7 +578,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, f.Checksums.MD5, force)
|
err = publishedStorage.LinkFromPool(publishedDirectory, f.Filename, packagePool, sourcePoolPath, f.Checksums, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -556,29 +619,25 @@ func (p *Package) PoolDirectory() (string, error) {
|
|||||||
|
|
||||||
// PackageDownloadTask is a element of download queue for the package
|
// PackageDownloadTask is a element of download queue for the package
|
||||||
type PackageDownloadTask struct {
|
type PackageDownloadTask struct {
|
||||||
RepoURI string
|
File *PackageFile
|
||||||
DestinationPath string
|
Additional []PackageDownloadTask
|
||||||
Checksums utils.ChecksumInfo
|
TempDownPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadList returns list of missing package files for download in format
|
// DownloadList returns list of missing package files for download in format
|
||||||
// [[srcpath, dstpath]]
|
// [[srcpath, dstpath]]
|
||||||
func (p *Package) DownloadList(packagePool aptly.PackagePool) (result []PackageDownloadTask, err error) {
|
func (p *Package) DownloadList(packagePool aptly.PackagePool, checksumStorage aptly.ChecksumStorage) (result []PackageDownloadTask, err error) {
|
||||||
result = make([]PackageDownloadTask, 0, 1)
|
result = make([]PackageDownloadTask, 0, 1)
|
||||||
|
|
||||||
for _, f := range p.Files() {
|
files := p.Files()
|
||||||
poolPath, err := packagePool.Path(f.Filename, f.Checksums.MD5)
|
for idx := range files {
|
||||||
if err != nil {
|
verified, err := files[idx].Verify(packagePool, checksumStorage)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
verified, err := f.Verify(packagePool)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !verified {
|
if !verified {
|
||||||
result = append(result, PackageDownloadTask{RepoURI: f.DownloadURL(), DestinationPath: poolPath, Checksums: f.Checksums})
|
result = append(result, PackageDownloadTask{File: &files[idx]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,11 +645,11 @@ func (p *Package) DownloadList(packagePool aptly.PackagePool) (result []PackageD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VerifyFiles verifies that all package files have neen correctly downloaded
|
// VerifyFiles verifies that all package files have neen correctly downloaded
|
||||||
func (p *Package) VerifyFiles(packagePool aptly.PackagePool) (result bool, err error) {
|
func (p *Package) VerifyFiles(packagePool aptly.PackagePool, checksumStorage aptly.ChecksumStorage) (result bool, err error) {
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
for _, f := range p.Files() {
|
for _, f := range p.Files() {
|
||||||
result, err = f.Verify(packagePool)
|
result, err = f.Verify(packagePool, checksumStorage)
|
||||||
if err != nil || !result {
|
if err != nil || !result {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -605,7 +664,7 @@ func (p *Package) FilepathList(packagePool aptly.PackagePool) ([]string, error)
|
|||||||
result := make([]string, len(p.Files()))
|
result := make([]string, len(p.Files()))
|
||||||
|
|
||||||
for i, f := range p.Files() {
|
for i, f := range p.Files() {
|
||||||
result[i], err = packagePool.RelativePath(f.Filename, f.Checksums.MD5)
|
result[i], err = f.GetPoolPath(packagePool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-14
@@ -3,15 +3,16 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/smira/aptly/aptly"
|
||||||
"github.com/smira/aptly/database"
|
"github.com/smira/aptly/database"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
"path/filepath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PackageCollection does management of packages in DB
|
// PackageCollection does management of packages in DB
|
||||||
type PackageCollection struct {
|
type PackageCollection struct {
|
||||||
db database.Storage
|
db database.Storage
|
||||||
encodeBuffer bytes.Buffer
|
|
||||||
codecHandle *codec.MsgpackHandle
|
codecHandle *codec.MsgpackHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,45 +162,86 @@ func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles {
|
|||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadContents loads or calculates and saves package contents
|
||||||
|
func (collection *PackageCollection) loadContents(p *Package, packagePool aptly.PackagePool, progress aptly.Progress) []string {
|
||||||
|
encoded, err := collection.db.Get(p.Key("xC"))
|
||||||
|
if err == nil {
|
||||||
|
contents := []string{}
|
||||||
|
|
||||||
|
decoder := codec.NewDecoderBytes(encoded, collection.codecHandle)
|
||||||
|
err = decoder.Decode(&contents)
|
||||||
|
if err != nil {
|
||||||
|
panic("unable to decode contents")
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != database.ErrNotFound {
|
||||||
|
panic("unable to load contents")
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := p.CalculateContents(packagePool, progress)
|
||||||
|
if err != nil {
|
||||||
|
// failed to acquire contents, don't persist it
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = codec.NewEncoder(&buf, collection.codecHandle).Encode(contents)
|
||||||
|
if err != nil {
|
||||||
|
panic("unable to encode contents")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.db.Put(p.Key("xC"), buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
panic("unable to save contents")
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
// 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, collection.codecHandle)
|
var encodeBuffer bytes.Buffer
|
||||||
|
|
||||||
collection.encodeBuffer.Reset()
|
encoder := codec.NewEncoder(&encodeBuffer, collection.codecHandle)
|
||||||
collection.encodeBuffer.WriteByte(0xc1)
|
|
||||||
collection.encodeBuffer.WriteByte(0x1)
|
encodeBuffer.Reset()
|
||||||
|
encodeBuffer.WriteByte(0xc1)
|
||||||
|
encodeBuffer.WriteByte(0x1)
|
||||||
err := encoder.Encode(p)
|
err := encoder.Encode(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.db.Put(p.Key(""), collection.encodeBuffer.Bytes())
|
err = collection.db.Put(p.Key(""), encodeBuffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode offloaded fields one by one
|
// Encode offloaded fields one by one
|
||||||
if p.files != nil {
|
if p.files != nil {
|
||||||
collection.encodeBuffer.Reset()
|
encodeBuffer.Reset()
|
||||||
err = encoder.Encode(*p.files)
|
err = encoder.Encode(*p.files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.db.Put(p.Key("xF"), collection.encodeBuffer.Bytes())
|
err = collection.db.Put(p.Key("xF"), encodeBuffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.deps != nil {
|
if p.deps != nil {
|
||||||
collection.encodeBuffer.Reset()
|
encodeBuffer.Reset()
|
||||||
err = encoder.Encode(*p.deps)
|
err = encoder.Encode(*p.deps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.db.Put(p.Key("xD"), collection.encodeBuffer.Bytes())
|
err = collection.db.Put(p.Key("xD"), encodeBuffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -208,13 +250,13 @@ func (collection *PackageCollection) Update(p *Package) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.extra != nil {
|
if p.extra != nil {
|
||||||
collection.encodeBuffer.Reset()
|
encodeBuffer.Reset()
|
||||||
err = encoder.Encode(*p.extra)
|
err = encoder.Encode(*p.extra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collection.db.Put(p.Key("xE"), collection.encodeBuffer.Bytes())
|
err = collection.db.Put(p.Key("xE"), encodeBuffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -245,7 +287,7 @@ func (collection *PackageCollection) DeleteByKey(key []byte) error {
|
|||||||
|
|
||||||
// Scan does full scan on all the packages
|
// Scan does full scan on all the packages
|
||||||
func (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) {
|
func (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) {
|
||||||
result = NewPackageList()
|
result = NewPackageListWithDuplicates(true, 0)
|
||||||
|
|
||||||
for _, key := range collection.db.KeysByPrefix([]byte("P")) {
|
for _, key := range collection.db.KeysByPrefix([]byte("P")) {
|
||||||
pkg, err := collection.ByKey(key)
|
pkg, err := collection.ByKey(key)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user