Compare commits

..

1073 Commits

Author SHA1 Message Date
Andrey Smirnov 35e2253944 Merge pull request #614 from smira/600-fix-double-mirror-update
Fix bug with `PoolPath` field being overwritten on mirror update
2017-08-11 20:48:04 +03:00
Andrey Smirnov a584b2e058 Fix bug with PoolPath field being overwritten on mirror update
While updating mirror, if package file is already in pool path,
field `PoolPath` was left as empty which results in package file
being unavailable later on while publishing.
2017-08-11 20:05:55 +03:00
Andrey Smirnov 587bfd742f Merge pull request #613 from smira/607-trim-slashes
Trim slashes while parsing publish prefix
2017-08-09 13:36:35 +03:00
Andrey Smirnov 84ef963d7d Trim slashes while parsing publish prefix
Fixes: #607
2017-08-09 01:26:47 +03:00
Andrey Smirnov e70ef0a518 Merge pull request #612 from smira/610-stderr
Print error messagge 'unable to open database' to stderr
2017-08-09 01:00:18 +03:00
Andrey Smirnov e05768737f Print error messagge 'unable to open database' to stderr
Fixes #610
2017-08-09 00:01:51 +03:00
Andrey Smirnov a626e4693b Merge pull request #604 from AlekSi/patch-1
Try to reduce build duration
2017-08-08 22:04:49 +03:00
Alexey Palazhchenko 4d9b4298d8 Merge branch 'master' into patch-1 2017-07-31 22:13:27 +03:00
Andrey Smirnov 4cca7272ce Merge pull request #603 from smira/improve-pgp-internal
Improve internal PGP provider
2017-07-31 20:57:43 +03:00
Andrey Smirnov e9b2c18e2f Attempt to fix the tests 2017-07-28 22:37:20 +03:00
Andrey Smirnov cbb576cbcc Fix up system tests 2017-07-28 22:37:20 +03:00
Alexey Palazhchenko bcc83bff31 Try to reduce build duration 2017-07-28 10:10:34 +03:00
Andrey Smirnov 68da8a674a Improve internal PGP provider
1. Print additional details about keys being used for signing
2. Skip expired keys
3. Add `\n` to logged messages
2017-07-28 00:53:50 +03:00
Andrey Smirnov cafa82f018 Merge pull request #601 from AlekSi/patch-1
Use SVG badges
2017-07-28 00:47:08 +03:00
Andrey Smirnov 83a9c394f3 Merge pull request #602 from AlekSi/patch-2
Update Travis CI configuration
2017-07-28 00:46:55 +03:00
Alexey Palazhchenko 2c0a1b836c Update Travis CI configuration
* Use Ubuntu 14.04 with Docker.
* Use latest patch versions of Go.
* Replace hacks for forks with an official solution.
2017-07-28 00:08:27 +03:00
Alexey Palazhchenko 28ae18792d Use SVG badges 2017-07-27 23:57:13 +03:00
Andrey Smirnov 2811ad02d5 Merge pull request #581 from smira/pgp-golang
Implement new PGP provider via Go internal library
2017-07-26 22:14:46 +03:00
Andrey Smirnov ab20c2d329 Add publishing & repo include tests 2017-07-26 00:31:27 +03:00
Andrey Smirnov d137bcf8d4 Fix up/add mirror update tests 2017-07-26 00:00:06 +03:00
Andrey Smirnov 3674e1adee System tests for mirror create/update with internal PGP implementation 2017-07-21 01:09:20 +03:00
Andrey Smirnov 05a5e69483 Fix misspelling 2017-07-21 01:01:58 +03:00
Andrey Smirnov 5e9515a912 Add --batch in batch mode (fixes #519) 2017-07-21 01:01:58 +03:00
Andrey Smirnov 84a6d573f8 Implement GPG signer 2017-07-21 01:01:58 +03:00
Andrey Smirnov 6228a399cf Mute goconst warnings 2017-07-21 01:01:58 +03:00
Andrey Smirnov 0e9f966dd1 Fix up other code to support new GPG provider structure 2017-07-21 01:01:58 +03:00
Andrey Smirnov 07fde3177b GoVerifier implementation 2017-07-21 01:01:58 +03:00
Andrey Smirnov f9377b2aa6 Update bash completion for new flag 2017-07-21 01:01:58 +03:00
Andrey Smirnov 499ab35012 Implement flag/config falue for GPG provider 2017-07-21 01:01:58 +03:00
Andrey Smirnov 3c95f92b95 Now using openpgp package from golang.org/x/crypto 2017-07-21 01:01:58 +03:00
Andrey Smirnov d7a7aa93a4 Merge pull request #596 from smira/s3-opts-man
Document additional S3 options [ci skip]
2017-07-21 01:00:45 +03:00
Andrey Smirnov 58ab4e8902 Document additional S3 options [ci skip] 2017-07-21 00:57:06 +03:00
Andrey Smirnov fcd453118b Merge pull request #590 from smira/1.1-fixups
Small fixups for upcoming 1.1.0 release
2017-07-07 01:17:37 +03:00
Andrey Smirnov 7d179dd405 Small fixups for upcoming 1.1.0 release
Fix system tests, add -db-open-attempts to bash completion, small nits
for man page.
2017-07-07 00:14:03 +03:00
Andrey Smirnov 20b874f81f Merge pull request #577 from sliverc/backport_support
Added support for NotAutomatic, ButAutomaticUpgrades and Origin fields
2017-07-06 00:18:18 +03:00
Oliver Sauder e3f1880ad4 Added support for NotAutomatic, ButAutomaticUpgrades and Origin fields 2017-07-05 15:08:02 +02:00
Andrey Smirnov 39293d7faf Merge pull request #589 from smira/api-db-no-lock-fix
Rework the way database is open/re-open in aptly
2017-07-05 12:57:26 +03:00
Andrey Smirnov c13eb99925 Style fixups 2017-07-05 00:36:48 +03:00
Andrey Smirnov 211ac0501f Rework the way database is open/re-open in aptly
Allow database to be initialized without opening, unify all the
open paths to retry on failure.

In API router make sure open requests are matched with acks in explicit
way.

This also enables re-open attempts in all the aptly commands, so it
should make running aptly CLI much easier now hopefully.

Fix up system tests for oldoldstable ;)
2017-07-05 00:17:48 +03:00
Andrey Smirnov af2f7baf63 Merge pull request #556 from smira/contributing-documentation
Docs on contributing to aptly [ci skip]
2017-06-27 01:15:25 +03:00
Andrey Smirnov 3c25db3ffb Docs on contributing to aptly [ci skip] 2017-06-27 01:13:18 +03:00
Andrey Smirnov 12a6b0ceb8 Merge pull request #575 from smira/pgp-refactoring
Refactor GPG signer/verifier
2017-05-24 19:24:38 +03:00
Andrey Smirnov 0d041898ca Merge pull request #574 from smira/376-checksum-search-fix
Fix checksum matching from Release file
2017-05-23 21:53:06 +03:00
Andrey Smirnov 982c093fbf Fix system tests 2017-05-23 16:24:49 +03:00
Andrey Smirnov f54e798eac Add system test for fixed checksum matching 2017-05-23 03:00:16 +03:00
Andrey Smirnov cafb89f30f Re-work the way checksum matching works against Release file
Break up URL into base part and relative path. Match checksum against relative path
and never against full URL.

This might be fixing security issue if aptly was incorrectly matching against
wrong part of Release file.
2017-05-23 03:00:15 +03:00
Andrey Smirnov f0360cf2d3 Use longest suffix match to pick up checksum 2017-05-23 03:00:15 +03:00
Andrey Smirnov 1be8d39105 Refactor GPG signer/verifier
Goal is to make it easier to plug in another implementation.
2017-05-23 02:54:56 +03:00
Andrey Smirnov c026106352 Merge pull request #571 from sliverc/travis_for_forks
Making travis CI run again on forks
2017-05-19 22:41:15 +03:00
Oliver Sauder c507d0620b Making travis CI run again on forks 2017-05-19 16:37:04 +02:00
Andrey Smirnov f84672239a Merge pull request #569 from smira/linters-4
Enable goconst & interfacer linters
2017-05-17 15:33:02 +03:00
Andrey Smirnov c9bd7b4b5d Merge pull request #567 from smira/431-500-api
Fix possible cause for spurious 500s
2017-05-17 15:32:52 +03:00
Andrey Smirnov 470165a419 Enable goconst & interfacer linters 2017-05-17 00:53:10 +03:00
Andrey Smirnov 9de9fbe6bd Merge branch 'master' into 431-500-api 2017-05-17 00:52:32 +03:00
Andrey Smirnov e396a2e6c3 Merge pull request #568 from smira/fix-flat-tests
Fix tests for flat repos
2017-05-17 00:52:19 +03:00
Andrey Smirnov 829ea2e65c Fix tests for flat repos 2017-05-17 00:09:18 +03:00
Andrey Smirnov 39d2d273dc Fix possible cause for spurious 500s
When DB fails to be open, aptly was skipping "close" phase, so that next
request considered database to be still open (while it's closed) leading
to panic.

Fixes: #431
2017-05-16 00:42:59 +03:00
Andrey Smirnov 5a3e660c0d Merge pull request #566 from smira/135-sort-search
Sort package lists when searching or showing objects
2017-05-10 00:19:53 +03:00
Andrey Smirnov 589dc93380 Sort package lists when searching or showing objects
Fixes #135
Fixes #214
2017-05-05 18:42:46 +03:00
Andrey Smirnov 33357c1fe4 Merge pull request #565 from smira/linters-3
Enable `vetshadow` linter
2017-05-05 17:43:09 +03:00
Andrey Smirnov 5ce6bf8718 Enable vetshadow linter 2017-05-04 23:00:13 +03:00
Andrey Smirnov d7bcf372c4 Merge pull request #564 from jyundt/add_mirror_edit_bash_autocomplete
Add mirror edit option to bash auto-complete
2017-05-04 21:26:11 +03:00
Jacob Yundt 3aa044d722 Add mirror edit option to bash auto-complete
Fixes smira/aptly#563
2017-05-04 13:24:34 -04:00
Andrey Smirnov a9a5a73dfd Merge pull request #560 from smira/linters-2
More Go linters enabled, issues fixed
2017-05-04 02:13:06 +03:00
Andrey Smirnov 66b44e68a9 Attempt to workaround Travis "10 minutes without output". 2017-05-03 20:28:57 +03:00
Andrey Smirnov 51213899b7 More Go linters enabled, issues fixed
Ref: #528

Enables "staticcheck", "varcheck", "structcheck", "aligncheck"
2017-05-03 18:23:14 +03:00
Andrey Smirnov 7a7c9cd26c Merge pull request #559 from smira/linters-extend-deadline
Extend linters deadline
2017-05-02 22:12:45 +03:00
Andrey Smirnov 1b9ab46c5f Extend linters deadline
Ref: #528
2017-05-02 20:35:16 +03:00
Andrey Smirnov 2cbed28446 Merge pull request #558 from smira/linters-python
Add system's requirements.txt, enforce flake8 linter
2017-04-28 16:14:32 +03:00
Andrey Smirnov 39aa0fdbfe Merge pull request #557 from smira/linters-1
Enable `gosimple` and `ineffasign` linters
2017-04-28 00:29:20 +03:00
Andrey Smirnov c983810e2d Manually create system/env, workaround Travis issues 2017-04-28 00:16:21 +03:00
Andrey Smirnov c798db8056 Add system's requirements.txt, enforce flake8 linter
Fix style issues in functional tests.
2017-04-28 00:05:11 +03:00
Andrey Smirnov 1e4a80252e Extend linter deadline to 1 minute 2017-04-27 22:32:35 +03:00
Andrey Smirnov bae3f949b4 Enable gosimple and ineffasign linters 2017-04-27 18:34:30 +03:00
Andrey Smirnov 7a7b981d4f Merge pull request #539 from smira/public-pool-paths
New package pool with configurable hashing base
2017-04-27 16:24:14 +03:00
Andrey Smirnov 2ffefeb1e0 Add man page for skipLegacyPool 2017-04-27 00:51:46 +03:00
Andrey Smirnov 1941418c10 Add system test for disabled legacy pool path support 2017-04-27 00:51:46 +03:00
Andrey Smirnov 186bb2dff0 Add flag to disable/enable support for legacy pool paths
Legacy pool paths are enabled by default, but for new aptly installations
(when aptly config is first generated), it would be disabled explicitly.
2017-04-26 23:37:31 +03:00
Andrey Smirnov 2308632683 Add system tests for legacy pool files 2017-04-26 23:17:04 +03:00
Andrey Smirnov ee21b69402 Add test for mirror without MD5 checksums 2017-04-26 23:17:04 +03:00
Andrey Smirnov 01512df853 Rework mirror update to support closing/reoping DB for the download duration
This requires splitting up import file phase as separate step in then end,
it should be pretty fast, as it only does file move (hardlink) and
DB update for new checksums.
2017-04-26 23:17:04 +03:00
Andrey Smirnov 7dcc0d597d Fix S3/Swift tests 2017-04-26 23:17:04 +03:00
Andrey Smirnov 154ef7fe65 Fix system tests for aptly repo include 2017-04-26 23:17:04 +03:00
Andrey Smirnov 4601f07349 Fix system tests for aptly repo add 2017-04-26 23:17:04 +03:00
Andrey Smirnov b7b9f12c88 Update system tests for SHA512 checksums being generated 2017-04-26 23:17:04 +03:00
Andrey Smirnov b48e8425ec Fix bug with file not being updated properly 2017-04-26 23:17:04 +03:00
Andrey Smirnov 3ce8227122 Add baseName to LinkFromPool as explicit argument 2017-04-26 23:17:04 +03:00
Andrey Smirnov 0bc3f71d27 Use SHA256 as main checksum in the pool 2017-04-26 23:17:04 +03:00
Andrey Smirnov c1d4c0fb88 Temporarily disable db close/open cycle (to be addressed later) 2017-04-26 23:17:04 +03:00
Andrey Smirnov 8078f3b588 We should remove file only when checksums are calculated 2017-04-26 23:17:04 +03:00
Andrey Smirnov 5dd11a2ec2 Pull original packages when skipping existing packages 2017-04-26 23:17:04 +03:00
Andrey Smirnov cc34a021ce Fix misspellings 2017-04-26 23:17:04 +03:00
Andrey Smirnov 10c096fbb6 Update all other pieces for the CheckumStorage and Verify 2017-04-26 23:17:04 +03:00
Andrey Smirnov a85d8b6f90 Mock for ChecksumStorage 2017-04-26 23:17:04 +03:00
Andrey Smirnov 5566111a7b New ChecksumStorage and new PackagePool interfaces 2017-04-26 23:17:04 +03:00
Andrey Smirnov 6994e35119 ChecksumCollection implementation 2017-04-26 23:17:04 +03:00
Andrey Smirnov 4eedb62418 Fix misspelling 2017-04-26 23:17:04 +03:00
Andrey Smirnov 1f3cb2db5d When downloading/importing packages, enforce all checksums 2017-04-26 23:17:04 +03:00
Andrey Smirnov c40025a335 Add progress bar on package saving progress 2017-04-26 23:17:03 +03:00
Andrey Smirnov 4171a73995 Fix up system test 2017-04-26 23:17:03 +03:00
Andrey Smirnov 29e5f4ca10 Vendor import github.com/pkg/errors 2017-04-26 23:17:03 +03:00
Andrey Smirnov 05f6c75743 Add PR #506 original author [ci skip] 2017-04-26 23:17:03 +03:00
Andrey Smirnov 45d187bc14 Fix up system test 2017-04-26 23:17:03 +03:00
Andrey Smirnov bc7903f86e Rework mirror update (download packages) implementation
`PackageDownloadTask` is just a reference to file now. Whole process
was rewritten to follow pattern: download to temp location inside the pool,
verify/update checksums, import into pool as final step.

This removes a lot of edge cases when aptly internal state might be broken
if updating from rogue mirror.

Also this changes whole memory model: package list/files are kept in memory
now during the duration of `mirror update` command and saved to disk
only in the end.
2017-04-26 23:17:03 +03:00
Andrey Smirnov 72d233b587 Final round of updates, everything except mirror download should be ready 2017-04-26 23:17:03 +03:00
Andrey Smirnov 2535367c3c Update Swift published storage to work with new package pool 2017-04-26 23:17:03 +03:00
Andrey Smirnov f4ff8d957f Fix S3 published storage to use new PackagePool interface
Change PackagePool to return Seeker interface from Open call.
2017-04-26 23:17:03 +03:00
Andrey Smirnov 7bad358408 Local package pool and local publishing rewritten with new constraints
Local package pool now implements more generic package pool API.
The base idea is to never expose full paths to files, so that other
kinds of package pools (e.g. package pool in S3) could be used to implement
the same interface.

Files get into the pool only using `Import` method. `Import` method is
now more smart, it supports moving files into the pool, it can detect if
files reside on the same filesystem and use hardlinking instead of copying.
This will make direct mirror downloads still as fast as they were with previous
version which was performing download directly to package pool.

New package pool doesn't have two things implemented yet:

1. New file placement according to SHA256 or other configured hash

2. Calculate at least SHA256/MD5 for each imported files.
MD5 would be required for S3/Swift publishing
2017-04-26 23:17:03 +03:00
Andrey Smirnov 94b49818a1 Refactor HTTP downloader package
* Drop multi-threaded downloader. It doesn't really belong here -
some places require it, some do not, but it's definitely not the
right place to handle it, as it's being used only when updating
mirrors
* Pass expectedChecksums as pointer, so it's easy to drive `nil` value,
and also downloader can fill back checksums (not implemented right now).
* Break down downloader and tests into more files
* Use pkg/errors instead of fmt
2017-04-26 23:17:03 +03:00
Andrey Smirnov a245b722a8 Merge pull request #555 from smira/288-empty-repo-snapshot
Allow snapshot to be created from empty local repo
2017-04-26 01:11:40 +03:00
Andrey Smirnov 8dc6a14766 Allow snapshot to be created from empty local repo
Fixes #288
2017-04-26 00:33:09 +03:00
Andrey Smirnov d66185ca03 Merge pull request #554 from smira/flat-system-test
Fix system tests for flat repos
2017-04-24 23:45:58 +03:00
Andrey Smirnov c3acabe303 Fix system tests for flat repos
Old mirror used for testing is gone, switch to CRAN.
2017-04-24 23:04:48 +03:00
Andrey Smirnov 4697d8eaf8 Merge pull request #550 from smira/549-fix-deps
Rewrite `dep` files into new format
2017-04-14 22:48:53 +03:00
Andrey Smirnov 8bf71a5561 Rewrite dep files into new format
Conversion to TOML plus some manual fixups.
2017-04-14 17:24:49 +03:00
Andrey Smirnov 898cbd2c83 Merge pull request #548 from smira/546-fix-depends
Update `Depends:` for homegrown packages
2017-04-14 16:22:03 +03:00
Andrey Smirnov 62762f1616 Merge branch 'master' into 546-fix-depends 2017-04-14 00:57:36 +03:00
Andrey Smirnov 4d38e0bc87 Merge pull request #521 from seeraven/feature_localfiles_publishstorage
Implemented local filesystem endpoint with support for hardlinks, symlinks and copy
2017-04-14 00:52:49 +03:00
Clemens Rabe 25f9c29f00 Implemented filesystem endpoint with support for hardlinks, symlinks and copy. 2017-04-13 20:25:40 +02:00
Andrey Smirnov 096b30b5e8 Update Depends: for homegrown packages
This should match upstream Debian package, at the same time
`goxc` seems to be broken and it ignores `Suggests`.
2017-04-13 01:33:30 +03:00
Andrey Smirnov ac475c0a10 Merge pull request #544 from smira/543-tmp-dirs-leftover
Fix temporary contents DB being left behind after publishing
2017-04-11 00:28:13 +03:00
Andrey Smirnov 60800b5f25 Fix temporary contents DB being left behind after publishing
NB: Go `defer` order execution is reverse to the order `defer` statements
are executed.

So before the change, `Drop()` was called before `Close()`, which was no-op.

Change that to explicit order in single func, print errors if they happen.
2017-04-10 23:43:33 +03:00
Andrey Smirnov 36a4d78162 Merge pull request #535 from smira/public-pool-checksums
Refactoring: use checksums instead of MD5 for pool/published
2017-04-03 17:31:04 +03:00
Andrey Smirnov 50cf2b49bd Refactoring: use checksums instead of MD5 for pool/published
This is related to #506

As a first step, don't pass MD5 explicitly, pass checksum info object,
so that as a next step we can choose which hash to use.

There should be no functional changes so far.

Next step: stop returning explicit paths from public package pool.
2017-04-01 00:12:31 +03:00
Andrey Smirnov 675d35c7a1 Merge pull request #508 from smira/dep-verbose-resolve
Add new option for detailed logging on dependency resolving
2017-03-31 19:58:23 +03:00
Andrey Smirnov bc469eecfb Merge branch 'master' into dep-verbose-resolve 2017-03-31 19:17:57 +03:00
Andrey Smirnov bc01d9ed5b Merge pull request #534 from smira/533-ignore-contents-failures
When contents generation fails, don't bail out
2017-03-31 16:38:25 +03:00
Andrey Smirnov 7a5be6736d When contents generation fails, don't bail out
This replaces `panic` which aborts aptly execution with warning
message on console. So aptly continues publishing actions, but
`Contents` indexes might be incomplete.

Error will be printed every time contents generation is triggered.
2017-03-31 00:57:18 +03:00
Andrey Smirnov eb48460b7b Update bash completion 2017-03-28 22:58:53 +03:00
Andrey Smirnov 85b4a8b1ae Add new option for detailed logging on dependency resolving
This adds command-line arg and config option, with option enabled
aptly is more verbose on internal depeendency resolving cycles:

```
Missing dependencies: file-rc (>= 0.8.16) [amd64], python:any (>= 2.7.1-0ubuntu2) [amd64], python3:any (>= 3.3.2-2~) [amd64], file-rc [amd64], perl (<< 5.17) [amd64], iptables-router (>= 1.2.3) [amd64], systemd [amd64], sgml-base (>= 1.26+nmu2) [amd64], sed (>= 4.1.2-8) [amd64]
Unsatisfied dependency: file-rc (>= 0.8.16) [amd64]
Unsatisfied dependency: python:any (>= 2.7.1-0ubuntu2) [amd64]
Unsatisfied dependency: python3:any (>= 3.3.2-2~) [amd64]
Unsatisfied dependency: file-rc [amd64]
Unsatisfied dependency: perl (<< 5.17) [amd64]
Unsatisfied dependency: iptables-router (>= 1.2.3) [amd64]
Unsatisfied dependency: systemd [amd64]
Injecting package: sgml-base_1.26+nmu4ubuntu1_all
Injecting package: sed_4.2.2-4ubuntu1_amd64
```
2017-03-28 22:58:07 +03:00
Andrey Smirnov e6bad637fd Merge pull request #532 from smira/530-bash-completion
Move bash completion to main aptly repo
2017-03-28 22:22:10 +03:00
Andrey Smirnov 47b5cc27c8 Move bash completion to main aptly repo
Fixes #530

Original repository: https://github.com/aptly-dev/aptly-bash-completion
2017-03-28 21:41:57 +03:00
Andrey Smirnov ca16841223 Merge pull request #520 from seeraven/feature_skip_existing_packages_latest
Add option -skip-existing-packages to mirror update to speed up download queue setup
2017-03-28 21:39:36 +03:00
Andrey Smirnov 800c5c1e06 Merge branch 'master' into feature_skip_existing_packages_latest 2017-03-28 21:26:28 +03:00
Andrey Smirnov 7fd8bd0171 Merge pull request #531 from smira/release-1.0.0-preparation
Prepare for new release, update build instructions [ci skip]
2017-03-28 00:17:08 +03:00
Andrey Smirnov 4707efe4d6 Prepare for new release, update build instructions [ci skip] 2017-03-28 00:15:41 +03:00
Andrey Smirnov 8ae61f9448 Merge pull request #523 from smira/versioning
Automatic versioning for aptly
2017-03-27 16:02:25 +03:00
Andrey Smirnov a138d0111d Update README to use go install which will build with version 2017-03-26 19:24:32 +03:00
Andrey Smirnov af1adb44ce Remove -x flag for go install 2017-03-26 19:23:53 +03:00
Clemens Rabe 4ddf85bbc1 Rebuilt man page with patched ronn. 2017-03-25 08:52:08 +01:00
Clemens Rabe 9978595c59 Merge branch 'master' into feature_skip_existing_packages_latest 2017-03-25 08:50:24 +01:00
Andrey Smirnov 2943422d5d Automatic versioning for aptly
New version format:

* for releases, `x.y.z` (follows tag without leading `v`)
* for nightly builds, `x.y.z+N+hash` (previous version, not the upcoming one)

This means that each nightly build `aptly` would report
correct version now.

Version is now complied into the aptly binary, system tests
automatically check for current version, no need to update them
anymore.
2017-03-25 00:18:45 +03:00
Andrey Smirnov 91219e3a0a Merge pull request #522 from smira/man-gen-rework
Rework man generator with new `go install` format
2017-03-24 21:50:28 +03:00
Andrey Smirnov 7f8db9087a Rework man generator with new go install format
With previous version, `go install` automatically picks up
package `man` and installs `gen.go` as `main` to the $GOPATH/bin which
is not what is expected. Move man page generator to separate
private folder.
2017-03-24 21:07:38 +03:00
Clemens Rabe aa16899c60 Adaption of tests. 2017-03-24 06:25:46 +01:00
Clemens Rabe 16a0d0d428 Added option --skip-existing-packages to speed up mirror update. 2017-03-23 22:01:11 +01:00
Clemens Rabe 66f51d2b17 Added option --skip-existing-packages to speed up mirror update. 2017-03-23 21:55:22 +01:00
Andrey Smirnov 92c844b8ac Merge pull request #517 from smira/goreportcard
Fix goreportcard badge [ci skip]
2017-03-23 18:33:00 +03:00
Andrey Smirnov 2b56a3937b Fix goreportcard badge [ci skip] 2017-03-23 18:32:00 +03:00
Andrey Smirnov 9cea9b6470 Merge pull request #512 from smira/500-xdg-open
Customize viewer per platform
2017-03-23 18:28:24 +03:00
Andrey Smirnov e3e68b9f22 Customize viewer per platform 2017-03-23 17:12:34 +03:00
Andrey Smirnov d56839664d Merge pull request #513 from smira/gometalinter
Switch to gometalinter
2017-03-23 16:32:33 +03:00
Andrey Smirnov 516dd7b044 Switch to gometalinter
Only small amount of required checks is enabled,
plan is to enable more linters as issues are fixed in the code.
2017-03-23 01:51:08 +03:00
Andrey Smirnov 53e59d3765 Merge pull request #509 from smira/golint-govet
Add govet/golint into Travis CI build
2017-03-22 22:14:30 +03:00
Andrey Smirnov 11d828b3b1 Add govet/golint into Travis CI build
Fix current issues
2017-03-22 21:49:16 +03:00
Andrey Smirnov 07472bec50 Merge pull request #511 from smira/dep-experiment
Convert to regular Go vendor + `dep` tool
2017-03-22 20:34:36 +03:00
Andrey Smirnov f737787c01 Fix up system tests 2017-03-22 19:56:23 +03:00
Andrey Smirnov c6c1012330 Conver to regular Go vendor + dep tool 2017-03-22 19:24:06 +03:00
Andrey Smirnov 070347295e Merge pull request #505 from smira/rmedaer-master
Added '.xz' extension in HTTP download.
2017-03-17 00:47:33 +03:00
Andrey Smirnov acd8d4a6ea Go 1.6 compatibility 2017-03-17 00:17:07 +03:00
Andrey Smirnov c9768416ed Fix up tests 2017-03-16 23:06:41 +03:00
Raphael Medaer bfb9ffad1d Added expected error on 'Packages.xz' for TestDownload[WithSources]Flat. 2017-03-16 22:41:25 +03:00
Raphael Medaer 9cfe1307e3 Added download tests for xz compression. 2017-03-16 22:41:25 +03:00
Raphael Medaer db8595711b Added '.xz' reader in HTTP download. This fixed issue when you try to mirror a distribution with only Packages.xz (example 'debian/experimental'). 2017-03-16 22:41:25 +03:00
Andrey Smirnov ce0001f94c Merge pull request #504 from smira/go-1.8
Add Travis CI build on Go 1.8
2017-03-16 22:16:01 +03:00
Andrey Smirnov b102562478 Fix up system test for Go 1.8 2017-03-16 20:58:21 +03:00
Andrey Smirnov 69cbe10690 Add Travis CI build on Go 1.8 2017-03-16 18:46:03 +03:00
Andrey Smirnov e3e4ea91bd Merge pull request #502 from smira/aws-sdk-bump
Bump Go AWS SDK to the latest version
2017-03-16 18:45:26 +03:00
Andrey Smirnov 02c582e227 Merge pull request #503 from smira/pr-template-update
Add bash completion to PR template [ci skip]
2017-03-16 01:31:52 +03:00
Andrey Smirnov 6e96cd29dc Add bash completion to PR template [ci skip] 2017-03-16 01:30:00 +03:00
Andrey Smirnov 17044f43dc Bump Go AWS SDK to the latest version
This seems to fix `NotImplemented` S3 error
2017-03-16 01:28:44 +03:00
Andrey Smirnov 5d3b170ffc Merge pull request #497 from smira/repo-create-from-snap
Implement new command `aptly repo create ... from snapshot ...`
2017-03-16 01:12:09 +03:00
Andrey Smirnov a0f7b2242d Merge pull request #499 from sobczyk/dbgsym
include dbgsym packages
2017-03-14 22:51:24 +03:00
Szymon Sobik b8e7ad9022 update changes unit test to account for dbgsym matching 2017-03-08 10:32:11 +01:00
Szymon Sobik 1b80d55ea4 since -dbgsym is for each binary package use that for PackageQuery 2017-03-08 10:31:44 +01:00
Szymon Sobik a0832adfa5 include dbgsym packages
fixes #331
2017-03-07 17:06:59 +01:00
Andrey Smirnov f17d398e8f Implement new command aptly repo create ... from snapshot ... 2017-03-04 00:12:18 +03:00
Andrey Smirnov bc3b2ed5a8 Merge pull request #495 from apachelogger/systemd-activation
support systemd activation for `api serve`
2017-03-03 22:55:47 +03:00
Harald Sitter 07cf8925f9 support systemd activation for api serve
systemd has a feature called socket activation where initially systemd
manages and listens on ports/uds and only invokes a service when traffic
appears. to then hand over the involved sockets, systemd will pass the
relevant FDs into the invoked process and defines them in the environment.

use coreos/go-systemd to grab the active listeners passed by systemd and
use them to serve the api routes. only one listener may be specified right
now as we also only support one -listen argument for the binary.

this allows admins to craft a systemd socket and service file for aptly
where systemd manages the socket, its permission and its live time, and
lazy start aptly when needed.
2017-03-01 11:12:10 +01:00
Andrey Smirnov 564ebf3130 Merge pull request #493 from apachelogger/api-over-socket
support serving the API over unix domain socket
2017-02-28 23:41:09 +03:00
Harald Sitter dbee214259 support serving the API over unix domain socket
`unix://$PATH` as listen argument will bind aptly to a unix domain socket
rather than TCP.

This allows binding the API to a UDS rather than a port.
Since aptly has no concept of authentication or any amount of high level
API hardening one needs to bottle it up in some other manner. Binding
to a localhost port is often a step in the right direction, ultimately is
still a scary insecure setup as any user on that host getting compromised
would mean that the entire archive is compromised as well.
UDS on the other hand are basically files and have their access managed
by regular file permission. As such, binding to a socket is in fact
the least insecure way to listen as you'd have to explicitly open up the
socket permissions to an access qualified group. In the most conservative
scenario that means no one but the aptly user can talk to the API, in a
more practical setup apache might get access as well and proxy the UDS
with authentication or limited to GET operations.

Using UDS allows reducing the attack surface of the API server while
preserving all the flexibility.
2017-02-28 09:58:39 +01:00
Andrey Smirnov 6267c5cb25 Merge pull request #490 from smira/contents-low-footprint
Use temporary LevelDB to store contents index
2017-02-27 17:26:11 +03:00
Andrey Smirnov 4c06e26d85 Throttle compaction on temporary DB 2017-02-23 01:01:17 +03:00
Andrey Smirnov f2dc4eeec9 Generating contents indexes via temporary LevelDB 2017-02-21 19:09:51 +03:00
Andrey Smirnov f86e6ebf1f Merge pull request #491 from charz/master
Fix URL path for Swift.
2017-02-17 00:37:47 +03:00
Charles Hsu 0d208c93bc Merge branch 'master' of https://github.com/smira/aptly 2017-02-16 23:14:02 +08:00
Charles Hsu 485f311498 Fix URL path for Swift. 2017-02-16 23:09:18 +08:00
Andrey Smirnov 46b0d637e2 Merge pull request #484 from jola5/master
Abort serve command if rootDir is inaccessible
2017-02-15 23:54:42 +03:00
jola5 5a71847b7f Simplify test implementation 2017-02-15 20:18:47 +01:00
jola5 38a9917815 Handle dependencies in gomfile 2017-02-15 20:18:47 +01:00
jola5 4456f8da57 Refactor 2017-02-15 20:18:47 +01:00
jola5 970b1a424a Fix bugged implementation 2017-02-15 20:18:47 +01:00
jola5 edffa24658 Test startup checks for serve command 2017-02-15 20:18:47 +01:00
jola5 3040e7360a Fix golang.org/x/sys/unix dependency issue 2017-02-15 20:18:47 +01:00
jola5 b948180b4e Abort serve command if rootDir is inaccesible 2017-02-15 20:18:47 +01:00
Andrey Smirnov f58d2627c1 Add temporary DB and prefix methods to Storage 2017-02-14 02:26:32 +03:00
Andrey Smirnov ab0d77f6f9 Merge pull request #488 from smira/empty-filters
Allow filter to be empty for `aptly * search` commands
2017-02-14 01:43:46 +03:00
Andrey Smirnov 33d6cd8c0a Allow filter to be empty for aptly * search commands
Empty filter implies "select all packages".
2017-02-10 23:07:06 +03:00
Andrey Smirnov 4eef4f1803 Merge pull request #481 from smira/data-tar-gz-as-tar
Add workaround for reading data.tar.gz as data.tar
2017-01-24 20:19:50 +03:00
Andrey Smirnov c75d4c749c Add workaround for reading data.tar.gz as data.tar
It seems that in the wild there are .deb package which have
`data.tar.gz` which is actually `.tar` archive.

Add magic detection based on signature.
2017-01-24 19:30:53 +03:00
Andrey Smirnov c8a1b9a1f0 Merge pull request #482 from smira/fix-travis
Fixing Travis build
2017-01-24 19:27:58 +03:00
Andrey Smirnov d8d8973ad5 Fixing Travis build 2017-01-24 18:56:01 +03:00
Andrey Smirnov d1ded5c224 Merge pull request #480 from smira/man-generator
Add `make` automation to re-generate man page [ci skip]
2017-01-20 23:55:22 +03:00
Andrey Smirnov 155a801bc1 Add make automation to re-generate man page [ci skip]
This also updates man page with latest changes
2017-01-20 23:53:00 +03:00
Andrey Smirnov 6212b39264 Merge pull request #475 from jola5/master
Support a vertical graph layout in addition to the existing horizontal
2017-01-20 23:41:56 +03:00
jolo 92116072c2 Fix and enable broken graph layout tests 2017-01-20 02:19:45 +01:00
jolo b0ab39e07f Manually undo unintended changes 2017-01-20 02:19:44 +01:00
jola5 4bf27d1dae Merge branch 'master' into master 2017-01-19 23:07:49 +01:00
Andrey Smirnov 207ebffbb8 Merge pull request #472 from sliverc/print_sources
Print sources details of snapshots and published repositories
2017-01-19 01:05:53 +03:00
Andrey Smirnov b0dd83335f Merge branch 'master' into print_sources 2017-01-19 00:50:13 +03:00
Andrey Smirnov 8df6457931 Merge pull request #478 from smira/476-sorted-paths
Sort paths when generating checksums for `Release`/`InRelease`
2017-01-19 00:28:42 +03:00
Andrey Smirnov 7d2a396b27 Merge pull request #474 from apachelogger/support-graph.dot
Allow requesting the unrendered dot graph from the graph endpoint
2017-01-18 23:53:42 +03:00
Andrey Smirnov d5df049630 Sort paths when generating checksums for Release/InRelease 2017-01-18 23:50:22 +03:00
jolo 7c62a706c4 Disable tests failing due to inappropriate test data 2017-01-17 01:04:07 +01:00
jolo 96948d6f18 Basic test of graph layout 2017-01-17 00:46:51 +01:00
jolo 43e6498713 Add me to authors 2017-01-16 22:39:47 +01:00
jolo 91561b40f6 Change 'vertical' argument to a more generic 'layout', fix api 2017-01-16 22:13:13 +01:00
jolo 0e8ea6363a Support vertical graph layouts 2017-01-14 02:18:56 +01:00
Harald Sitter 345fa02fdc Allow requesting the unrendered dot graph from the graph endpoint
When api/graph.{dot,gv} is requested the raw string for dot gets returned.
This allows client-side rendering rather than server-side. It also makes
the optional dependency on graphivz for dot unnecessary to use the graph
endpoint.
2017-01-13 12:57:42 +01:00
Oliver Sauder 064adbae57 generate aptly.1 man page with patched ronn 2017-01-12 13:23:21 +01:00
Oliver Sauder ab458f4dfc Updated aptly man page and authors 2017-01-10 11:14:09 +01:00
Oliver Sauder 0fdee9cbf6 Added publish show command 2017-01-10 10:59:07 +01:00
Oliver Sauder 50e3e93166 print snapshot sources in snapshot show command 2017-01-09 17:29:01 +01:00
Andrey Smirnov 570835227b Merge pull request #470 from smira/templates
Add PR and issue templates
2016-12-30 17:07:54 +03:00
Andrey Smirnov 781c22e256 Add PR and issue templates 2016-12-30 00:11:45 +03:00
Andrey Smirnov babccfa21f Merge pull request #469 from smira/code-of-conduct
Add Contributor Covenant Code of Conduct [ci skip]
2016-12-29 00:03:35 +03:00
Andrey Smirnov 891113717e Add Contributor Covenant Code of Conduct [ci skip] 2016-12-29 00:01:17 +03:00
Andrey Smirnov bfb9045fa9 Merge pull request #465 from SHyx0rmZ/allow-empty-repo-edits-in-api
Allow comment and defaults to be empty when editing a repo through the API
2016-12-08 18:26:45 +03:00
Patrick Pokatilo 1c6b174b8a Make comment and defaults nullable in repo edit 2016-12-08 15:45:19 +01:00
Patrick Pokatilo fb27fb01ea Allow comment and defaults to be empty when editing a repo through the API 2016-12-08 02:14:31 +01:00
Andrey Smirnov b6327ecc43 Merge pull request #463 from sliverc/download_tries
Added --max-tries option to mirror update command
2016-11-29 17:12:36 +03:00
Oliver Sauder af71b9541c Fixing typo 2016-11-29 09:00:16 +01:00
Oliver Sauder f31b5ec3f8 Adjusted test with new maxTries param for download 2016-11-28 17:02:24 +01:00
Oliver Sauder 6becd5a3aa Added max-tries flag for mirror update 2016-11-28 17:02:24 +01:00
Andrey Smirnov 653255c728 Merge pull request #462 from smira/goxc-cleanup
Fix up build for recent versions of Go
2016-11-28 18:19:42 +03:00
Andrey Smirnov 5f0ce38161 Fix compression test for Go 1.7+ 2016-11-28 17:14:36 +03:00
Andrey Smirnov d100033b46 Fix up build for recent versions of Go
Forbid broken goxc tasks.
2016-11-28 16:37:47 +03:00
Andrey Smirnov d008cabf07 Add link to aptly_api_cli by @TimSusa 2016-11-16 02:27:01 +03:00
Andrey Smirnov f3214144a4 Merge pull request #458 from sliverc/travis-fix2
Make Travis CI be able to run on forks
2016-11-15 22:21:52 +03:00
Oliver Sauder d41841b84a Make Travis CI be able to run on forks 2016-11-15 16:19:55 +01:00
Andrey Smirnov 2a95e0eb1b Merge pull request #441 from ZettaIO/v3-auth
Identity v3 support for Swift
2016-11-11 00:19:06 +03:00
Andrey Smirnov 81f8ab2691 Merge pull request #433 from Pryz/master
Add TubeMogul Puppet module to README.rst
2016-11-09 23:55:28 +03:00
Andrey Smirnov 4e61db8d0f Fix man page (help) for aptly package show. 2016-11-09 23:07:41 +03:00
Andrey Smirnov 273d4cfa1b Merge pull request #449 from adfinis-forks/bug_415_memoryhandling
only create bzip file if needed. #415
2016-11-09 22:55:08 +03:00
Andrey Smirnov d290950d4f Merge pull request #453 from sliverc/update_leveldb
Updating goleveldb dependency
2016-11-09 22:52:29 +03:00
Oliver Sauder 2ade5b8a7f Updating leveldb fixing memory usage issue
See fix for issue https://github.com/syndtr/goleveldb/issues/138
2016-11-03 16:49:36 +01:00
Oliver Sauder fcd4429370 only create bzip file if needed. #415 2016-10-13 13:48:28 +02:00
Einar Forselv 8e62620880 Fixing tests after adding v3 swift auth 2016-09-14 20:53:01 +02:00
Einar Forselv 511fb439ac Identity v3 support for Swift
ncw/swift was bumped to latest version
2016-09-14 15:29:11 +02:00
Andrey Smirnov 34ea7e8d61 Merge pull request #418 from btkostner/test-fix
update unit tests
2016-08-22 22:25:37 +03:00
Julien Fabre 543c986885 Add TubeMogul Puppet module to README.rst 2016-08-08 09:20:28 -07:00
Blake Kostner f939532461 added exit code 2 for go 1.5 support 2016-06-22 21:59:56 -07:00
Blake Kostner aa4e225455 fix unit tests 2016-06-22 21:00:51 -07:00
Andrey Smirnov 65541a1df2 Fix system tests. 2016-05-16 13:02:00 +03:00
Andrey Smirnov 0a74b50a12 Merge branch 'dstelter-master' 2016-05-16 12:06:29 +03:00
Andrey Smirnov 902c6487da Merge branch 'master' of https://github.com/dstelter/aptly into dstelter-master 2016-05-16 12:06:00 +03:00
Andrey Smirnov 1d5b7f59cf Update system tests. 2016-05-16 11:38:20 +03:00
Daniel Stelter-Gliese 1c45c79cc1 Allow overriding architecture info from Release file
Adds a flag -force-architectures to ignore missing architectures from
mirrors. This flag can be used in cases where the mirrored repository
does not provide an "Architecture: " line.

Example Release file:
http://mitaka-jessie.pkgs.mirantis.com/debian/dists/jessie-mitaka-backports/Release
2016-05-16 03:25:00 +02:00
Andrey Smirnov 85c5aeddae Merge pull request #391 from karras/master
fix missing comma in man page example
2016-04-29 17:01:46 +03:00
Michael Hofer a95e409f52 fix missing comma in man page example 2016-04-29 09:57:06 +02:00
Andrey Smirnov 53b571d6fc Attempt to fix the build. 2016-04-25 15:13:50 +03:00
Andrey Smirnov 7a8af044ee 0.9.8~dev version. 2016-04-21 12:24:36 +03:00
Andrey Smirnov a667744502 Updte system tests. 2016-04-18 13:16:34 +03:00
Andrey Smirnov aa53b8da15 Go 1.6. 2016-04-18 12:47:00 +03:00
Andrey Smirnov 52f7c83f95 Release 0.9.7. 2016-04-18 12:30:36 +03:00
Andrey Smirnov d7665119e4 Few more fixes. 2016-03-28 13:44:19 +03:00
Andrey Smirnov 587086beb4 Misc style and simple mistakes fixes. 2016-03-28 13:34:05 +03:00
Andrey Smirnov 644d24d1cc Attempt to lower memory usage when publishing with contents. 2016-03-28 13:28:26 +03:00
Andrey Smirnov 2fe8cfdc12 Allow credentials for S3 SigV2 to be specified in config once again. #356 2016-03-28 12:52:50 +03:00
Andrey Smirnov 2ecd933d50 Merge pull request #372 from amalakar/add_sbt_aptly
Add link to scala sbt plugin
2016-03-27 23:02:15 +03:00
Andrey Smirnov 90ea1111e2 Merge pull request #371 from smira/s3-list-fix
Replace object listing with SDK-standard iteration.
2016-03-27 23:01:53 +03:00
Andrey Smirnov 165a1c53b5 Merge pull request #368 from smira/225-duplicates-package-search
Fix package search missing duplicate packages. #225
2016-03-27 23:01:38 +03:00
Arup Malakar 876935050a Add link to scala sbt plugin 2016-03-26 10:35:32 -07:00
Andrey Smirnov d9a1299f6b Replace object listing with SDK-standard iteration. 2016-03-24 13:04:16 +03:00
Andrey Smirnov ff52d2655a Fix package search missing duplicate packages. #225
Implement package list with duplicate entries, use it when
initiating search from PackageCollection.
2016-03-22 12:23:13 +03:00
Andrey Smirnov bc438ff694 Add @bentoi and @geofft to AUTHORS. [ci skip] 2016-03-20 22:12:28 +03:00
Andrey Smirnov 0db3cac281 Merge pull request #366 from geofft/signing
gpg: Sign with SHA-256 for compatibility with APT 1.2.7
2016-03-20 22:10:48 +03:00
Andrey Smirnov 9ed6e8dbbd Merge pull request #367 from bentoi/mkdir_mask_bug
Fixed mkdir mode from 755 to 777
2016-03-20 22:10:32 +03:00
Andrey Smirnov 7294241c08 Merge branch 's3-sigv2-debug' 2016-03-20 22:02:03 +03:00
Andrey Smirnov 60cca0245b Fix system tests. 2016-03-20 22:01:41 +03:00
Andrey Smirnov 75b860e0b1 Support SigV2 and S3 debug for publishing. 2016-03-20 20:11:19 +03:00
Benoit Foucher 7f5a7323a6 Fixed mkdir mode from 755 to 777 2016-03-18 09:37:18 +00:00
Geoffrey Thomas 1069458aee gpg: Sign with SHA-256 for compatibility with APT 1.2.7 2016-03-15 17:04:15 -04:00
Andrey Smirnov 76edf9649b Update goleveldb with latest fix. 2016-03-15 12:47:47 +03:00
Andrey Smirnov 8b0d293c6a Update public key ID for repo.aptly.info 2016-03-15 12:30:32 +03:00
Andrey Smirnov 281d0dd68d Merge branch 'apachelogger-issue/361' 2016-03-10 18:42:49 +03:00
Andrey Smirnov cfaa8f3881 Fix system tests. 2016-03-10 18:42:40 +03:00
Harald Sitter f1b6841757 add Checksums-Sha512 to isMultilineField
Otherwise line breaks are not properly handled and the output contains
excess newlines

Fixes #361
2016-03-10 14:40:10 +01:00
Andrey Smirnov b966b2eabf Fix HOME expansion. 2016-03-02 13:56:11 +03:00
Andrey Smirnov a4e573bb07 Fix system tests after squeeze->squeeze-lts move. 2016-03-02 13:25:12 +03:00
Andrey Smirnov 067d197dac Update to latest version of goleveldb. 2016-03-01 12:53:25 +03:00
Andrey Smirnov 18d04c7977 Fix failure not being reported from API. #290 2016-03-01 12:52:54 +03:00
Andrey Smirnov a29453805c Publish under root using :. explicit prefix. #339 2016-03-01 12:34:59 +03:00
Andrey Smirnov 05b1296144 Merge pull request #354 from smira/313-sha512
Support for SHA-512 hashes on publishing/downloads.
2016-02-18 13:06:44 +03:00
Andrey Smirnov 29e33069aa Merge pull request #355 from smira/remove-s3-retrying-client
Remove S3 retrying client which is leftover from goamz times.
2016-02-18 13:05:46 +03:00
Andrey Smirnov ee05bb23c9 Fix Swift tests for SHA512. 2016-02-18 12:29:12 +03:00
Andrey Smirnov 505da096e6 Remove S3 retrying client which is leftover from goamz times.
Also workaround go vet warnings in s3/sever_test.go
2016-02-18 12:03:04 +03:00
Andrey Smirnov 77be7b9e3b Support for SHA-512 hashes on publishing/downloads. 2016-02-18 12:01:51 +03:00
Andrey Smirnov ffafed472c Merge pull request #347 from smira/skip-contents
Make 'skipContents' configurable in API. #345
2016-02-14 15:06:49 +03:00
Andrey Smirnov 8c9cc41099 Fix nil pointer dereference on S3 publishing. #338 2016-02-14 14:52:49 +03:00
Andrey Smirnov f50e008763 Make 'SkipContents' configurable in API. #345
Also add global configuration to disable 'skipContents' by
default for all new published repos/snapshots.
2016-02-14 14:49:16 +03:00
Andrey Smirnov 64b04c2764 Merge pull request #346 from smira/api-no-lock-fix
Flush collection contents on each DB unlock in API.
2016-02-14 14:13:52 +03:00
Andrey Smirnov d6c7a9a89c Flush collection contents on each DB unlock in API.
See #343
2016-02-13 13:36:35 +03:00
Andrey Smirnov 0339f0fe23 Allow additional options for goxc [ci skip] 2016-02-11 12:46:09 +03:00
Andrey Smirnov fcedaa3fc5 Merge branch 'bitglue-aws_sdk' #344 2016-02-09 12:00:16 +03:00
Andrey Smirnov 7acfc84c9d Add @bitglue to AUTHORS. [ci skip] 2016-02-09 11:47:29 +03:00
Andrey Smirnov 02b937ad17 Fix unit-tests. 2016-02-08 14:42:30 +03:00
Andrey Smirnov 7ad1c1ad17 Fix dependencies. #344 2016-02-04 11:05:37 +03:00
Phil Frost 640bd2b530 Use official AWS SDK; support STS credentials
Now that there's an official Go AWS SDK from Amazon, use that instead of
goamz. goamz isn't getting much love these days.

Implement support for STS credentials, as in assumed roles and EC2
instance profiles. The configuration is extended to support a session
token, though I'm not sure why anyone would put temporary credentials in
a configuration file. More likely, no credentials will be explicitly
configured at all, and they will be discovered through the standard SDK
mechanisms described at
<https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs>.

Resolves #342.
2016-02-03 15:13:01 -05:00
Andrey Smirnov 06149ef2bb Merge pull request #340 from bryanhong/master
another example of aptly in Docker
2016-02-03 17:44:00 +03:00
Bryan Hong b271e8fe31 another example of aptly in Docker 2016-02-03 00:33:03 -08:00
Andrey Smirnov efc6ab27db goxc-based build system 2016-02-02 13:03:18 +03:00
Andrey Smirnov 05c063839d Update to smira/lzma with import path fixed. 2016-02-02 11:56:13 +03:00
Andrey Smirnov fd30b37a0e Bump version to 0.9.7~dev. 2016-01-24 23:15:23 +03:00
Andrey Smirnov 9738687116 Add -no-lock to aptly api serve to excercise locking. 2016-01-24 23:02:46 +03:00
Andrey Smirnov 219315c01d Fix one more system test on version. 2016-01-24 22:45:53 +03:00
Andrey Smirnov 62f44e53fd Version bump to 0.9.6. 2016-01-24 21:48:33 +03:00
Andrey Smirnov b25f8e438c Re-generate man [ci skip] 2016-01-24 21:46:45 +03:00
Andrey Smirnov f14fce01e9 Merge pull request #300 from vincentbernat/fix/api-serve-lock
Add a flag to unlock database after each API request
2016-01-24 19:53:17 +03:00
Andrey Smirnov a790770a19 Add @x539 to AUTHORS. [ci skip] 2015-12-24 14:11:26 +03:00
Andrey Smirnov 7bb052ac37 Fix unit-tests. #324 2015-12-24 14:08:37 +03:00
Andrey Smirnov 631fe44c6b Security: don't download files we don't have checksums for. #324 2015-12-22 13:52:53 +03:00
Andrey Smirnov ca319c804e Print warning message to stderr. #311 2015-12-03 13:18:45 +03:00
Andrey Smirnov 3e368690fd Stop building Go 1.3, only Go 1.4 supported. 2015-12-03 12:47:23 +03:00
Andrey Smirnov 339bf0a90b Add new comitters to AUTHORS. 2015-12-03 12:23:29 +03:00
Andrey Smirnov c5b48f0362 Merge pull request #307 from vincentbernat/fix/defer-lock
Fix lock handling in cache flusher for API
2015-12-03 12:12:35 +03:00
Andrey Smirnov d5f50732c1 Merge pull request #320 from paul-krohn/master
add diagnostic output
2015-12-03 12:11:25 +03:00
Paul Krohn 9d973aeceb shorten regex to match generated error only 2015-11-26 00:03:51 +00:00
Paul Krohn 7caeac7515 add diagnostic output 2015-11-25 23:48:47 +00:00
Vincent Bernat 7f6a52019f Add a flag to unlock database after each API request
After the first API request, the database was locked as long as the API
server is running. This prevents a user to also use the command-line
client. This commit adds a new flag `-no-lock` that will close the
database after each API request.

Closes #234
2015-10-02 20:04:48 +02:00
Vincent Bernat 16101b56fe Fix lock handling in cache flusher for API
Unlocking the different elements in cache flusher was deferred to the
end of the function. Unfortunately, being a for loop wrapped in a
goroutine, deferred were never executed.
2015-10-02 19:59:47 +02:00
Andrey Smirnov a294a91685 Cache filepath list in s3.LinkFromPool instead of doing Get checks #297
This speeds up publishing with many files already present in the pool
2015-10-01 14:02:32 +03:00
Andrey Smirnov cf644289a3 Lower limit for goleveldb open files cache to 256 #260 2015-10-01 12:37:43 +03:00
Andrey Smirnov 33c905ce02 Upgrade goleveldb to the latest version. #260 2015-10-01 12:27:19 +03:00
Andrey Smirnov 8fdc222196 Fix retry policy. #297 2015-09-24 13:21:13 +03:00
Andrey Smirnov 1e4d825d36 Disable keep alives, fix return on last retry. #297 2015-09-24 12:21:31 +03:00
Andrey Smirnov 76bf7cba04 Fix handling of folded fields in Stanza. #270
Discovered by @sobczyk

When whitespace is stripped from folded stanza fields, some fields
values are glued together without whitespace which might change its meaning.
2015-09-24 11:59:49 +03:00
Andrey Smirnov 08bc5ac934 Fix system tests for Go 1.5.
Some error strings in Go 1.5 have changed. Until we have something
better for that, use string replace.
2015-09-22 12:40:42 +03:00
Andrey Smirnov c160cbccc7 Fix mirror system tests. 2015-09-22 11:42:05 +03:00
Andrey Smirnov c473a5cba8 Really randomize port in Swift unit-tests. 2015-09-22 11:25:10 +03:00
Andrey Smirnov 84801bce78 Fix unit-tests
Go 1.5 has different error message, randomize port number in test to avoid
collisions.
2015-09-22 11:18:57 +03:00
Andrey Smirnov b95b3473bf Switch to Travis CI new build infra. 2015-09-21 13:58:17 +03:00
Andrey Smirnov f1d5caab8b Enable build for Go 1.5. 2015-09-20 13:14:33 +03:00
Andrey Smirnov 6a973554ad Update goar to the latest version. #275 2015-09-20 13:13:18 +03:00
Andrey Smirnov 698e239f45 Include all aptly contributors in man section AUTHORS. 2015-07-04 13:16:15 +03:00
Andrey Smirnov 205297d0b8 Update license to mention that there are many contributors. 2015-07-04 13:06:58 +03:00
Andrey Smirnov ba4669a9c4 Man page for package display format in search commands. #254 2015-07-04 13:02:33 +03:00
Andrey Smirnov 8bda799545 Support for Go-style templating in format for aptly * search. #254 2015-07-02 12:19:41 +03:00
Andrey Smirnov 6c28e3aca8 Update flat repository. 2015-06-26 13:24:31 +03:00
Andrey Smirnov 901babe500 Fix test. 2015-06-26 13:15:11 +03:00
Andrey Smirnov 0c6f38ab08 Fix system test. 2015-06-26 13:09:57 +03:00
Andrey Smirnov a131d6093c Fix unit-test. 2015-06-26 12:56:02 +03:00
Andrey Smirnov 974cec3e73 Fix publish tests. 2015-06-26 03:14:22 +03:00
Andrey Smirnov 442c5f090f Fix package tests. 2015-06-26 03:08:22 +03:00
Andrey Smirnov d04f08c1cf Correctly handle multine fields in Release/non-Release files. #266 2015-06-26 03:07:33 +03:00
Andrey Smirnov 767c7ca0db Merge branch '261-fix-multiline-fields' 2015-06-20 19:04:06 +03:00
Andrey Smirnov dd27aad751 Add @sobczyk to the list of AUTHORS. #266 2015-06-20 19:03:41 +03:00
Andrey Smirnov ddfdeaf2d0 Merge pull request #266 from sobczyk/master
Fix EOF error during mirror update.
2015-06-20 18:59:21 +03:00
Szymon Sobik 4c51350517 fix EOF during mirror update
see http://stackoverflow.com/a/19006050
2015-06-19 12:36:28 +02:00
Andrey Smirnov 40e48c963a Revert "Update publish tests, as some multiline fields are actually multiline."
This reverts commit a030e24b96.
2015-06-18 03:33:09 +03:00
Andrey Smirnov c44d347540 Don't need to manually insert \n, multiline fields are handled correctly. 2015-06-18 03:32:35 +03:00
Andrey Smirnov 4a54bff225 Add missing return statements. 2015-06-18 03:32:23 +03:00
Andrey Smirnov e39736153d Update package tests. 2015-06-18 03:29:12 +03:00
Andrey Smirnov f032196d70 Mirror has been updated. 2015-06-18 02:15:39 +03:00
Andrey Smirnov a030e24b96 Update publish tests, as some multiline fields are actually multiline. 2015-06-18 02:13:15 +03:00
Andrey Smirnov c2993c6691 Use other port that doesn't interfere with PostgreSQL. 2015-06-18 01:52:37 +03:00
Andrey Smirnov 7d4a70ba25 Make first line of multiline field empty for all fields except for Description. #261 2015-06-10 13:44:03 +03:00
Andrey Smirnov 38dfe3435a For plusWorkaround, correctly handle cleanup, deletions. #239 2015-05-29 02:13:59 +03:00
Andrey Smirnov 313c71dff6 Rework s3 retry policy by copying sources from goamz :( #255 2015-05-29 01:47:02 +03:00
Andrey Smirnov a88d92436f Fix system test for mirrors list. 2015-05-29 00:15:54 +03:00
Andrey Smirnov 9d298dee51 Remove deadline timeout. #255 2015-05-28 12:52:29 +03:00
Andrey Smirnov 9abc772b16 Change our flat repo for testing, old one is dead. 2015-05-28 12:14:36 +03:00
Andrey Smirnov 2f1df39204 Use S3 retrying transport. #255 2015-05-28 11:45:37 +03:00
Andrey Smirnov 0f328ec1fe Seed Python random generator on start. 2015-05-28 11:40:11 +03:00
Andrey Smirnov 78b6d6ca7b Send error messages to stderr. #249 2015-05-28 11:30:35 +03:00
Andrey Smirnov 5cd3c33854 Update goamz library to latest version. #253 2015-05-28 11:21:57 +03:00
Andrey Smirnov 9af76843b5 Fix README formatting. [ci skip] 2015-05-28 11:17:01 +03:00
Andrey Smirnov 53506124a4 Add integrations to README, aptly API CLI. [ci skip] 2015-05-28 11:12:55 +03:00
Andrey Smirnov 9bbf9c7b13 Merge branch 'graph-output-filename' 2015-05-18 00:39:41 +03:00
Andrey Smirnov 82e6e8242e Update man page. #242 2015-05-18 00:39:01 +03:00
Andrey Smirnov 2bf11a556c Update custom output filename generation. #242 2015-05-18 00:38:15 +03:00
Andrey Smirnov c62828bf14 Merge branch 'graph-specific-output-filename' of https://github.com/gdbdzgd/aptly into graph-output-filename 2015-05-18 00:30:41 +03:00
Andrey Smirnov b53cf7e710 Merge pull request #246 from GLolol/typofix
Typo fix (depdency -> dependency)
2015-05-18 00:22:42 +03:00
Andrey Smirnov 780277d0a6 Build on go 1.4 as well. 2015-05-14 12:51:01 +03:00
Andrey Smirnov a6f5631542 Fix system tests. 2015-05-14 12:27:01 +03:00
Andrey Smirnov 52b1501ec0 Update to new debian archive keyring. 2015-05-14 12:26:50 +03:00
James Lu c9339f5cca Typo fix (depdency -> dependency) 2015-05-11 18:01:21 -07:00
Andrey Smirnov a9c23fb4aa Fix silly bug with non-encodable value being encoded. 2015-04-26 09:02:40 +02:00
Andrey Smirnov 72e3eaebfe Add optional notice. 2015-04-26 08:56:29 +02:00
Zhang, Guodong f3bcaa6cfb aptly graph specific output filename
https://github.com/smira/aptly/issues/241

	modify:     cmd/graph.go

Signed-off-by: Zhang, Guodong <gdzhang@linx-info.com>
2015-04-24 11:14:04 +08:00
Andrey Smirnov 1c8f1517f8 Update man. #218 [ci skip] 2015-04-17 01:26:37 +03:00
Andrey Smirnov 50ae34cc19 Create S3 with endpoint/multidel param from config. #218 2015-04-17 01:20:58 +03:00
Andrey Smirnov 8cc7d1345b Support for new S3 configuration options: endpoint & multi del disabling. #218 2015-04-17 01:18:34 +03:00
Andrey Smirnov 0791c88a02 Support for custom endpoints and multi del disabling. #218 2015-04-17 01:16:50 +03:00
Andrey Smirnov ba08ffe38b Add dependency on xz-utils. #142 2015-04-14 23:16:41 +03:00
Andrey Smirnov 1bec1e4dc4 Use external binary 'xz' implementation. #142 2015-04-14 23:16:16 +03:00
Andrey Smirnov bcf8074f31 Revert "Use Go native lzma implementation, so that there are no external dependencies. #142"
This reverts commit 709e14ecc1.
2015-04-10 22:24:45 +03:00
Andrey Smirnov 6a2d564eee Revert "Add liblzma-dev to list of build dependencies in Travis. #142"
This reverts commit 4651e41247.
2015-04-10 22:09:56 +03:00
Andrey Smirnov 709e14ecc1 Use Go native lzma implementation, so that there are no external dependencies. #142 2015-04-10 22:09:18 +03:00
Andrey Smirnov 5b1f446a6b Ignore empty 'Depends:' while parsing control file. #233 2015-04-10 21:10:53 +03:00
Andrey Smirnov f41146c750 Revert "Build static binaries on !OS X. #142"
This reverts commit d56ac81fd6.
2015-04-06 00:18:25 +03:00
Andrey Smirnov d56ac81fd6 Build static binaries on !OS X. #142 2015-04-05 22:52:08 +03:00
Andrey Smirnov fb213ef6eb Fix system tests. #142 2015-04-05 22:47:19 +03:00
Andrey Smirnov 933b019f71 Fix -skip-contents + system tests. #142 2015-04-05 21:55:41 +03:00
Andrey Smirnov 6293ca3206 Add -skip-contents flag. #142 2015-04-05 21:27:35 +03:00
Andrey Smirnov d46d8de5f7 Make sure contents don't have duplicate package entries. #142 2015-04-05 21:12:25 +03:00
Andrey Smirnov 4e3284cd98 Check contents of contents index being generated. #142 2015-04-02 01:19:51 +03:00
Andrey Smirnov 10876b99f5 Check for contents file generation. #142 2015-04-02 01:00:28 +03:00
Andrey Smirnov 61d31ce7c0 Check that contents are generated on repo publish. #142 2015-04-02 00:49:08 +03:00
Andrey Smirnov e0f284d68f Check that contents files are generated. #142 2015-04-02 00:45:08 +03:00
Andrey Smirnov df887d871b Skipping contents generation. #142 2015-04-02 00:29:49 +03:00
Andrey Smirnov 99f6ffe1ca Fix system test for content generation. #142 2015-04-02 00:29:49 +03:00
Andrey Smirnov 138f9f7994 Generate only .gz file for Contents index. #142 2015-04-02 00:29:49 +03:00
Andrey Smirnov 3886db9d4f Merge pull request #231 from seaninspace/patch-1
Update import.go
2015-03-31 23:19:41 +03:00
Sean b877e06a02 Update import.go
Add support for Automatic Debug Packages (.ddeb's)
2015-03-31 12:17:32 -07:00
Andrey Smirnov 38f4fc209b Contents index support. #142 2015-03-31 00:08:23 +03:00
Andrey Smirnov b223acdecb Contents index generator. #142 2015-03-31 00:07:42 +03:00
Andrey Smirnov cc8a87b448 Cached calculation of package contents. #142 2015-03-31 00:07:07 +03:00
Andrey Smirnov ee3d414ed5 Don't use shared encodeBuffer, not safe for API mode. 2015-03-30 23:59:00 +03:00
Andrey Smirnov d791aa0f15 Remove debugging output. #142 2015-03-30 23:54:36 +03:00
Andrey Smirnov 393ae8adbd Regenerate man page. #163 2015-03-30 23:54:11 +03:00
Andrey Smirnov 7037c6be7e Rename -output to -format. #163 2015-03-30 23:53:37 +03:00
Andrey Smirnov c10645f4f2 Support custom output formats for aptly graph. #163 2015-03-30 20:26:05 +03:00
Andrey Smirnov 27da1015af Test case for filters. #227 2015-03-30 19:56:53 +03:00
Andrey Smirnov 78b0fe0e90 Fix system tests. 2015-03-25 00:42:02 +03:00
Andrey Smirnov 4651e41247 Add liblzma-dev to list of build dependencies in Travis. #142 2015-03-24 23:35:24 +03:00
Andrey Smirnov a6c40f3193 Getting contents from .deb files. #142 2015-03-24 22:09:36 +03:00
Andrey Smirnov 3e138fd6db Add dependency on .xz and .lzma decompression. This introduces dependency on liblzma. #142 2015-03-24 22:09:03 +03:00
Andrey Smirnov 3c20b5472e Tests for aptly repo include with per-repo uploaders.json. #71 2015-03-22 19:24:37 +03:00
Andrey Smirnov 8b782ce370 Support for per-repo uploader.json in aptly repo commands. #71 2015-03-22 19:02:20 +03:00
Andrey Smirnov a160a39d53 -uploaders-file for aptly repo edit/create. #71 2015-03-22 18:48:17 +03:00
Andrey Smirnov 1c4b44e772 Uploaders to JSON transformation via String. #71 2015-03-22 12:39:37 +03:00
Andrey Smirnov b4b03f2752 Update system tests. #71 2015-03-20 22:29:50 +03:00
Andrey Smirnov 1d21d3cfeb Uploader.json from repo overrides global uploaders.json. #71 2015-03-20 22:29:11 +03:00
Andrey Smirnov d2ce33e66a Allow local repo to carry uploaders.json config. #71 2015-03-20 22:28:45 +03:00
Andrey Smirnov f0fbb8259b Document uploaders.json file in man. #71 2015-03-20 00:21:50 +03:00
Andrey Smirnov 962c4a842d Additional tests for aptly repo include. #71 2015-03-20 00:18:32 +03:00
Andrey Smirnov 54e21afee7 Use uploaders.json in repo include. #71 2015-03-20 00:18:12 +03:00
Andrey Smirnov cc3f5149c6 Return detailed error if uploaders deny upload. #71 2015-03-20 00:11:30 +03:00
Andrey Smirnov c8713aa412 Fix bugs. #71 2015-03-19 23:58:48 +03:00
Andrey Smirnov 02a82f3545 Use relaxed config reader. #71 2015-03-19 23:54:35 +03:00
Andrey Smirnov c573746896 Refactor to get Keys from Changes. #71 2015-03-19 01:36:39 +03:00
Andrey Smirnov 813b9593fa Uploaders rules facility: controlling who can upload .changes. #71 2015-03-19 01:05:09 +03:00
Andrey Smirnov bc68513708 Implementation of PackageLike interface for Changes. #71 2015-03-19 00:26:18 +03:00
Andrey Smirnov c4692bec3d Matching short/long GPG key IDs. #71 2015-03-19 00:25:54 +03:00
Andrey Smirnov c53060d95a Add system test on package restriction check. #71 2015-03-18 23:32:21 +03:00
Andrey Smirnov 22c656d18e Style fix [ci skip]. #71 2015-03-18 23:27:21 +03:00
Andrey Smirnov 4d622e467c Refactoring: make PackageQuery work on PackageLike objects (not necessarily Packages). #71 2015-03-18 23:25:20 +03:00
Andrey Smirnov 36326788b0 When importing package into local repo, verify that it matches package restriction based on .changes file. #71 2015-03-18 22:20:52 +03:00
Andrey Smirnov 782ac1a36a ChangesFile can produce Query each package file should satisfy. #71 2015-03-18 22:19:12 +03:00
Andrey Smirnov 8ca07d9acd Fix unit tests. #71 2015-03-18 22:10:49 +03:00
Andrey Smirnov 4a57fe3c39 Refactoring: make gpg verification return missing/good key IDs. #71
Eliminate "hint" on missing keys which doesn't apply to .changes.

Would be good to eventually stop using GPG and start calling golang.org/x/crypto/openpgp
2015-03-18 21:34:54 +03:00
Andrey Smirnov 7579f1998c Fix system tests. #71 2015-03-17 01:09:09 +03:00
Andrey Smirnov 67a31d5eaa Merge branch '71-changes-support' 2015-03-17 00:19:28 +03:00
Andrey Smirnov 5b9d287b62 Add aptly repo include to man. #71 2015-03-17 00:19:06 +03:00
Andrey Smirnov 775670181c System tests for -ignore-signatures + -accept-unsigned. #71 2015-03-17 00:17:43 +03:00
Andrey Smirnov 2a3bd5546a Unsigned files shouldn't be accepted. #71 2015-03-17 00:15:45 +03:00
Andrey Smirnov 197e230ef1 System tests: wrong signature. #71 2015-03-17 00:08:47 +03:00
Andrey Smirnov c6eeac11a4 System test for wrong checkums. #71 2015-03-17 00:02:39 +03:00
Andrey Smirnov 90d3b623b4 Check file size as well as checksums. #71 2015-03-17 00:01:58 +03:00
Andrey Smirnov a59c2ac859 Tests for file removal + missing files. #71 2015-03-16 23:55:47 +03:00
Andrey Smirnov 103fa5310f First pack of system tests for aptly repo include. #71 2015-03-16 22:50:58 +03:00
Andrey Smirnov 71b7de7a63 Initialize empty verifier if -ignore-signatures is given to check for signature. #71 2015-03-16 22:49:41 +03:00
Andrey Smirnov a937ebc744 First version aptly repo include command processing .changes files. #71 2015-03-15 21:30:54 +03:00
Andrey Smirnov 925882b253 Collect .changes file in directory hiearchy. #71 2015-03-15 21:26:58 +03:00
Andrey Smirnov 615a5ee3f9 Example of package upload with .changes file. #71 2015-03-15 21:21:23 +03:00
Andrey Smirnov 4a6d6a85f7 Remove unused error argument. 2015-03-15 20:06:59 +03:00
Andrey Smirnov 2937435960 Add missing commands api, config. 2015-03-15 18:44:43 +03:00
Andrey Smirnov 2f3b5f5a51 Refactor Changes structure, new method prepare to verify checksums and copy files. #71 2015-03-15 18:16:11 +03:00
Andrey Smirnov 5b4563f250 Simple CopyFile utility function. #71 2015-03-15 18:15:46 +03:00
Andrey Smirnov 5da4bde428 Fix reference to go-uuid. 2015-03-15 14:07:38 +03:00
Andrey Smirnov 42c4644be3 Move go-uuid to GitHub. No more code.google.com. RIP. 2015-03-15 14:06:40 +03:00
Andrey Smirnov 1845c493f4 Update mxk/flowcontrol package from Google Code to mxk/flowrate from GitHub. 2015-03-15 14:00:04 +03:00
Andrey Smirnov 8a0f754fe2 Snappy has moved, remove reference. 2015-03-15 13:51:37 +03:00
Andrey Smirnov 77bb4d423d Update import path for gographviz. 2015-03-15 13:51:14 +03:00
Andrey Smirnov 1d483dc817 Update reference to gographviz (code.google.com is going to be shut down). 2015-03-15 13:50:08 +03:00
Andrey Smirnov a7103623af .changes files parsing. #71 2015-03-13 21:46:32 +03:00
Andrey Smirnov 903e999cdc Refactor checksum parsing out of package parsing code. #71 2015-03-13 21:23:22 +03:00
Andrey Smirnov 69eff97b34 Relax .dsc checkshums parsing. #71 2015-03-13 20:53:53 +03:00
Andrey Smirnov 8e20daa927 Refactor out IsClearSigned to separate method. #71 2015-03-13 18:42:34 +03:00
Andrey Smirnov 9e39dbf81e Version bump to 0.9.6~dev. 2015-03-13 16:07:25 +03:00
Andrey Smirnov 7a4feebe6f Version bump to 0.9.5. 2015-03-13 13:26:39 +03:00
Andrey Smirnov 1d1561c6c3 Add missing files. 2015-03-12 00:50:27 +03:00
Andrey Smirnov 9a5b3aeedc System test. #193 2015-03-11 23:38:16 +03:00
Andrey Smirnov ed931e7ed4 Fix unit-tests. #153 2015-03-11 23:29:07 +03:00
Andrey Smirnov 5ff9cecc5a Regenerate man page. #153 2015-03-11 22:20:43 +03:00
Andrey Smirnov f8bca463bb Add -force-drop to aptly publish drop, ?force=1 to DELETE publish/... to drop
published repositories even if cleanup fails. #153
2015-03-11 22:02:11 +03:00
Andrey Smirnov d5c6f0b623 Collect and report unused package reference sources. #217 2015-03-11 21:40:53 +03:00
Andrey Smirnov 7e57f443ed Style fix. #193 2015-03-11 21:40:04 +03:00
Andrey Smirnov b4cf2e7065 Canonical case fixes. #193 2015-03-11 01:25:54 +03:00
Andrey Smirnov 2ceabb69e6 Remove extra \n, system tests. #217 2015-03-11 01:22:49 +03:00
Andrey Smirnov aa9d3360ba Canonical case-folding for Debian stanzas. #193 2015-03-11 00:46:39 +03:00
Andrey Smirnov 4580a64192 Make import skip file if Name/Version/Arch is empty. #193 2015-03-11 00:34:48 +03:00
Andrey Smirnov 4cb0526980 Commands * search should exit with failure on no results. #213 2015-03-11 00:31:53 +03:00
Andrey Smirnov 03e2a8d558 Regenerate man page. #217 2015-03-11 00:17:08 +03:00
Andrey Smirnov ab09cbfe3c Add -verbose and -dry-run to aptly db cleanup. #217 2015-03-11 00:16:23 +03:00
Andrey Smirnov 0467e0c929 More sophisticated color codes stripper. #217 2015-03-11 00:15:56 +03:00
Andrey Smirnov 6e1c9afdd9 Bump version to 0.9.5~dev. 2015-03-09 22:26:25 +03:00
Andrey Smirnov 4b3b961b69 Version bump to 0.9.1 2015-03-06 15:05:33 +03:00
Andrey Smirnov e63adffdf5 Introduce back reflist merging without conflict removal. aptly db cleanup requires
full reference list collection. #217

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

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

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

[1]https://github.com/ccollicutt/docker-swift-onlyone/commit/c9f5e41b745eee18e7ddc807481bc9729d8cdac0
2015-02-11 15:35:45 +01:00
Sylvain Baubeau 355a98b51f Use upstream version of ncw/swift 2015-02-10 09:17:54 +01:00
Andrey Smirnov 1f73a34a54 Don't put empty Source: fields into package stanza. #195 2015-02-09 19:23:41 +03:00
Andrey Smirnov 7925af9fd6 Exit with failure if aptly package search yields no results. #188 2015-02-07 23:33:44 +03:00
Andrey Smirnov fb03a3baf9 Add @shadeslayer and @rra to list of AUTHORS. [ci skip] 2015-02-07 23:24:10 +03:00
Andrey Smirnov f097cd20c1 Add @sbadia to AUTHORS. #191 2015-02-07 20:05:16 +03:00
Andrey Smirnov 0489ba9d16 Swift startup script. #191
(Doesn't work yet)
2015-02-07 20:03:51 +03:00
Andrey Smirnov 46b3f8fbaf PEP8 fixes. #191 2015-02-07 19:39:58 +03:00
Andrey Smirnov cacd0cf103 Merge branch 'swift' of https://github.com/sbadia/aptly into sbadia-swift 2015-02-07 19:18:04 +03:00
Andrey Smirnov c933668c16 Merge branch 'mkoval-feature/RepoUpdateAPI' 2015-02-07 19:01:24 +03:00
Andrey Smirnov 24418ab0a4 Small fixes to publish update API. #174 2015-02-07 19:01:06 +03:00
Andrey Smirnov 4963d0a1d7 Add @mkoval to list of AUTHORS. 2015-02-07 18:53:25 +03:00
Andrey Smirnov ea8bfeb8a7 Merge branch 'feature/RepoUpdateAPI' of https://github.com/mkoval/aptly into mkoval-feature/RepoUpdateAPI 2015-02-07 18:52:29 +03:00
Andrey Smirnov a582493a6e Packages show API with tests. #116 2015-02-07 18:50:52 +03:00
Andrey Smirnov 930f76887b Final version of system tests for snapshot API. #168 2015-02-07 18:37:10 +03:00
Andrey Smirnov a4201a40d2 Allow to override architectures when publishing. #116 2015-02-07 18:36:42 +03:00
Andrey Smirnov 4990bb98e5 Update gin to latest available version. #116 2015-02-07 18:36:14 +03:00
Andrey Smirnov 00d4674aa5 Update (fix) system test. 2015-02-07 13:23:30 +03:00
Andrey Smirnov 06b4016338 More fixes related to locking and overall operations. #168 2015-02-06 22:44:25 +03:00
Andrey Smirnov c1b2e4fabb Fix for snapshot creation APIs: locking, package existence checks, consistency checks. #168
More system tests.
2015-02-06 22:37:57 +03:00
Andrey Smirnov f438637a98 Don't expose UUIDs in API. #168
Probably we should expose sources, but not as UUIDs. TODO.
2015-02-06 22:37:14 +03:00
Andrey Smirnov ce208f347e Merge branch 'lebauce-snapshot-api' 2015-02-06 20:18:52 +03:00
Andrey Smirnov 06502584cf Check component names (that they do exist) before publish switching. #192 2015-02-06 20:16:00 +03:00
Sebastien Badia 0f22dc590a Fix config tests and update man page
Fix ConfigSuite.TestSaveConfig, ConfigShowTest and CreateConfigTest
tests
2015-02-05 21:27:09 +01:00
Sylvain Baubeau 11716f06f0 Add test suite for the Swift backend 2015-02-05 18:03:35 +01:00
Sylvain Baubeau 1ba06e828d Remove prefix in Filelist and RemoveDir 2015-02-05 17:56:16 +01:00
Sebastien Badia bc357a19a1 Added swift python tests 2015-02-05 17:56:09 +01:00
Sylvain Baubeau 9004f8578c Detect if bulk-delete is supported 2015-02-05 17:54:09 +01:00
Sebastien Badia 7f038be1cb Add swift backend for repository publishing 2015-02-05 17:54:09 +01:00
Andrey Smirnov 13fc1122f0 Use Python requests URL params instead of manual GET params. #168 2015-02-05 01:56:55 +03:00
Andrey Smirnov cb99cbec58 Fix final system test. #168 2015-02-05 01:53:07 +03:00
Andrey Smirnov 5d16cf06cf Gobuild is gone. [ci skip] 2015-02-05 01:49:29 +03:00
Andrey Smirnov b0489117c8 Update system tests for new Package serialization. #168 2015-02-05 01:48:14 +03:00
Andrey Smirnov fa2eef564c Enhance Package JSON representation with Key, ShortKey and FilesHash. #168 2015-02-05 01:47:10 +03:00
Andrey Smirnov d20300b152 Whitespace fix. #168 2015-02-05 01:46:57 +03:00
Andrey Smirnov 398303235a Custom JSON marshalling for PackageDiff, updated test for snapshot diff API. #168 2015-02-05 01:34:02 +03:00
Andrey Smirnov 25d048fe49 Add Package serialization to JSON via stanza. #168 2015-02-05 01:26:55 +03:00
Andrey Smirnov 8c15a0ca95 Add AlekSi/pointer to dependencies. #168 2015-02-05 01:24:44 +03:00
Andrey Smirnov 8e8ff8ba65 Revert "Make files hash a type for proper JSON serialization"
This reverts commit e138212593.
2015-02-05 00:27:14 +03:00
Andrey Smirnov 1b0eb9d45a Attempt to fix #189 and #130: disable Amazon workaround when using proxy. 2015-02-03 21:49:55 +03:00
Andrey Smirnov 403c7272cd When loading package index for the mirror, ignore duplicate packages (and print about them). #183 2015-01-31 21:27:26 +03:00
Andrey Smirnov 0412646151 Add @bcandrea to list of authors. 2015-01-30 19:26:57 +03:00
Andrey Smirnov 0725003107 Merge pull request #186 from bcandrea/master
Update dependency definition in conflicts (fixes #185)
2015-01-30 19:24:56 +03:00
Andrea Bernardo Ciddio 7a1553dc55 Update dependency definition in conflicts (fixes #185)
The dependency specified when looking for conflicting packages
does not take into account the package version, as by default the
Relation field in the Dependency structure is set to VersionDontCare.
This should fix #185.
2015-01-30 16:22:27 +00:00
Andrey Smirnov 8375a2c30f Update system test. 2015-01-30 18:39:50 +03:00
Andrey Smirnov 5bbbdb3c19 Use long gpg key IDs. #178 2015-01-30 18:33:05 +03:00
Andrey Smirnov 1fd80c40d0 Add --no-default-keyring to example command. #182 2015-01-30 18:24:30 +03:00
Andrey Smirnov ae5ab2d138 Use https:// in example when talking about Release.key download. #179 2015-01-30 18:19:28 +03:00
Andrey Smirnov eb087fd291 When generating index files, make udeb forced false for "source" architecture. #180
Otherwise two index files are generated (source arch, "udeb" true/false) which end up
sharing same final filename, and empty one might overwrite "real" one.
2015-01-26 21:16:44 +03:00
Andrey Smirnov 3f6491b8a3 Non-working test on format=details. 2015-01-26 21:06:28 +03:00
Andrey Smirnov 9250479846 Extract common part of show and search packages from snapshots and repos. #168 2015-01-24 22:23:16 +03:00
Andrey Smirnov 9c60421bd6 Python style fixes. #168 2015-01-24 21:53:06 +03:00
Andrey Smirnov ebea4f10a0 Make snapshot diff GET, not POST (as it doesn't change anything in the system). #168 2015-01-24 21:51:33 +03:00
Andrey Smirnov d828732307 Refactoring: make snapshot sorting non-intrusive to collection contents. #168 2015-01-22 22:01:00 +03:00
Andrey Smirnov 7c3629337c Merge branch 'snapshot-api' of https://github.com/lebauce/aptly into lebauce-snapshot-api
Conflicts:
	api/router.go
	system/t12_api/__init__.py
2015-01-22 21:29:58 +03:00
Michael Koval a29034caa5 Implemented apiPublishUpdateSwitch. 2015-01-21 02:23:08 -05:00
Andrey Smirnov c1fd633ed7 Add Sylvain Baubeau to authors. [ci skip] 2015-01-19 22:06:15 +03:00
Andrey Smirnov bd2cc45524 Fix order of 'Component' and 'Architecture' fields. #172 2015-01-19 22:03:31 +03:00
Andrey Smirnov 0665f2231a Sort packages when generating Packages index file. #172 2015-01-19 21:39:02 +03:00
Andrey Smirnov 836abdc81e Bring back "Archive" into canonical order. #172 2015-01-19 21:33:00 +03:00
Andrey Smirnov 876eeedb14 Update canonical order of fields in stanza to match what apt tools generate. #172 2015-01-19 21:17:58 +03:00
Andrey Smirnov fd502264a9 Install graphviz in Travis. #169 2015-01-13 22:16:30 +03:00
Andrey Smirnov b155eaa91c Merge branch 'lebauce-graph-api' 2015-01-13 22:15:37 +03:00
Andrey Smirnov a0d7ae28bf Simple tests for graph generation API. #169 2015-01-13 22:15:06 +03:00
Andrey Smirnov 2816647809 Allow to generate graph in formats supported by dot. #169 2015-01-13 19:22:24 +03:00
Andrey Smirnov 67ce828eeb Lock collections before building graph. #169 2015-01-13 19:17:19 +03:00
Andrey Smirnov 427c42f4b8 Move graph into deb/ package, passing collection factory. #169 2015-01-13 19:10:39 +03:00
Andrey Smirnov b50cb70a0e Merge branch 'graph-api' of https://github.com/lebauce/aptly into lebauce-graph-api 2015-01-13 19:04:07 +03:00
Andrey Smirnov c832a5cdc4 Add missed file. #167 2015-01-13 18:50:09 +03:00
Andrey Smirnov 1bd625f17f Merge branch 'lebauce-api-version' 2015-01-13 18:49:57 +03:00
Andrey Smirnov a0fa0becc2 Add system test on version API. #167 2015-01-13 18:49:32 +03:00
Andrey Smirnov d489694ea9 Refactoring: simplify version generation. Rename API to /api/version. #167 2015-01-13 18:47:41 +03:00
Andrey Smirnov 982b5dc886 Merge branch 'api-version' of https://github.com/lebauce/aptly into lebauce-api-version 2015-01-13 18:44:10 +03:00
Andrey Smirnov 1ddaecfb94 New Debian version: update system tests. 2015-01-12 20:14:18 +03:00
Andrey Smirnov 129c34806c Pass --no-use-agent when running with --passphare flag. #162 2015-01-12 20:00:18 +03:00
Sylvain Baubeau 6c7f3b3bbd Add /api route to show API version #116 2015-01-12 10:56:54 +01:00
Sylvain Baubeau 38cb6bd133 Graph REST API #116 2015-01-12 10:56:28 +01:00
Andrey Smirnov 98ca0cdf33 Publish repo REST API. #116 2015-01-07 16:11:34 +03:00
Andrey Smirnov 6e32e3dcf4 Fix some variable shadowing. 2015-01-07 16:11:13 +03:00
Sylvain Baubeau 6a1a871dda Lock snapshot collection before sorting 2015-01-06 18:06:59 +01:00
Sylvain Baubeau 6bc7048166 Fix wrong method comment 2015-01-06 18:06:53 +01:00
Andrey Smirnov 87fbd5201b Add Chris Read to authors. 2015-01-05 14:24:47 +03:00
Andrey Smirnov dcf5798229 Merge remote-tracking branch 'cread/gocheck' 2015-01-05 14:23:38 +03:00
Andrey Smirnov 382ad10cf7 Update man page. 2014-12-28 13:44:24 +03:00
Antonio Santos ddb2dd7eb6 Fix typo 2014-12-26 17:51:51 +01:00
Andrey Smirnov 9b1b43c8b4 Add custom JSON representation of PublishedRepo. #116 2014-12-26 00:58:23 +03:00
Andrey Smirnov ee7d84205b Update system test. 2014-12-23 01:19:37 +03:00
Andrey Smirnov 93e8e18ca6 Document lock order acquisition. #116 [ci skip] 2014-12-23 00:59:29 +03:00
Andrey Smirnov d586f31247 Move ParsePrefix into common code. #116 2014-12-23 00:50:28 +03:00
Sylvain Baubeau dd9fc8e40e Allow API creation of snapshots using package references 2014-12-18 18:17:43 +01:00
Sylvain Baubeau d847cba870 Make repos and snapshots API return JSON objects for packages when asked 2014-12-18 18:16:35 +01:00
Sylvain Baubeau d983e10d08 Add snapshots API test suite 2014-12-18 16:33:15 +01:00
Sylvain Baubeau a6fc65ff4e Sanitize snapshots API return codes 2014-12-18 12:05:16 +01:00
Sylvain Baubeau 85f38cd739 Allow setting description on snapshots using API 2014-12-18 11:55:54 +01:00
Sylvain Baubeau acde6ff2b2 Fix wrong methods comments 2014-12-18 11:55:45 +01:00
Sylvain Baubeau c733129de9 Add search API for packages in snapshots 2014-12-18 11:16:49 +01:00
Sylvain Baubeau e138212593 Make files hash a type for proper JSON serialization 2014-12-18 11:14:56 +01:00
Sylvain Baubeau 64ef342121 Add /snapshots/ API. #116
Implements :
	- CRUD
	- Difference between snapshots
2014-12-15 10:47:35 +01:00
Sylvain Baubeau 66c9bb86f5 Move command line snapshot sorting to common snapshot code 2014-12-15 10:44:46 +01:00
Andrey Smirnov 923e2e1e50 Update system test. 2014-12-12 09:27:17 +03:00
Andrey Smirnov 0e552eda55 When merging reflists, never allow duplicate (name, version, arch) tuples. #154
Even for conficting packages.
2014-12-11 21:56:50 +03:00
Andrey Smirnov ba32d16c8a Fix system test. 2014-12-09 11:44:40 +03:00
Andrey Smirnov 4320144024 Merge pull request #155 from davewongillies/master
Typo in an error message fix
2014-12-09 11:26:18 +03:00
David Gillies 2564564601 Typo in an error message fix 2014-12-09 15:57:26 +11:00
Andrey Smirnov f228ad811b Accept MD5 in package from 'MD5Sum' as well. #151 2014-11-29 16:18:23 +03:00
Andrey Smirnov 5fe442f191 Fix issue with missing comma in JSON. 2014-11-28 22:57:01 +03:00
Andrey Smirnov 50c4aba9ab Upgrade to latest goleveldb. #150 2014-11-28 22:56:28 +03:00
Andrey Smirnov b3627738c2 Add Gitter.im badge. [ci skip] 2014-11-27 01:25:58 +03:00
Andrey Smirnov eec6743fe4 Use ${HOME}, fix test. #146 2014-11-27 00:55:28 +03:00
Andrey Smirnov 42bf2f5e98 Fix one more system test. #147 2014-11-27 00:53:31 +03:00
Andrey Smirnov 35b9a8ea91 Don't sign repo in test. #146 2014-11-27 00:34:04 +03:00
Andrey Smirnov 26c0502307 Introduce new flag -force-components to aptly mirror create to ignore
components in Release file while doing checks. #147
2014-11-27 00:32:34 +03:00
Andrey Smirnov 5fa487e2dc Add gitter.im notification. 2014-11-26 23:51:21 +03:00
Andrey Smirnov d9c62780c2 When doing db cleanup, consider package references stored in PublishedRepos
of local repo publishes. #146
2014-11-26 23:41:18 +03:00
Andrey Smirnov 8c54e15a11 Add information about nightly builds, go 1.3. [ci skip] 2014-11-25 23:46:58 +03:00
Andrey Smirnov cc2cc16004 Fix system test. 2014-11-18 12:16:47 +03:00
Andrey Smirnov 726f12c537 Repos APIs: searching for packages, adding and deleting packages from the repo. #116 2014-11-18 00:50:59 +03:00
Andrey Smirnov f1c235f5c5 Fix error message. 2014-11-16 14:01:46 +03:00
Andrey Smirnov 9072ba5981 Revert part of test that shouldn't have been comitted yet. #140 2014-11-14 00:25:40 +03:00
Andrey Smirnov 7beb90d4fc Strings() for PackageList: turning list into sequence of package Ids. #116 2014-11-14 00:19:58 +03:00
Andrey Smirnov 61e22743af Fix for stripping part with slashes from component names. #140
Now aptly won't strip by default, but if distribution contains slash (like 'wheezy/updates')
and component starts with last part of distribution ('updates/main'), it would be stripped.
2014-11-14 00:13:58 +03:00
Andrey Smirnov 036baa2264 Exclude "source" architecture from list of Release architectures. #140 2014-11-14 00:01:41 +03:00
Andrey Smirnov ccd8c2551f Update location of go tool cover. 2014-11-11 01:25:52 +03:00
Andrey Smirnov 74f9787884 Implementation of upload file to local repo APIs. #116 2014-11-11 01:12:52 +03:00
Andrey Smirnov 83af66a8f6 Refactoring: move package files importing code to common deb from command. #116 2014-11-06 00:13:16 +03:00
Chris Read daf887e54f Upgrade gocheck 2014-11-05 13:27:15 -06:00
Andrey Smirnov 552b11e28d Workaround for '+' escaping in Amazon S3. #130 2014-11-05 02:46:01 +03:00
Andrey Smirnov 80a88a2248 Merge branch 'shadeslayer-master' 2014-10-26 19:54:11 +03:00
Andrey Smirnov 4c1d6d1463 Fix operator precedence. #131 2014-10-26 19:53:49 +03:00
Rohan Garg 140a11c04a Continue even when a server replies with 403
Amazon S3 replies with a 403 when accessing files that don't exist,
while this should be fixed at Amazon, we can workaround it in aptly
for the moment.
2014-10-25 21:41:57 +02:00
Andrey Smirnov 7be2ef8b85 Don't fallback between compression methods available unless we get strictly HTTP 404. #129 #125
Prior to that, some real errors could have been masked away by that fallback.
2014-10-24 08:51:04 +04:00
Andrey Smirnov d2d21c3df7 Update system test. 2014-10-23 11:03:19 +04:00
Andrey Smirnov f81a91bde9 First step of aptly repo add refactoring: extract collection of files. #116 2014-10-23 01:07:53 +04:00
Andrey Smirnov 7efd0de67c Fix shadowing of variable. #116 2014-10-23 00:57:27 +04:00
Andrey Smirnov 6a9db17460 Stubs for API calls. #116 2014-10-23 00:41:24 +04:00
Andrey Smirnov b1053826e3 Debian 7.7 has been released, update tests. 2014-10-20 12:22:19 +04:00
Andrey Smirnov 18953c1c90 Merge pull request #128 from rra/master
Allow variation of formatting of Debian control.tar.gz
2014-10-20 10:12:23 +04:00
Russ Allbery aeb85a1b3c Allow variation of formatting of Debian control.tar.gz
While all the normal Debian package building tools create a
control.tar.gz archive member that contains files with a leading
"./" path element, such as "./control", dpkg and other archive tools
like reprepro are happy with omitting the "./" path element and
having files like "control".  Allow for either for compatibility
with weird packages that people may want to import into aptly.
2014-10-17 13:36:58 -07:00
Andrey Smirnov 0a6d57ea1a Fixing system tests. 2014-10-17 02:03:48 +04:00
Andrey Smirnov aa4dee3c60 Merge branch 'queeno-fix_truncation_bug' 2014-10-17 00:55:09 +04:00
Andrey Smirnov 9fbe33b356 System test for file override from pool. #127 2014-10-17 00:54:42 +04:00
Andrey Smirnov 2a9871e2e9 We should never ever overwrite files in package pool. #127 2014-10-17 00:54:15 +04:00
Simon Aquino 951b6e9004 Test to avoid published file truncation when added to repo
This test will make sure that when a published file is added to repo, it
doesn't get truncated.
2014-10-16 16:57:50 +01:00
Simon Aquino 2173d3ab65 Fix file truncation bug
This commix prevents files from being truncated when attempting to add
a hard-linked package which already exists inside the pool directory.
2014-10-16 15:01:36 +01:00
Andrey Smirnov 9c834f410c API for file upload. #116 2014-10-16 00:04:50 +04:00
Andrey Smirnov eef44f5cd5 Correctly set config override. #123
Without that, config override gets set in parent class propagating
to all tests.
2014-10-14 19:27:47 +04:00
Andrey Smirnov aa77ea2835 Test for config show. #123 2014-10-14 18:28:53 +04:00
Andrey Smirnov 119bb0195b Merge branch 'queeno-add_config_show_command-2' 2014-10-14 18:26:45 +04:00
Andrey Smirnov 017dca57ed Re-generate man page. #123 #96 2014-10-14 18:26:27 +04:00
Andrey Smirnov 88351503b0 Fix misprint. #96 2014-10-14 18:26:12 +04:00
Andrey Smirnov 37b2d49aea A bit more style fixes. #123 2014-10-14 18:22:57 +04:00
Andrey Smirnov 0afb1f4306 Style fixes. 2014-10-14 18:20:09 +04:00
Andrey Smirnov 6ac0658478 Merge branch 'add_config_show_command' of https://github.com/queeno/aptly into queeno-add_config_show_command-2 2014-10-14 18:17:21 +04:00
Andrey Smirnov 50c8e35a90 Re-enable task run command. #96 2014-10-14 00:47:17 +04:00
Andrey Smirnov d6c3389d7c First steps towards /files/ API (file upload). #116 2014-10-14 00:47:17 +04:00
Andrey Smirnov 6b83213cf4 Add UploadPath to Context. #116 2014-10-14 00:47:17 +04:00
Simon Aquino 24927f9a29 config_show should output pretty json
This commit changes the output of aptly config show to be pretty json
rather than YAML.
2014-10-13 18:29:45 +01:00
Simon Aquino ecbb9ad20c Fixed failing system test
Added config command to aptly main output.
2014-10-13 15:58:41 +01:00
Simon Aquino 81e9189853 Config show now outputs a clean data structure 2014-10-13 15:58:41 +01:00
Simon Aquino 8efb7903b2 Added map to to_string and tabs to config_show 2014-10-13 15:58:41 +01:00
Simon Aquino c1995beff1 Config_show prints out strings,structs,ints,bools
More development for aptly config show
2014-10-13 15:58:41 +01:00
Simon Aquino 192152b215 Config command created - config show started 2014-10-13 15:58:41 +01:00
Andrey Smirnov 972e8c1373 Merge pull request #124 from queeno/queeno_username_change
Change simonaquino's github username to queeno
2014-10-11 20:57:00 +04:00
Simon Aquino c501fc63f8 Change simonaquino's github username to queeno
simonaquino changed its github username to queeno.
2014-10-11 16:39:48 +00:00
Andrey Smirnov bf08ad800f Attempt to build in Travis with python & virtualenv. #116 2014-10-10 19:28:04 +04:00
Andrey Smirnov ebc223a895 System tests for API. #116 2014-10-10 18:35:39 +04:00
Andrey Smirnov 01b1f23d6b Merge branch '122-gpg-batch' 2014-10-10 17:54:50 +04:00
Andrey Smirnov 6b08b64d62 Add latest contributors. 2014-10-10 17:53:49 +04:00
Andrey Smirnov 89bb20388f Fix unit tests. #122 2014-10-10 17:52:09 +04:00
Andrey Smirnov 9857789204 Regenerate man page. #122 2014-10-10 17:52:00 +04:00
Andrey Smirnov 6d1efe0200 Docstrings, gofmt. #122 2014-10-10 17:50:43 +04:00
Andrey Smirnov a85aa11ecd Update flag description/include it everywhere. #122 2014-10-10 17:50:08 +04:00
Andrey Smirnov 27ea769ad3 Merge branch 'master' of https://github.com/freehck/aptly into 122-gpg-batch 2014-10-10 17:44:55 +04:00
Andrey Smirnov 523d0d0945 Library for API system tests. #116 2014-10-10 17:43:04 +04:00
Andrey Smirnov 53f7fef4cf Force flag for API repos delete. #116 2014-10-10 17:18:47 +04:00
Dmitrii Kashin b590efa45f Merge branch 'master' of https://github.com/freehck/aptly 2014-10-10 04:22:43 +04:00
Dmitrii Kashin 59055d7fbd Add batch flag for publish commands 2014-10-10 04:04:44 +04:00
Ivan Kurnosov 22bcacf143 Typo in a error message fix
`s/Unknwon/Unknown/`
2014-10-09 16:19:15 +13:00
Andrey Smirnov 877109b3b7 Don't build under go1.2 (gin incompatible), use go 1.3.3. #116 2014-10-08 17:27:44 +04:00
Andrey Smirnov 10056b8571 Add first /repos/ API, command api serve. #116 2014-10-08 16:19:15 +04:00
Andrey Smirnov ac983ff65d Add RwMutexes to all collections. #116 2014-10-08 16:16:07 +04:00
Andrey Smirnov 2ed76f1e4c Add method to convert reflist to list of strings. #116 2014-10-08 16:15:54 +04:00
Andrey Smirnov cb6b18acfe Add missing dependency to github.com/jlaffaye/ftp. 2014-10-08 16:15:12 +04:00
Andrey Smirnov 3cd8c5adab Add gin and its dependencies. #116 2014-10-08 16:13:26 +04:00
Andrey Smirnov dd7b7b5f20 Refactor RefList.FilterLatestRefs to be method instead of standalone func. 2014-10-07 19:29:01 +04:00
Andrey Smirnov 1f6880fcad Disable api command, it's not committed yet. #117 2014-10-07 15:26:03 +04:00
Andrey Smirnov c8d9bef686 Close database before writing first byte to stdout. #117
Should fix conflict with commands like 'aptly xxx list -raw | xargs -n 1 aptly xxxx'
2014-10-07 15:20:27 +04:00
Andrey Smirnov 8a787d2c35 Refactor by separating AptlyContext into separate package. #116 2014-10-06 21:54:15 +04:00
Andrey Smirnov 159608cef3 Make LocalRepo JSON-serializable. #116 2014-10-06 21:08:46 +04:00
Andrey Smirnov 52c5934eb6 Lock down CollectionFactory. #116 2014-10-06 19:55:46 +04:00
Andrey Smirnov e4b9e974d2 bytes.Equal should be faster than bytes.Compare. 2014-10-06 15:17:25 +04:00
Andrey Smirnov d541b4f137 Version bump to 0.9~dev. 2014-10-06 15:07:31 +04:00
Andrey Smirnov eece643ea5 Include bash completion into source tarball. 2014-10-05 00:40:47 +04:00
Andrey Smirnov 14bd443d4d Disable support for aptly task for now. #96 2014-10-03 12:52:46 +04:00
Andrey Smirnov 9109c60c43 Version bump to 0.8. 2014-10-03 12:20:21 +04:00
Andrey Smirnov 445ecbe8f3 Update man page. #45 #114 2014-10-03 11:06:14 +04:00
Andrey Smirnov 27de979733 More comments. #45 #114 2014-10-03 11:02:31 +04:00
Andrey Smirnov ad11053412 Support for locking, unlocking, interruption, cleanup. #45 #114 2014-10-03 01:34:22 +04:00
Andrey Smirnov a356f3dff9 Marking RemoteRepo as being updated, with worker PID, checking for locks. #45 #114 2014-10-03 01:32:19 +04:00
Andrey Smirnov 1042894123 Abort downloader on shutdown, don't wait for downloads to finish. #45 #114 2014-10-03 01:31:38 +04:00
Andrey Smirnov 43eb993160 Don't panic on double re-open/close, ignore it. #45 #114 2014-10-03 01:20:26 +04:00
Andrey Smirnov d190ffd39a Update goleveldb to the latest version. 2014-10-03 01:19:36 +04:00
Andrey Smirnov 93c1c7aaab Support for closing and re-opening database. #45 #114 2014-10-02 21:41:58 +04:00
Andrey Smirnov 3e5ba27cb7 Ability to re-open db after close. #45 #114 2014-10-02 21:13:56 +04:00
Andrey Smirnov cd3b24799a Use less files for the download test. 2014-10-02 21:00:00 +04:00
Andrey Smirnov a0870f6726 Refactor mirror download code, split it into separate methods. #45 #114 2014-10-02 19:30:37 +04:00
Andrey Smirnov d45b456334 Update test. #26 2014-10-02 14:32:52 +04:00
Andrey Smirnov 91c753ad2f Add URL to all download errrors, so that they're easier to understand. #26 2014-10-02 14:12:43 +04:00
Andrey Smirnov 40509f73b3 Update system test. 2014-10-02 10:55:57 +04:00
Andrey Smirnov 1daa076d65 Don't allow '/' in distribution name, auto-replace '/' with '-' while guessing. #110 2014-10-01 22:59:05 +04:00
Andrey Smirnov aeae6009c4 Introduce plusWorkaround: generate copy of file with '+' -> ' ' to workaround S3/apt bug. #98 2014-10-01 21:32:56 +04:00
Andrey Smirnov 8049d69793 Update goamz to version with fix for multidel, re-enable S3 delete test. 2014-10-01 19:41:32 +04:00
Andrey Smirnov 8aa1954ba7 Support for custom storage class and encryption method. #105 2014-10-01 19:16:15 +04:00
Andrey Smirnov a02a90a3d8 Remove validate argument, not supported by Travi CI boto version. 2014-10-01 18:39:35 +04:00
Andrey Smirnov f303aabf26 Another way to install boto. 2014-10-01 18:17:55 +04:00
Andrey Smirnov 735cbac60d Install boto library for system tests. 2014-10-01 18:09:51 +04:00
Andrey Smirnov 5d69871ca4 Tests for publishing to Amazon S3. 2014-10-01 17:48:51 +04:00
Andrey Smirnov 1afbae8f7c Add AWS credentials. 2014-10-01 17:29:40 +04:00
Andrey Smirnov 1ed647e1b0 List storage & prefix in publish list. #113 2014-10-01 00:44:01 +04:00
Andrey Smirnov 01b8e9eda5 Fix system tests. #108 2014-10-01 00:31:10 +04:00
Andrey Smirnov f43d514804 Merge branch '108-udebs' 2014-09-30 23:29:27 +04:00
Andrey Smirnov 7e8f692b2c Use better words. #108 2014-09-30 23:24:51 +04:00
Andrey Smirnov 4b50f817d7 Fix system tests. #108 2014-09-30 23:09:57 +04:00
Andrey Smirnov e123e4dfac .udebs are supported now. #108 2014-09-30 21:53:19 +04:00
Andrey Smirnov 4fb09d9e85 Update man page. #108 2014-09-30 21:52:25 +04:00
Andrey Smirnov d9b23167bc Test on publishing repo with .udebs. #108 2014-09-30 21:51:38 +04:00
Andrey Smirnov 2c84faaf8d System test for repo adding .udebs. #108 2014-09-30 21:26:28 +04:00
Andrey Smirnov 6514b87e3e Add keyring for publish. #108 2014-09-30 21:25:52 +04:00
Andrey Smirnov bd34ba4088 Pregenerate all udebs indexes if at least one udeb has been discovered. #108 2014-09-30 21:11:01 +04:00
Andrey Smirnov fae6e977c3 System tests for publishing snapshot from mirror with .udebs. #108 2014-09-30 19:40:16 +04:00
Andrey Smirnov 2ae34cd873 Use Package.MatchesArchitecture instead of homegrown function. #108 2014-09-30 19:39:31 +04:00
Andrey Smirnov b365e5e0b2 System test: regular publish doesn't generate debian-installer files. #108 2014-09-27 02:15:08 +04:00
Andrey Smirnov e171f90fd5 Restore ${HOME} links. #108 2014-09-27 01:56:35 +04:00
Andrey Smirnov db499f872d Major refactoring of the publishing method, now uses helper indexFile(s). #108 2014-09-27 01:39:02 +04:00
Andrey Smirnov 8f9944117c Update tests on show mirror format change. #108 2014-09-25 23:38:45 +04:00
Andrey Smirnov ea399a335a Update tests on show mirror format change. #108 2014-09-25 23:37:11 +04:00
Andrey Smirnov 976ddb5ff9 Fix tests on arguments help. #108 2014-09-25 23:34:53 +04:00
Andrey Smirnov 7d8600b840 Add support for mirroring, showing, and editing remote repos with .udebs. #108 2014-09-25 22:12:59 +04:00
Andrey Smirnov 7ad1bb387b Support for .udeb downloads from remote mirrors. #108 2014-09-25 19:34:16 +04:00
Andrey Smirnov 2fbf465fbf Support for .udeb in deb.Package. #108 2014-09-25 19:31:21 +04:00
Andrey Smirnov fa786332de Allow changing "download sources" option for the mirror. #109 2014-09-22 19:36:48 +04:00
Andrey Smirnov 5e1bd0ff0e Correctly parse boolean flags in combination with config options. #104
Config option should act as default, while flag should override it only if set.
2014-09-22 13:41:26 +04:00
Andrey Smirnov 9c92b81706 Remove -dry-run flag for aptly snapshot filter, as it is useless. #82 2014-09-22 01:54:35 +04:00
Andrey Smirnov 144ccbf809 Make order of configuration file loading clear. 2014-09-21 00:55:23 +04:00
Andrey Smirnov a11805efb4 Update to goleveldb with misspellings fixed. 2014-09-20 21:46:45 +04:00
Andrey Smirnov 5b6cea2d62 Fix system test after spelling fixes. 2014-09-20 21:39:08 +04:00
Andrey Smirnov d4699a3b24 Fix system test. 2014-09-20 18:21:32 +04:00
Andrey Smirnov 09a695a128 Fix spelling mistakes. 2014-09-20 18:10:13 +04:00
Andrey Smirnov ec4d2bcefe Fix spelling mistakes found by lintian. 2014-09-20 18:09:47 +04:00
Andrey Smirnov 3040aceb7f Update goleveldb to the version which reduces memory usage significantly. 2014-09-20 18:03:24 +04:00
Andrey Smirnov 61d8639a8a System tests for aptly snapshot filter. #82 2014-09-01 22:25:17 +04:00
Andrey Smirnov b47754a106 Update man page. #82 2014-09-01 22:11:07 +04:00
Andrey Smirnov 1b08b7311f Implementation of command aptly snapshot filter. #82 2014-09-01 22:09:58 +04:00
Andrey Smirnov 0130fc0392 Add -force-replace flag to repo aptly add to replace conflicting packages. #83 2014-09-01 17:59:29 +04:00
Andrey Smirnov de32595d29 Fix test. #94 2014-09-01 16:03:35 +04:00
Andrey Smirnov 95e5fdd34a Update README. [ci skip] 2014-09-01 15:30:31 +04:00
Andrey Smirnov a05f00d9f1 Regenerate man page. #94 2014-09-01 15:13:54 +04:00
Andrey Smirnov 97158ef37b Support for --passphrase & --passphrase-file arguments on publishing. #94 2014-09-01 15:13:07 +04:00
Andrey Smirnov f01ac06d97 Remove extra whitespace [ci skip] 2014-08-30 01:23:48 +04:00
Andrey Smirnov a549778754 Fix system tests. #48 2014-08-30 01:16:42 +04:00
Andrey Smirnov 47d952f712 System test for ftp:// download. #48 2014-08-29 19:46:58 +04:00
Andrey Smirnov 166f31c34d Regenerate man page. #48 2014-08-29 19:39:17 +04:00
Andrey Smirnov 4940fdc951 Note support of FTP. #48 2014-08-29 19:38:25 +04:00
Andrey Smirnov 7ae785f5a3 Implementation of ftp:// support for downloading. #48 2014-08-29 19:37:10 +04:00
Andrey Smirnov 09c8421648 Update man page. 2014-08-29 00:58:47 +04:00
Andrey Smirnov 6a2059150f Add Vincent Batoufflet to list of authors. 2014-08-29 00:53:56 +04:00
Andrey Smirnov 9b3dfe920d Merge branch 'vbatoufflet-mirror-edit-arch' 2014-08-29 00:53:15 +04:00
Andrey Smirnov 72f8e4ab61 Check architectures before applying arch change. #99 2014-08-29 00:52:47 +04:00
Andrey Smirnov 755944652f System tests for mirror edit with architectures. #99 2014-08-29 00:52:03 +04:00
Andrey Smirnov b29d42d023 Fix system test, use ${HOME}. #80 2014-08-29 00:46:59 +04:00
Andrey Smirnov f19ece776d Merge branch 'mirror-edit-arch' of https://github.com/vbatoufflet/aptly into vbatoufflet-mirror-edit-arch 2014-08-28 22:49:19 +04:00
Andrey Smirnov 839763c0b9 Command package show with tests. #80 2014-08-28 22:47:41 +04:00
Andrey Smirnov c56ecab06f Method PackageRefList.Has(). #80 2014-08-28 22:25:19 +04:00
Andrey Smirnov 02d86422a8 Fix tests by introducing stable sort. #80 2014-08-28 22:03:06 +04:00
Andrey Smirnov 65efe0cd2a System tests for aptly package search. #80 2014-08-28 21:44:41 +04:00
Andrey Smirnov 468b1f11b9 New command: package search to search whole package DB for matching packages. #80 2014-08-28 19:42:47 +04:00
Andrey Smirnov 608870265c New common interface for PackgeCollection & PackageList: PackageCatalog. #80 2014-08-28 19:41:30 +04:00
Andrey Smirnov ed03a7c69e New algorithm for dependency resolution, tests. #100 #81 2014-08-28 19:07:39 +04:00
Andrey Smirnov 5a42c60af4 Simplify and make more deterministic algorithm for dependency pulling. #100 2014-08-28 19:05:32 +04:00
Vincent Batoufflet f66302ef31 Add ability to edit mirror architectures 2014-08-26 23:22:51 +02:00
Andrey Smirnov 346a7bcce9 System tests for mirror, snapshot, repo search. #81 2014-08-27 00:04:01 +04:00
Andrey Smirnov 9bee7cdd08 Simplify dependency verification code. #81 2014-08-27 00:03:46 +04:00
Andrey Smirnov 74eee3496c Capture test results in prepared format. #81 2014-08-26 23:58:25 +04:00
Andrey Smirnov 3ef5429212 System tests for aptly mirror search. #81 2014-08-26 19:38:27 +04:00
Andrey Smirnov 3030e66d4c Fix -with-deps searching. #81 2014-08-26 19:25:02 +04:00
Andrey Smirnov 9ae5a5ffb2 Update system tests. #96 2014-08-26 02:03:58 +04:00
Andrey Smirnov 833d37d22c Update system tests. #81 2014-08-26 02:03:06 +04:00
Andrey Smirnov 03ec1f97a7 Fix after style fix. #96 2014-08-26 02:02:11 +04:00
Andrey Smirnov a2df51b40e Commands mirror/repo/snapshot search. #81 2014-08-26 02:01:11 +04:00
Andrey Smirnov ae906f525e Style fixes. #96 2014-08-26 01:09:49 +04:00
Andrey Smirnov b4a5a55cac Style fixes. #96 2014-08-26 01:06:33 +04:00
Andrey Smirnov 6003764ff5 Add more system tests. #96 2014-08-25 22:15:21 +04:00
Andrey Smirnov 099a82c816 Style fixes. #96 2014-08-25 22:06:25 +04:00
Andrey Smirnov ef992e2b44 Merge branch 'queeno-script_run_command' 2014-08-25 22:05:34 +04:00
Andrey Smirnov 68e600974d Refactoring: remove context switching, another way to catch panics, colored output. #96 2014-08-25 22:05:02 +04:00
Andrey Smirnov 39a1f0ec2d Use go1.3.1. 2014-08-25 21:22:55 +04:00
Andrey Smirnov 318fc5b7f4 Merge branch 'script_run_command' of https://github.com/queeno/aptly into queeno-script_run_command 2014-08-23 22:04:50 +04:00
Simon Aquino 72e54aa3d1 Fixed a bug with the context switching
The context switching wasn't really happening. Now the issue is fixed.
2014-08-16 23:32:38 +00:00
Simon Aquino 91ff904ac4 Adding filename flag to specify task run filename.
Just realised commands can not have any subcommands and therefore
consist of single words (for example serve or version). Hence I can't
assume that if len(args)==1 then the user has entered the filename.
I have created the filename flag so the user is forced to specify it
when they wish to run aptly tasks from files.
2014-08-16 22:13:24 +00:00
Simon Aquino b59471ad35 Added RunTask acceptance tests
Added a basic RunTask test to test the functionality of the new aptly
task run command. More to come...
2014-08-16 15:11:14 +00:00
Simon Aquino 6ff601f4a2 Making sure context is initialised before using it
Now checkong context is not nil before setting panicked = true
2014-08-16 14:33:36 +00:00
Simon Aquino 0c09bdedaa Fixed t03_help:MainTest failing due to new cmd 2014-08-16 14:22:04 +00:00
Simon Aquino dfc1f27d4c Better wording for task run message. 2014-08-16 14:18:35 +00:00
Simon Aquino 005cee572e Aptly script has now become aptly task
It makes more sense. Multiple lines of aptly commands can now be called
'aptly tasks' which could potentially be automated, in the future?
2014-08-16 14:14:56 +00:00
Simon Aquino 18e3ed5d64 aptly script run implementation
This new aptly command will allow to run multiple commands within a single
aptly command, running in a single thread. The commands can be included
in a text file or added to the aptly script run command in one line,
separated by comas ','.
If one command returns an error, then all the subsequent commands will
be skipped.
Each command output will appear on the console within coloured strings,
clearly stating which command is running and the start and the end of
the received output.
2014-08-16 13:55:13 +00:00
Simon Aquino 3c7696ef7e Refactored main.go
The main function - whuch runs aptly commands - has been taken out from
main.go and included to the cmd package. This is useful for the aptly
script run command, which should use that behaviour.
2014-08-16 13:55:13 +00:00
Simon Aquino b2779d7a88 Go-shellwords added to Gomfile
This packages allows us to parse shell commands. Useful for
script_run.go (later added)
2014-08-16 13:55:13 +00:00
Simon Aquino cdd34b4759 Added panicked attribute to context.go
This attribute is set to false during initalisation, and it's set to
true when error arises.
2014-08-16 13:55:13 +00:00
Simon Aquino 1f2ddca32b Add switchContext function to context.go 2014-08-16 13:55:13 +00:00
Simon Aquino df06dc356b Added script cmd in cmd.go 2014-08-16 13:54:46 +00:00
Simon Aquino b6c82f073f Added new script command 2014-08-16 10:17:44 +00:00
Andrey Smirnov 9a03b5f696 Update leveldb to the latest version. 2014-08-15 21:50:41 +04:00
Andrey Smirnov 047270540a Version bump: 0.8~dev 2014-08-06 23:34:51 +04:00
Andrey Smirnov eff3823edf Upload src-package to bintray. 2014-08-06 13:40:13 +04:00
Andrey Smirnov 9d02f057c6 Version bump: 0.7.1. 2014-08-06 02:24:43 +04:00
Andrey Smirnov 8387586cc8 Man page update: -force-overwrite flag. #90 2014-08-06 02:04:25 +04:00
Andrey Smirnov b433e7dad5 Workaround for broken time.Time encoding in msgpack with go < 1.2. #89
Decoding looses value of time.Time field, but that is not critical.
2014-08-06 01:56:13 +04:00
Andrey Smirnov dec4bdee71 Merge branch 'patch-1' of https://github.com/guilhem/aptly 2014-08-05 17:01:55 +04:00
Andrey Smirnov bb6593d21e Add -force-overwrite flag to publish update, switch, snapshot and repo commands. #90
Includes new and updated system tests.
2014-08-05 17:01:18 +04:00
Andrey Smirnov fe879acf9c Remove Makefile part specific for go1.1 2014-08-05 15:59:32 +04:00
Andrey Smirnov 5b8390c644 Uncomment and fix publish updat tests. 2014-08-05 15:58:47 +04:00
Andrey Smirnov d558791070 Add -force-overwrite command flag. #90 2014-08-05 15:47:38 +04:00
Andrey Smirnov 38ea595c9a Add forceOverwrite on the path to LinkFromPool. #90 2014-08-05 15:47:23 +04:00
Andrey Smirnov c03b7929d4 Fix line ends: system tests. 2014-08-05 15:44:12 +04:00
Andrey Smirnov d122ab6013 Fix system tests. 2014-08-05 15:27:39 +04:00
Andrey Smirnov a7b594d076 Drop support for go1.1 2014-08-05 15:26:41 +04:00
Andrey Smirnov e07bcf8e51 Fix style and add comments. #90 2014-08-05 14:50:15 +04:00
Andrey Smirnov da6d5b7cf8 Add 'force' to LinkFromPool method: overwrite file even if exists and different content. #90 2014-08-05 14:50:06 +04:00
Guilhem Lettron 15ef5c63c5 Add gobuild.io badge 2014-07-31 14:52:22 +02:00
Andrey Smirnov 625a38c578 aptly version 0.7 2014-07-29 00:33:53 +04:00
Andrey Smirnov 03a79ebe4c Update goamz to fixed version with signing & encoding. #15 2014-07-28 23:41:59 +04:00
Andrey Smirnov 60fa0aa68e Update command usage. 2014-07-28 19:17:21 +04:00
Andrey Smirnov 04bd9929e1 Update man page: S3, package queries. 2014-07-28 19:17:10 +04:00
Andrey Smirnov 8407e70347 Fix system tests. #15 2014-07-28 16:20:38 +04:00
Andrey Smirnov bf91744078 <endpoint> in command usage. #15 2014-07-28 15:03:55 +04:00
Andrey Smirnov 2c470c1535 Rename config option to endpoint. #15 2014-07-28 15:01:51 +04:00
Andrey Smirnov a18011bdc0 Update goamz: fixed bug with '+' in filenames. #15 2014-07-27 02:49:05 +04:00
Andrey Smirnov af8af0f3d7 Fix tests on aptly mirror edit. #63 2014-07-26 18:22:47 +04:00
Andrey Smirnov 89d26b7dc6 Man for aptly mirror edit. #63 2014-07-26 18:02:01 +04:00
Andrey Smirnov 8649ee3b37 Command aptly mirror edit with tests. #63 2014-07-26 17:59:46 +04:00
Andrey Smirnov b9c8a8d9da System tests for mirror/repo/snapshot rename commands. #63 2014-07-26 17:28:16 +04:00
Andrey Smirnov c5922737ed Man page updates for 'rename' commands. #63 2014-07-26 17:12:00 +04:00
Andrey Smirnov 772111ad26 Commands mirror/repo/snapshot rename. #63 2014-07-26 17:11:26 +04:00
Andrey Smirnov d7ef1a0c4b Allow saving snapshot without package refs loaded. #63 2014-07-26 17:09:47 +04:00
Andrey Smirnov bd221bf869 Sort dependencies. 2014-07-26 16:42:59 +04:00
Andrey Smirnov 0485a36de1 Add Recommends: dependency on bzip2. #84 2014-07-26 01:44:30 +04:00
Andrey Smirnov 77d6a10984 Implementation of Rename method for S3 PublishedStorage. #15 2014-07-26 01:11:23 +04:00
Andrey Smirnov 8015966663 Optimize package encoding/decoding a bit by reusing codec handle. 2014-07-25 16:46:52 +04:00
Andrey Smirnov 94114f2c3d Use idiomatic chan struct{} when there's nothing to send. 2014-07-24 01:25:59 +04:00
Andrey Smirnov 2906369a3b Reuse default HTTP transport options. 2014-07-24 01:17:58 +04:00
Andrey Smirnov 521c52f600 Remove unused field. 2014-07-24 01:15:01 +04:00
Andrey Smirnov 52bb33dc69 Fix bugs with prefix/storage parsing. #15 2014-07-22 00:27:49 +04:00
Andrey Smirnov 71d90947c9 Remove debugging output. #15 2014-07-22 00:27:38 +04:00
Andrey Smirnov b3a4936e06 Fix system tests. 2014-07-21 18:14:09 +04:00
Andrey Smirnov 237d25fe5b Fix issue with ETag/MD5 comparison, add extra info in error messages. #15 2014-07-21 17:43:42 +04:00
Andrey Smirnov de0954732a Style fix. 2014-07-21 17:43:35 +04:00
Andrey Smirnov 915b0d1697 Integrate PublishedRepos with storages & context. #15 2014-07-21 17:43:12 +04:00
Andrey Smirnov 6d026afc69 Support for multiple storages in PublishedRepository. #15 2014-07-21 17:19:13 +04:00
Andrey Smirnov 27a5578d30 Fix system tests. #15 2014-07-18 19:21:29 +04:00
Andrey Smirnov 96e878a2e0 Separate out LocalPublishedStorage interface. #15 2014-07-18 17:44:54 +04:00
Andrey Smirnov 7a7bb56557 Config options for S3 storage. #15 2014-07-18 17:37:08 +04:00
Andrey Smirnov 076ecd586f Fix style issues. #15 2014-07-17 18:09:13 +04:00
Andrey Smirnov c54406e29f First version of PublishedStorage for S3. #15 2014-07-17 18:05:38 +04:00
Andrey Smirnov b260b0010a Refactoring: add MD5 to LinkFromPool. #15 2014-07-17 18:04:56 +04:00
Andrey Smirnov fbf1bc14b7 Refactoring PublishedStorage interface: leave operations suitable for S3. #15 2014-07-17 00:54:44 +04:00
Andrey Smirnov f12cf935ba GPG signer shouldn't report full path name. #15 2014-07-17 00:53:36 +04:00
Andrey Smirnov 4e169c3d10 Update system tests on command help. #64 2014-07-16 14:10:40 +04:00
Andrey Smirnov ea2bfea2a3 Man page with new flags for aptly mirror create. #64 2014-07-16 13:58:07 +04:00
Andrey Smirnov cf4619784e System tests for mirror show & update with filters. #64 2014-07-16 13:58:02 +04:00
Andrey Smirnov 69ad2ccd84 System tests for mirror create with filter. #64 2014-07-16 13:45:46 +04:00
Andrey Smirnov fe1046a7a3 Support for filters in mirror create/update/show. #64 2014-07-16 13:28:11 +04:00
Andrey Smirnov ce1df9447d Support for filters in RemoteRepo: filtering mirror contents by query. #62 2014-07-16 02:27:29 +04:00
Andrey Smirnov 2a7a2de84a Allow empty source in PackageList.Filter. #64 2014-07-16 02:23:55 +04:00
Andrey Smirnov 238bdfad96 Add String() method for queries. 2014-07-15 21:49:30 +04:00
Andrey Smirnov 56d777af0a Introduce regexp query matching. 2014-07-15 19:00:06 +04:00
Andrey Smirnov a632469890 Support for quoted string arguments. 2014-07-14 23:20:31 +04:00
Andrey Smirnov 3601cc15ed Fix unit-tests for new $Architecture matching. 2014-07-14 19:45:27 +04:00
Andrey Smirnov 61cd4c6af1 Use special matcher for $Architecture, so that 'any' matches any arch. 2014-07-14 19:39:19 +04:00
Andrey Smirnov 401bb768d7 Fix bug with architectures query: it was always true. 2014-07-14 19:37:12 +04:00
Andrey Smirnov 5880d11899 Fix refactoring leftover bug. 2014-07-14 19:16:02 +04:00
Andrey Smirnov ed6e261bd0 Rewrite snapshot pull to use PackageList.Filter instead of homebrew algorithm. 2014-07-14 19:02:15 +04:00
Andrey Smirnov fb660efeb5 Make list sort really stable: if all properties match, use architecture
as sort key.
2014-07-14 18:51:07 +04:00
Andrey Smirnov 80de65f28d Fix system test. #62 2014-07-13 16:19:03 +04:00
Andrey Smirnov 9893e4af3d Add flag to control downlod limit in aptly mirror update. #62 2014-07-13 16:11:18 +04:00
Andrey Smirnov 7416cc403d Update system test config file. #62 2014-07-13 16:11:07 +04:00
Andrey Smirnov 83ceee1e3f Remove debug output. #62 2014-07-13 16:10:53 +04:00
Andrey Smirnov a54a366c95 New config setting: downloadSpeedLimit to limit download speed. #62 2014-07-13 15:47:44 +04:00
Andrey Smirnov fb1e28b91b Setting for downloader to limit download speed to specified level. #62 2014-07-12 23:56:32 +04:00
Andrey Smirnov 86206df58d Use flowcontrol library. #62 2014-07-12 23:55:54 +04:00
Andrey Smirnov 1d49a717b9 Unit-tests for queries. 2014-07-12 23:26:08 +04:00
Andrey Smirnov 3b0b0b76ec One more fix for Debian 7.6 2014-07-12 23:18:20 +04:00
Andrey Smirnov 904b9e101b Update tests: wheezy 7.6 released. 2014-07-12 21:54:13 +04:00
Andrey Smirnov 9fb8a0ea4b Capturing results for other command output. 2014-07-12 21:53:47 +04:00
Andrey Smirnov bc27c6e14d System test on using complex query when importing. 2014-07-12 18:02:57 +04:00
Andrey Smirnov ae3c98c210 Implementation for FieldQuery. 2014-07-12 18:02:33 +04:00
Andrey Smirnov 34f545b8cf Package index may not be indexed when schanning. 2014-07-12 18:02:13 +04:00
Andrey Smirnov d523d2b415 Package.GetField implementation for querying. 2014-07-12 18:00:13 +04:00
Andrey Smirnov e320ac31d5 Switch aptly repo move/copy/import/remove to use new filters based on queries. 2014-07-12 13:58:38 +04:00
Andrey Smirnov e08d44ff0a Fix bug with matching in Search method. 2014-07-12 13:58:22 +04:00
Andrey Smirnov 898870038a Rename s/Searchable/Fast/ 2014-07-12 00:30:53 +04:00
Andrey Smirnov c485cf41f7 PkgQueries, concept of 'Searchable', rewrite Filter using PackageQueries. 2014-07-12 00:15:33 +04:00
Andrey Smirnov d54ef1e921 Fix bug with special chars handling in strings, detect package key queries,
arch condition for dependency-like queries.
2014-07-12 00:14:49 +04:00
Andrey Smirnov b42fd71acf MatchesDependency should check on Provides: as well. 2014-07-11 16:30:41 +04:00
Andrey Smirnov ede5449440 Refactoring: move query tree to deb package. 2014-07-11 00:09:31 +04:00
Andrey Smirnov eef49516ef Fix bugs after style fixes. 2014-07-10 23:31:53 +04:00
Andrey Smirnov e745747370 Style fixes as suggested by tools. 2014-07-10 21:34:52 +04:00
Andrey Smirnov 7e5b2ae8f5 Bugfix: unit-test was creating dirs in source directory. 2014-07-10 21:31:12 +04:00
Andrey Smirnov ada3ae0094 Introduce query language (resembling reprepro syntax). 2014-07-10 21:28:02 +04:00
Andrey Smirnov d262a131cc Preparation for query matching: introduce Regexp + PatternMatch. 2014-07-10 21:16:30 +04:00
Andrey Smirnov f0e69144ed Merge branch 'simonaquino-pull_multiple_packages' 2014-07-10 00:56:16 +04:00
Andrey Smirnov a7cb40ee7a System tests for aptly snapshot pull -all-matches. #70 2014-07-10 00:55:53 +04:00
Andrey Smirnov 2a9b2f87f9 Update man page. #70 2014-07-10 00:48:24 +04:00
Andrey Smirnov 9af10bc422 Refactoring: simplification. #70 2014-07-10 00:43:42 +04:00
Andrey Smirnov bdbb5acb11 Remove 'allMatches' on version equal. #70 2014-07-10 00:20:28 +04:00
Andrey Smirnov 81d506b226 Dependencies should be matched for each package one by one. #70 2014-07-10 00:13:19 +04:00
Andrey Smirnov 1c30b2b9de Simplification: we are already able to search for all packages. #70 2014-07-10 00:10:53 +04:00
Andrey Smirnov 566604d4ba Revert changes related to NextVersion. #70
It would be done in a bit different way: by introducing powerful search queries.
2014-07-10 00:04:48 +04:00
Andrey Smirnov 58a57f2b2c Merge branch 'pull_multiple_packages' of https://github.com/simonaquino/aptly into simonaquino-pull_multiple_packages 2014-07-09 18:16:46 +04:00
Andrey Smirnov 20d744f398 Fix system tests (whitespace). 2014-07-08 13:12:10 +04:00
Andrey Smirnov 360981de4a Merge branch 'simonaquino-sort_snapshots_time' 2014-07-07 23:37:40 +04:00
Andrey Smirnov 79016f7f98 Slight refactoring, make wrong param real error. #73 2014-07-07 23:36:59 +04:00
Andrey Smirnov 1a92d8bfe9 Add --capture to auto-create 'gold' results when fail. 2014-07-07 23:29:36 +04:00
Andrey Smirnov d3707b4cfe Add system test on wrong --sort parameter. #73 2014-07-07 23:29:21 +04:00
Andrey Smirnov de1fa85127 Update man page. #73 2014-07-07 23:14:32 +04:00
Andrey Smirnov d9b35cea01 Allow running system tests by mask.
E.g. system/run.py 'ListSnapshot*'.
2014-07-07 23:13:49 +04:00
Andrey Smirnov b75b4d1488 Merge branch 'sort_snapshots_time' of https://github.com/simonaquino/aptly into simonaquino-sort_snapshots_time 2014-07-07 18:18:58 +04:00
Andrey Smirnov da55f18b0e Fix system tests. 2014-07-02 10:10:39 +04:00
Andrey Smirnov 165dd0053e Merge branch 'specify_long_tests' of https://github.com/simonaquino/aptly 2014-07-02 10:03:19 +04:00
Andrey Smirnov 22a4e6b67b Update goleveldb to fixed version (dropping data after compaction + recover). syndtr/goleveldb#53 2014-07-02 09:54:20 +04:00
Simon Aquino 20513e1c16 Specify individual long tests to run
The test script currently only allows to run all the long test. This
change will allow a user to specify individual long tests to run.
2014-07-01 00:51:58 +01:00
Simon Aquino b4ea963744 List snapshots by time: added integration tests
Added a couple of integration tests for the new list snapshot by
creation time feature.
2014-06-30 20:23:20 +01:00
Simon Aquino 429788db0f List snapshots by time
Users now have the choice of listing the snapshot by time as well as
name (default behaviour).
An additional flag has been added '--sort=' which controls the sort
method applied to the list produced by aptly snapshot list.

The possible values are:
--sort=name (default): sorts the snapshot list by name (lexicographic
order)
--sort=time: sorts the snapshot list in chronological order (oldest to
newest)
2014-06-30 19:25:13 +01:00
Andrey Smirnov 1e70e954da System test on pulling latest version by default. #67 2014-06-29 10:12:18 +04:00
Andrey Smirnov 319f3e6bb2 Updated fixture: sensu mirror, new mirror contents. 2014-06-29 09:56:30 +04:00
Andrey Smirnov 56915c4357 Fix order of Component & Archive fields. 2014-06-29 09:55:51 +04:00
Simon Aquino e1348ab88f Merge smira/master into pull_multiple_packages
Resolved conflicts arisen following smira's new commits into master.
2014-06-28 01:34:28 +01:00
Andrey Smirnov 026dc540d2 Merge branch 'simonaquino-deterministic_package_search' 2014-06-28 00:37:33 +04:00
Andrey Smirnov 44ce4c8a77 Insert into right position when adding as well. #67 2014-06-28 00:26:56 +04:00
Andrey Smirnov 980102462b Simplify Makefile. 2014-06-28 00:10:35 +04:00
Andrey Smirnov 86b0860463 Enable system tests fixture under go1.3 as well. #72 2014-06-27 23:53:37 +04:00
Andrey Smirnov e311d41dd7 Add Simon Aquino to authors. #67 2014-06-27 23:49:05 +04:00
Andrey Smirnov c3ce886990 Merge branch 'deterministic_package_search' of https://github.com/simonaquino/aptly into simonaquino-deterministic_package_search 2014-06-27 23:47:57 +04:00
Andrey Smirnov 959ecf696c Build under go1.3 as well. #72 2014-06-27 23:39:32 +04:00
Andrey Smirnov 48d01f5700 Fix reference to home directory. #24 2014-06-27 23:30:04 +04:00
Andrey Smirnov aeecc1ec91 System test on conflicting files when publishing. #65 2014-06-27 22:29:01 +04:00
Andrey Smirnov 685a4de4e7 Fix link to Ryan Uber's profile. 2014-06-27 22:18:03 +04:00
Andrey Smirnov 667efc2b90 Add AUTHORS files. 2014-06-27 21:44:43 +04:00
Simon Aquino 3cf281965b Implementation of all-matches functionality + tests
When performing an *aptly snapshot pull*, users might list dependency
versions that can potentially match multiple packages in the source
snapshot. However, the current implementation of the 'snapshot pull'
command only allows one package to be pulled from a snapshot at a time
for a given dependency.

The newly implemented all-matches flag allows users to pull all the
matching packages from a source snapshot, provided that they satisfy the
version requirements indicated by the dependencies.

The all-matches flag defaults to false and only produces the described
behaviour when it is explicitly set to true.
2014-06-27 03:36:03 +01:00
Simon Aquino e19a615641 In PackageList, sort the package version from latest to oldest
This enables us to return the latest version of a package when no version is specified rather than the oldest.
Therefore, we don't need to modify the search algorithm to return the latest version of a package.
2014-06-13 12:06:33 +01:00
Simon Aquino ff77fbf5d9 Sort PackagesList by name and version 2014-06-13 11:42:20 +01:00
Andrey Smirnov 856dd7021c System tests for publish update empty -> empty. #66 2014-06-11 20:42:13 +04:00
Andrey Smirnov ebc47f7d5d Add unit-test. #65 2014-06-11 20:32:45 +04:00
Andrey Smirnov 082fda62b5 Add unit-test. #66 2014-06-11 20:28:56 +04:00
Andrey Smirnov 3199fd85fb Fix publish updating (switching) for empty -> empty scenario. #66 2014-06-11 20:27:49 +04:00
Andrey Smirnov 0c6951fcd2 When linking, check that inode file matches if linking to same file. #65
Otherwise files from conflicting packages might override each other in the published
pool. This is explicitly POSIX-only.
2014-06-11 20:03:34 +04:00
Andrey Smirnov 35e57026ac Version bump: 0.7~dev 2014-06-11 20:02:50 +04:00
Andrey Smirnov 28e050c14e Fix style warnings. 2014-06-07 18:00:36 +04:00
Andrey Smirnov 8fb399026d Version bump for 0.6. 2014-06-07 17:58:33 +04:00
Andrey Smirnov e15f23962a Add missing files. #61 2014-06-07 17:27:24 +04:00
Andrey Smirnov 81af5882b9 Test on per-component/arch Release contents. #61 2014-06-07 17:19:02 +04:00
Andrey Smirnov e554d8befa Update tests with introduction of Release files under each arch/component. #61 2014-06-07 17:01:37 +04:00
Andrey Smirnov 17c564358a Refactoring, support for atomic updates and checksumming. #61 2014-06-07 16:44:54 +04:00
Andrey Smirnov 2e4c1c491e Merge branch 'debinst' of https://github.com/ryanuber/aptly into ryanuber-debinst
Conflicts:
	deb/publish_test.go
2014-06-07 16:34:39 +04:00
Andrey Smirnov e7230d9ee6 One more test fix. #36 2014-06-07 16:28:15 +04:00
Andrey Smirnov 0f1074a721 Fix tests. #36 2014-06-07 16:17:59 +04:00
Andrey Smirnov 17ed34fdaa System tests for aptly publish switch with multiple repositories. #36 2014-06-07 15:18:58 +04:00
Andrey Smirnov 17b320eac4 Sort components when doing string representation. #36 2014-06-07 15:18:36 +04:00
Andrey Smirnov e3a71c81e1 Both components should contain same architectures. #36 2014-06-06 03:05:22 +04:00
Andrey Smirnov bf900deb4b Remove whitespace. 2014-06-06 03:03:58 +04:00
Andrey Smirnov 142387311b Tests for aptly publish update and multi-component repository. #36 2014-06-06 03:00:44 +04:00
Andrey Smirnov 68fbb0cbb9 Fix tests, don't break pool contents. #36 2014-06-06 03:00:29 +04:00
Andrey Smirnov 835da9cb3c One more test fix: duplicate archs. #36 2014-06-06 02:37:09 +04:00
Andrey Smirnov 7cd0d394d4 Test fix: source shouldn't be part of Architectures: field. #36 2014-06-06 02:25:11 +04:00
Andrey Smirnov 2040be2f8a Deduplicate architectures when guessed. #36 2014-06-06 02:14:15 +04:00
Andrey Smirnov 1957c811e8 Tests for multi-component and aptly publish list/snapshot/repo, aptly serve. #36 2014-06-06 02:09:13 +04:00
Andrey Smirnov 2dae9b01a1 Grammar fix. #36 2014-06-06 02:09:00 +04:00
Andrey Smirnov 9a34b4ff1f Update commands to handle multiple components repositories. #36 2014-06-04 17:43:16 +04:00
Andrey Smirnov d218159455 Multiple connections for published repo. #36 2014-06-04 17:06:34 +04:00
Andrey Smirnov 8be6911238 Fix multiple component in aptly graph. #36 2014-06-04 17:04:05 +04:00
Andrey Smirnov 20a7c5ae2d A bit more unit-tests with multi-component repositories. #36 2014-06-04 01:07:32 +04:00
Andrey Smirnov e161313efa Fix aptly serve: correct components list. #36 2014-06-03 17:25:15 +04:00
Andrey Smirnov 7192049c16 Update to new PublishedRepo with multiple components. #36
Multiple component publishing doesn't work yet, but old features are working.
2014-06-03 17:09:00 +04:00
Andrey Smirnov ee71b93669 Major change: published repo now supports multiple components <> snapshots (local repos). #36 2014-06-03 14:34:26 +04:00
Andrey Smirnov 43ee735aa4 Fix error capitalization. 2014-06-03 14:33:19 +04:00
Andrey Smirnov da5b0c9a66 Test fixes. #55 2014-05-31 21:27:36 +04:00
Andrey Smirnov e1dbab6988 Allow publishing of empty snapshots and local repos. #55 2014-05-31 21:13:30 +04:00
Andrey Smirnov bcdfb7d99a Fix system tests: update test data. 2014-05-31 19:37:31 +04:00
Andrey Smirnov ac85a0897a Update gographviz to fixed version with XML escaping. #58 2014-05-30 22:08:11 +04:00
Ryan Uber 9a4543500c Handle source repos while creating dist release file 2014-05-29 22:24:19 -07:00
Ryan Uber b0f9a4a419 Added tests for Release file in distribution directory 2014-05-29 22:07:59 -07:00
Ryan Uber 71ea2be6c1 deb: added Release file to each individual arch dir for d-i. 2014-05-29 14:04:06 -07:00
Andrey Smirnov b717caeda4 Make 'Package:' line field first when serializing stanza. #49 2014-05-30 00:43:19 +04:00
Andrey Smirnov fcc283bdb1 Regenerate man page (-no-remove for aptly snapshot merge). #57 2014-05-30 00:05:15 +04:00
Andrey Smirnov d3d41dd1c9 Add missing files. #57 2014-05-29 20:49:35 +04:00
Andrey Smirnov c72ef05a2a Support for -no-remove while merging snapshots. #57 2014-05-29 18:05:52 +04:00
Andrey Smirnov a1e360b07b Conflict detection for packages in one list. #60 2014-05-29 18:01:07 +04:00
Andrey Smirnov 90bba977d7 Introduce Package.ShortKey(): packages should have no conflict on that key in one list. #60 2014-05-29 18:00:14 +04:00
Andrey Smirnov dc248c5603 Fix diff algorithm for reflists with duplicate entries. #57 2014-05-29 17:58:59 +04:00
Andrey Smirnov f007465d18 Change the way package key works: now it includes FilesHash. #60
Now duplicate packages (the same name/version/arch) but with different set of files
would be handled as separate entities.
2014-05-29 16:49:05 +04:00
Andrey Smirnov 869e83713d Remove debugging output. #51 2014-05-29 00:49:26 +04:00
Andrey Smirnov 7b9e3429fd Use gographviz escaping instead of homegrown escape function. #51 #58
It doesn't fix the bug #58, waiting for maintainer of gographviz to
accept the patch: https://code.google.com/p/gographviz/issues/detail?id=2
2014-05-29 00:47:37 +04:00
Andrey Smirnov 5b75dbc481 Fix system tests. #54 2014-05-18 00:16:00 +04:00
Andrey Smirnov d96839f99d Fix unit tests. #54 2014-05-17 22:47:47 +04:00
Andrey Smirnov 8b2920d5dd aptly repo add now exists with non-zero exit code if some files fail to add. #54 2014-05-17 22:23:20 +04:00
Andrey Smirnov 4240b134e6 Fix spelling. 2014-05-17 22:08:31 +04:00
Andrey Smirnov 1d31a5c25f Don't use fixed cap, as it might be more than length. #53 2014-05-16 00:29:43 +04:00
Andrey Smirnov 05a42f4cba aptly exits with 2 on command/flag parse error. #52 2014-05-16 00:22:51 +04:00
Andrey Smirnov 2cbb486f6b Update tests with new Varnish repo. 2014-05-13 13:39:14 +04:00
Andrey Smirnov d0ff11390b Revert "Version bump for 0.5.1."
This reverts commit b5d025f141.
2014-05-13 12:29:13 +04:00
Andrey Smirnov b5d025f141 Version bump for 0.5.1. 2014-05-10 18:10:19 +04:00
Andrey Smirnov 3c7a2281b2 Fix "production" libraries in Gomfile with exact SHA. 2014-05-10 18:08:12 +04:00
Andrey Smirnov be3ad21fbe Add target to build "all-in-one" source tarball for Debian. 2014-05-10 18:06:23 +04:00
Andrey Smirnov 5301e8a341 Fix pool directory for packages with version in Source: field. #44 2014-05-10 17:25:44 +04:00
Andrey Smirnov 49eed59238 Fix test on command line help. #47 2014-05-10 17:13:49 +04:00
Andrey Smirnov 3e78240b39 Remove debugging output. #47 2014-05-10 17:07:17 +04:00
Andrey Smirnov 10bbefeb25 Fix support for flat format repositories in subdirectories with common pool. #47 2014-05-10 16:56:50 +04:00
Andrey Smirnov 35eac72226 Missed file. #46 2014-05-10 13:35:06 +04:00
Andrey Smirnov 5371f94b7a Debian 7.5 has been released, update tests. 2014-05-10 13:34:46 +04:00
Andrey Smirnov 53adf39d89 Bring back automatic HTTP_PROXY setting from environment. #46 2014-05-10 13:27:01 +04:00
Andrey Smirnov bc7972ff68 Version bump for 0.6~dev. 2014-04-24 14:24:03 +04:00
4051 changed files with 2702120 additions and 572907 deletions
+14
View File
@@ -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 -->
+22
View File
@@ -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`
+7 -3
View File
@@ -27,8 +27,12 @@ coverage*.out
*.pyc
_vendor/
xc-out/
root/
gen
man/aptly.1.html
man/aptly.1.ronn
man/aptly.1.ronn
.goxc.local.json
system/env/
+46
View File
@@ -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"
}
+33 -6
View File
@@ -1,20 +1,47 @@
dist: trusty
sudo: required
language: go
go:
- 1.1
- 1.2.1
- tip
- 1.6.x
- 1.7.x
- 1.8.x
- master
go_import_path: github.com/smira/aptly
addons:
apt:
packages:
- python-virtualenv
- graphviz
env:
global:
- secure: "YSwtFrMqh4oUvdSQTXBXMHHLWeQgyNEL23ChIZwU0nuDGIcQZ65kipu0PzefedtUbK4ieC065YCUi4UDDh6gPotB/Wu1pnYg3dyQ7rFvhaVYAAUEpajAdXZhlx+7+J8a4FZMeC/kqiahxoRgLbthF9019ouIqhGB9zHKI6/yZwc="
- secure: "V7OjWrfQ8UbktgT036jYQPb/7GJT3Ol9LObDr8FYlzsQ+F1uj2wLac6ePuxcOS4FwWOJinWGM1h+JiFkbxbyFqfRNJ0jj0O2p93QyDojxFVOn1mXqqvV66KFqAWR2Vzkny/gDvj8LTvdB1cgAIm2FNOkQc6E1BFnyWS2sN9ea5E="
- secure: "OxiVNmre2JzUszwPNNilKDgIqtfX2gnRSsVz6nuySB1uO2yQsOQmKWJ9cVYgH2IB5H8eWXKOhexcSE28kz6TPLRuEcU9fnqKY3uEkdwm7rJfz9lf+7C4bJEUdA1OIzJppjnWUiXxD7CEPL1DlnMZM24eDQYqa/4WKACAgkK53gE="
before_install:
- virtualenv system/env
- . system/env/bin/activate
- pip install six packaging appdirs
- pip install -U pip setuptools
- pip install -r system/requirements.txt
- make version
install:
- make prepare
script: make travis
matrix:
allow_failures:
- go: tip
- go: master
notifications:
webhooks:
urls:
- "https://webhooks.gitter.im/e/c691da114a41eed6ec45"
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
+30
View File
@@ -0,0 +1,30 @@
List of contributors, in chronological order:
* Andrey Smirnov (https://github.com/smira)
* Sebastien Binet (https://github.com/sbinet)
* Ryan Uber (https://github.com/ryanuber)
* Simon Aquino (https://github.com/queeno)
* Vincent Batoufflet (https://github.com/vbatoufflet)
* Ivan Kurnosov (https://github.com/zerkms)
* Dmitrii Kashin (https://github.com/freehck)
* Chris Read (https://github.com/cread)
* Rohan Garg (https://github.com/shadeslayer)
* Russ Allbery (https://github.com/rra)
* Sylvain Baubeau (https://github.com/lebauce)
* Andrea Bernardo Ciddio (https://github.com/bcandrea)
* Michael Koval (https://github.com/mkoval)
* Alexander Guy (https://github.com/alexanderguy)
* Sebastien Badia (https://github.com/sbadia)
* 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)
+74
View File
@@ -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
View File
@@ -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
-22
View File
@@ -1,22 +0,0 @@
gom 'code.google.com/p/go-uuid/uuid', :commit => '5fac954758f5'
gom 'code.google.com/p/go.crypto/ssh/terminal', :commit => '7aa593ce8cea'
gom 'code.google.com/p/gographviz', :commit => '212766062629'
gom 'code.google.com/p/snappy-go/snappy', :commit => '12e4b4183793'
gom 'github.com/cheggaaa/pb', :commit => '74be7a1388046f374ac36e93d46f5d56e856f827'
gom 'github.com/smira/commander'
gom 'github.com/smira/flag'
gom 'github.com/mkrautz/goar', :commit => '36eb5f3452b1283a211fa35bc00c646fd0db5c4b'
gom 'github.com/syndtr/goleveldb/leveldb', :commit => 'ff3719c6816e2cd194f05058452d660608e178ac'
gom 'github.com/ugorji/go/codec', :commit => '71c2886f5a673a35f909803f38ece5810165097b'
gom 'github.com/wsxiaoys/terminal/color', :commit => '5668e431776a7957528361f90ce828266c69ed08'
group :test do
gom 'launchpad.net/gocheck'
end
group :development do
gom 'github.com/golang/lint/golint'
gom 'github.com/mattn/goveralls'
gom 'github.com/axw/gocov/gocov'
gom 'code.google.com/p/go.tools/cmd/cover'
end
Generated
+191
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
Copyright 2013-2014 Andrey Smirnov. All rights reserved.
Copyright 2013-2015 aptly authors. All rights reserved.
MIT License
+44 -34
View File
@@ -1,75 +1,85 @@
GOVERSION=$(shell go version | awk '{print $$3;}')
PACKAGES=database deb files http utils
ALL_PACKAGES=aptly cmd console database deb files http utils
BINPATH=$(abspath ./_vendor/bin)
GOM_ENVIRONMENT=-test
VERSION=$(shell git describe --tags | sed 's@^v@@' | sed 's@-@+@g')
PACKAGES=context database deb files gpg http query swift s3 utils
PYTHON?=python
TESTS?=
BINPATH?=$(GOPATH)/bin
ifeq ($(GOVERSION), devel)
TRAVIS_TARGET=coveralls
GOM_ENVIRONMENT+=-development
else
TRAVIS_TARGET=test
endif
ifeq ($(TRAVIS), true)
GOM=$(HOME)/gopath/bin/gom
else
GOM=gom
endif
all: test check system-test
prepare:
go get -u github.com/mattn/gom
$(GOM) $(GOM_ENVIRONMENT) install
go get -u github.com/mattn/goveralls
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:
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
grep -v -h "mode: count" coverage.*.out >> coverage.out
rm -f coverage.*.out
coverage: coverage.out
$(GOM) exec go tool cover -html=coverage.out
go tool cover -html=coverage.out
rm -f coverage.out
check:
$(GOM) exec go tool vet -all=true -shadow=true $(ALL_PACKAGES:%=./%)
$(GOM) exec golint $(ALL_PACKAGES:%=./%)
check: system/env
if [ -x travis_wait ]; then \
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:
$(GOM) build -o $(BINPATH)/aptly
go install -v -ldflags "-X main.Version=$(VERSION)"
system-test: install
ifeq ($(GOVERSION),$(filter $(GOVERSION),go1.2 go1.2.1 devel))
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
endif
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
PATH=$(BINPATH)/:$(PATH) $(PYTHON) system/run.py --long
. 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:
$(GOM) test -v ./... -gocheck.v=true
go test -v `go list ./... | grep -v vendor/` -gocheck.v=true
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
gnuplot mem.gp
open mem.png
package:
goxc:
rm -rf root/
mkdir -p root/usr/bin/ root/usr/share/man/man1/ root/etc/bash_completion.d
cp $(BINPATH)/aptly root/usr/bin
mkdir -p root/usr/share/man/man1/ root/etc/bash_completion.d
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
fpm -s dir -t deb -n aptly -v $(VERSION) --url=http://www.aptly.info/ --license=MIT --vendor="Andrey Smirnov <me@smira.ru>" \
-f -m "Andrey Smirnov <me@smira.ru>" --description="Debian repository management tool" -C root/ .
mv aptly_$(VERSION)_*.deb ~
goxc -pv=$(VERSION) -max-processors=4 $(GOXC_OPTS)
.PHONY: coverage.out
man:
make -C man
version:
@echo $(VERSION)
.PHONY: coverage.out man version
+63 -17
View File
@@ -2,15 +2,24 @@
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
.. image:: https://coveralls.io/repos/smira/aptly/badge.png?branch=HEAD
:target: https://coveralls.io/r/smira/aptly?branch=HEAD
.. image:: https://coveralls.io/repos/smira/aptly/badge.svg?branch=master
:target: https://coveralls.io/r/smira/aptly?branch=master
.. image:: https://badges.gitter.im/Join Chat.svg
:target: https://gitter.im/smira/aptly?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. image:: http://goreportcard.com/badge/smira/aptly
:target: http://goreportcard.com/report/smira/aptly
Aptly is a swiss army knife for Debian repository management.
Documentation is available at `http://www.aptly.info/ <http://www.aptly.info/>`_. For support use
.. image:: http://www.aptly.info/img/aptly_logo.png
:target: http://www.aptly.info/
Documentation is available at `http://www.aptly.info/ <http://www.aptly.info/>`_. For support please use
mailing list `aptly-discuss <https://groups.google.com/forum/#!forum/aptly-discuss>`_.
Aptly features: ("+" means planned features)
@@ -20,26 +29,26 @@ Aptly features: ("+" means planned features)
* publish snapshot as Debian repository, ready to be consumed by apt
* controlled update of one or more packages in snapshot from upstream mirror, tracking dependencies
* merge two or more snapshots into one
* filter repository by search query, pulling dependencies when required (+)
* publish self-made packages as Debian repositories (+)
* filter repository by search query, pulling dependencies when required
* publish self-made packages as Debian repositories
* REST API for remote access
* mirror repositories "as-is" (without resigning with user's key) (+)
* support for yum repositories (+)
Current limitations:
* debian-installer and translations not supported yet
* translations are not supported yet
Download
--------
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
And import key that is used to sign the release::
$ gpg --keyserver keys.gnupg.net --recv-keys 2A194991
$ gpg -a --export 2A194991 | sudo apt-key add -
$ apt-key adv --keyserver keys.gnupg.net --recv-keys 9E3E53F19C7DE460
After that you can install aptly as any other software package::
@@ -49,20 +58,57 @@ After that you can install aptly as any other software package::
Don't worry about squeeze part in repo name: aptly package should work on Debian squeeze+,
Ubuntu 10.0+. Package contains aptly binary, man page and bash completion.
If you would like to use nightly builds (unstable), please use following repository::
deb http://repo.aptly.info/ nightly main
Binary executables (depends almost only on libc) are available for download from `Bintray <http://dl.bintray.com/smira/aptly/>`_.
If you have Go environment set up, you can build aptly from source by running (go 1.1+ required)::
If you have Go environment set up, you can build aptly from source by running (go 1.6+ required)::
go get -u github.com/mattn/gom
mkdir -p $GOPATH/src/github.com/smira/aptly
git clone https://github.com/smira/aptly $GOPATH/src/github.com/smira/aptly
cd $GOPATH/src/github.com/smira/aptly
gom -production install
gom build -o $GOPATH/bin/aptly
make install
Aptly is using `gom <https://github.com/mattn/gom>`_ to fix external dependencies, so regular ``go get github.com/smira/aptly``
should work as well, but might fail or produce different result (if external libraries got updated).
Binary would be installed to ```$GOPATH/bin/aptly``.
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
+27 -6
View File
@@ -2,16 +2,17 @@ package main
import (
"fmt"
"github.com/smira/aptly/cmd"
"github.com/smira/commander"
"github.com/smira/flag"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"text/template"
"github.com/smira/aptly/cmd"
"github.com/smira/commander"
"github.com/smira/flag"
)
func allFlags(flags *flag.FlagSet) []*flag.Flag {
@@ -43,22 +44,42 @@ func capitalize(s string) string {
return strings.Join(parts, " ")
}
var authorsS string
func authors() string {
return authorsS
}
func main() {
command := cmd.RootCommand()
command.UsageLine = "aptly"
command.Dispatch(nil)
_, _File, _, _ := runtime.Caller(0)
_File, _ = filepath.Abs(_File)
_File, _ := filepath.Abs("./man")
templ := template.New("man").Funcs(template.FuncMap{
"allFlags": allFlags,
"findCommand": findCommand,
"toUpper": strings.ToUpper,
"capitalize": capitalize,
"authors": authors,
})
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"))
if err != nil {
log.Fatal(err)
+165
View File
@@ -0,0 +1,165 @@
// Package api provides implementation of aptly REST API
package api
import (
"fmt"
"sort"
"time"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
)
// Lock order acquisition (canonical):
// 1. RemoteRepoCollection
// 2. LocalRepoCollection
// 3. SnapshotCollection
// 4. PublishedRepoCollection
// GET /api/version
func apiVersion(c *gin.Context) {
c.JSON(200, gin.H{"Version": aptly.Version})
}
type dbRequestKind int
const (
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
r := context.CollectionFactory().RemoteRepoCollection()
r.Lock()
defer r.Unlock()
l := context.CollectionFactory().LocalRepoCollection()
l.Lock()
defer l.Unlock()
s := context.CollectionFactory().SnapshotCollection()
s.Lock()
defer s.Unlock()
p := context.CollectionFactory().PublishedRepoCollection()
p.Lock()
defer p.Unlock()
// all collections locked, flush them
context.CollectionFactory().Flush()
}
// 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
}
}
}
// Common piece of code to show list of packages,
// with searching & details if requested
func showPackages(c *gin.Context, reflist *deb.PackageRefList) {
result := []*deb.Package{}
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(404, err)
return
}
queryS := c.Request.URL.Query().Get("q")
if queryS != "" {
q, err := query.Parse(c.Request.URL.Query().Get("q"))
if err != nil {
c.Fail(400, err)
return
}
withDeps := c.Request.URL.Query().Get("withDeps") == "1"
architecturesList := []string{}
if withDeps {
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = list.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
c.Fail(400, fmt.Errorf("unable to determine list of architectures, please specify explicitly"))
return
}
}
list.PrepareIndex()
list, err = list.Filter([]deb.PackageQuery{q}, withDeps,
nil, context.DependencyOptions(), architecturesList)
if err != nil {
c.Fail(500, fmt.Errorf("unable to search: %s", err))
return
}
}
if c.Request.URL.Query().Get("format") == "details" {
list.ForEach(func(p *deb.Package) error {
result = append(result, p)
return nil
})
c.JSON(200, result)
} else {
c.JSON(200, list.Strings())
}
}
+186
View File
@@ -0,0 +1,186 @@
package api
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
func verifyPath(path string) bool {
path = filepath.Clean(path)
for _, part := range strings.Split(path, string(filepath.Separator)) {
if part == ".." || part == "." {
return false
}
}
return true
}
func verifyDir(c *gin.Context) bool {
if !verifyPath(c.Params.ByName("dir")) {
c.Fail(400, fmt.Errorf("wrong dir"))
return false
}
return true
}
// GET /files
func apiFilesListDirs(c *gin.Context) {
list := []string{}
err := filepath.Walk(context.UploadPath(), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == context.UploadPath() {
return nil
}
if info.IsDir() {
list = append(list, filepath.Base(path))
return filepath.SkipDir
}
return nil
})
if err != nil && !os.IsNotExist(err) {
c.Fail(400, err)
return
}
c.JSON(200, list)
}
// POST /files/:dir/
func apiFilesUpload(c *gin.Context) {
if !verifyDir(c) {
return
}
path := filepath.Join(context.UploadPath(), c.Params.ByName("dir"))
err := os.MkdirAll(path, 0777)
if err != nil {
c.Fail(500, err)
return
}
err = c.Request.ParseMultipartForm(10 * 1024 * 1024)
if err != nil {
c.Fail(400, err)
return
}
stored := []string{}
for _, files := range c.Request.MultipartForm.File {
for _, file := range files {
src, err := file.Open()
if err != nil {
c.Fail(500, err)
return
}
defer src.Close()
destPath := filepath.Join(path, filepath.Base(file.Filename))
dst, err := os.Create(destPath)
if err != nil {
c.Fail(500, err)
return
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
c.Fail(500, err)
return
}
stored = append(stored, filepath.Join(c.Params.ByName("dir"), filepath.Base(file.Filename)))
}
}
c.JSON(200, stored)
}
// GET /files/:dir
func apiFilesListFiles(c *gin.Context) {
if !verifyDir(c) {
return
}
list := []string{}
root := filepath.Join(context.UploadPath(), c.Params.ByName("dir"))
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == root {
return nil
}
list = append(list, filepath.Base(path))
return nil
})
if err != nil {
if os.IsNotExist(err) {
c.Fail(404, err)
} else {
c.Fail(500, err)
}
return
}
c.JSON(200, list)
}
// DELETE /files/:dir
func apiFilesDeleteDir(c *gin.Context) {
if !verifyDir(c) {
return
}
err := os.RemoveAll(filepath.Join(context.UploadPath(), c.Params.ByName("dir")))
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// DELETE /files/:dir/:name
func apiFilesDeleteFile(c *gin.Context) {
if !verifyDir(c) {
return
}
if !verifyPath(c.Params.ByName("name")) {
c.Fail(400, fmt.Errorf("wrong file"))
return
}
err := os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("name")))
if err != nil {
if err1, ok := err.(*os.PathError); !ok || !os.IsNotExist(err1.Err) {
c.Fail(500, err)
return
}
}
c.JSON(200, gin.H{})
}
+84
View File
@@ -0,0 +1,84 @@
package api
import (
"bytes"
"fmt"
"io"
"mime"
"os"
"os/exec"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/deb"
)
// GET /api/graph.:ext?layout=[vertical|horizontal(default)]
func apiGraph(c *gin.Context) {
var (
err error
output []byte
)
ext := c.Params.ByName("ext")
layout := c.Request.URL.Query().Get("layout")
factory := context.CollectionFactory()
factory.RemoteRepoCollection().RLock()
defer factory.RemoteRepoCollection().RUnlock()
factory.LocalRepoCollection().RLock()
defer factory.LocalRepoCollection().RUnlock()
factory.SnapshotCollection().RLock()
defer factory.SnapshotCollection().RUnlock()
factory.PublishedRepoCollection().RLock()
defer factory.PublishedRepoCollection().RUnlock()
graph, err := deb.BuildGraph(factory, layout)
if err != nil {
c.JSON(500, err)
return
}
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.Stderr = os.Stderr
stdin, err := command.StdinPipe()
if err != nil {
c.Fail(500, err)
return
}
_, err = io.Copy(stdin, buf)
if err != nil {
c.Fail(500, err)
return
}
err = stdin.Close()
if err != nil {
c.Fail(500, err)
return
}
output, err = command.Output()
if err != nil {
c.Fail(500, fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err))
return
}
mimeType := mime.TypeByExtension("." + ext)
if mimeType == "" {
mimeType = "application/octet-stream"
}
c.Data(200, mimeType, output)
}
+16
View File
@@ -0,0 +1,16 @@
package api
import (
"github.com/gin-gonic/gin"
)
// GET /api/packages/:key
func apiPackagesShow(c *gin.Context) {
p, err := context.CollectionFactory().PackageCollection().ByKey([]byte(c.Params.ByName("key")))
if err != nil {
c.Fail(404, err)
return
}
c.JSON(200, p)
}
+366
View File
@@ -0,0 +1,366 @@
package api
import (
"fmt"
"strings"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/pgp"
"github.com/smira/aptly/utils"
)
// SigningOptions is a shared between publish API GPG options structure
type SigningOptions struct {
Skip bool
Batch bool
GpgKey string
Keyring string
SecretKeyring string
Passphrase string
PassphraseFile string
}
func getSigner(options *SigningOptions) (pgp.Signer, error) {
if options.Skip {
return nil, nil
}
signer := context.GetSigner()
signer.SetKey(options.GpgKey)
signer.SetKeyRing(options.Keyring, options.SecretKeyring)
signer.SetPassphrase(options.Passphrase, options.PassphraseFile)
signer.SetBatch(options.Batch)
err := signer.Init()
if err != nil {
return nil, err
}
return signer, nil
}
// Replace '_' with '/' and double '__' with single '_'
func parseEscapedPath(path string) string {
result := strings.Replace(strings.Replace(path, "_", "/", -1), "//", "_", -1)
if result == "" {
result = "."
}
return result
}
// GET /publish
func apiPublishList(c *gin.Context) {
localCollection := context.CollectionFactory().LocalRepoCollection()
localCollection.RLock()
defer localCollection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.RLock()
defer collection.RUnlock()
result := make([]*deb.PublishedRepo, 0, collection.Len())
err := collection.ForEach(func(repo *deb.PublishedRepo) error {
err := collection.LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
}
result = append(result, repo)
return nil
})
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, result)
}
// POST /publish/:prefix
func apiPublishRepoOrSnapshot(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
var b struct {
SourceKind string `binding:"required"`
Sources []struct {
Component string
Name string `binding:"required"`
} `binding:"required"`
Distribution string
Label string
Origin string
NotAutomatic string
ButAutomaticUpgrades string
ForceOverwrite bool
SkipContents *bool
Architectures []string
Signing SigningOptions
}
if !c.Bind(&b) {
return
}
signer, err := getSigner(&b.Signing)
if err != nil {
c.Fail(500, fmt.Errorf("unable to initialize GPG signer: %s", err))
return
}
if len(b.Sources) == 0 {
c.Fail(400, fmt.Errorf("unable to publish: soures are empty"))
return
}
var components []string
var sources []interface{}
if b.SourceKind == "snapshot" {
var snapshot *deb.Snapshot
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
for _, source := range b.Sources {
components = append(components, source.Component)
snapshot, err = snapshotCollection.ByName(source.Name)
if err != nil {
c.Fail(404, fmt.Errorf("unable to publish: %s", err))
return
}
err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
sources = append(sources, snapshot)
}
} else if b.SourceKind == deb.SourceLocalRepo {
var localRepo *deb.LocalRepo
localCollection := context.CollectionFactory().LocalRepoCollection()
localCollection.RLock()
defer localCollection.RUnlock()
for _, source := range b.Sources {
components = append(components, source.Component)
localRepo, err = localCollection.ByName(source.Name)
if err != nil {
c.Fail(404, fmt.Errorf("unable to publish: %s", err))
return
}
err = localCollection.LoadComplete(localRepo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
}
sources = append(sources, localRepo)
}
} else {
c.Fail(400, fmt.Errorf("unknown SourceKind"))
return
}
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
published, err := deb.NewPublishedRepo(storage, prefix, b.Distribution, b.Architectures, components, sources, context.CollectionFactory())
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
if 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.SkipContents = context.Config().SkipContentsPublishing
if b.SkipContents != nil {
published.SkipContents = *b.SkipContents
}
duplicate := collection.CheckDuplicate(published)
if duplicate != nil {
context.CollectionFactory().PublishedRepoCollection().LoadComplete(duplicate, context.CollectionFactory())
c.Fail(400, fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate))
return
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, nil, b.ForceOverwrite)
if err != nil {
c.Fail(500, fmt.Errorf("unable to publish: %s", err))
return
}
err = collection.Add(published)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
return
}
c.JSON(201, published)
}
// PUT /publish/:prefix/:distribution
func apiPublishUpdateSwitch(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
var b struct {
ForceOverwrite bool
Signing SigningOptions
SkipContents *bool
Snapshots []struct {
Component string `binding:"required"`
Name string `binding:"required"`
}
}
if !c.Bind(&b) {
return
}
signer, err := getSigner(&b.Signing)
if err != nil {
c.Fail(500, fmt.Errorf("unable to initialize GPG signer: %s", err))
return
}
// published.LoadComplete would touch local repo collection
localRepoCollection := context.CollectionFactory().LocalRepoCollection()
localRepoCollection.RLock()
defer localRepoCollection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
c.Fail(404, fmt.Errorf("unable to update: %s", err))
return
}
err = collection.LoadComplete(published, context.CollectionFactory())
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
return
}
var updatedComponents []string
if published.SourceKind == deb.SourceLocalRepo {
if len(b.Snapshots) > 0 {
c.Fail(400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
return
}
updatedComponents = published.Components()
for _, component := range updatedComponents {
published.UpdateLocalRepo(component)
}
} else if published.SourceKind == "snapshot" {
publishedComponents := published.Components()
for _, snapshotInfo := range b.Snapshots {
if !utils.StrSliceHasItem(publishedComponents, snapshotInfo.Component) {
c.Fail(404, fmt.Errorf("component %s is not in published repository", snapshotInfo.Component))
return
}
snapshot, err2 := snapshotCollection.ByName(snapshotInfo.Name)
if err != nil {
c.Fail(404, err2)
return
}
err2 = snapshotCollection.LoadComplete(snapshot)
if err2 != nil {
c.Fail(500, err2)
return
}
published.UpdateSnapshot(snapshotInfo.Component, snapshot)
updatedComponents = append(updatedComponents, snapshotInfo.Component)
}
} else {
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)
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
return
}
err = collection.Update(published)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save to DB: %s", err))
return
}
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
context.GetPublishedStorage(storage), context.CollectionFactory(), nil)
if err != nil {
c.Fail(500, fmt.Errorf("unable to update: %s", err))
return
}
c.JSON(200, published)
}
// DELETE /publish/:prefix/:distribution
func apiPublishDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
// published.LoadComplete would touch local repo collection
localRepoCollection := context.CollectionFactory().LocalRepoCollection()
localRepoCollection.RLock()
defer localRepoCollection.RUnlock()
collection := context.CollectionFactory().PublishedRepoCollection()
collection.Lock()
defer collection.Unlock()
err := collection.Remove(context, storage, prefix, distribution,
context.CollectionFactory(), context.Progress(), force)
if err != nil {
c.Fail(500, fmt.Errorf("unable to drop: %s", err))
return
}
c.JSON(200, gin.H{})
}
+366
View File
@@ -0,0 +1,366 @@
package api
import (
"fmt"
"os"
"path/filepath"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
)
// GET /api/repos
func apiReposList(c *gin.Context) {
result := []*deb.LocalRepo{}
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
context.CollectionFactory().LocalRepoCollection().ForEach(func(r *deb.LocalRepo) error {
result = append(result, r)
return nil
})
c.JSON(200, result)
}
// POST /api/repos
func apiReposCreate(c *gin.Context) {
var b struct {
Name string `binding:"required"`
Comment string
DefaultDistribution string
DefaultComponent string
}
if !c.Bind(&b) {
return
}
repo := deb.NewLocalRepo(b.Name, b.Comment)
repo.DefaultComponent = b.DefaultComponent
repo.DefaultDistribution = b.DefaultDistribution
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
err := context.CollectionFactory().LocalRepoCollection().Add(repo)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, repo)
}
// PUT /api/repos/:name
func apiReposEdit(c *gin.Context) {
var b struct {
Comment *string
DefaultDistribution *string
DefaultComponent *string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
if b.Comment != nil {
repo.Comment = *b.Comment
}
if b.DefaultDistribution != nil {
repo.DefaultDistribution = *b.DefaultDistribution
}
if b.DefaultComponent != nil {
repo.DefaultComponent = *b.DefaultComponent
}
err = collection.Update(repo)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, repo)
}
// GET /api/repos/:name
func apiReposShow(c *gin.Context) {
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
c.JSON(200, repo)
}
// DELETE /api/repos/:name
func apiReposDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
publishedCollection.RLock()
defer publishedCollection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
published := publishedCollection.ByLocalRepo(repo)
if len(published) > 0 {
c.Fail(409, fmt.Errorf("unable to drop, local repo is published"))
return
}
if !force {
snapshots := snapshotCollection.ByLocalRepoSource(repo)
if len(snapshots) > 0 {
c.Fail(409, fmt.Errorf("unable to drop, local repo has snapshots, use ?force=1 to override"))
return
}
}
err = collection.Drop(repo)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// GET /api/repos/:name/packages
func apiReposPackagesShow(c *gin.Context) {
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
showPackages(c, repo.RefList())
}
// Handler for both add and delete
func apiReposPackagesAddDelete(c *gin.Context, cb func(list *deb.PackageList, p *deb.Package) error) {
var b struct {
PackageRefs []string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, err)
return
}
// verify package refs and build package list
for _, ref := range b.PackageRefs {
var p *deb.Package
p, err = context.CollectionFactory().PackageCollection().ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
c.Fail(404, fmt.Errorf("package %s: %s", ref, err))
} else {
c.Fail(500, err)
}
return
}
err = cb(list, p)
if err != nil {
c.Fail(400, err)
return
}
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save: %s", err))
return
}
c.JSON(200, repo)
}
// POST /repos/:name/packages
func apiReposPackagesAdd(c *gin.Context) {
apiReposPackagesAddDelete(c, func(list *deb.PackageList, p *deb.Package) error {
return list.Add(p)
})
}
// DELETE /repos/:name/packages
func apiReposPackagesDelete(c *gin.Context) {
apiReposPackagesAddDelete(c, func(list *deb.PackageList, p *deb.Package) error {
list.Remove(p)
return nil
})
}
// POST /repos/:name/file/:dir/:file
func apiReposPackageFromFile(c *gin.Context) {
// redirect all work to dir method
apiReposPackageFromDir(c)
}
// POST /repos/:name/file/:dir
func apiReposPackageFromDir(c *gin.Context) {
forceReplace := c.Request.URL.Query().Get("forceReplace") == "1"
noRemove := c.Request.URL.Query().Get("noRemove") == "1"
if !verifyDir(c) {
return
}
fileParam := c.Params.ByName("file")
if fileParam != "" && !verifyPath(fileParam) {
c.Fail(400, fmt.Errorf("wrong file"))
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
repo, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
verifier := context.GetVerifier()
var (
sources []string
packageFiles, failedFiles []string
processedFiles, failedFiles2 []string
reporter = &aptly.RecordingResultReporter{
Warnings: []string{},
AddedLines: []string{},
RemovedLines: []string{},
}
list *deb.PackageList
)
if fileParam == "" {
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"))}
} else {
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))}
}
packageFiles, failedFiles = deb.CollectPackageFiles(sources, reporter)
list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, fmt.Errorf("unable to load packages: %s", err))
return
}
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
context.CollectionFactory().PackageCollection(), reporter, nil, context.CollectionFactory().ChecksumCollection())
failedFiles = append(failedFiles, failedFiles2...)
if err != nil {
c.Fail(500, fmt.Errorf("unable to import package files: %s", err))
return
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
c.Fail(500, fmt.Errorf("unable to save: %s", err))
return
}
if !noRemove {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
err := os.Remove(file)
if err != nil {
reporter.Warning("unable to remove file %s: %s", file, err)
}
}
// atempt to remove dir, if it fails, that's fine: probably it's not empty
os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir")))
}
if failedFiles == nil {
failedFiles = []string{}
}
c.JSON(200, gin.H{
"Report": reporter,
"FailedFiles": failedFiles,
})
}
+116
View File
@@ -0,0 +1,116 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
ctx "github.com/smira/aptly/context"
)
var context *ctx.AptlyContext
// Router returns prebuilt with routes http.Handler
func Router(c *ctx.AptlyContext) http.Handler {
context = c
router := gin.Default()
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.GET("/version", apiVersion)
}
{
root.GET("/repos", apiReposList)
root.POST("/repos", apiReposCreate)
root.GET("/repos/:name", apiReposShow)
root.PUT("/repos/:name", apiReposEdit)
root.DELETE("/repos/:name", apiReposDrop)
root.GET("/repos/:name/packages", apiReposPackagesShow)
root.POST("/repos/:name/packages", apiReposPackagesAdd)
root.DELETE("/repos/:name/packages", apiReposPackagesDelete)
root.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile)
root.POST("/repos/:name/file/:dir", apiReposPackageFromDir)
root.POST("/repos/:name/snapshots", apiSnapshotsCreateFromRepository)
}
{
root.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
}
{
root.GET("/files", apiFilesListDirs)
root.POST("/files/:dir", apiFilesUpload)
root.GET("/files/:dir", apiFilesListFiles)
root.DELETE("/files/:dir", apiFilesDeleteDir)
root.DELETE("/files/:dir/:name", apiFilesDeleteFile)
}
{
root.GET("/publish", apiPublishList)
root.POST("/publish", apiPublishRepoOrSnapshot)
root.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
root.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
root.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
}
{
root.GET("/snapshots", apiSnapshotsList)
root.POST("/snapshots", apiSnapshotsCreate)
root.PUT("/snapshots/:name", apiSnapshotsUpdate)
root.GET("/snapshots/:name", apiSnapshotsShow)
root.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages)
root.DELETE("/snapshots/:name", apiSnapshotsDrop)
root.GET("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff)
}
{
root.GET("/packages/:key", apiPackagesShow)
}
{
root.GET("/graph.:ext", apiGraph)
}
return router
}
+411
View File
@@ -0,0 +1,411 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
)
// GET /api/snapshots
func apiSnapshotsList(c *gin.Context) {
SortMethodString := c.Request.URL.Query().Get("sort")
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
if SortMethodString == "" {
SortMethodString = "name"
}
result := []*deb.Snapshot{}
collection.ForEachSorted(SortMethodString, func(snapshot *deb.Snapshot) error {
result = append(result, snapshot)
return nil
})
c.JSON(200, result)
}
// POST /api/mirrors/:name/snapshots/
func apiSnapshotsCreateFromMirror(c *gin.Context) {
var (
err error
repo *deb.RemoteRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().RemoteRepoCollection()
collection.RLock()
defer collection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = repo.CheckLock()
if err != nil {
c.Fail(409, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/snapshots
func apiSnapshotsCreate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
SourceSnapshots []string
PackageRefs []string
}
if !c.Bind(&b) {
return
}
if b.Description == "" {
if len(b.SourceSnapshots)+len(b.PackageRefs) == 0 {
b.Description = "Created as empty"
}
}
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
sources := make([]*deb.Snapshot, len(b.SourceSnapshots))
for i := range b.SourceSnapshots {
sources[i], err = snapshotCollection.ByName(b.SourceSnapshots[i])
if err != nil {
c.Fail(404, err)
return
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
c.Fail(500, err)
return
}
}
list := deb.NewPackageList()
// verify package refs and build package list
for _, ref := range b.PackageRefs {
var p *deb.Package
p, err = context.CollectionFactory().PackageCollection().ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
c.Fail(404, fmt.Errorf("package %s: %s", ref, err))
} else {
c.Fail(500, err)
}
return
}
err = list.Add(p)
if err != nil {
c.Fail(400, err)
return
}
}
snapshot = deb.NewSnapshotFromRefList(b.Name, sources, deb.NewPackageRefListFromPackageList(list), b.Description)
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/repos/:name/snapshots
func apiSnapshotsCreateFromRepository(c *gin.Context) {
var (
err error
repo *deb.LocalRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.RLock()
defer collection.RUnlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(400, err)
return
}
c.JSON(201, snapshot)
}
// PUT /api/snapshots/:name
func apiSnapshotsUpdate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().SnapshotCollection()
collection.Lock()
defer collection.Unlock()
snapshot, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
_, err = collection.ByName(b.Name)
if err == nil {
c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
return
}
if b.Name != "" {
snapshot.Name = b.Name
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, snapshot)
}
// GET /api/snapshots/:name
func apiSnapshotsShow(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, snapshot)
}
// DELETE /api/snapshots/:name
func apiSnapshotsDrop(c *gin.Context) {
name := c.Params.ByName("name")
force := c.Request.URL.Query().Get("force") == "1"
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
publishedCollection.RLock()
defer publishedCollection.RUnlock()
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
c.Fail(404, err)
return
}
published := publishedCollection.BySnapshot(snapshot)
if len(published) > 0 {
c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
return
}
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
return
}
}
err = snapshotCollection.Drop(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// GET /api/snapshots/:name/diff/:withSnapshot
func apiSnapshotsDiff(c *gin.Context) {
onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshotA, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshotA)
if err != nil {
c.Fail(500, err)
return
}
err = collection.LoadComplete(snapshotB)
if err != nil {
c.Fail(500, err)
return
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
if err != nil {
c.Fail(500, err)
return
}
result := []deb.PackageDiff{}
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
result = append(result, pdiff)
}
c.JSON(200, result)
}
// GET /api/snapshots/:name/packages
func apiSnapshotsSearchPackages(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
showPackages(c, snapshot.RefList())
}
+67 -23
View File
@@ -3,49 +3,90 @@
package aptly
import (
"github.com/smira/aptly/utils"
"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 stores all the package files, deduplicating them.
type PackagePool interface {
// Path returns full path to package file in pool given any name and hash of file contents
Path(filename string, hashMD5 string) (string, error)
// RelativePath returns path relative to pool's root for package files given MD5 and original filename
RelativePath(filename string, hashMD5 string) (string, error)
// Verify checks whether file exists in the pool and fills back checksum info
//
// if poolPath is empty, poolPath is generated automatically based on checksum info (if available)
// 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(progress Progress) ([]string, error)
// Remove deletes file in package pool returns its size
Remove(path string) (size int64, err error)
// Import copies file into package pool
Import(path string, hashMD5 string) error
}
// 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
type PublishedStorage interface {
// PublicPath returns root of public part
PublicPath() string
// MkDir creates directory recursively under public path
MkDir(path string) error
// CreateFile creates file for writing under public path
CreateFile(path string) (*os.File, error)
// PutFile puts file into published storage at specified path
PutFile(path string, sourceFilename string) error
// RemoveDirs removes directory structure under public path
RemoveDirs(path string, progress Progress) error
// Remove removes single file under public path
Remove(path string) error
// LinkFromPool links package file from pool to dist's pool location
LinkFromPool(publishedDirectory string, sourcePool PackagePool, sourcePath string) error
LinkFromPool(publishedDirectory, baseName string, sourcePool PackagePool, sourcePath string, sourceChecksums utils.ChecksumInfo, force bool) error
// Filelist returns list of files under prefix
Filelist(prefix string) ([]string, error)
// ChecksumsForFile proxies requests to utils.ChecksumsForFile, joining public path
ChecksumsForFile(path string) (utils.ChecksumInfo, error)
// RenameFile renames (moves) file
RenameFile(oldName, newName string) error
}
// FileSystemPublishedStorage is published storage on filesystem
type FileSystemPublishedStorage interface {
// PublicPath returns root of public part
PublicPath() string
}
// PublishedStorageProvider is a thing that returns PublishedStorage by name
type PublishedStorageProvider interface {
// GetPublishedStorage returns PublishedStorage by name
GetPublishedStorage(name string) PublishedStorage
}
// Progress is a progress displaying entity, it allows progress bars & simple prints
type Progress interface {
// Writer interface to support progress bar ticking
@@ -68,21 +109,24 @@ type Progress interface {
Printf(msg string, a ...interface{})
// ColoredPrintf does printf in colored way + newline
ColoredPrintf(msg string, a ...interface{})
// PrintfStdErr does printf but in safe manner to stderr
PrintfStdErr(msg string, a ...interface{})
}
// Downloader is parallel HTTP fetcher
type Downloader interface {
// Download starts new download task
Download(url string, destination string, result chan<- error)
Download(url string, destination string) error
// DownloadWithChecksum starts new download task with checksum verification
DownloadWithChecksum(url string, destination string, result chan<- error, expected utils.ChecksumInfo, ignoreMismatch bool)
// Pause pauses task processing
Pause()
// Resume resumes task processing
Resume()
// Shutdown stops downloader after current tasks are finished,
// but doesn't process rest of queue
Shutdown()
DownloadWithChecksum(url string, destination string, expected *utils.ChecksumInfo, ignoreMismatch bool, maxTries int) error
// GetProgress returns Progress object
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
}
+67
View File
@@ -0,0 +1,67 @@
package aptly
import (
"fmt"
)
// ResultReporter is abstraction for result reporting from complex processing functions
type ResultReporter interface {
// Warning is non-fatal error message
Warning(msg string, a ...interface{})
// Removed is signal that something has been removed
Removed(msg string, a ...interface{})
// Added is signal that something has been added
Added(msg string, a ...interface{})
}
// ConsoleResultReporter is implementation of ResultReporter that prints in colors to console
type ConsoleResultReporter struct {
Progress Progress
}
// Check interface
var (
_ ResultReporter = &ConsoleResultReporter{}
)
// Warning is non-fatal error message (yellow)
func (c *ConsoleResultReporter) Warning(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@y[!]@| @!"+msg+"@|", a...)
}
// Removed is signal that something has been removed (red)
func (c *ConsoleResultReporter) Removed(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@r[-]@| "+msg, a...)
}
// Added is signal that something has been added (green)
func (c *ConsoleResultReporter) Added(msg string, a ...interface{}) {
c.Progress.ColoredPrintf("@g[+]@| "+msg, a...)
}
// RecordingResultReporter is implementation of ResultReporter that collects all messages
type RecordingResultReporter struct {
Warnings []string
AddedLines []string `json:"Added"`
RemovedLines []string `json:"Removed"`
}
// Check interface
var (
_ ResultReporter = &RecordingResultReporter{}
)
// Warning is non-fatal error message
func (r *RecordingResultReporter) Warning(msg string, a ...interface{}) {
r.Warnings = append(r.Warnings, fmt.Sprintf(msg, a...))
}
// Removed is signal that something has been removed
func (r *RecordingResultReporter) Removed(msg string, a ...interface{}) {
r.RemovedLines = append(r.RemovedLines, fmt.Sprintf(msg, a...))
}
// Added is signal that something has been added
func (r *RecordingResultReporter) Added(msg string, a ...interface{}) {
r.AddedLines = append(r.AddedLines, fmt.Sprintf(msg, a...))
}
+3 -3
View File
@@ -1,7 +1,7 @@
package aptly
// Version of aptly
const Version = "0.5"
// Version of aptly (filled in at link time)
var Version string
// Enable debugging features?
// EnableDebug triggers some debugging features
const EnableDebug = false
+633
View File
@@ -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
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdAPI() *commander.Command {
return &commander.Command{
UsageLine: "api",
Short: "start API server/issue requests",
Subcommands: []*commander.Command{
makeCmdAPIServe(),
},
}
}
+109
View File
@@ -0,0 +1,109 @@
package cmd
import (
"fmt"
"net"
"net/http"
"net/url"
"os"
"github.com/smira/aptly/api"
"github.com/smira/aptly/systemd/activation"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyAPIServe(cmd *commander.Command, args []string) error {
var (
err error
)
if len(args) != 0 {
cmd.Usage()
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
}
// 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)
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))
if err != nil {
return fmt.Errorf("unable to serve: %s", err)
}
return err
}
func makeCmdAPIServe() *commander.Command {
cmd := &commander.Command{
Run: aptlyAPIServe,
UsageLine: "serve",
Short: "start API HTTP service",
Long: `
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:
$ aptly api serve -listen=:8080
$ aptly api serve -listen=unix:///tmp/aptly.sock
`,
Flag: *flag.NewFlagSet("aptly-serve", flag.ExitOnError),
}
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
}
+63 -13
View File
@@ -2,13 +2,22 @@
package cmd
import (
"bytes"
"fmt"
"os"
"text/template"
"time"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"time"
)
// Various command flags/UI things
const (
Yes = "yes"
No = "no"
)
// ListPackagesRefList shows list of packages in PackageRefList
@@ -19,18 +28,52 @@ func ListPackagesRefList(reflist *deb.PackageRefList) (err error) {
return
}
err = reflist.ForEach(func(key []byte) error {
p, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
return err2
}
fmt.Printf(" %s\n", p)
return nil
})
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
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
// setting
func LookupOption(defaultValue bool, flags *flag.FlagSet, name string) (result bool) {
result = defaultValue
if flags.IsSet(name) {
result = flags.Lookup(name).Value.Get().(bool)
}
return
}
@@ -46,30 +89,37 @@ upgrade individual packages, take snapshots and publish them
back as Debian repositories.
aptly's goal is to establish repeatability and controlled changes
in a package-centric environment. aptly allows to fix a set of packages
in a package-centric environment. aptly allows one to fix a set of packages
in a repository, so that package installation and upgrade becomes
deterministic. At the same time aptly allows to perform controlled,
deterministic. At the same time aptly allows one to perform controlled,
fine-grained changes in repository contents to transition your
package environment to new version.`,
Flag: *flag.NewFlagSet("aptly", flag.ExitOnError),
Subcommands: []*commander.Command{
makeCmdConfig(),
makeCmdDb(),
makeCmdGraph(),
makeCmdMirror(),
makeCmdRepo(),
makeCmdServe(),
makeCmdSnapshot(),
makeCmdTask(),
makeCmdPublish(),
makeCmdVersion(),
makeCmdPackage(),
makeCmdAPI(),
},
}
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-source", false, "when processing dependencies, follow from binary to Source packages")
cmd.Flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
cmd.Flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if depdency is 'a|b'")
cmd.Flag.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("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 {
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdConfig() *commander.Command {
return &commander.Command{
UsageLine: "config",
Short: "manage aptly configuration",
Subcommands: []*commander.Command{
makeCmdConfigShow(),
},
}
}
+39
View File
@@ -0,0 +1,39 @@
package cmd
import (
"encoding/json"
"fmt"
"github.com/smira/commander"
)
func aptlyConfigShow(cmd *commander.Command, args []string) error {
config := context.Config()
prettyJSON, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("unable to dump the config file: %s", err)
}
fmt.Println(string(prettyJSON))
return nil
}
func makeCmdConfigShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyConfigShow,
UsageLine: "show",
Short: "show current aptly's config",
Long: `
Command show displays the current aptly configuration.
Example:
$ aptly config show
`,
}
return cmd
}
+13 -263
View File
@@ -1,281 +1,31 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/console"
"github.com/smira/aptly/database"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/files"
"github.com/smira/aptly/http"
"github.com/smira/aptly/utils"
ctx "github.com/smira/aptly/context"
"github.com/smira/flag"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"time"
)
// AptlyContext is a common context shared by all commands
type AptlyContext struct {
flags *flag.FlagSet
configLoaded bool
progress aptly.Progress
downloader aptly.Downloader
database database.Storage
packagePool aptly.PackagePool
publishedStorage aptly.PublishedStorage
collectionFactory *deb.CollectionFactory
dependencyOptions int
architecturesList []string
// Debug features
fileCPUProfile *os.File
fileMemProfile *os.File
fileMemStats *os.File
}
var context *AptlyContext
// FatalError is type for panicking to abort execution with non-zero
// exit code and print meaningful explanation
type FatalError struct {
ReturnCode int
Message string
}
// Fatal panics and aborts execution with exit code 1
func Fatal(err error) {
panic(&FatalError{ReturnCode: 1, Message: err.Error()})
}
// Config loads and returns current configuration
func (context *AptlyContext) Config() *utils.ConfigStructure {
if !context.configLoaded {
var err error
configLocation := context.flags.Lookup("config").Value.String()
if configLocation != "" {
err = utils.LoadConfig(configLocation, &utils.Config)
if err != nil {
Fatal(err)
}
} else {
configLocations := []string{
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
"/etc/aptly.conf",
}
for _, configLocation := range configLocations {
err = utils.LoadConfig(configLocation, &utils.Config)
if err == nil {
break
}
if !os.IsNotExist(err) {
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
}
}
if err != nil {
fmt.Printf("Config file not found, creating default config at %s\n\n", configLocations[0])
utils.SaveConfig(configLocations[0], &utils.Config)
}
}
context.configLoaded = true
}
return &utils.Config
}
// DependencyOptions calculates options related to dependecy handling
func (context *AptlyContext) DependencyOptions() int {
if context.dependencyOptions == -1 {
context.dependencyOptions = 0
if context.Config().DepFollowSuggests || context.flags.Lookup("dep-follow-suggests").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSuggests
}
if context.Config().DepFollowRecommends || context.flags.Lookup("dep-follow-recommends").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowRecommends
}
if context.Config().DepFollowAllVariants || context.flags.Lookup("dep-follow-all-variants").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if context.Config().DepFollowSource || context.flags.Lookup("dep-follow-source").Value.Get().(bool) {
context.dependencyOptions |= deb.DepFollowSource
}
}
return context.dependencyOptions
}
// ArchitecturesList returns list of architectures fixed via command line or config
func (context *AptlyContext) ArchitecturesList() []string {
if context.architecturesList == nil {
context.architecturesList = context.Config().Architectures
optionArchitectures := context.flags.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
}
return context.architecturesList
}
// Progress creates or returns Progress object
func (context *AptlyContext) Progress() aptly.Progress {
if context.progress == nil {
context.progress = console.NewProgress()
context.progress.Start()
}
return context.progress
}
// Downloader returns instance of current downloader
func (context *AptlyContext) Downloader() aptly.Downloader {
if context.downloader == nil {
context.downloader = http.NewDownloader(context.Config().DownloadConcurrency, context.Progress())
}
return context.downloader
}
// DBPath builds path to database
func (context *AptlyContext) DBPath() string {
return filepath.Join(context.Config().RootDir, "db")
}
// Database opens and returns current instance of database
func (context *AptlyContext) Database() (database.Storage, error) {
if context.database == nil {
var err error
context.database, err = database.OpenDB(context.DBPath())
if err != nil {
return nil, fmt.Errorf("can't open database: %s", err)
}
}
return context.database, nil
}
// CollectionFactory builds factory producing all kinds of collections
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
if context.collectionFactory == nil {
db, err := context.Database()
if err != nil {
Fatal(err)
}
context.collectionFactory = deb.NewCollectionFactory(db)
}
return context.collectionFactory
}
// PackagePool returns instance of PackagePool
func (context *AptlyContext) PackagePool() aptly.PackagePool {
if context.packagePool == nil {
context.packagePool = files.NewPackagePool(context.Config().RootDir)
}
return context.packagePool
}
// PublishedStorage returns instance of PublishedStorage
func (context *AptlyContext) PublishedStorage() aptly.PublishedStorage {
if context.publishedStorage == nil {
context.publishedStorage = files.NewPublishedStorage(context.Config().RootDir)
}
return context.publishedStorage
}
var context *ctx.AptlyContext
// ShutdownContext shuts context down
func ShutdownContext() {
if aptly.EnableDebug {
if context.fileMemProfile != nil {
pprof.WriteHeapProfile(context.fileMemProfile)
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
if context.fileCPUProfile != nil {
pprof.StopCPUProfile()
context.fileCPUProfile.Close()
context.fileCPUProfile = nil
}
if context.fileMemProfile != nil {
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
}
if context.database != nil {
context.database.Close()
}
if context.downloader != nil {
context.downloader.Shutdown()
}
if context.progress != nil {
context.progress.Shutdown()
}
context.Shutdown()
}
// CleanupContext does partial shutdown of context
func CleanupContext() {
context.Cleanup()
}
// InitContext initializes context with default settings
func InitContext(flags *flag.FlagSet) error {
var err error
context = &AptlyContext{flags: flags, dependencyOptions: -1}
if aptly.EnableDebug {
cpuprofile := flags.Lookup("cpuprofile").Value.String()
if cpuprofile != "" {
context.fileCPUProfile, err = os.Create(cpuprofile)
if err != nil {
return err
}
pprof.StartCPUProfile(context.fileCPUProfile)
}
memprofile := flags.Lookup("memprofile").Value.String()
if memprofile != "" {
context.fileMemProfile, err = os.Create(memprofile)
if err != nil {
return err
}
}
memstats := flags.Lookup("memstats").Value.String()
if memstats != "" {
interval := flags.Lookup("meminterval").Value.Get().(time.Duration)
context.fileMemStats, err = os.Create(memstats)
if err != nil {
return err
}
context.fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
go func() {
var stats runtime.MemStats
start := time.Now().UnixNano()
for {
runtime.ReadMemStats(&stats)
if context.fileMemStats != nil {
context.fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval)
} else {
break
}
}
}()
}
if context != nil {
panic("context already initialized")
}
return nil
context, err = ctx.NewContext(flags)
return err
}
+181 -47
View File
@@ -2,10 +2,12 @@ package cmd
import (
"fmt"
"sort"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"sort"
)
// aptly db cleanup
@@ -14,34 +16,101 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
verbose := context.Flags().Lookup("verbose").Value.Get().(bool)
dryRun := context.Flags().Lookup("dry-run").Value.Get().(bool)
// collect information about references packages...
existingPackageRefs := deb.NewPackageRefList()
context.Progress().Printf("Loading mirrors, local repos and snapshots...\n")
// used only in verbose mode to report package use source
packageRefSources := map[string][]string{}
context.Progress().ColoredPrintf("@{w!}Loading mirrors, local repos, snapshots and published repos...@|")
if verbose {
context.Progress().ColoredPrintf("@{y}Loading mirrors:@|")
}
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return err
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
}
e := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if e != nil {
return e
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
if verbose {
description := fmt.Sprintf("mirror %s", repo.Name)
repo.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
return nil
})
if err != nil {
return err
}
if verbose {
context.Progress().ColoredPrintf("@{y}Loading local repos:@|")
}
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", repo.Name)
}
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if e != nil {
return e
}
if repo.RefList() != nil {
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false, true)
if verbose {
description := fmt.Sprintf("local repo %s", repo.Name)
repo.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
return nil
})
if err != nil {
return err
}
if verbose {
context.Progress().ColoredPrintf("@{y}Loading snapshots:@|")
}
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s@|", snapshot.Name)
}
e := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if e != nil {
return e
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false, true)
if verbose {
description := fmt.Sprintf("snapshot %s", snapshot.Name)
snapshot.RefList().ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
return nil
})
@@ -49,12 +118,32 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
return err
}
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return err
if verbose {
context.Progress().ColoredPrintf("@{y}Loading published repositories:@|")
}
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(published *deb.PublishedRepo) error {
if verbose {
context.Progress().ColoredPrintf("- @{g}%s:%s/%s{|}", published.Storage, published.Prefix, published.Distribution)
}
if published.SourceKind != deb.SourceLocalRepo {
return nil
}
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(published, context.CollectionFactory())
if e != nil {
return e
}
for _, component := range published.Components() {
existingPackageRefs = existingPackageRefs.Merge(published.RefList(component), false, true)
if verbose {
description := fmt.Sprintf("published repository %s:%s/%s component %s",
published.Storage, published.Prefix, published.Distribution, component)
published.RefList(component).ForEach(func(key []byte) error {
packageRefSources[string(key)] = append(packageRefSources[string(key)], description)
return nil
})
}
}
existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
return nil
})
if err != nil {
@@ -62,38 +151,65 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
}
// ... and compare it to the list of all packages
context.Progress().Printf("Loading list of all packages...\n")
context.Progress().ColoredPrintf("@{w!}Loading list of all packages...@|")
allPackageRefs := context.CollectionFactory().PackageCollection().AllPackageRefs()
toDelete := allPackageRefs.Substract(existingPackageRefs)
toDelete := allPackageRefs.Subtract(existingPackageRefs)
// delete packages that are no longer referenced
context.Progress().Printf("Deleting unreferenced packages (%d)...\n", toDelete.Len())
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced packages (%d)...@|", toDelete.Len())
// database can't err as collection factory already constructed
db, _ := context.Database()
db.StartBatch()
err = toDelete.ForEach(func(ref []byte) error {
return context.CollectionFactory().PackageCollection().DeleteByKey(ref)
})
if err != nil {
return err
}
err = db.FinishBatch()
if err != nil {
return fmt.Errorf("unable to write to DB: %s", err)
if toDelete.Len() > 0 {
if verbose {
context.Progress().ColoredPrintf("@{r}List of package keys to delete:@|")
err = toDelete.ForEach(func(ref []byte) error {
context.Progress().ColoredPrintf(" - @{r}%s@|", string(ref))
return nil
})
if err != nil {
return err
}
}
if !dryRun {
db.StartBatch()
err = toDelete.ForEach(func(ref []byte) error {
return context.CollectionFactory().PackageCollection().DeleteByKey(ref)
})
if err != nil {
return err
}
err = db.FinishBatch()
if err != nil {
return fmt.Errorf("unable to write to DB: %s", err)
}
} else {
context.Progress().ColoredPrintf("@{y!}Skipped deletion, as -dry-run has been requested.@|")
}
}
// now, build a list of files that should be present in Repository (package pool)
context.Progress().Printf("Building list of files referenced by packages...\n")
context.Progress().ColoredPrintf("@{w!}Building list of files referenced by packages...@|")
referencedFiles := make([]string, 0, existingPackageRefs.Len())
context.Progress().InitBar(int64(existingPackageRefs.Len()), false)
err = existingPackageRefs.ForEach(func(key []byte) error {
pkg, err2 := context.CollectionFactory().PackageCollection().ByKey(key)
if err2 != nil {
return err2
tail := ""
if verbose {
tail = fmt.Sprintf(" (sources: %s)", strings.Join(packageRefSources[string(key)], ", "))
}
if dryRun {
context.Progress().ColoredPrintf("@{r!}Unresolvable package reference, skipping (-dry-run): %s: %s%s",
string(key), err2, tail)
return nil
}
return fmt.Errorf("unable to load package %s: %s%s", string(key), err2, tail)
}
paths, err2 := pkg.FilepathList(context.PackagePool())
if err2 != nil {
@@ -112,7 +228,7 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
context.Progress().ShutdownBar()
// build a list of files in the package pool
context.Progress().Printf("Building list of files in package pool...\n")
context.Progress().ColoredPrintf("@{w!}Building list of files in package pool...@|")
existingFiles, err := context.PackagePool().FilepathList(context.Progress())
if err != nil {
return fmt.Errorf("unable to collect file paths: %s", err)
@@ -122,28 +238,43 @@ func aptlyDbCleanup(cmd *commander.Command, args []string) error {
filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)
// delete files that are no longer referenced
context.Progress().Printf("Deleting unreferenced files (%d)...\n", len(filesToDelete))
context.Progress().ColoredPrintf("@{r!}Deleting unreferenced files (%d)...@|", len(filesToDelete))
if len(filesToDelete) > 0 {
context.Progress().InitBar(int64(len(filesToDelete)), false)
var size, totalSize int64
for _, file := range filesToDelete {
size, err = context.PackagePool().Remove(file)
if err != nil {
return err
if verbose {
context.Progress().ColoredPrintf("@{r}List of files to be deleted:@|")
for _, file := range filesToDelete {
context.Progress().ColoredPrintf(" - @{r}%s@|", file)
}
context.Progress().AddBar(1)
totalSize += size
}
context.Progress().ShutdownBar()
context.Progress().Printf("Disk space freed: %s...\n", utils.HumanBytes(totalSize))
if !dryRun {
context.Progress().InitBar(int64(len(filesToDelete)), false)
var size, totalSize int64
for _, file := range filesToDelete {
size, err = context.PackagePool().Remove(file)
if err != nil {
return err
}
context.Progress().AddBar(1)
totalSize += size
}
context.Progress().ShutdownBar()
context.Progress().ColoredPrintf("@{w!}Disk space freed: %s...@|", utils.HumanBytes(totalSize))
} else {
context.Progress().ColoredPrintf("@{y!}Skipped file deletion, as -dry-run has been requested.@|")
}
}
context.Progress().Printf("Compacting database...\n")
err = db.CompactDB()
if !dryRun {
context.Progress().ColoredPrintf("@{w!}Compacting database...@|")
err = db.CompactDB()
} else {
context.Progress().ColoredPrintf("@{y!}Skipped DB compaction, as -dry-run has been requested.@|")
}
return err
}
@@ -163,5 +294,8 @@ Example:
`,
}
cmd.Flag.Bool("verbose", false, "be verbose when loading objects/removing them")
cmd.Flag.Bool("dry-run", false, "don't delete anything")
return cmd
}
+1 -1
View File
@@ -11,7 +11,7 @@ func aptlyDbRecover(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
context.Progress().Printf("Recovering database...\n")
+69 -121
View File
@@ -2,136 +2,37 @@ package cmd
import (
"bytes"
"code.google.com/p/gographviz"
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
"time"
func graphvizEscape(s string) string {
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", 0))
}
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
)
func aptlyGraph(cmd *commander.Command, args []string) error {
var err error
graph := gographviz.NewGraph()
graph.SetDir(true)
graph.SetName("aptly")
existingNodes := map[string]bool{}
fmt.Printf("Loading mirrors...\n")
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
err := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages())),
})
existingNodes[repo.UUID] = true
return nil
})
if err != nil {
return err
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("Loading local repos...\n")
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
}
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "mediumseagreen",
"label": graphvizEscape(fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
repo.Name, repo.Comment, repo.NumPackages())),
})
existingNodes[repo.UUID] = true
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading snapshots...\n")
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
existingNodes[snapshot.UUID] = true
return nil
})
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
err := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return err
}
description := snapshot.Description
if snapshot.SourceKind == "repo" {
description = "Snapshot from repo"
}
graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
})
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
for _, uuid := range snapshot.SourceIDs {
_, exists := existingNodes[uuid]
if exists {
graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
}
}
}
return nil
})
if err != nil {
return err
}
fmt.Printf("Loading published repos...\n")
context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
})
_, exists := existingNodes[repo.SourceUUID]
if exists {
graph.AddEdge(graphvizEscape(repo.SourceUUID), "", graphvizEscape(repo.UUID), "", true, nil)
}
return nil
})
layout := context.Flags().Lookup("layout").Value.String()
fmt.Printf("Generating graph...\n")
graph, err := deb.BuildGraph(context.CollectionFactory(), layout)
if err != nil {
return err
}
buf := bytes.NewBufferString(graph.String())
@@ -142,9 +43,16 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
tempfile.Close()
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
stdin, err := command.StdinPipe()
@@ -172,15 +80,51 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
return err
}
err = exec.Command("open", tempfilename).Run()
if err != nil {
fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
err = nil
defer func() {
_ = os.Remove(tempfilename)
}()
if output != "" {
err = utils.CopyFile(tempfilename, output)
if err != nil {
return fmt.Errorf("unable to copy %s -> %s: %s", tempfilename, output, err)
}
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
}
// 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 {
cmd := &commander.Command{
Run: aptlyGraph,
@@ -197,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
}
+9 -5
View File
@@ -1,20 +1,21 @@
package cmd
import (
"github.com/smira/aptly/utils"
"strings"
"github.com/smira/aptly/pgp"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func getVerifier(flags *flag.FlagSet) (utils.Verifier, error) {
if context.Config().GpgDisableVerify || flags.Lookup("ignore-signatures").Value.Get().(bool) {
func getVerifier(flags *flag.FlagSet) (pgp.Verifier, error) {
if LookupOption(context.Config().GpgDisableVerify, flags, "ignore-signatures") {
return nil, nil
}
keyRings := flags.Lookup("keyring").Value.Get().([]string)
verifier := &utils.GpgVerifier{}
verifier := context.GetVerifier()
for _, keyRing := range keyRings {
verifier.AddKeyring(keyRing)
}
@@ -54,6 +55,9 @@ func makeCmdMirror() *commander.Command {
makeCmdMirrorShow(),
makeCmdMirrorDrop(),
makeCmdMirrorUpdate(),
makeCmdMirrorRename(),
makeCmdMirrorEdit(),
makeCmdMirrorSearch(),
},
}
}
+28 -6
View File
@@ -2,20 +2,23 @@ package cmd
import (
"fmt"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
var err error
if !(len(args) == 2 && strings.HasPrefix(args[1], "ppa:") || len(args) >= 3) {
cmd.Usage()
return err
return commander.ErrCommandError
}
downloadSources := context.Config().DownloadSourcePackages || context.flags.Lookup("with-sources").Value.Get().(bool)
downloadSources := LookupOption(context.Config().DownloadSourcePackages, context.Flags(), "with-sources")
downloadUdebs := context.Flags().Lookup("with-udebs").Value.Get().(bool)
var (
mirrorName, archiveURL, distribution string
@@ -32,12 +35,25 @@ func aptlyMirrorCreate(cmd *commander.Command, args []string) error {
archiveURL, distribution, components = args[1], args[2], args[3:]
}
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(), downloadSources)
repo, err := deb.NewRemoteRepo(mirrorName, archiveURL, distribution, components, context.ArchitecturesList(),
downloadSources, downloadUdebs)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
verifier, err := getVerifier(context.flags)
repo.Filter = context.Flags().Lookup("filter").Value.String()
repo.FilterWithDeps = context.Flags().Lookup("filter-with-deps").Value.Get().(bool)
repo.SkipComponentCheck = context.Flags().Lookup("force-components").Value.Get().(bool)
repo.SkipArchitectureCheck = context.Flags().Lookup("force-architectures").Value.Get().(bool)
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to create mirror: %s", err)
}
}
verifier, err := getVerifier(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
@@ -63,7 +79,8 @@ func makeCmdMirrorCreate() *commander.Command {
Short: "create new mirror",
Long: `
Creates mirror <name> of remote repository, aptly supports both regular and flat Debian repositories exported
via HTTP. aptly would try download Release file from remote repository and verify its' signature.
via HTTP and FTP. aptly would try download Release file from remote repository and verify its' signature. Command
line format resembles apt utlitily sources.list(5).
PPA urls could specified in short format:
@@ -78,6 +95,11 @@ Example:
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
cmd.Flag.Bool("force-components", false, "(only with component list) skip check that requested components are listed in Release file")
cmd.Flag.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)")
return cmd
+8 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +11,7 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -20,7 +21,12 @@ func aptlyMirrorDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: %s", err)
}
force := context.flags.Lookup("force").Value.Get().(bool)
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to drop: %s", err)
}
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByRemoteRepoSource(repo)
+92
View File
@@ -0,0 +1,92 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyMirrorEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
context.Flags().Visit(func(flag *flag.Flag) {
switch flag.Name {
case "filter":
repo.Filter = flag.Value.String()
case "filter-with-deps":
repo.FilterWithDeps = flag.Value.Get().(bool)
case "with-sources":
repo.DownloadSources = flag.Value.Get().(bool)
case "with-udebs":
repo.DownloadUdebs = flag.Value.Get().(bool)
}
})
if repo.IsFlat() && repo.DownloadUdebs {
return fmt.Errorf("unable to edit: flat mirrors don't support udebs")
}
if repo.Filter != "" {
_, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
if context.GlobalFlags().Lookup("architectures").Value.String() != "" {
repo.Architectures = context.ArchitecturesList()
err = repo.Fetch(context.Downloader(), nil)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
}
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to edit: %s", err)
}
fmt.Printf("Mirror %s successfully updated.\n", repo)
return err
}
func makeCmdMirrorEdit() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorEdit,
UsageLine: "edit <name>",
Short: "edit mirror settings",
Long: `
Command edit allows one to change settings of mirror:
filters, list of architectures.
Example:
$ aptly mirror edit -filter=nginx -filter-with-deps some-mirror
`,
Flag: *flag.NewFlagSet("aptly-mirror-edit", flag.ExitOnError),
}
cmd.Flag.String("filter", "", "filter packages in mirror")
cmd.Flag.Bool("filter-with-deps", false, "when filtering, include dependencies of matching packages as well")
cmd.Flag.Bool("with-sources", false, "download source packages in addition to binary packages")
cmd.Flag.Bool("with-udebs", false, "download .udeb packages (Debian installer support)")
return cmd
}
+5 -2
View File
@@ -2,16 +2,17 @@ package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyMirrorList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -28,6 +29,8 @@ func aptlyMirrorList(cmd *commander.Command, args []string) error {
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
+65
View File
@@ -0,0 +1,65 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyMirrorRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.RemoteRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().RemoteRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: mirror %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nMirror %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdMirrorRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyMirrorRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames mirror",
Long: `
Command changes name of the mirror.Mirror name should be unique.
Example:
$ aptly mirror rename wheezy-min wheezy-main
`,
}
return cmd
}
+29
View File
@@ -0,0 +1,29 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdMirrorSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> [<package-query>]",
Short: "search mirror for packages matching query",
Long: `
Command search displays list of packages in mirror that match package query
If query is not specified, all the packages are displayed.
Example:
$ aptly mirror search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-mirror-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
cmd.Flag.String("format", "", "custom format for result printing")
return cmd
}
+23 -5
View File
@@ -2,17 +2,19 @@ package cmd
import (
"fmt"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyMirrorShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -28,15 +30,31 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
}
fmt.Printf("Name: %s\n", repo.Name)
if repo.Status == deb.MirrorUpdating {
fmt.Printf("Status: In Update (PID %d)\n", repo.WorkerPID)
}
fmt.Printf("Archive Root URL: %s\n", repo.ArchiveRoot)
fmt.Printf("Distribution: %s\n", repo.Distribution)
fmt.Printf("Components: %s\n", strings.Join(repo.Components, ", "))
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, ", "))
downloadSources := "no"
downloadSources := No
if repo.DownloadSources {
downloadSources = "yes"
downloadSources = Yes
}
fmt.Printf("Download Sources: %s\n", downloadSources)
downloadUdebs := No
if repo.DownloadUdebs {
downloadUdebs = Yes
}
fmt.Printf("Download .udebs: %s\n", downloadUdebs)
if repo.Filter != "" {
fmt.Printf("Filter: %s\n", repo.Filter)
filterWithDeps := No
if repo.FilterWithDeps {
filterWithDeps = Yes
}
fmt.Printf("Filter With Deps: %s\n", filterWithDeps)
}
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Last update: never\n")
} else {
@@ -49,7 +67,7 @@ func aptlyMirrorShow(cmd *commander.Command, args []string) error {
fmt.Printf("%s: %s\n", k, repo.Meta[k])
}
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
if repo.LastDownloadDate.IsZero() {
fmt.Printf("Unable to show package list, mirror hasn't been downloaded yet.\n")
+214 -4
View File
@@ -2,6 +2,15 @@ package cmd
import (
"fmt"
"os"
"os/signal"
"strings"
"sync"
"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"
)
@@ -10,7 +19,7 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -25,9 +34,18 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
ignoreMismatch := context.flags.Lookup("ignore-checksums").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
}
verifier, err := getVerifier(context.flags)
ignoreMismatch := context.Flags().Lookup("ignore-checksums").Value.Get().(bool)
maxTries := context.Flags().Lookup("max-tries").Value.Get().(int)
verifier, err := getVerifier(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG verifier: %s", err)
}
@@ -37,11 +55,199 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
err = repo.Download(context.Progress(), context.Downloader(), context.CollectionFactory(), context.PackagePool(), ignoreMismatch)
context.Progress().Printf("Downloading & parsing package files...\n")
err = repo.DownloadPackageIndexes(context.Progress(), context.Downloader(), context.CollectionFactory(), ignoreMismatch, maxTries)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if repo.Filter != "" {
context.Progress().Printf("Applying filter...\n")
var filterQuery deb.PackageQuery
filterQuery, err = query.Parse(repo.Filter)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
var oldLen, newLen int
oldLen, newLen, err = repo.ApplyFilter(context.DependencyOptions(), filterQuery, context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
context.Progress().Printf("Packages filtered: %d -> %d.\n", oldLen, newLen)
}
var (
downloadSize int64
queue []deb.PackageDownloadTask
)
skipExistingPackages := context.Flags().Lookup("skip-existing-packages").Value.Get().(bool)
context.Progress().Printf("Building download queue...\n")
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool(), context.CollectionFactory().PackageCollection(),
context.CollectionFactory().ChecksumCollection(), skipExistingPackages)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
defer func() {
// on any interruption, unlock the mirror
err = context.ReOpenDatabase()
if err == nil {
repo.MarkAsIdle()
context.CollectionFactory().RemoteRepoCollection().Update(repo)
}
}()
repo.MarkAsUpdating()
err = context.CollectionFactory().RemoteRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
err = context.CloseDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
// Catch ^C
sigch := make(chan os.Signal)
signal.Notify(sigch, os.Interrupt)
defer signal.Stop(sigch)
abort := make(chan struct{})
go func() {
<-sigch
signal.Stop(sigch)
close(abort)
}()
count := len(queue)
context.Progress().Printf("Download queue: %d items (%s)\n", count, utils.HumanBytes(downloadSize))
// Download from the queue
context.Progress().InitBar(downloadSize, true)
downloadQueue := make(chan int)
var (
errors []string
errLock sync.Mutex
)
pushError := func(err error) {
errLock.Lock()
errors = append(errors, err.Error())
errLock.Unlock()
}
go func() {
for idx := range queue {
select {
case downloadQueue <- idx:
case <-abort:
return
}
}
close(downloadQueue)
}()
var wg sync.WaitGroup
for i := 0; i < context.Config().DownloadConcurrency; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case idx, ok := <-downloadQueue:
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")
default:
}
context.Progress().ShutdownBar()
if len(errors) > 0 {
return fmt.Errorf("unable to update: download errors:\n %s", strings.Join(errors, "\n "))
}
err = context.ReOpenDatabase()
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
// 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)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
@@ -68,8 +274,12 @@ Example:
Flag: *flag.NewFlagSet("aptly-mirror-update", flag.ExitOnError),
}
cmd.Flag.Bool("force", false, "force update mirror even if it is locked by another process")
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.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.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)")
return cmd
+16
View File
@@ -0,0 +1,16 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdPackage() *commander.Command {
return &commander.Command{
UsageLine: "package",
Short: "operations on packages",
Subcommands: []*commander.Command{
makeCmdPackageSearch(),
makeCmdPackageShow(),
},
}
}
+63
View File
@@ -0,0 +1,63 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyPackageSearch(cmd *commander.Command, args []string) error {
var (
err error
q deb.PackageQuery
)
if len(args) > 1 {
cmd.Usage()
return commander.ErrCommandError
}
if len(args) == 1 {
q, err = query.Parse(args[0])
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
} else {
q = &deb.MatchAllQuery{}
}
result := q.Query(context.CollectionFactory().PackageCollection())
if result.Len() == 0 {
return fmt.Errorf("no results")
}
format := context.Flags().Lookup("format").Value.String()
PrintPackageList(result, format, "")
return err
}
func makeCmdPackageSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPackageSearch,
UsageLine: "search [<package-query>]",
Short: "search for packages matching query",
Long: `
Command search displays list of packages in whole DB that match package query.
If query is not specified, all the packages are displayed.
Example:
$ aptly package search '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-package-search", flag.ExitOnError),
}
cmd.Flag.String("format", "", "custom format for result printing")
return cmd
}
+143
View File
@@ -0,0 +1,143 @@
package cmd
import (
"bufio"
"fmt"
"os"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func printReferencesTo(p *deb.Package) (err error) {
err = context.CollectionFactory().RemoteRepoCollection().ForEach(func(repo *deb.RemoteRepo) error {
e := context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if e != nil {
return e
}
if repo.RefList() != nil {
if repo.RefList().Has(p) {
fmt.Printf(" mirror %s\n", repo)
}
}
return nil
})
if err != nil {
return err
}
err = context.CollectionFactory().LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if e != nil {
return e
}
if repo.RefList() != nil {
if repo.RefList().Has(p) {
fmt.Printf(" local repo %s\n", repo)
}
}
return nil
})
if err != nil {
return err
}
err = context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
e := context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if e != nil {
return e
}
if snapshot.RefList().Has(p) {
fmt.Printf(" snapshot %s\n", snapshot)
}
return nil
})
return err
}
func aptlyPackageShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return commander.ErrCommandError
}
q, err := query.Parse(args[0])
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
withFiles := context.Flags().Lookup("with-files").Value.Get().(bool)
withReferences := context.Flags().Lookup("with-references").Value.Get().(bool)
w := bufio.NewWriter(os.Stdout)
result := q.Query(context.CollectionFactory().PackageCollection())
err = result.ForEach(func(p *deb.Package) error {
p.Stanza().WriteTo(w, p.IsSource, false)
w.Flush()
fmt.Printf("\n")
if withFiles {
fmt.Printf("Files in the pool:\n")
packagePool := context.PackagePool()
for _, f := range p.Files() {
var path string
path, err = f.GetPoolPath(packagePool)
if err != nil {
return err
}
if pp, ok := packagePool.(aptly.LocalPackagePool); ok {
path = pp.FullPath(path)
}
fmt.Printf(" %s\n", path)
}
fmt.Printf("\n")
}
if withReferences {
fmt.Printf("References to package:\n")
printReferencesTo(p)
fmt.Printf("\n")
}
return nil
})
if err != nil {
return fmt.Errorf("unable to show: %s", err)
}
return err
}
func makeCmdPackageShow() *commander.Command {
cmd := &commander.Command{
Run: aptlyPackageShow,
UsageLine: "show <package-query>",
Short: "show details about packages matching query",
Long: `
Command shows displays detailed meta-information about packages
matching query. Information from Debian control file is displayed.
Optionally information about package files and
inclusion into mirrors/snapshots/local repos is shown.
Example:
$ aptly package show 'nginx-light_1.2.1-2.2+wheezy2_i386'
`,
Flag: *flag.NewFlagSet("aptly-package-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-files", false, "display information about files from package pool")
cmd.Flag.Bool("with-references", false, "display information about mirrors, snapshots and local repos referencing this package")
return cmd
}
+7 -4
View File
@@ -1,19 +1,21 @@
package cmd
import (
"github.com/smira/aptly/utils"
"github.com/smira/aptly/pgp"
"github.com/smira/commander"
"github.com/smira/flag"
)
func getSigner(flags *flag.FlagSet) (utils.Signer, error) {
if flags.Lookup("skip-signing").Value.Get().(bool) || context.Config().GpgDisableSign {
func getSigner(flags *flag.FlagSet) (pgp.Signer, error) {
if LookupOption(context.Config().GpgDisableSign, flags, "skip-signing") {
return nil, nil
}
signer := &utils.GpgSigner{}
signer := context.GetSigner()
signer.SetKey(flags.Lookup("gpg-key").Value.String())
signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").Value.String())
signer.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").Value.String())
signer.SetBatch(flags.Lookup("batch").Value.Get().(bool))
err := signer.Init()
if err != nil {
@@ -35,6 +37,7 @@ func makeCmdPublish() *commander.Command {
makeCmdPublishSnapshot(),
makeCmdPublishSwitch(),
makeCmdPublishUpdate(),
makeCmdPublishShow(),
},
}
}
+14 -8
View File
@@ -2,6 +2,8 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
@@ -9,18 +11,20 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
if len(args) == 2 {
prefix = args[1]
param = args[1]
}
err = context.CollectionFactory().PublishedRepoCollection().Remove(context.PublishedStorage(), prefix, distribution,
context.CollectionFactory(), context.Progress())
storage, prefix := deb.ParsePrefix(param)
err = context.CollectionFactory().PublishedRepoCollection().Remove(context, storage, prefix, distribution,
context.CollectionFactory(), context.Progress(), context.Flags().Lookup("force-drop").Value.Get().(bool))
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
@@ -33,11 +37,11 @@ func aptlyPublishDrop(cmd *commander.Command, args []string) error {
func makeCmdPublishDrop() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishDrop,
UsageLine: "drop <distribution> [<prefix>]",
UsageLine: "drop <distribution> [[<endpoint>:]<prefix>]",
Short: "remove published repository",
Long: `
Command removes whatever has been published under specified <prefix> and
<distribution> name.
Command removes whatever has been published under specified <prefix>,
publishing <endpoint> and <distribution> name.
Example:
@@ -45,5 +49,7 @@ Example:
`,
}
cmd.Flag.Bool("force-drop", false, "remove published repository even if some files could not be cleaned up")
return cmd
}
+9 -6
View File
@@ -2,16 +2,17 @@ package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyPublishList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -19,13 +20,13 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
published := make([]string, 0, context.CollectionFactory().PublishedRepoCollection().Len())
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if e != nil {
return e
}
if raw {
published = append(published, fmt.Sprintf("%s %s", repo.Prefix, repo.Distribution))
published = append(published, fmt.Sprintf("%s %s", repo.StoragePrefix(), repo.Distribution))
} else {
published = append(published, repo.String())
}
@@ -36,6 +37,8 @@ func aptlyPublishList(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to load list of repos: %s", err)
}
context.CloseDatabase()
sort.Strings(published)
if raw {
+15 -2
View File
@@ -8,13 +8,19 @@ import (
func makeCmdPublishRepo() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "repo <name> [<prefix>]",
UsageLine: "repo <name> [[<endpoint>:]<prefix>]",
Short: "publish local repository",
Long: `
Command publishes current state of local repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple local
repositories as the arguments:
aptly publish repo -component=main,contrib repo-main repo-contrib
It is not recommended to publish local repositories directly unless the
repository is for testing purposes and changes happen frequently. For
production usage please take snapshot of repository and publish it
@@ -27,13 +33,20 @@ Example:
Flag: *flag.NewFlagSet("aptly-publish-repo", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("batch", false, "run GPG with detached tty")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.Bool("skip-contents", false, "don't generate Contents indexes")
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.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+81
View File
@@ -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
}
+136 -46
View File
@@ -2,75 +2,140 @@ package cmd
import (
"fmt"
"strings"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"strings"
)
func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components) || len(args) > len(components)+1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
var prefix string
if len(args) == 2 {
prefix = args[1]
var param string
if len(args) == len(components)+1 {
param = args[len(components)]
args = args[0 : len(args)-1]
} else {
prefix = ""
param = ""
}
storage, prefix := deb.ParsePrefix(param)
var (
source interface{}
sources = []interface{}{}
message string
)
if cmd.Name() == "snapshot" {
var snapshot *deb.Snapshot
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if cmd.Name() == "snapshot" { // nolint: goconst
var (
snapshot *deb.Snapshot
emptyWarning = false
parts = []string{}
)
for _, name := range args {
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, snapshot)
parts = append(parts, snapshot.Name)
if snapshot.NumPackages() == 0 {
emptyWarning = true
}
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if len(parts) == 1 {
message = fmt.Sprintf("Snapshot %s has", parts[0])
} else {
message = fmt.Sprintf("Snapshots %s have", strings.Join(parts, ", "))
}
source = snapshot
message = fmt.Sprintf("Snapshot %s", snapshot.Name)
} else if cmd.Name() == "repo" {
var localRepo *deb.LocalRepo
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else if cmd.Name() == "repo" { // nolint: goconst
var (
localRepo *deb.LocalRepo
emptyWarning = false
parts = []string{}
)
for _, name := range args {
localRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
sources = append(sources, localRepo)
parts = append(parts, localRepo.Name)
if localRepo.NumPackages() == 0 {
emptyWarning = true
}
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
if len(parts) == 1 {
message = fmt.Sprintf("Local repo %s has", parts[0])
} else {
message = fmt.Sprintf("Local repos %s have", strings.Join(parts, ", "))
}
source = localRepo
message = fmt.Sprintf("Local repo %s", localRepo.Name)
if emptyWarning {
context.Progress().Printf("Warning: publishing from empty source, architectures list should be complete, it can't be changed after publishing (use -architectures flag)\n")
}
} else {
panic("unknown command")
}
component := context.flags.Lookup("component").Value.String()
distribution := context.flags.Lookup("distribution").Value.String()
distribution := context.Flags().Lookup("distribution").Value.String()
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(prefix, distribution, component, context.ArchitecturesList(), source, context.CollectionFactory())
published, err := deb.NewPublishedRepo(storage, prefix, distribution, context.ArchitecturesList(), components, sources, context.CollectionFactory())
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
published.Origin = cmd.Flag.Lookup("origin").Value.String()
published.Label = cmd.Flag.Lookup("label").Value.String()
if origin != "" {
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)
if duplicate != nil {
@@ -78,12 +143,18 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
}
signer, err := getSigner(context.flags)
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
err = published.Publish(context.PackagePool(), context, context.CollectionFactory(), signer, context.Progress(), forceOverwrite)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
@@ -93,19 +164,25 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
prefix, component, distribution = published.Prefix, published.Component, published.Distribution
var repoComponents string
prefix, repoComponents, distribution = published.Prefix, strings.Join(published.Components(), " "), published.Distribution
if prefix == "." {
prefix = ""
} else if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
context.Progress().Printf("\n%s has been successfully published.\nPlease setup your webserver to serve directory '%s' with autoindexing.\n",
message, context.PublishedStorage().PublicPath())
context.Progress().Printf("\n%s been successfully published.\n", message)
if localStorage, ok := context.GetPublishedStorage(storage).(aptly.FileSystemPublishedStorage); ok {
context.Progress().Printf("Please setup your webserver to serve directory '%s' with autoindexing.\n",
localStorage.PublicPath())
}
context.Progress().Printf("Now you can add following line to apt sources:\n")
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, component)
if utils.StrSliceHasItem(published.Architectures, "source") {
context.Progress().Printf(" deb-src http://your-server/%s %s %s\n", prefix, distribution, component)
context.Progress().Printf(" deb http://your-server/%s %s %s\n", prefix, distribution, repoComponents)
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("Don't forget to add your GPG key to apt with apt-key.\n")
context.Progress().Printf("\nYou can also use `aptly serve` to publish your repositories over HTTP quickly.\n")
@@ -116,13 +193,19 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
func makeCmdPublishSnapshot() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSnapshotOrRepo,
UsageLine: "snapshot <name> [<prefix>]",
UsageLine: "snapshot <name> [[<endpoint>:]<prefix>]",
Short: "publish snapshot",
Long: `
Command publishes snapshot as Debian repository ready to be consumed
by apt tools. Published repostiories appear under rootDir/public directory.
Valid GPG key is required for publishing.
Multiple component repository could be published by specifying several
components split by commas via -component flag and multiple snapshots
as the arguments:
aptly publish snapshot -component=main,contrib snap-main snap-contrib
Example:
$ aptly publish snapshot wheezy-main
@@ -130,13 +213,20 @@ Example:
Flag: *flag.NewFlagSet("aptly-publish-snapshot", flag.ExitOnError),
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("batch", false, "run GPG with detached tty")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.String("origin", "", "origin name to publish")
cmd.Flag.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.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+77 -28
View File
@@ -2,51 +2,49 @@ package cmd
import (
"fmt"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 || len(args) > 3 {
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
if len(args) < len(components)+1 || len(args) > len(components)+2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
var (
name string
names []string
snapshot *deb.Snapshot
)
if len(args) == 3 {
prefix = args[1]
name = args[2]
if len(args) == len(components)+2 {
param = args[1]
names = args[2:]
} else {
name = args[1]
names = args[1:]
}
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "snapshot" {
if published.SourceKind != deb.SourceSnapshot {
return fmt.Errorf("unable to update: not a snapshot publish")
}
@@ -55,14 +53,49 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
published.UpdateSnapshot(snapshot)
publishedComponents := published.Components()
if len(components) == 1 && len(publishedComponents) == 1 && components[0] == "" {
components = publishedComponents
}
signer, err := getSigner(context.flags)
if len(names) != len(components) {
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
}
for i, component := range components {
if !utils.StrSliceHasItem(publishedComponents, component) {
return fmt.Errorf("unable to switch: component %s is not in published repository", component)
}
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
published.UpdateSnapshot(component, snapshot)
}
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
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)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
@@ -72,8 +105,8 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component,
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
@@ -86,22 +119,38 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
func makeCmdPublishSwitch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSwitch,
UsageLine: "switch <distribution> [<prefix>] <new-snapshot>",
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
Short: "update published repository by switching to new snapshot",
Long: `
Command switches in-place published repository with new snapshot contents. All
publishing parameters are preserved (architecture list, distribution, component).
Command switches in-place published snapshots with new snapshot contents. All
publishing parameters are preserved (architecture list, distribution,
component).
For multiple component repositories, flag -component should be given with
list of components to update. Corresponding snapshots should be given in the
same order, e.g.:
aptly publish switch -component=main,contrib wheezy wh-main wh-contrib
Example:
$ aptly publish update wheezy ppa wheezy-7.5
$ aptly publish switch wheezy ppa wheezy-7.5
This command would switch published repository (with one component) named ppa/wheezy
(prefix ppa, dsitribution wheezy to new snapshot wheezy-7.5).
`,
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("batch", false, "run GPG with detached tty")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.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.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+34 -11
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
@@ -11,24 +12,25 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
distribution := args[0]
prefix := "."
param := "."
if len(args) == 2 {
prefix = args[1]
param = args[1]
}
storage, prefix := deb.ParsePrefix(param)
var published *deb.PublishedRepo
published, err = context.CollectionFactory().PublishedRepoCollection().ByPrefixDistribution(prefix, distribution)
published, err = context.CollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
if published.SourceKind != "local" {
if published.SourceKind != deb.SourceLocalRepo {
return fmt.Errorf("unable to update: not a local repository publish")
}
@@ -37,14 +39,27 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to update: %s", err)
}
published.UpdateLocalRepo()
components := published.Components()
for _, component := range components {
published.UpdateLocalRepo(component)
}
signer, err := getSigner(context.flags)
signer, err := getSigner(context.Flags())
if err != nil {
return fmt.Errorf("unable to initialize GPG signer: %s", err)
}
err = published.Publish(context.PackagePool(), context.PublishedStorage(), context.CollectionFactory(), signer, context.Progress())
forceOverwrite := context.Flags().Lookup("force-overwrite").Value.Get().(bool)
if forceOverwrite {
context.Progress().ColoredPrintf("@rWARNING@|: force overwrite mode enabled, aptly might corrupt other published repositories sharing " +
"the same package pool.\n")
}
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)
if err != nil {
return fmt.Errorf("unable to publish: %s", err)
}
@@ -54,8 +69,8 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save to DB: %s", err)
}
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, published.Component,
context.PublishedStorage(), context.CollectionFactory(), context.Progress())
err = context.CollectionFactory().PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
context.GetPublishedStorage(storage), context.CollectionFactory(), context.Progress())
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
@@ -68,7 +83,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
func makeCmdPublishUpdate() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishUpdate,
UsageLine: "update <distribution> [<prefix>]",
UsageLine: "update <distribution> [[<endpoint>:]<prefix>]",
Short: "update published local repository",
Long: `
Command re-publishes (updates) published local repository. <distribution>
@@ -76,6 +91,9 @@ and <prefix> should be occupied with local repository published
using command aptly publish repo. Update happens in-place with
minimum possible downtime for published repository.
For multiple component published repositories, all local repositories
are updated.
Example:
$ aptly publish update wheezy ppa
@@ -85,7 +103,12 @@ Example:
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passhprase for the key (warning: could be insecure)")
cmd.Flag.String("passphrase-file", "", "GPG passhprase-file for the key (warning: could be insecure)")
cmd.Flag.Bool("batch", false, "run GPG with detached tty")
cmd.Flag.Bool("skip-signing", false, "don't sign Release files with GPG")
cmd.Flag.Bool("skip-contents", false, "don't generate Contents indexes")
cmd.Flag.Bool("force-overwrite", false, "overwrite files in package pool in case of mismatch")
return cmd
}
+3
View File
@@ -19,6 +19,9 @@ func makeCmdRepo() *commander.Command {
makeCmdRepoMove(),
makeCmdRepoRemove(),
makeCmdRepoShow(),
makeCmdRepoRename(),
makeCmdRepoSearch(),
makeCmdRepoInclude(),
},
}
}
+29 -122
View File
@@ -2,26 +2,25 @@ package cmd
import (
"fmt"
"os"
"github.com/smira/aptly/aptly"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"os"
"path/filepath"
"sort"
"strings"
)
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
verifier := &utils.GpgVerifier{}
verifier := context.GetVerifier()
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
@@ -40,122 +39,20 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to load packages: %s", err)
}
packageFiles := []string{}
forceReplace := context.Flags().Lookup("force-replace").Value.Get().(bool)
for _, location := range args[1:] {
info, err2 := os.Stat(location)
if err2 != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err2)
continue
}
if info.IsDir() {
err2 = filepath.Walk(location, func(path string, info os.FileInfo, err3 error) error {
if err3 != nil {
return err3
}
if info.IsDir() {
return nil
}
var packageFiles, failedFiles []string
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, path)
}
packageFiles, failedFiles = deb.CollectPackageFiles(args[1:], &aptly.ConsoleResultReporter{Progress: context.Progress()})
return nil
})
} else {
if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
packageFiles = append(packageFiles, location)
} else {
context.Progress().ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
continue
}
}
}
var processedFiles, failedFiles2 []string
processedFiles := []string{}
sort.Strings(packageFiles)
for _, file := range packageFiles {
var (
stanza deb.Stanza
p *deb.Package
)
candidateProcessedFiles := []string{}
isSourcePackage := strings.HasSuffix(file, ".dsc")
if isSourcePackage {
stanza, err = deb.GetControlFileFromDsc(file, verifier)
if err == nil {
stanza["Package"] = stanza["Source"]
delete(stanza, "Source")
p, err = deb.NewSourcePackageFromControlFile(stanza)
}
} else {
stanza, err = deb.GetControlFileFromDeb(file)
p = deb.NewPackageFromControlFile(stanza)
}
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
continue
}
var checksums utils.ChecksumInfo
checksums, err = utils.ChecksumsForFile(file)
if err != nil {
return err
}
if isSourcePackage {
p.UpdateFiles(append(p.Files(), deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}))
} else {
p.UpdateFiles([]deb.PackageFile{deb.PackageFile{Filename: filepath.Base(file), Checksums: checksums}})
}
err = context.PackagePool().Import(file, checksums.MD5)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err)
continue
}
candidateProcessedFiles = append(candidateProcessedFiles, file)
// go over all files, except for the last one (.dsc/.deb itself)
for _, f := range p.Files() {
if filepath.Base(f.Filename) == filepath.Base(file) {
continue
}
sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(f.Filename))
err = context.PackagePool().Import(sourceFile, f.Checksums.MD5)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err)
break
}
candidateProcessedFiles = append(candidateProcessedFiles, sourceFile)
}
if err != nil {
// some files haven't been imported
continue
}
err = context.CollectionFactory().PackageCollection().Update(p)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err)
continue
}
err = list.Add(p)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
continue
}
context.Progress().ColoredPrintf("@g[+]@| %s added@|", p)
processedFiles = append(processedFiles, candidateProcessedFiles...)
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
context.CollectionFactory().PackageCollection(), &aptly.ConsoleResultReporter{Progress: context.Progress()}, nil,
context.CollectionFactory().ChecksumCollection())
failedFiles = append(failedFiles, failedFiles2...)
if err != nil {
return fmt.Errorf("unable to import package files: %s", err)
}
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
@@ -165,17 +62,26 @@ func aptlyRepoAdd(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save: %s", err)
}
if context.flags.Lookup("remove-files").Value.Get().(bool) {
if context.Flags().Lookup("remove-files").Value.Get().(bool) {
processedFiles = utils.StrSliceDeduplicate(processedFiles)
for _, file := range processedFiles {
err := os.Remove(file)
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
}
@@ -185,8 +91,8 @@ func makeCmdRepoAdd() *commander.Command {
UsageLine: "add <name> <package file.deb>|<directory> ...",
Short: "add packages to local repository",
Long: `
Command adds packages to local repository from .deb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.deb or *.dsc
Command adds packages to local repository from .deb, .udeb (binary packages) and .dsc (source packages) files.
When importing from directory aptly would do recursive scan looking for all files matching *.[u]deb or *.dsc
patterns. Every file discovered would be analyzed to extract metadata, package would then be created and added
to the database. Files would be imported to internal package pool. For source packages, all required files are
added automatically as well. Extra files for source package should be in the same directory as *.dsc file.
@@ -199,6 +105,7 @@ Example:
}
cmd.Flag.Bool("remove-files", false, "remove files that have been imported successfully into repository")
cmd.Flag.Bool("force-replace", false, "when adding package that conflicts with existing package, remove existing package")
return cmd
}
+2 -2
View File
@@ -8,10 +8,10 @@ import (
func makeCmdRepoCopy() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "copy <src-name> <dst-name> <package-spec> ...",
UsageLine: "copy <src-name> <dst-name> <package-query> ...",
Short: "copy packages between local repositories",
Long: `
Command copy copies packages matching <package-spec> from local repo
Command copy copies packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
+37 -6
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
@@ -9,14 +10,38 @@ import (
func aptlyRepoCreate(cmd *commander.Command, args []string) 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()
return err
return commander.ErrCommandError
}
repo := deb.NewLocalRepo(args[0], context.flags.Lookup("comment").Value.String())
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
repo := deb.NewLocalRepo(args[0], context.Flags().Lookup("comment").Value.String())
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
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)
if err != nil {
@@ -30,16 +55,21 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
func makeCmdRepoCreate() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoCreate,
UsageLine: "create <name>",
UsageLine: "create <name> [ from snapshot <snapshot> ]",
Short: "create local repository",
Long: `
Create local package repository. Repository would be empty when
created, packages could be added from files, copied or moved from
another local repository or imported from the mirror.
If local package repository is created from snapshot, repo initial
contents are copied from snapsot contents.
Example:
$ aptly repo create testing
$ aptly repo create mysql35 from snapshot mysql-35-2017
`,
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("distribution", "", "default distribution 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
}
+3 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +11,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -34,7 +35,7 @@ func aptlyRepoDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: local repo is published")
}
force := context.flags.Lookup("force").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().ByLocalRepoSource(repo)
+28 -10
View File
@@ -2,6 +2,9 @@ package cmd
import (
"fmt"
"github.com/AlekSi/pointer"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +13,7 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
repo, err := context.CollectionFactory().LocalRepoCollection().ByName(args[0])
@@ -23,16 +26,30 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to edit: %s", err)
}
if context.flags.Lookup("comment").Value.String() != "" {
repo.Comment = context.flags.Lookup("comment").Value.String()
}
var uploadersFile *string
if context.flags.Lookup("distribution").Value.String() != "" {
repo.DefaultDistribution = context.flags.Lookup("distribution").Value.String()
}
context.Flags().Visit(func(flag *flag.Flag) {
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() != "" {
repo.DefaultComponent = context.flags.Lookup("component").Value.String()
if uploadersFile != nil {
if *uploadersFile != "" {
repo.Uploaders, err = deb.NewUploadersFromFile(*uploadersFile)
if err != nil {
return err
}
} else {
repo.Uploaders = nil
}
}
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
@@ -50,7 +67,7 @@ func makeCmdRepoEdit() *commander.Command {
UsageLine: "edit <name>",
Short: "edit properties of local repository",
Long: `
Command edit allows to change metadata of local repository:
Command edit allows one to change metadata of local repository:
comment, default distribution and component.
Example:
@@ -63,6 +80,7 @@ Example:
cmd.Flag.String("comment", "", "any text that would be used to described local repository")
cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "", "default component when publishing")
cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository")
return cmd
}
+2 -2
View File
@@ -8,10 +8,10 @@ import (
func makeCmdRepoImport() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "import <src-mirror> <dst-repo> <package-spec> ...",
UsageLine: "import <src-mirror> <dst-repo> <package-query> ...",
Short: "import packages from mirror to local repository",
Long: `
Command import looks up packages matching <package-spec> in mirror <src-mirror>
Command import looks up packages matching <package-query> in mirror <src-mirror>
and copies them to local repo <dst-repo>.
Example:
+237
View File
@@ -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
}
+8 -5
View File
@@ -2,16 +2,17 @@ package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlyRepoList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
@@ -22,9 +23,9 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
if raw {
repos[i] = repo.Name
} else {
err := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return err
e := context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if e != nil {
return e
}
repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
@@ -33,6 +34,8 @@ func aptlyRepoList(cmd *commander.Command, args []string) error {
return nil
})
context.CloseDatabase()
sort.Strings(repos)
if raw {
+24 -14
View File
@@ -2,17 +2,19 @@ package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
)
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return err
return commander.ErrCommandError
}
command := cmd.Name()
@@ -32,7 +34,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
srcRepo *deb.LocalRepo
)
if command == "copy" || command == "move" {
if command == "copy" || command == "move" { // nolint: goconst
srcRepo, err = context.CollectionFactory().LocalRepoCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
@@ -48,7 +50,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
}
srcRefList = srcRepo.RefList()
} else if command == "import" {
} else if command == "import" { // nolint: goconst
var srcRemoteRepo *deb.RemoteRepo
srcRemoteRepo, err = context.CollectionFactory().RemoteRepoCollection().ByName(args[0])
@@ -86,7 +88,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
var architecturesList []string
withDeps := context.flags.Lookup("with-deps").Value.Get().(bool)
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
if withDeps {
dstList.PrepareIndex()
@@ -105,18 +107,26 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
}
}
toProcess, err := srcList.Filter(args[2:], withDeps, dstList, context.DependencyOptions(), architecturesList)
queries := make([]deb.PackageQuery, len(args)-2)
for i := 0; i < len(args)-2; i++ {
queries[i], err = query.Parse(args[i+2])
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
}
toProcess, err := srcList.FilterWithProgress(queries, withDeps, dstList, context.DependencyOptions(), architecturesList, context.Progress())
if err != nil {
return fmt.Errorf("unable to %s: %s", command, err)
}
var verb string
if command == "move" {
if command == "move" { // nolint: goconst
verb = "moved"
} else if command == "copy" {
} else if command == "copy" { // nolint: goconst
verb = "copied"
} else if command == "import" {
} else if command == "import" { // nolint: goconst
verb = "imported"
}
@@ -126,7 +136,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
return err
}
if command == "move" {
if command == "move" { // nolint: goconst
srcList.Remove(p)
}
context.Progress().ColoredPrintf("@g[o]@| %s %s", p, verb)
@@ -136,7 +146,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to %s: %s", command, err)
}
if context.flags.Lookup("dry-run").Value.Get().(bool) {
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
dstRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(dstList))
@@ -146,7 +156,7 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to save: %s", err)
}
if command == "move" {
if command == "move" { // nolint: goconst
srcRepo.UpdateRefList(deb.NewPackageRefListFromPackageList(srcList))
err = context.CollectionFactory().LocalRepoCollection().Update(srcRepo)
@@ -162,10 +172,10 @@ func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
func makeCmdRepoMove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoMoveCopyImport,
UsageLine: "move <src-name> <dst-name> <package-spec> ...",
UsageLine: "move <src-name> <dst-name> <package-query> ...",
Short: "move packages between local repositories",
Long: `
Command move moves packages matching <package-spec> from local repo
Command move moves packages matching <package-query> from local repo
<src-name> to local repo <dst-name>.
Example:
+15 -5
View File
@@ -2,7 +2,9 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -11,7 +13,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -33,8 +35,16 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to load packages: %s", err)
}
queries := make([]deb.PackageQuery, len(args)-1)
for i := 0; i < len(args)-1; i++ {
queries[i], err = query.Parse(args[i+1])
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
}
list.PrepareIndex()
toRemove, err := list.Filter(args[1:], false, nil, 0, nil)
toRemove, err := list.Filter(queries, false, nil, 0, nil)
if err != nil {
return fmt.Errorf("unable to remove: %s", err)
}
@@ -45,7 +55,7 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
return nil
})
if context.flags.Lookup("dry-run").Value.Get().(bool) {
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nChanges not saved, as dry run has been requested.\n")
} else {
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
@@ -62,10 +72,10 @@ func aptlyRepoRemove(cmd *commander.Command, args []string) error {
func makeCmdRepoRemove() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRemove,
UsageLine: "remove <name> <package-spec> ...",
UsageLine: "remove <name> <package-query> ...",
Short: "remove packages from local repository",
Long: `
Commands removes packages matching <package-spec> from local repository
Commands removes packages matching <package-query> from local repository
<name>. If removed packages are not referenced by other repos or
snapshots, they can be removed completely (including files) by running
'aptly db cleanup'.
+60
View File
@@ -0,0 +1,60 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlyRepoRename(cmd *commander.Command, args []string) error {
var (
err error
repo *deb.LocalRepo
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().LocalRepoCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: local repo %s already exists", newName)
}
repo.Name = newName
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nLocal repo %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdRepoRename() *commander.Command {
cmd := &commander.Command{
Run: aptlyRepoRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames local repository",
Long: `
Command changes name of the local repo. Local repo name should be unique.
Example:
$ aptly repo rename wheezy-min wheezy-main
`,
}
return cmd
}
+29
View File
@@ -0,0 +1,29 @@
package cmd
import (
"github.com/smira/commander"
"github.com/smira/flag"
)
func makeCmdRepoSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> [<package-query>]",
Short: "search repo for packages matching query",
Long: `
Command search displays list of packages in local repository that match package query
If query is not specified, all the packages are displayed.
Example:
$ aptly repo search my-software '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-repo-show", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
cmd.Flag.String("format", "", "custom format for result printing")
return cmd
}
+6 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +11,7 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -29,9 +30,12 @@ func aptlyRepoShow(cmd *commander.Command, args []string) error {
fmt.Printf("Comment: %s\n", repo.Comment)
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
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())
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(repo.RefList())
}
+47
View File
@@ -0,0 +1,47 @@
package cmd
import (
"fmt"
"os"
ctx "github.com/smira/aptly/context"
"github.com/smira/commander"
)
// Run runs single command starting from root cmd with args, optionally initializing context
func Run(cmd *commander.Command, cmdArgs []string, initContext bool) (returnCode int) {
defer func() {
if r := recover(); r != nil {
fatal, ok := r.(*ctx.FatalError)
if !ok {
panic(r)
}
fmt.Fprintln(os.Stderr, "ERROR:", fatal.Message)
returnCode = fatal.ReturnCode
}
}()
returnCode = 0
flags, args, err := cmd.ParseFlags(cmdArgs)
if err != nil {
ctx.Fatal(err)
}
if initContext {
err = InitContext(flags)
if err != nil {
ctx.Fatal(err)
}
defer ShutdownContext()
}
context.UpdateFlags(flags)
err = cmd.Dispatch(args)
if err != nil {
ctx.Fatal(err)
}
return
}
+31 -12
View File
@@ -2,25 +2,44 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/utils"
"github.com/smira/commander"
"github.com/smira/flag"
"net"
"net/http"
"os"
"sort"
"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 {
var err error
if len(args) != 0 {
cmd.Usage()
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 {
fmt.Printf("No published repositories, unable to serve.\n")
return nil
}
listen := context.flags.Lookup("listen").Value.String()
listen := context.Flags().Lookup("listen").Value.String()
listenHost, listenPort, err := net.SplitHostPort(listen)
@@ -41,9 +60,9 @@ func aptlyServe(cmd *commander.Command, args []string) error {
published := make(map[string]*deb.PublishedRepo, context.CollectionFactory().PublishedRepoCollection().Len())
err = context.CollectionFactory().PublishedRepoCollection().ForEach(func(repo *deb.PublishedRepo) error {
err := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if err != nil {
return err
e := context.CollectionFactory().PublishedRepoCollection().LoadComplete(repo, context.CollectionFactory())
if e != nil {
return e
}
sources = append(sources, repo.String())
@@ -69,15 +88,15 @@ func aptlyServe(cmd *commander.Command, args []string) error {
}
fmt.Printf("# %s\ndeb http://%s:%s/%s %s %s\n",
repo, listenHost, listenPort, prefix, repo.Distribution, repo.Component)
repo, listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
if utils.StrSliceHasItem(repo.Architectures, "source") {
if utils.StrSliceHasItem(repo.Architectures, deb.ArchitectureSource) {
fmt.Printf("deb-src http://%s:%s/%s %s %s\n",
listenHost, listenPort, prefix, repo.Distribution, repo.Component)
listenHost, listenPort, prefix, repo.Distribution, strings.Join(repo.Components(), " "))
}
}
publicPath := context.PublishedStorage().PublicPath()
publicPath := context.GetPublishedStorage("").(aptly.FileSystemPublishedStorage).PublicPath()
ShutdownContext()
fmt.Printf("\nStarting web server at: %s (press Ctrl+C to quit)...\n", listen)
+3
View File
@@ -17,6 +17,9 @@ func makeCmdSnapshot() *commander.Command {
makeCmdSnapshotDiff(),
makeCmdSnapshotMerge(),
makeCmdSnapshotDrop(),
makeCmdSnapshotRename(),
makeCmdSnapshotSearch(),
makeCmdSnapshotFilter(),
},
}
}
+9 -3
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
@@ -12,7 +13,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
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
var repo *deb.RemoteRepo
@@ -23,6 +24,11 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = repo.CheckLock()
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
@@ -32,7 +38,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
if err != nil {
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
var repo *deb.LocalRepo
@@ -61,7 +67,7 @@ func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
snapshot = deb.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
} else {
cmd.Usage()
return err
return commander.ErrCommandError
}
err = context.CollectionFactory().SnapshotCollection().Add(snapshot)
+3 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,10 +11,10 @@ func aptlySnapshotDiff(cmd *commander.Command, args []string) error {
var err error
if len(args) != 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
onlyMatching := context.flags.Lookup("only-matching").Value.Get().(bool)
onlyMatching := context.Flags().Lookup("only-matching").Value.Get().(bool)
// Load <name-a> snapshot
snapshotA, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
+3 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +11,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -35,7 +36,7 @@ func aptlySnapshotDrop(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to drop: snapshot is published")
}
force := context.flags.Lookup("force").Value.Get().(bool)
force := context.Flags().Lookup("force").Value.Get().(bool)
if !force {
snapshots := context.CollectionFactory().SnapshotCollection().BySnapshotSource(snapshot)
if len(snapshots) > 0 {
+108
View File
@@ -0,0 +1,108 @@
package cmd
import (
"fmt"
"sort"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotFilter(cmd *commander.Command, args []string) error {
var err error
if len(args) < 3 {
cmd.Usage()
return commander.ErrCommandError
}
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
// Load <source> snapshot
source, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(source)
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Convert snapshot to package list
context.Progress().Printf("Loading packages (%d)...\n", source.RefList().Len())
packageList, err := deb.NewPackageListFromRefList(source.RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to load packages: %s", err)
}
context.Progress().Printf("Building indexes...\n")
packageList.PrepareIndex()
// Calculate architectures
var architecturesList []string
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = packageList.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 && withDeps {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-2)
for i, arg := range args[2:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
}
// Filter with dependencies as requested
result, err := packageList.FilterWithProgress(queries, withDeps, nil, context.DependencyOptions(), architecturesList, context.Progress())
if err != nil {
return fmt.Errorf("unable to filter: %s", err)
}
// Create <destination> snapshot
destination := deb.NewSnapshotFromPackageList(args[1], []*deb.Snapshot{source}, result,
fmt.Sprintf("Filtered '%s', query was: '%s'", source.Name, strings.Join(args[2:], " ")))
err = context.CollectionFactory().SnapshotCollection().Add(destination)
if err != nil {
return fmt.Errorf("unable to create snapshot: %s", err)
}
context.Progress().Printf("\nSnapshot %s successfully filtered.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", destination.Name, destination.Name)
return err
}
func makeCmdSnapshotFilter() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotFilter,
UsageLine: "filter <source> <destination> <package-query> ...",
Short: "filter packages in snapshot producing another snapshot",
Long: `
Command filter does filtering in snapshot <source>, producing another
snapshot <destination>. Packages could be specified simply
as 'package-name' or as package queries.
Example:
$ aptly snapshot filter wheezy-main wheezy-required 'Priorioty (required)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-filter", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependent packages as well")
return cmd
}
+18 -23
View File
@@ -2,45 +2,39 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error
if len(args) != 0 {
cmd.Usage()
return err
return commander.ErrCommandError
}
raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
snapshots := make([]string, context.CollectionFactory().SnapshotCollection().Len())
i := 0
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
if raw {
snapshots[i] = snapshot.Name
} else {
snapshots[i] = snapshot.String()
}
i++
return nil
})
sort.Strings(snapshots)
collection := context.CollectionFactory().SnapshotCollection()
if raw {
for _, snapshot := range snapshots {
fmt.Printf("%s\n", snapshot)
}
collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
fmt.Printf("%s\n", snapshot.Name)
return nil
})
} else {
if len(snapshots) > 0 {
if collection.Len() > 0 {
fmt.Printf("List of snapshots:\n")
for _, snapshot := range snapshots {
fmt.Printf(" * %s\n", snapshot)
err = collection.ForEachSorted(sortMethodString, func(snapshot *deb.Snapshot) error {
fmt.Printf(" * %s\n", snapshot.String())
return nil
})
if err != nil {
return err
}
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
@@ -48,8 +42,8 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
}
}
return err
return err
}
func makeCmdSnapshotList() *commander.Command {
@@ -67,6 +61,7 @@ Example:
}
cmd.Flag.Bool("raw", false, "display list in machine-readable format")
cmd.Flag.String("sort", "name", "display list in 'name' or creation 'time' order")
return cmd
}
+14 -7
View File
@@ -2,16 +2,17 @@ package cmd
import (
"fmt"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"strings"
)
func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
var err error
if len(args) < 2 {
cmd.Usage()
return err
return commander.ErrCommandError
}
sources := make([]*deb.Snapshot, len(args)-1)
@@ -28,17 +29,22 @@ func aptlySnapshotMerge(cmd *commander.Command, args []string) error {
}
}
latest := context.flags.Lookup("latest").Value.Get().(bool)
overrideMatching := !latest
latest := context.Flags().Lookup("latest").Value.Get().(bool)
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
if noRemove && latest {
return fmt.Errorf("-no-remove and -latest can't be specified together")
}
overrideMatching := !latest && !noRemove
result := sources[0].RefList()
for i := 1; i < len(sources); i++ {
result = result.Merge(sources[i].RefList(), overrideMatching)
result = result.Merge(sources[i].RefList(), overrideMatching, false)
}
if latest {
deb.FilterLatestRefs(result)
result.FilterLatestRefs()
}
sourceDescription := make([]string, len(sources))
@@ -79,6 +85,7 @@ Example:
}
cmd.Flag.Bool("latest", false, "use only the latest version of each package")
cmd.Flag.Bool("no-remove", false, "don't remove duplicate arch/name packages")
return cmd
}
+57 -73
View File
@@ -2,22 +2,25 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
"sort"
"strings"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotPull(cmd *commander.Command, args []string) error {
var err error
if len(args) < 4 {
cmd.Usage()
return err
return commander.ErrCommandError
}
noDeps := context.flags.Lookup("no-deps").Value.Get().(bool)
noRemove := context.flags.Lookup("no-remove").Value.Get().(bool)
noDeps := context.Flags().Lookup("no-deps").Value.Get().(bool)
noRemove := context.Flags().Lookup("no-remove").Value.Get().(bool)
allMatches := context.Flags().Lookup("all-matches").Value.Get().(bool)
// Load <name> snapshot
snapshot, err := context.CollectionFactory().SnapshotCollection().ByName(args[0])
@@ -75,79 +78,59 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
// Initial dependencies out of arguments
initialDependencies := make([]deb.Dependency, len(args)-3)
for i, arg := range args[3:] {
initialDependencies[i], err = deb.ParseDependency(arg)
if err != nil {
return fmt.Errorf("unable to parse argument: %s", err)
}
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
var archQuery deb.PackageQuery = &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: ""}
for _, arch := range architecturesList {
archQuery = &deb.OrQuery{L: &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: arch}, R: archQuery}
}
// Perform pull
for _, arch := range architecturesList {
dependencies := make([]deb.Dependency, len(initialDependencies), 128)
for i := range dependencies {
dependencies[i] = initialDependencies[i]
dependencies[i].Architecture = arch
// Initial queries out of arguments
queries := make([]deb.PackageQuery, len(args)-3)
for i, arg := range args[3:] {
queries[i], err = query.Parse(arg)
if err != nil {
return fmt.Errorf("unable to parse query: %s", err)
}
// Add architecture filter
queries[i] = &deb.AndQuery{L: queries[i], R: archQuery}
}
// Filter with dependencies as requested
result, err := sourcePackageList.FilterWithProgress(queries, !noDeps, packageList, context.DependencyOptions(), architecturesList, context.Progress())
if err != nil {
return fmt.Errorf("unable to pull: %s", err)
}
result.PrepareIndex()
alreadySeen := map[string]bool{}
result.ForEachIndexed(func(pkg *deb.Package) error {
key := pkg.Architecture + "_" + pkg.Name
_, seen := alreadySeen[key]
// If we haven't seen such name-architecture pair and were instructed to remove, remove it
if !noRemove && !seen {
// Remove all packages with the same name and architecture
pS := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}, true)
for _, p := range pS {
packageList.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
}
}
// Go over list of initial dependencies + list of dependencies found
for i := 0; i < len(dependencies); i++ {
dep := dependencies[i]
// Search for package that can satisfy dependencies
pkg := sourcePackageList.Search(dep)
if pkg == nil {
context.Progress().ColoredPrintf("@y[!]@| @!Dependency %s can't be satisfied with source %s@|", &dep, source)
continue
}
if !noRemove {
// Remove all packages with the same name and architecture
for p := packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name}); p != nil; {
packageList.Remove(p)
context.Progress().ColoredPrintf("@r[-]@| %s removed", p)
p = packageList.Search(deb.Dependency{Architecture: pkg.Architecture, Pkg: pkg.Name})
}
}
// Add new discovered package
// If !allMatches, add only first matching name-arch package
if !seen || allMatches {
packageList.Add(pkg)
context.Progress().ColoredPrintf("@g[+]@| %s added", pkg)
if noDeps {
continue
}
// Find missing dependencies for single added package
pL := deb.NewPackageList()
pL.Add(pkg)
var missing []deb.Dependency
missing, err = pL.VerifyDependencies(context.DependencyOptions(), []string{arch}, packageList, nil)
if err != nil {
context.Progress().ColoredPrintf("@y[!]@| @!Error while verifying dependencies for pkg %s: %s@|", pkg, err)
}
// Append missing dependencies to the list of dependencies to satisfy
for _, misDep := range missing {
found := false
for _, d := range dependencies {
if d == misDep {
found = true
break
}
}
if !found {
dependencies = append(dependencies, misDep)
}
}
}
}
if context.flags.Lookup("dry-run").Value.Get().(bool) {
alreadySeen[key] = true
return nil
})
alreadySeen = nil
if context.Flags().Lookup("dry-run").Value.Get().(bool) {
context.Progress().Printf("\nNot creating snapshot, as dry run was requested.\n")
} else {
// Create <destination> snapshot
@@ -167,14 +150,14 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
func makeCmdSnapshotPull() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotPull,
UsageLine: "pull <name> <source> <destination> <package-name> ...",
UsageLine: "pull <name> <source> <destination> <package-query> ...",
Short: "pull packages from another snapshot",
Long: `
Command pull pulls new packages along with its' dependencies to snapshot <name>
from snapshot <source>. Pull can upgrade package version in <name> with
versions from <source> following dependencies. New snapshot <destination>
is created as a result of this process. Packages could be specified simply
as 'package-name' or as dependency 'package-name (>= version)'.
as 'package-name' or as package queries.
Example:
@@ -186,6 +169,7 @@ Example:
cmd.Flag.Bool("dry-run", false, "don't create destination snapshot, just show what would be pulled")
cmd.Flag.Bool("no-deps", false, "don't process dependencies, just pull listed packages")
cmd.Flag.Bool("no-remove", false, "don't remove other package versions when pulling package")
cmd.Flag.Bool("all-matches", false, "pull all the packages that satisfy the dependency version requirements")
return cmd
}
+60
View File
@@ -0,0 +1,60 @@
package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
)
func aptlySnapshotRename(cmd *commander.Command, args []string) error {
var (
err error
snapshot *deb.Snapshot
)
if len(args) != 2 {
cmd.Usage()
return commander.ErrCommandError
}
oldName, newName := args[0], args[1]
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(oldName)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
_, err = context.CollectionFactory().SnapshotCollection().ByName(newName)
if err == nil {
return fmt.Errorf("unable to rename: snapshot %s already exists", newName)
}
snapshot.Name = newName
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
return fmt.Errorf("unable to rename: %s", err)
}
fmt.Printf("\nSnapshot %s -> %s has been successfully renamed.\n", oldName, newName)
return err
}
func makeCmdSnapshotRename() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotRename,
UsageLine: "rename <old-name> <new-name>",
Short: "renames snapshot",
Long: `
Command changes name of the snapshot. Snapshot name should be unique.
Example:
$ aptly snapshot rename wheezy-min wheezy-main
`,
}
return cmd
}
+142
View File
@@ -0,0 +1,142 @@
package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
"github.com/smira/commander"
"github.com/smira/flag"
)
func aptlySnapshotMirrorRepoSearch(cmd *commander.Command, args []string) error {
var (
err error
q deb.PackageQuery
)
if len(args) < 1 || len(args) > 2 {
cmd.Usage()
return commander.ErrCommandError
}
name := args[0]
command := cmd.Parent.Name()
var reflist *deb.PackageRefList
if command == "snapshot" { // nolint: goconst
var snapshot *deb.Snapshot
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = snapshot.RefList()
} else if command == "mirror" {
var repo *deb.RemoteRepo
repo, err = context.CollectionFactory().RemoteRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().RemoteRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = repo.RefList()
} else if command == "repo" { // nolint: goconst
var repo *deb.LocalRepo
repo, err = context.CollectionFactory().LocalRepoCollection().ByName(name)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
err = context.CollectionFactory().LocalRepoCollection().LoadComplete(repo)
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
reflist = repo.RefList()
} else {
panic("unknown command")
}
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
list.PrepareIndex()
if len(args) == 2 {
q, err = query.Parse(args[1])
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
} else {
q = &deb.MatchAllQuery{}
}
withDeps := context.Flags().Lookup("with-deps").Value.Get().(bool)
architecturesList := []string{}
if withDeps {
if len(context.ArchitecturesList()) > 0 {
architecturesList = context.ArchitecturesList()
} else {
architecturesList = list.Architectures(false)
}
sort.Strings(architecturesList)
if len(architecturesList) == 0 {
return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
}
}
result, err := list.FilterWithProgress([]deb.PackageQuery{q}, withDeps,
nil, context.DependencyOptions(), architecturesList, context.Progress())
if err != nil {
return fmt.Errorf("unable to search: %s", err)
}
if result.Len() == 0 {
return fmt.Errorf("no results")
}
format := context.Flags().Lookup("format").Value.String()
PrintPackageList(result, format, "")
return err
}
func makeCmdSnapshotSearch() *commander.Command {
cmd := &commander.Command{
Run: aptlySnapshotMirrorRepoSearch,
UsageLine: "search <name> [<package-query>]",
Short: "search snapshot for packages matching query",
Long: `
Command search displays list of packages in snapshot that match package query
If query is not specified, all the packages are displayed.
Example:
$ aptly snapshot search wheezy-main '$Architecture (i386), Name (% *-dev)'
`,
Flag: *flag.NewFlagSet("aptly-snapshot-search", flag.ExitOnError),
}
cmd.Flag.Bool("with-deps", false, "include dependencies into search results")
cmd.Flag.String("format", "", "custom format for result printing")
return cmd
}
+36 -2
View File
@@ -2,6 +2,8 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"github.com/smira/flag"
)
@@ -10,7 +12,7 @@ func aptlySnapshotShow(cmd *commander.Command, args []string) error {
var err error
if len(args) != 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
name := args[0]
@@ -29,8 +31,40 @@ 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("Description: %s\n", snapshot.Description)
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
}
withPackages := context.flags.Lookup("with-packages").Value.Get().(bool)
if name != "" {
fmt.Printf(" %s [%s]\n", name, snapshot.SourceKind)
}
}
}
withPackages := context.Flags().Lookup("with-packages").Value.Get().(bool)
if withPackages {
ListPackagesRefList(snapshot.RefList())
}
+7 -6
View File
@@ -2,16 +2,17 @@ package cmd
import (
"fmt"
"sort"
"github.com/smira/aptly/deb"
"github.com/smira/commander"
"sort"
)
func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
var err error
if len(args) < 1 {
cmd.Usage()
return err
return commander.ErrCommandError
}
snapshots := make([]*deb.Snapshot, len(args))
@@ -31,25 +32,25 @@ func aptlySnapshotVerify(cmd *commander.Command, args []string) error {
packageList, err := deb.NewPackageListFromRefList(snapshots[0].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
return fmt.Errorf("unable to load packages: %s", err)
}
sourcePackageList := deb.NewPackageList()
err = sourcePackageList.Append(packageList)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
return fmt.Errorf("unable to merge sources: %s", err)
}
var pL *deb.PackageList
for i := 1; i < len(snapshots); i++ {
pL, err = deb.NewPackageListFromRefList(snapshots[i].RefList(), context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
fmt.Errorf("unable to load packages: %s", err)
return fmt.Errorf("unable to load packages: %s", err)
}
err = sourcePackageList.Append(pL)
if err != nil {
fmt.Errorf("unable to merge sources: %s", err)
return fmt.Errorf("unable to merge sources: %s", err)
}
}
+15
View File
@@ -0,0 +1,15 @@
package cmd
import (
"github.com/smira/commander"
)
func makeCmdTask() *commander.Command {
return &commander.Command{
UsageLine: "task",
Short: "manage aptly tasks",
Subcommands: []*commander.Command{
makeCmdTaskRun(),
},
}
}
+154
View File
@@ -0,0 +1,154 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/mattn/go-shellwords"
"github.com/smira/commander"
)
func aptlyTaskRun(cmd *commander.Command, args []string) error {
var err error
var cmdList [][]string
if filename := cmd.Flag.Lookup("filename").Value.Get().(string); filename != "" {
var text string
cmdArgs := []string{}
var finfo os.FileInfo
if finfo, err = os.Stat(filename); os.IsNotExist(err) || finfo.IsDir() {
return fmt.Errorf("no such file, %s", filename)
}
fmt.Print("Reading file...\n\n")
var file *os.File
file, err = os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
text = strings.TrimSpace(scanner.Text()) + ","
parsedArgs, _ := shellwords.Parse(text)
cmdArgs = append(cmdArgs, parsedArgs...)
}
if err = scanner.Err(); err != nil {
return err
}
if len(cmdArgs) == 0 {
return fmt.Errorf("the file is empty")
}
cmdList = formatCommands(cmdArgs)
} else if len(args) == 0 {
var text string
cmdArgs := []string{}
fmt.Println("Please enter one command per line and leave one blank when finished.")
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("> ")
text, _ = reader.ReadString('\n')
if text == "\n" {
break
} else {
text = strings.TrimSpace(text) + ","
parsedArgs, _ := shellwords.Parse(text)
cmdArgs = append(cmdArgs, parsedArgs...)
}
}
if len(cmdArgs) == 0 {
return fmt.Errorf("nothing entered")
}
cmdList = formatCommands(cmdArgs)
} else {
cmdList = formatCommands(args)
}
commandErrored := false
for i, command := range cmdList {
if !commandErrored {
err = context.ReOpenDatabase()
if err != nil {
return fmt.Errorf("failed to reopen DB: %s", err)
}
context.Progress().ColoredPrintf("@g%d) [Running]: %s@!", (i + 1), strings.Join(command, " "))
context.Progress().ColoredPrintf("\n@yBegin command output: ----------------------------@!")
context.Progress().Flush()
returnCode := Run(RootCommand(), command, false)
if returnCode != 0 {
commandErrored = true
}
context.Progress().ColoredPrintf("\n@yEnd command output: ------------------------------@!")
CleanupContext()
} else {
context.Progress().ColoredPrintf("@r%d) [Skipping]: %s@!", (i + 1), strings.Join(command, " "))
}
}
if commandErrored {
err = fmt.Errorf("at least one command has reported an error")
}
return err
}
func formatCommands(args []string) [][]string {
var cmd []string
var cmdArray [][]string
for _, s := range args {
if sTrimmed := strings.TrimRight(s, ","); sTrimmed != s {
cmd = append(cmd, sTrimmed)
cmdArray = append(cmdArray, cmd)
cmd = []string{}
} else {
cmd = append(cmd, s)
}
}
if len(cmd) > 0 {
cmdArray = append(cmdArray, cmd)
}
return cmdArray
}
func makeCmdTaskRun() *commander.Command {
cmd := &commander.Command{
Run: aptlyTaskRun,
UsageLine: "run -filename=<filename> | <command1>, <command2>, ...",
Short: "run aptly tasks",
Long: `
Command helps organise multiple aptly commands in one single aptly task, running as single thread.
Example:
$ aptly task run
> repo create local
> repo add local pkg1
> publish repo local
> serve
>
`,
}
cmd.Flag.String("filename", "", "specifies the filename that contains the commands to run")
return cmd
}
+6
View File
@@ -2,11 +2,17 @@ package cmd
import (
"fmt"
"github.com/smira/aptly/aptly"
"github.com/smira/commander"
)
func aptlyVersion(cmd *commander.Command, args []string) error {
if len(args) != 0 {
cmd.Usage()
return commander.ErrCommandError
}
fmt.Printf("aptly version: %s\n", aptly.Version)
return nil
}
+46 -11
View File
@@ -2,18 +2,23 @@ package console
import (
"fmt"
"os"
"strings"
"github.com/cheggaaa/pb"
"github.com/smira/aptly/aptly"
"github.com/wsxiaoys/terminal/color"
"strings"
)
const (
codePrint = iota
codePrintStdErr
codeProgress
codeHideProgress
codeStop
codeFlush
codeBarEnabled
codeBarDisabled
)
type printTask struct {
@@ -25,7 +30,6 @@ type printTask struct {
// Progress is a progress displaying subroutine, it allows to show download and other operations progress
// mixed with progress bar
type Progress struct {
stop chan bool
stopped chan bool
queue chan printTask
bar *pb.ProgressBar
@@ -81,6 +85,8 @@ func (p *Progress) InitBar(count int64, isBytes bool) {
p.bar.SetUnits(pb.U_BYTES)
p.bar.ShowSpeed = true
}
p.queue <- printTask{code: codeBarEnabled}
p.bar.Start()
}
}
@@ -91,6 +97,7 @@ func (p *Progress) ShutdownBar() {
return
}
p.bar.Finish()
p.queue <- printTask{code: codeBarDisabled}
p.bar = nil
p.queue <- printTask{code: codeHideProgress}
}
@@ -122,25 +129,41 @@ func (p *Progress) Printf(msg string, a ...interface{}) {
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
func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
if RunningOnTerminal() {
p.queue <- printTask{code: codePrint, message: color.Sprintf(msg, a...) + "\n"}
} else {
// stip color marks
var prev rune
var inColorMark, inCurly bool
msg = strings.Map(func(r rune) rune {
if prev == '@' {
prev = 0
if r == '@' {
return r
if inColorMark {
if inCurly {
if r == '}' {
inCurly = false
inColorMark = false
return -1
}
} else {
if r == '{' {
inCurly = true
} else if r == '@' {
return '@'
} else {
inColorMark = false
}
}
return -1
}
prev = r
if r == '@' {
return -1
if r == '@' {
inColorMark = true
return -1
}
return r
@@ -151,17 +174,29 @@ func (p *Progress) ColoredPrintf(msg string, a ...interface{}) {
}
func (p *Progress) worker() {
hasBar := false
for {
task := <-p.queue
switch task.code {
case codeBarEnabled:
hasBar = true
case codeBarDisabled:
hasBar = false
case codePrint:
if p.barShown {
fmt.Print("\r\033[2K")
p.barShown = false
}
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:
if p.bar != nil {
if hasBar {
fmt.Print("\r" + task.message)
p.barShown = true
}
+2 -3
View File
@@ -1,10 +1,9 @@
// +build !freebsd
package console
import (
"code.google.com/p/go.crypto/ssh/terminal"
"syscall"
"golang.org/x/crypto/ssh/terminal"
)
// RunningOnTerminal checks whether stdout is terminal
-10
View File
@@ -1,10 +0,0 @@
// +build freebsd
package console
// RunningOnTerminal checks whether stdout is terminal
//
// Stub for FreeBSD, until in go1.3 terminal.IsTerminal would start working for FreeBSD
func RunningOnTerminal() bool {
return false
}
+549
View File
@@ -0,0 +1,549 @@
// Package context provides single entry to all resources
package context
import (
"fmt"
"math/rand"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"sync"
"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
type AptlyContext struct {
sync.Mutex
flags, globalFlags *flag.FlagSet
configLoaded bool
progress aptly.Progress
downloader aptly.Downloader
database database.Storage
packagePool aptly.PackagePool
publishedStorages map[string]aptly.PublishedStorage
collectionFactory *deb.CollectionFactory
dependencyOptions int
architecturesList []string
// Debug features
fileCPUProfile *os.File
fileMemProfile *os.File
fileMemStats *os.File
}
// Check interface
var _ aptly.PublishedStorageProvider = &AptlyContext{}
// FatalError is type for panicking to abort execution with non-zero
// exit code and print meaningful explanation
type FatalError struct {
ReturnCode int
Message string
}
// Fatal panics and aborts execution with exit code 1
func Fatal(err error) {
returnCode := 1
if err == commander.ErrFlagError || err == commander.ErrCommandError {
returnCode = 2
}
panic(&FatalError{ReturnCode: returnCode, Message: err.Error()})
}
// Config loads and returns current configuration
func (context *AptlyContext) Config() *utils.ConfigStructure {
context.Lock()
defer context.Unlock()
return context.config()
}
func (context *AptlyContext) config() *utils.ConfigStructure {
if !context.configLoaded {
var err error
configLocation := context.globalFlags.Lookup("config").Value.String()
if configLocation != "" {
err = utils.LoadConfig(configLocation, &utils.Config)
if err != nil {
Fatal(err)
}
} else {
configLocations := []string{
filepath.Join(os.Getenv("HOME"), ".aptly.conf"),
"/etc/aptly.conf",
}
for _, configLocation := range configLocations {
err = utils.LoadConfig(configLocation, &utils.Config)
if err == nil {
break
}
if !os.IsNotExist(err) {
Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
}
}
if err != nil {
fmt.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)
}
}
context.configLoaded = true
}
return &utils.Config
}
// LookupOption checks boolean flag with default (usually config) and command-line
// setting
func (context *AptlyContext) LookupOption(defaultValue bool, name string) (result bool) {
context.Lock()
defer context.Unlock()
return context.lookupOption(defaultValue, name)
}
func (context *AptlyContext) lookupOption(defaultValue bool, name string) (result bool) {
result = defaultValue
if context.globalFlags.IsSet(name) {
result = context.globalFlags.Lookup(name).Value.Get().(bool)
}
return
}
// DependencyOptions calculates options related to dependecy handling
func (context *AptlyContext) DependencyOptions() int {
context.Lock()
defer context.Unlock()
if context.dependencyOptions == -1 {
context.dependencyOptions = 0
if context.lookupOption(context.config().DepFollowSuggests, "dep-follow-suggests") {
context.dependencyOptions |= deb.DepFollowSuggests
}
if context.lookupOption(context.config().DepFollowRecommends, "dep-follow-recommends") {
context.dependencyOptions |= deb.DepFollowRecommends
}
if context.lookupOption(context.config().DepFollowAllVariants, "dep-follow-all-variants") {
context.dependencyOptions |= deb.DepFollowAllVariants
}
if context.lookupOption(context.config().DepFollowSource, "dep-follow-source") {
context.dependencyOptions |= deb.DepFollowSource
}
if context.lookupOption(context.config().DepVerboseResolve, "dep-verbose-resolve") {
context.dependencyOptions |= deb.DepVerboseResolve
}
}
return context.dependencyOptions
}
// ArchitecturesList returns list of architectures fixed via command line or config
func (context *AptlyContext) ArchitecturesList() []string {
context.Lock()
defer context.Unlock()
if context.architecturesList == nil {
context.architecturesList = context.config().Architectures
optionArchitectures := context.globalFlags.Lookup("architectures").Value.String()
if optionArchitectures != "" {
context.architecturesList = strings.Split(optionArchitectures, ",")
}
}
return context.architecturesList
}
// Progress creates or returns Progress object
func (context *AptlyContext) Progress() aptly.Progress {
context.Lock()
defer context.Unlock()
return context._progress()
}
func (context *AptlyContext) _progress() aptly.Progress {
if context.progress == nil {
context.progress = console.NewProgress()
context.progress.Start()
}
return context.progress
}
// Downloader returns instance of current downloader
func (context *AptlyContext) Downloader() aptly.Downloader {
context.Lock()
defer context.Unlock()
if context.downloader == nil {
var downloadLimit int64
limitFlag := context.flags.Lookup("download-limit")
if limitFlag != nil {
downloadLimit = limitFlag.Value.Get().(int64)
}
if downloadLimit == 0 {
downloadLimit = context.config().DownloadLimit
}
context.downloader = http.NewDownloader(downloadLimit*1024, context._progress())
}
return context.downloader
}
// DBPath builds path to database
func (context *AptlyContext) DBPath() string {
context.Lock()
defer context.Unlock()
return context.dbPath()
}
// DBPath builds path to database
func (context *AptlyContext) dbPath() string {
return filepath.Join(context.config().RootDir, "db")
}
// Database opens and returns current instance of database
func (context *AptlyContext) Database() (database.Storage, error) {
context.Lock()
defer context.Unlock()
return context._database()
}
func (context *AptlyContext) _database() (database.Storage, error) {
if context.database == nil {
var err error
context.database, err = database.NewDB(context.dbPath())
if err != nil {
return nil, fmt.Errorf("can't instantiate database: %s", err)
}
}
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
func (context *AptlyContext) CloseDatabase() error {
context.Lock()
defer context.Unlock()
if context.database == nil {
return nil
}
return context.database.Close()
}
// ReOpenDatabase reopens the db after close
func (context *AptlyContext) ReOpenDatabase() error {
_, err := context.Database()
return err
}
// CollectionFactory builds factory producing all kinds of collections
func (context *AptlyContext) CollectionFactory() *deb.CollectionFactory {
context.Lock()
defer context.Unlock()
if context.collectionFactory == nil {
db, err := context._database()
if err != nil {
Fatal(err)
}
context.collectionFactory = deb.NewCollectionFactory(db)
}
return context.collectionFactory
}
// PackagePool returns instance of PackagePool
func (context *AptlyContext) PackagePool() aptly.PackagePool {
context.Lock()
defer context.Unlock()
if context.packagePool == nil {
context.packagePool = files.NewPackagePool(context.config().RootDir, !context.config().SkipLegacyPool)
}
return context.packagePool
}
// GetPublishedStorage returns instance of PublishedStorage
func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedStorage {
context.Lock()
defer context.Unlock()
publishedStorage, ok := context.publishedStorages[name]
if !ok {
if name == "" {
publishedStorage = files.NewPublishedStorage(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:") {
params, ok := context.config().S3PublishRoots[name[3:]]
if !ok {
Fatal(fmt.Errorf("published S3 storage %v not configured", name[3:]))
}
var err error
publishedStorage, err = s3.NewPublishedStorage(
params.AccessKeyID, params.SecretAccessKey, params.SessionToken,
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 {
Fatal(err)
}
} else if strings.HasPrefix(name, "swift:") {
params, ok := context.config().SwiftPublishRoots[name[6:]]
if !ok {
Fatal(fmt.Errorf("published Swift storage %v not configured", name[6:]))
}
var err error
publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
params.AuthURL, params.Tenant, params.TenantID, params.Domain, params.DomainID, params.TenantDomain, params.TenantDomainID, params.Container, params.Prefix)
if err != nil {
Fatal(err)
}
} else {
Fatal(fmt.Errorf("unknown published storage format: %v", name))
}
context.publishedStorages[name] = publishedStorage
}
return publishedStorage
}
// UploadPath builds path to upload storage
func (context *AptlyContext) UploadPath() string {
return filepath.Join(context.Config().RootDir, "upload")
}
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
func (context *AptlyContext) UpdateFlags(flags *flag.FlagSet) {
context.Lock()
defer context.Unlock()
context.flags = flags
}
// Flags returns current command flags
func (context *AptlyContext) Flags() *flag.FlagSet {
context.Lock()
defer context.Unlock()
return context.flags
}
// GlobalFlags returns flags passed to all commands
func (context *AptlyContext) GlobalFlags() *flag.FlagSet {
context.Lock()
defer context.Unlock()
return context.globalFlags
}
// Shutdown shuts context down
func (context *AptlyContext) Shutdown() {
context.Lock()
defer context.Unlock()
if aptly.EnableDebug {
if context.fileMemProfile != nil {
pprof.WriteHeapProfile(context.fileMemProfile)
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
if context.fileCPUProfile != nil {
pprof.StopCPUProfile()
context.fileCPUProfile.Close()
context.fileCPUProfile = nil
}
if context.fileMemProfile != nil {
context.fileMemProfile.Close()
context.fileMemProfile = nil
}
}
if context.database != nil {
context.database.Close()
context.database = nil
}
if context.downloader != nil {
context.downloader = nil
}
if context.progress != nil {
context.progress.Shutdown()
context.progress = nil
}
}
// Cleanup does partial shutdown of context
func (context *AptlyContext) Cleanup() {
context.Lock()
defer context.Unlock()
if context.downloader != nil {
context.downloader = nil
}
if context.progress != nil {
context.progress.Shutdown()
context.progress = nil
}
}
// NewContext initializes context with default settings
func NewContext(flags *flag.FlagSet) (*AptlyContext, error) {
var err error
context := &AptlyContext{
flags: flags,
globalFlags: flags,
dependencyOptions: -1,
publishedStorages: map[string]aptly.PublishedStorage{},
}
if aptly.EnableDebug {
cpuprofile := flags.Lookup("cpuprofile").Value.String()
if cpuprofile != "" {
context.fileCPUProfile, err = os.Create(cpuprofile)
if err != nil {
return nil, err
}
pprof.StartCPUProfile(context.fileCPUProfile)
}
memprofile := flags.Lookup("memprofile").Value.String()
if memprofile != "" {
context.fileMemProfile, err = os.Create(memprofile)
if err != nil {
return nil, err
}
}
memstats := flags.Lookup("memstats").Value.String()
if memstats != "" {
interval := flags.Lookup("meminterval").Value.Get().(time.Duration)
context.fileMemStats, err = os.Create(memstats)
if err != nil {
return nil, err
}
context.fileMemStats.WriteString("# Time\tHeapSys\tHeapAlloc\tHeapIdle\tHeapReleased\n")
go func() {
var stats runtime.MemStats
start := time.Now().UnixNano()
for {
runtime.ReadMemStats(&stats)
if context.fileMemStats != nil {
context.fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval)
} else {
break
}
}
}()
}
}
return context, nil
}
+99 -8
View File
@@ -4,6 +4,9 @@ package database
import (
"bytes"
"errors"
"io/ioutil"
"os"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/opt"
@@ -16,20 +19,29 @@ var (
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
type Storage interface {
CreateTemporary() (Storage, error)
Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error
Delete(key []byte) error
HasPrefix(prefix []byte) bool
ProcessByPrefix(prefix []byte, proc StorageProcessor) error
KeysByPrefix(prefix []byte) [][]byte
FetchByPrefix(prefix []byte) [][]byte
Open() error
Close() error
StartBatch()
FinishBatch() error
CompactDB() error
Drop() error
}
type levelDB struct {
path string
db *leveldb.DB
batch *leveldb.Batch
}
@@ -39,22 +51,39 @@ var (
_ Storage = &levelDB{}
)
// OpenDB opens (creates) LevelDB database
func OpenDB(path string) (Storage, error) {
func internalOpen(path string, throttleCompaction bool) (*leveldb.DB, error) {
o := &opt.Options{
Filter: filter.NewBloomFilter(10),
Filter: filter.NewBloomFilter(10),
OpenFilesCacheCapacity: 256,
}
db, err := leveldb.OpenFile(path, o)
if throttleCompaction {
o.CompactionL0Trigger = 32
o.WriteL0PauseTrigger = 96
o.WriteL0SlowdownTrigger = 64
}
return leveldb.OpenFile(path, o)
}
// NewDB creates new instance of DB, but doesn't open it (yet)
func NewDB(path string) (Storage, error) {
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 {
return nil, err
}
return &levelDB{db: db}, nil
return db, db.Open()
}
// RecoverDB recovers LevelDB database from corruption
func RecoverDB(path string) error {
stor, err := storage.OpenFile(path)
stor, err := storage.OpenFile(path, false)
if err != nil {
return err
}
@@ -70,6 +99,20 @@ func RecoverDB(path string) error {
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
func (l *levelDB) Get(key []byte) ([]byte, error) {
value, err := l.db.Get(key, nil)
@@ -95,7 +138,7 @@ func (l *levelDB) Put(key []byte, value []byte) error {
return err
}
} else {
if bytes.Compare(old, value) == 0 {
if bytes.Equal(old, value) {
return nil
}
}
@@ -145,9 +188,48 @@ func (l *levelDB) FetchByPrefix(prefix []byte) [][]byte {
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
func (l *levelDB) Close() error {
return l.db.Close()
if l.db == nil {
return nil
}
err := l.db.Close()
l.db = nil
return err
}
// Reopen tries to open (re-open) the database
func (l *levelDB) Open() error {
if l.db != nil {
return nil
}
var err error
l.db, err = internalOpen(l.path, false)
return err
}
// StartBatch starts batch processing of keys
@@ -174,3 +256,12 @@ func (l *levelDB) FinishBatch() error {
func (l *levelDB) CompactDB() error {
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)
}
+78 -3
View File
@@ -1,8 +1,9 @@
package database
import (
. "launchpad.net/gocheck"
"testing"
. "gopkg.in/check.v1"
)
// Launch gocheck tests
@@ -21,7 +22,7 @@ func (s *LevelDBSuite) SetUpTest(c *C) {
var err error
s.path = c.MkDir()
s.db, err = OpenDB(s.path)
s.db, err = NewOpenDB(s.path)
c.Assert(err, IsNil)
}
@@ -45,7 +46,7 @@ func (s *LevelDBSuite) TestRecoverDB(c *C) {
err = RecoverDB(s.path)
c.Check(err, IsNil)
s.db, err = OpenDB(s.path)
s.db, err = NewOpenDB(s.path)
c.Check(err, IsNil)
result, err := s.db.Get(key)
@@ -70,6 +71,29 @@ func (s *LevelDBSuite) TestGetPut(c *C) {
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) {
var (
key = []byte("key")
@@ -106,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.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.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) {
var (
key = []byte("key")
@@ -155,3 +210,23 @@ func (s *LevelDBSuite) TestCompactDB(c *C) {
c.Check(s.db.CompactDB(), IsNil)
}
func (s *LevelDBSuite) TestReOpen(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
err = s.db.Close()
c.Assert(err, IsNil)
err = s.db.Open()
c.Assert(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
}
+290
View File
@@ -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
}
+92
View File
@@ -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`
+67
View File
@@ -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{}
)
+47
View File
@@ -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)
}
+52 -1
View File
@@ -1,26 +1,40 @@
package deb
import (
"sync"
"github.com/smira/aptly/database"
)
// CollectionFactory is a single place to generate all desired collections
type CollectionFactory struct {
*sync.Mutex
db database.Storage
packages *PackageCollection
remoteRepos *RemoteRepoCollection
snapshots *SnapshotCollection
localRepos *LocalRepoCollection
publishedRepos *PublishedRepoCollection
checksums *ChecksumCollection
}
// NewCollectionFactory creates new factory
func NewCollectionFactory(db database.Storage) *CollectionFactory {
return &CollectionFactory{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
func (factory *CollectionFactory) PackageCollection() *PackageCollection {
factory.Lock()
defer factory.Unlock()
if factory.packages == nil {
factory.packages = NewPackageCollection(factory.db)
}
@@ -30,6 +44,9 @@ func (factory *CollectionFactory) PackageCollection() *PackageCollection {
// RemoteRepoCollection returns (or creates) new RemoteRepoCollection
func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.remoteRepos == nil {
factory.remoteRepos = NewRemoteRepoCollection(factory.db)
}
@@ -39,6 +56,9 @@ func (factory *CollectionFactory) RemoteRepoCollection() *RemoteRepoCollection {
// SnapshotCollection returns (or creates) new SnapshotCollection
func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
factory.Lock()
defer factory.Unlock()
if factory.snapshots == nil {
factory.snapshots = NewSnapshotCollection(factory.db)
}
@@ -48,6 +68,9 @@ func (factory *CollectionFactory) SnapshotCollection() *SnapshotCollection {
// LocalRepoCollection returns (or creates) new LocalRepoCollection
func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.localRepos == nil {
factory.localRepos = NewLocalRepoCollection(factory.db)
}
@@ -57,9 +80,37 @@ func (factory *CollectionFactory) LocalRepoCollection() *LocalRepoCollection {
// PublishedRepoCollection returns (or creates) new PublishedRepoCollection
func (factory *CollectionFactory) PublishedRepoCollection() *PublishedRepoCollection {
factory.Lock()
defer factory.Unlock()
if factory.publishedRepos == nil {
factory.publishedRepos = NewPublishedRepoCollection(factory.db)
}
return factory.publishedRepos
}
// 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
func (factory *CollectionFactory) Flush() {
factory.Lock()
defer factory.Unlock()
factory.localRepos = nil
factory.snapshots = nil
factory.remoteRepos = nil
factory.publishedRepos = nil
factory.packages = nil
factory.checksums = nil
}
+135
View File
@@ -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
}
+106 -16
View File
@@ -3,13 +3,27 @@ package deb
import (
"archive/tar"
"bufio"
"compress/bzip2"
"compress/gzip"
"fmt"
"github.com/mkrautz/goar"
"github.com/smira/aptly/utils"
"io"
"os"
"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
@@ -24,16 +38,16 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
for {
header, err := library.Next()
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 {
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" {
ungzip, err := gzip.NewReader(library)
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()
@@ -41,15 +55,15 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
for {
tarHeader, err := untar.Next()
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 {
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" {
if tarHeader.Name == "./control" || tarHeader.Name == "control" {
reader := NewControlFileReader(untar)
stanza, err := reader.ReadStanza()
stanza, err := reader.ReadStanza(false)
if err != nil {
return nil, err
}
@@ -62,23 +76,23 @@ func GetControlFileFromDeb(packageFile string) (Stanza, error) {
}
// 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)
if err != nil {
return nil, err
}
defer file.Close()
line, err := bufio.NewReader(file).ReadString('\n')
isClearSigned, err := verifier.IsClearSigned(file)
file.Seek(0, 0)
if err != nil {
return nil, err
}
file.Seek(0, 0)
var text io.ReadCloser
var text *os.File
if strings.Index(line, "BEGIN PGP SIGN") != -1 {
if isClearSigned {
text, err = verifier.ExtractClearsigned(file)
if err != nil {
return nil, err
@@ -89,7 +103,7 @@ func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, err
}
reader := NewControlFileReader(text)
stanza, err := reader.ReadStanza()
stanza, err := reader.ReadStanza(false)
if err != nil {
return nil, err
}
@@ -97,3 +111,79 @@ func GetControlFileFromDsc(dscFile string, verifier utils.Verifier) (Stanza, err
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)
}
}
}
}

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