diff --git a/NEWS b/NEWS index e5ba5345b..43351a5ce 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +0.0.5 + + * Fixed regression with --verbose output being buffered. This means dropping the helpful error + message introduced in 0.0.4. + 0.0.4 * Now using tox to run tests against multiple versions of Python in one go. diff --git a/README.md b/README.md index c60827302..f39964623 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,18 @@ available](https://torsion.org/hg/atticmatic). It's also mirrored on ## Setup +To get up and running with Attic, follow the [Attic Quick +Start](https://attic-backup.org/quickstart.html) guide to create an Attic +repository on a local or remote host. Note that if you plan to run atticmatic +on a schedule with cron, and you encrypt your attic repository with a +passphrase instead of a key file, you'll need to set the `ATTIC_PASSPHRASE` +environment variable. See [attic's repository encryption +documentation](https://attic-backup.org/quickstart.html#encrypted-repos) for +more info. + +If the repository is on a remote host, make sure that your local root user has +key-based ssh access to the desired user account on the remote host. + To install atticmatic, run the following command to download and install it: sudo pip install --upgrade hg+https://torsion.org/hg/atticmatic @@ -47,24 +59,7 @@ Then copy the following configuration files: sudo mkdir /etc/atticmatic/ sudo cp sample/config sample/excludes /etc/atticmatic/ -Modify those files with your desired configuration, including the path to an -attic repository. - -If you don't yet have an attic repository, then the first time you run -atticmatic, you'll get an error with information on how to create a repository -on a local or remote host. - -And if the repository is on a remote host, make sure that your local root user -has key-based ssh access to the desired user account on the remote host. - -It is recommended that you create your attic repository with keyfile -encryption, as passphrase-based encryption is less suited for automated -backups. If you do plan to run atticmatic on a schedule with cron, and you -encrypt your attic repository with a passphrase instead of a key file, you'll -need to set the `ATTIC_PASSPHRASE` environment variable. See [attic's -repository encryption -documentation](https://attic-backup.org/quickstart.html#encrypted-repos) for -more info. +Lastly, modify those files with your desired configuration. ## Usage diff --git a/atticmatic/attic.py b/atticmatic/attic.py index d0b1cc896..d0c678d68 100644 --- a/atticmatic/attic.py +++ b/atticmatic/attic.py @@ -1,10 +1,7 @@ -from __future__ import print_function from datetime import datetime import os import platform -import re import subprocess -import sys def create_archive(excludes_filename, verbose, source_directories, repository): @@ -26,14 +23,7 @@ def create_archive(excludes_filename, verbose, source_directories, repository): ('--verbose', '--stats') if verbose else () ) - try: - subprocess.check_output(command, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as error: - print(error.output.strip(), file=sys.stderr) - - if re.search('Error: Repository .* does not exist', error.output): - raise RuntimeError('To create a repository, run: attic init --encryption=keyfile {}'.format(repository)) - raise error + subprocess.check_call(command) def make_prune_flags(retention_config): diff --git a/atticmatic/command.py b/atticmatic/command.py index e0f4eb18b..92976f0ea 100644 --- a/atticmatic/command.py +++ b/atticmatic/command.py @@ -46,6 +46,6 @@ def main(): create_archive(args.excludes_filename, args.verbose, **location_config) prune_archives(args.verbose, repository, retention_config) check_archives(args.verbose, repository) - except (ValueError, IOError, CalledProcessError, RuntimeError) as error: + except (ValueError, IOError, CalledProcessError) as error: print(error, file=sys.stderr) sys.exit(1) diff --git a/atticmatic/tests/unit/test_attic.py b/atticmatic/tests/unit/test_attic.py index e54f44573..2c93e8c78 100644 --- a/atticmatic/tests/unit/test_attic.py +++ b/atticmatic/tests/unit/test_attic.py @@ -1,49 +1,14 @@ from collections import OrderedDict -try: - # Python 2 - import __builtin__ as builtins -except ImportError: - # Python 3 - import builtins from flexmock import flexmock -from nose.tools import assert_raises from atticmatic import attic as module -class MockCalledProcessError(Exception): - def __init__(self, output): - self.output = output - - -def insert_subprocess_check_output_mock(call_command, error_output=None, **kwargs): - subprocess = flexmock(CalledProcessError=MockCalledProcessError, STDOUT=flexmock()) - - expectation = subprocess.should_receive('check_output').with_args( - call_command, - stderr=subprocess.STDOUT, - **kwargs - ).once() - - if error_output: - expectation.and_raise(MockCalledProcessError, output=error_output) - flexmock(builtins).should_receive('print') - - flexmock(module).subprocess = subprocess - return subprocess - - -def insert_subprocess_check_call_mock(call_command, **kwargs): +def insert_subprocess_mock(check_call_command, **kwargs): subprocess = flexmock() - - subprocess.should_receive('check_call').with_args( - call_command, - **kwargs - ).once() - + subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once() flexmock(module).subprocess = subprocess - return subprocess def insert_platform_mock(): @@ -57,7 +22,7 @@ def insert_datetime_mock(): def test_create_archive_should_call_attic_with_parameters(): - insert_subprocess_check_output_mock( + insert_subprocess_mock( ('attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar'), ) insert_platform_mock() @@ -72,7 +37,7 @@ def test_create_archive_should_call_attic_with_parameters(): def test_create_archive_with_verbose_should_call_attic_with_verbose_parameters(): - insert_subprocess_check_output_mock( + insert_subprocess_mock( ( 'attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar', '--verbose', '--stats', @@ -88,39 +53,6 @@ def test_create_archive_with_verbose_should_call_attic_with_verbose_parameters() repository='repo', ) -def test_create_archive_with_missing_repository_should_raise(): - insert_subprocess_check_output_mock( - ('attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar'), - error_output='Error: Repository repo does not exist', - ) - insert_platform_mock() - insert_datetime_mock() - - with assert_raises(RuntimeError): - module.create_archive( - excludes_filename='excludes', - verbose=False, - source_directories='foo bar', - repository='repo', - ) - - -def test_create_archive_with_other_error_should_raise(): - subprocess = insert_subprocess_check_output_mock( - ('attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar'), - error_output='Something went wrong', - ) - insert_platform_mock() - insert_datetime_mock() - - with assert_raises(subprocess.CalledProcessError): - module.create_archive( - excludes_filename='excludes', - verbose=False, - source_directories='foo bar', - repository='repo', - ) - BASE_PRUNE_FLAGS = ( ('--keep-daily', '1'), @@ -148,7 +80,7 @@ def test_prune_archives_should_call_attic_with_parameters(): flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return( BASE_PRUNE_FLAGS, ) - insert_subprocess_check_call_mock( + insert_subprocess_mock( ( 'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3', @@ -167,7 +99,7 @@ def test_prune_archives_with_verbose_should_call_attic_with_verbose_parameters() flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return( BASE_PRUNE_FLAGS, ) - insert_subprocess_check_call_mock( + insert_subprocess_mock( ( 'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3', '--verbose', @@ -183,7 +115,7 @@ def test_prune_archives_with_verbose_should_call_attic_with_verbose_parameters() def test_check_archives_should_call_attic_with_parameters(): stdout = flexmock() - insert_subprocess_check_call_mock( + insert_subprocess_mock( ('attic', 'check', 'repo'), stdout=stdout, ) @@ -199,7 +131,7 @@ def test_check_archives_should_call_attic_with_parameters(): def test_check_archives_with_verbose_should_call_attic_with_verbose_parameters(): - insert_subprocess_check_call_mock( + insert_subprocess_mock( ('attic', 'check', 'repo', '--verbose'), stdout=None, ) diff --git a/setup.py b/setup.py index 9bcd56d21..e73711233 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='atticmatic', - version='0.0.4', + version='0.0.2', description='A wrapper script for Attic backup software that creates and prunes backups', author='Dan Helfman', author_email='witten@torsion.org',