From b797fdc10ba96abb010d3a5dfaae1d8fb3e83295 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 13:00:54 +0200 Subject: [PATCH 01/30] Add config module --- changes/cli.py | 19 +++---------------- changes/config.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 changes/config.py diff --git a/changes/cli.py b/changes/cli.py index c14726a..c2e7a35 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -51,25 +51,12 @@ import virtualenv import changes -from changes import attributes, probe, shell, version +from changes import attributes, config, probe, shell, version log = logging.getLogger(__name__) -CHANGELOG = 'CHANGELOG.md' -arguments = None - -def common_arguments(): - """ - Return common arguments - - :return: tuple of , --dry-run, new_version - """ - return ( - arguments[''], - arguments['--dry-run'], - arguments['new_version'], - ) +arguments = config.arguments def write_new_changelog(app_name, filename, content_lines, dry_run=True): @@ -145,7 +132,7 @@ def changelog(): write_new_changelog( app_name, - CHANGELOG, + config.CHANGELOG, changelog_content, dry_run=dry_run ) diff --git a/changes/config.py b/changes/config.py new file mode 100644 index 0000000..37a56cf --- /dev/null +++ b/changes/config.py @@ -0,0 +1,18 @@ +CHANGELOG = 'CHANGELOG.md' +arguments = {} + +def common_arguments(): + """ + Return common arguments + + :return: tuple of , --dry-run, new_version + """ + version_prefix = arguments['--version-prefix'] + new_version = arguments['new_version'] + + common_arguments = ( + arguments[''], + arguments['--dry-run'], + version_prefix + new_version if version_prefix else new_version, + ) + return common_arguments \ No newline at end of file From 12a975eb1250d2c7825a3a4bdc3c83650bb3c8c4 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 14:01:04 +0200 Subject: [PATCH 02/30] Alias config.arguments and setup from docopt --- changes/cli.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index c2e7a35..b161970 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -52,12 +52,11 @@ import changes from changes import attributes, config, probe, shell, version +from changes.config import arguments log = logging.getLogger(__name__) -arguments = config.arguments - def write_new_changelog(app_name, filename, content_lines, dry_run=True): heading_and_newline = ( @@ -140,7 +139,7 @@ def changelog(): def bump_version(): - app_name, dry_run, new_version = common_arguments() + app_name, dry_run, new_version = config.common_arguments() attributes.replace_attribute( app_name, @@ -150,10 +149,10 @@ def bump_version(): def commit_version_change(): - app_name, dry_run, new_version = common_arguments() + app_name, dry_run, new_version = config.common_arguments() command = 'git commit -m %s %s/__init__.py %s' % ( - new_version, app_name, CHANGELOG + new_version, app_name, config.CHANGELOG ) if not (shell.execute(command, dry_run=dry_run) and @@ -278,15 +277,16 @@ def release(): def initialise(): - global arguments arguments = docopt(__doc__, version=changes.__version__) debug = arguments['--debug'] logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) log.debug('arguments: %s', arguments) + config.arguments = arguments + return arguments def main(): - initialise() + arguments = initialise() commands = ['release', 'changelog', 'test', 'bump_version', 'tag', 'upload', 'install', 'pypi'] @@ -295,7 +295,7 @@ def main(): if arguments['--new-version']: arguments['new_version'] = arguments['--new-version'] - app_name = arguments[''] + app_name = config.arguments[''] if not probe.probe_project(app_name): raise Exception('Project does not meet `changes` requirements') From 091f0b0e2bd6b1806deca78aa78aec5ccb6ea193 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 14:01:59 +0200 Subject: [PATCH 03/30] Refactor changelog --- changes/changelog.py | 107 +++++++++++++++++++ changes/cli.py | 81 +------------- tests/{test_changes.py => test_changelog.py} | 11 +- 3 files changed, 115 insertions(+), 84 deletions(-) create mode 100644 changes/changelog.py rename tests/{test_changes.py => test_changelog.py} (80%) diff --git a/changes/changelog.py b/changes/changelog.py new file mode 100644 index 0000000..559c572 --- /dev/null +++ b/changes/changelog.py @@ -0,0 +1,107 @@ +import logging +import re + +from changes import attributes, config, shell, version + +log = logging.getLogger(__name__) + + +def write_new_changelog(app_name, filename, content_lines, dry_run=True): + heading_and_newline = ( + '# [Changelog](%s/releases)\n' % + attributes.extract_attribute(app_name, '__url__') + ) + + with open(filename, 'r+') as f: + existing = f.readlines() + + output = existing[2:] + output.insert(0, '\n') + + for index, line in enumerate(content_lines): + output.insert(0, content_lines[len(content_lines) - index - 1]) + + output.insert(0, heading_and_newline) + + output = ''.join(output) + + if not dry_run: + with open(filename, 'w+') as f: + f.write(output) + else: + log.info('New changelog:\n%s', ''.join(content_lines)) + +def replace_sha_with_commit_link(git_log_content): + repo_url = attributes.extract_attribute( + config.common_arguments()[0], + '__url__' + ) + + commit_link = '' + for index, line in enumerate(git_log_content): + # http://stackoverflow.com/a/468378/5549 + sha1_re = re.match(r'^[0-9a-f]{5,40}\b', line) + if sha1_re: + sha1 = sha1_re.group() + + new_line = line.replace( + sha1, + '[%s](%s/commit/%s)' % ( + sha1, + attributes.extract_attribute( + config.common_arguments()[0], + '__url__' + ), + sha1 + ) + ) + log.debug('old line: %s\nnew line: %s', line, new_line) + git_log_content[index] = new_line + return git_log_content + +def changelog(): + app_name, dry_run, new_version = config.common_arguments() + + changelog_content = [ + '\n## [%s](%s/compare/%s...%s)\n\n' % ( + new_version, attributes.extract_attribute(app_name, '__url__'), + version.current_version(app_name), new_version, + ) + ] + + ## vcs (todo: config templatise all these commands) + git_log = 'git log --oneline --no-merges' + version_difference = '%s..master' % version.current_version(app_name) + + git_log_content = shell.execute( + '%s %s' % (git_log, version_difference), + dry_run=False + ) + log.debug('content: %s' % git_log_content) + + if not git_log_content: + log.debug('sniffing initial release, drop tags: %s', git_log) + git_log_content = shell.execute(git_log, dry_run=False) + + ## /vcs + + git_log_content = replace_sha_with_commit_link(git_log_content) + + log.debug('content: %s' % git_log_content) + + # makes change log entries into bullet points + if git_log_content: + [ + changelog_content.append('* %s' % line) + if line else line + for line in git_log_content[:-1] + # for all except the last line? + ] + + write_new_changelog( + app_name, + config.CHANGELOG, + changelog_content, + dry_run=dry_run + ) + log.info('Added content to CHANGELOG.md') diff --git a/changes/cli.py b/changes/cli.py index b161970..77b29d3 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -53,91 +53,12 @@ import changes from changes import attributes, config, probe, shell, version from changes.config import arguments +from changes.changelog import changelog log = logging.getLogger(__name__) -def write_new_changelog(app_name, filename, content_lines, dry_run=True): - heading_and_newline = ( - '# [Changelog](%s/releases)\n' % - attributes.extract_attribute(app_name, '__url__') - ) - - with open(filename, 'r+') as f: - existing = f.readlines() - - output = existing[2:] - output.insert(0, '\n') - - for index, line in enumerate(content_lines): - output.insert(0, content_lines[len(content_lines) - index - 1]) - - output.insert(0, heading_and_newline) - - output = ''.join(output) - - if not dry_run: - with open(filename, 'w+') as f: - f.write(output) - else: - log.info('New changelog:\n%s', ''.join(content_lines)) - - -def changelog(): - app_name, dry_run, new_version = common_arguments() - - changelog_content = [ - '\n## [%s](%s/compare/%s...%s)\n\n' % ( - new_version, attributes.extract_attribute(app_name, '__url__'), - version.current_version(app_name), new_version, - ) - ] - git_log = 'git log --oneline --no-merges' - version_difference = '%s..master' % version.current_version(app_name) - - git_log_content = shell.execute( - '%s %s' % (git_log, version_difference), - dry_run=False - ) - - if not git_log_content: - log.debug('sniffing initial release, drop tags: %s', git_log) - git_log_content = shell.execute(git_log, dry_run=False) - - for index, line in enumerate(git_log_content): - # http://stackoverflow.com/a/468378/5549 - sha1_re = re.match(r'^[0-9a-f]{5,40}\b', line) - if sha1_re: - sha1 = sha1_re.group() - - new_line = line.replace( - sha1, - '[%s](%s/commit/%s)' % ( - sha1, - attributes.extract_attribute(app_name, '__url__'), - sha1 - ) - ) - log.debug('old line: %s\nnew line: %s', line, new_line) - git_log_content[index] = new_line - - if git_log_content: - [ - changelog_content.append('* %s\n' % line) - if line else line - for line in git_log_content[:-1] - ] - - write_new_changelog( - app_name, - config.CHANGELOG, - changelog_content, - dry_run=dry_run - ) - log.info('Added content to CHANGELOG.md') - - def bump_version(): app_name, dry_run, new_version = config.common_arguments() diff --git a/tests/test_changes.py b/tests/test_changelog.py similarity index 80% rename from tests/test_changes.py rename to tests/test_changelog.py index 5557c2b..328d61d 100644 --- a/tests/test_changes.py +++ b/tests/test_changelog.py @@ -1,8 +1,8 @@ -from changes import cli +from changes import changelog from . import BaseTestCase -class CliTestCase(BaseTestCase): +class ChangeLogTestCase(BaseTestCase): def test_write_new_changelog(self): content = [ @@ -13,7 +13,7 @@ def test_write_new_changelog(self): with open(self.tmp_file, 'w') as existing_file: existing_file.writelines(content) - cli.write_new_changelog('test_app', self.tmp_file, 'Now this is') + changelog.write_new_changelog('test_app', self.tmp_file, 'Now this is') self.assertEquals( ''.join(content), @@ -25,7 +25,7 @@ def test_write_new_changelog(self): with open(self.tmp_file, 'w') as existing_file: existing_file.writelines(content) - cli.write_new_changelog( + changelog.write_new_changelog( 'test_app', self.tmp_file, 'Now this is', @@ -42,3 +42,6 @@ def test_write_new_changelog(self): open(self.tmp_file).readlines() ) ) + + def test_changelog(self): + pass From e3121c800b447b97b0ba418b8c13e6965b15570f Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 14:47:43 +0200 Subject: [PATCH 04/30] Cleanup and linting --- changes/changelog.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/changes/changelog.py b/changes/changelog.py index 559c572..993dd07 100644 --- a/changes/changelog.py +++ b/changes/changelog.py @@ -31,13 +31,13 @@ def write_new_changelog(app_name, filename, content_lines, dry_run=True): else: log.info('New changelog:\n%s', ''.join(content_lines)) + def replace_sha_with_commit_link(git_log_content): repo_url = attributes.extract_attribute( - config.common_arguments()[0], - '__url__' - ) + config.common_arguments()[0], + '__url__' + ) - commit_link = '' for index, line in enumerate(git_log_content): # http://stackoverflow.com/a/468378/5549 sha1_re = re.match(r'^[0-9a-f]{5,40}\b', line) @@ -46,19 +46,14 @@ def replace_sha_with_commit_link(git_log_content): new_line = line.replace( sha1, - '[%s](%s/commit/%s)' % ( - sha1, - attributes.extract_attribute( - config.common_arguments()[0], - '__url__' - ), - sha1 - ) + '[%s](%s/commit/%s)' % (sha1, repo_url, sha1) ) log.debug('old line: %s\nnew line: %s', line, new_line) git_log_content[index] = new_line + return git_log_content + def changelog(): app_name, dry_run, new_version = config.common_arguments() From 3243197aebf67ef87baccd289b98bd48788be7cb Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:06:59 +0200 Subject: [PATCH 05/30] Correct english --- changes/probe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes/probe.py b/changes/probe.py index 405f680..1685227 100644 --- a/changes/probe.py +++ b/changes/probe.py @@ -16,7 +16,7 @@ def probe_project(app_name): """ Check if the project meets `changes` requirements """ - log.info('Checking project for changes requirements..self.') + log.info('Checking project for changes requirements.') # on [github](https://github.com) git_remotes = shell.execute('git remote -v', dry_run=False) on_github = any(['github.com' in remote for remote in git_remotes]) From ea1454ec6232201844a0d8cd30047498fd3ee2c0 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:09:45 +0200 Subject: [PATCH 06/30] Move and generalise `extract_version_arguments` --- changes/util.py | 9 +++++++++ changes/version.py | 9 +-------- tests/test_util.py | 17 +++++++++++++++++ tests/test_version.py | 14 -------------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/changes/util.py b/changes/util.py index 12433c8..3486b2f 100644 --- a/changes/util.py +++ b/changes/util.py @@ -9,3 +9,12 @@ def extract(dictionary, keys): return dict( (k, dictionary[k]) for k in keys if k in dictionary ) + +def extract_arguments(arguments, long_keys, key_prefix='--'): + long_arguments = extract( + arguments, + long_keys, + ) + return dict([ + (key.replace(key_prefix, ''), value) for key, value in long_arguments.items() + ]) diff --git a/changes/version.py b/changes/version.py index b44edb2..fb3e92e 100644 --- a/changes/version.py +++ b/changes/version.py @@ -25,6 +25,7 @@ def get_new_version(app_name, current_version, 'What is the release version for "%s" ' '[Default: %s]: ' % ( app_name, proposed_new_version + **util.extract_arguments( ) ) if not new_version: @@ -33,14 +34,6 @@ def get_new_version(app_name, current_version, return new_version.strip() -def extract_version_arguments(arguments): - long_arguments = util.extract( - arguments, - ['--major', '--minor', '--patch'], - ) - return dict([ - (key[2:], value) for key, value in long_arguments.items() - ]) def increment(version, major=False, minor=False, patch=True): diff --git a/tests/test_util.py b/tests/test_util.py index 30768c8..463b7c4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -12,3 +12,20 @@ def test_extract(self): ['a', 'b'] ) ) + + def test_extract_arguments(self): + self.assertEquals( + { + 'major': True, + 'minor': False, + 'patch': False, + }, + util.extract_arguments( + { + '--major': True, + '--minor': False, + '--patch': False, + }, + ['--major', '--minor', '--patch'] + ), + ) diff --git a/tests/test_version.py b/tests/test_version.py index ff27c9c..c77b0be 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -23,20 +23,6 @@ def test_increment(self): version.increment('1.0.0', patch=True) ) - def test_extract_version_arguments(self): - self.assertEquals( - { - 'major': True, - 'minor': False, - 'patch': False, - }, - version.extract_version_arguments({ - '--major': True, - '--minor': False, - '--patch': False, - }) - ) - class VersionApplicationTestCase(BaseTestCase): From 91d24f62505462e5009cd5e0fb1176824d7c57d9 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:10:14 +0200 Subject: [PATCH 07/30] Test config --- tests/test_config.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/test_config.py diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..2c177fa --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,44 @@ +from changes import config +from . import BaseTestCase + + +class ConfigTestCase(BaseTestCase): + arguments = { + '--debug': True, + '--dry-run': False, + '--help': False, + '--major': False, + '--minor': False, + '--new-version': '0.0.1', + 'new_version': '0.0.1', + '--noinput': True, + '--patch': True, + '--pypi': None, + '--skip-changelog': False, + '--test-command': None, + '--tox': False, + '--version-prefix': None, + '': 'changes', + 'bump_version': False, + 'changelog': True, + 'install': False, + 'pypi': False, + 'release': False, + 'tag': False, + 'test': False, + 'upload': False + } + + def setUp(self): + config.arguments = self.arguments + + def test_common_arguments(self): + expected_arguments = ( + 'changes', + False, + '0.0.1', + ) + self.assertEquals( + expected_arguments, + config.common_arguments() + ) From 9fae7a517e387d5acaa146d0b0a80779fc993375 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:16:20 +0200 Subject: [PATCH 08/30] Refactor vcs functions --- changes/cli.py | 24 +----------------------- changes/vcs.py | 22 ++++++++++++++++++++++ tests/test_vcs.py | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 changes/vcs.py create mode 100644 tests/test_vcs.py diff --git a/changes/cli.py b/changes/cli.py index 77b29d3..0e15803 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -55,6 +55,7 @@ from changes.config import arguments from changes.changelog import changelog +from changes.vcs import tag, commit_version_change log = logging.getLogger(__name__) @@ -69,18 +70,8 @@ def bump_version(): dry_run=dry_run) -def commit_version_change(): - app_name, dry_run, new_version = config.common_arguments() - - command = 'git commit -m %s %s/__init__.py %s' % ( - new_version, app_name, config.CHANGELOG ) - if not (shell.execute(command, dry_run=dry_run) and - shell.execute('git push', dry_run=dry_run)): - raise Exception('Version change commit failed') - - def test(): command = 'nosetests' if arguments['--tox']: @@ -169,19 +160,6 @@ def pypi(): path(tmp_dir).rmtree(path(tmp_dir)) -def tag(): - _, dry_run, new_version = common_arguments() - - if arguments['--version-prefix']: - new_version = arguments['--version-prefix'] + new_version - - shell.execute( - 'git tag -a %s -m "%s"' % (new_version, new_version), - dry_run=dry_run - ) - shell.execute('git push --tags', dry_run=dry_run) - - def release(): try: if not arguments['--skip-changelog']: diff --git a/changes/vcs.py b/changes/vcs.py new file mode 100644 index 0000000..fab18fe --- /dev/null +++ b/changes/vcs.py @@ -0,0 +1,22 @@ +from changes import config, shell + + +def commit_version_change(): + app_name, dry_run, new_version = config.common_arguments() + + command = 'git commit -m %s %s/__init__.py %s' % ( + new_version, app_name, config.CHANGELOG + ) + + if not (shell.execute(command, dry_run=dry_run) and + shell.execute('git push', dry_run=dry_run)): + raise Exception('Version change commit failed') + +def tag(): + _, dry_run, new_version = config.common_arguments() + + shell.execute( + 'git tag -a %s -m "%s"' % (new_version, new_version), + dry_run=dry_run + ) + shell.execute('git push --tags', dry_run=dry_run) diff --git a/tests/test_vcs.py b/tests/test_vcs.py new file mode 100644 index 0000000..5d86234 --- /dev/null +++ b/tests/test_vcs.py @@ -0,0 +1,14 @@ +from changes import config, vcs +from . import BaseTestCase + + +class VcsTestCase(BaseTestCase): + + def setUp(self): + config.arguments['--dry-run'] = True + + def test_commit_version_change(self): + vcs.commit_version_change() + + def test_tag(self): + vcs.tag() From 6275e71dffbf9625f953e609290d0217550c80ff Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:26:42 +0200 Subject: [PATCH 09/30] Refactor testing module --- changes/cli.py | 20 +------------------- changes/testing.py | 24 ++++++++++++++++++++++++ tests/test_testing.py | 11 +++++++++++ 3 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 changes/testing.py create mode 100644 tests/test_testing.py diff --git a/changes/cli.py b/changes/cli.py index 0e15803..abafdb6 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -43,7 +43,6 @@ import re -import tempfile from docopt import docopt from path import path @@ -55,6 +54,7 @@ from changes.config import arguments from changes.changelog import changelog +from changes.testing import test, run_test_command from changes.vcs import tag, commit_version_change log = logging.getLogger(__name__) @@ -72,30 +72,12 @@ def bump_version(): ) -def test(): - command = 'nosetests' - if arguments['--tox']: - command = 'tox' - - if not shell.execute(command, dry_run=False): - raise Exception('Test command failed') - - def make_virtualenv(): tmp_dir = tempfile.mkdtemp() virtualenv.create_environment(tmp_dir, site_packages=False) return tmp_dir -def run_test_command(): - if arguments['--test-command']: - test_command = arguments['--test-command'] - result = shell.execute(test_command, dry_run=arguments['--dry-run']) - log.info('Test command "%s", returned %s', test_command, result) - else: - log.warning('Test command "%s" failed', test_command) - - def install(): app_name, dry_run, new_version = common_arguments() diff --git a/changes/testing.py b/changes/testing.py new file mode 100644 index 0000000..14a87d1 --- /dev/null +++ b/changes/testing.py @@ -0,0 +1,24 @@ +import tempfile +import logging + +from changes import shell + +log = logging.getLogger(__name__) + + +def test(): + command = 'nosetests' + if arguments['--tox']: + command = 'tox' + + if not shell.execute(command, dry_run=False): + raise Exception('Test command failed') + + +def run_test_command(): + if arguments['--test-command']: + test_command = arguments['--test-command'] + result = shell.execute(test_command, dry_run=arguments['--dry-run']) + log.info('Test command "%s", returned %s', test_command, result) + else: + log.warning('Test command "%s" failed', test_command) diff --git a/tests/test_testing.py b/tests/test_testing.py new file mode 100644 index 0000000..b4fb99a --- /dev/null +++ b/tests/test_testing.py @@ -0,0 +1,11 @@ +from changes import config, testing +from . import BaseTestCase + + +class TestingTestCase(BaseTestCase): + """ + def test(): + def make_virtualenv(): + def run_test_command(): + """ + pass \ No newline at end of file From de3b939642f2c089e7f7782f94cd1c8e2ee437b3 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:27:14 +0200 Subject: [PATCH 10/30] Improve attributes coverage --- tests/test_attributes.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index c69b3b1..cde3401 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -28,6 +28,22 @@ def test_replace_attribute(self): ) ) + def test_replace_attribute_dry_run(self): + attributes.replace_attribute( + 'test_app', + '__version__', + '1.0.0', + dry_run=True + ) + + expected_content = list(self.initial_init_content) + self.assertEquals( + '\n'.join(expected_content), + ''.join( + open(self.tmp_file).readlines() + ) + ) + def test_has_attribute(self): self.assertTrue( attributes.has_attribute( From de1741dd6ca875fd5306d3fa9543153a13f46b51 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:35:54 +0200 Subject: [PATCH 11/30] Renamed run_tests command --- README.md | 7 +++++-- changes/cli.py | 11 +++++------ changes/testing.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9f76b1c..d16f2eb 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Usage: changes [options] changelog changes [options] release changes [options] bump_version - changes [options] test + changes [options] run_tests changes [options] install changes [options] upload changes [options] pypi @@ -68,12 +68,15 @@ Options: --tox Use `tox` instead of the default: `nosetests` --test-command= Command to use to test the newly installed package --version-prefix= Specify a prefix for version number tags + --noinput To be used in conjuction with one of the increment + options above, this option stops `changes` from + confirming the new version number. --debug Debug output. The commands do the following: changelog Generates an automatic changelog from your commit messages bump_version Increments the __version__ attribute of your module's __init__ - test Runs your tests with nosetests + run_tests Runs your tests with nosetests install Attempts to install the sdist tag Tags your git repo with the new version number upload Uploads your project with setup.py clean sdist upload diff --git a/changes/cli.py b/changes/cli.py index abafdb6..61a2c28 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -5,7 +5,7 @@ changes [options] changelog changes [options] release changes [options] bump_version - changes [options] test + changes [options] run_tests changes [options] install changes [options] upload changes [options] pypi @@ -33,7 +33,7 @@ The commands do the following: changelog Generates an automatic changelog from your commit messages bump_version Increments the __version__ attribute of your module's __init__ - test Runs your tests with nosetests + run_tests Runs your tests with nosetests install Attempts to install the sdist tag Tags your git repo with the new version number upload Uploads your project with setup.py clean sdist upload @@ -54,7 +54,7 @@ from changes.config import arguments from changes.changelog import changelog -from changes.testing import test, run_test_command +from changes.testing import run_tests from changes.vcs import tag, commit_version_change log = logging.getLogger(__name__) @@ -169,10 +169,9 @@ def initialise(): def main(): arguments = initialise() - commands = ['release', 'changelog', 'test', 'bump_version', 'tag', + commands = ['release', 'changelog', 'run_tests', 'bump_version', 'tag', 'upload', 'install', 'pypi'] - suppress_version_prompt_for = ['test', 'upload'] - + suppress_version_prompt_for = ['run_tests', 'upload'] if arguments['--new-version']: arguments['new_version'] = arguments['--new-version'] diff --git a/changes/testing.py b/changes/testing.py index 14a87d1..e490735 100644 --- a/changes/testing.py +++ b/changes/testing.py @@ -6,7 +6,7 @@ log = logging.getLogger(__name__) -def test(): +def run_tests(): command = 'nosetests' if arguments['--tox']: command = 'tox' From bd9beae5938e25591130a7220abf5f025be7364f Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 15:37:03 +0200 Subject: [PATCH 12/30] Refactor packaging module --- changes/cli.py | 74 +---------------------------------------- changes/packaging.py | 74 +++++++++++++++++++++++++++++++++++++++++ tests/test_packaging.py | 10 ++++++ 3 files changed, 85 insertions(+), 73 deletions(-) create mode 100644 changes/packaging.py create mode 100644 tests/test_packaging.py diff --git a/changes/cli.py b/changes/cli.py index 61a2c28..bf35051 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -45,15 +45,14 @@ from docopt import docopt -from path import path import logging -import virtualenv import changes from changes import attributes, config, probe, shell, version from changes.config import arguments from changes.changelog import changelog +from changes.packaging import install, upload, pypi from changes.testing import run_tests from changes.vcs import tag, commit_version_change @@ -71,77 +70,6 @@ def bump_version(): ) - -def make_virtualenv(): - tmp_dir = tempfile.mkdtemp() - virtualenv.create_environment(tmp_dir, site_packages=False) - return tmp_dir - - -def install(): - app_name, dry_run, new_version = common_arguments() - - result = shell.execute('python setup.py clean sdist', dry_run=dry_run) - if result: - tmp_dir = make_virtualenv() - try: - virtualenv.install_sdist( - arguments[''], - 'dist/%s-%s.tar.gz' % (app_name, new_version), - '%s/bin/python' % tmp_dir - ) - log.info('Successfully installed %s sdist', app_name) - if run_test_command(): - log.info('Successfully ran test command: %s', - arguments['--test-command']) - except: - raise Exception('Error installing %s sdist', app_name) - - path(tmp_dir).rmtree(path(tmp_dir)) - - -def upload(): - app_name, dry_run, new_version = common_arguments() - pypi = arguments['--pypi'] - - upload = 'python setup.py clean sdist upload' - if pypi: - upload = upload + '-r %s' % pypi - - if not shell.execute(upload, dry_run=dry_run): - raise Exception('Error uploading') - else: - log.info('Succesfully uploaded %s %s', app_name, new_version) - - -def pypi(): - app_name, dry_run, _ = common_arguments() - pypi = arguments['--pypi'] - package_index = 'pypi' - - tmp_dir = make_virtualenv() - install = '%s/bin/pip install %s' % (tmp_dir, app_name) - - if pypi: - install = install + '-i %s' % pypi - package_index = pypi - - try: - result = shell.execute(install, dry_run=dry_run) - if result: - log.info('Successfully installed %s from %s', - app_name, package_index) - else: - log.error('Failed to install %s from %s', - app_name, package_index) - - run_test_command() - except: - raise Exception('Error installing %s from %s', app_name, package_index) - - path(tmp_dir).rmtree(path(tmp_dir)) - - def release(): try: if not arguments['--skip-changelog']: diff --git a/changes/packaging.py b/changes/packaging.py new file mode 100644 index 0000000..faff806 --- /dev/null +++ b/changes/packaging.py @@ -0,0 +1,74 @@ +import virtualenv +from path import path + +from changes import config, shell, testing + + +def make_virtualenv(): + tmp_dir = tempfile.mkdtemp() + virtualenv.create_environment(tmp_dir, site_packages=False) + return tmp_dir + + +def install(): + app_name, dry_run, new_version = config.common_arguments() + + result = shell.execute('python setup.py clean sdist', dry_run=dry_run) + if result: + tmp_dir = make_virtualenv() + try: + virtualenv.install_sdist( + arguments[''], + 'dist/%s-%s.tar.gz' % (app_name, new_version), + '%s/bin/python' % tmp_dir + ) + log.info('Successfully installed %s sdist', app_name) + if testing.run_test_command(): + log.info('Successfully ran test command: %s', + arguments['--test-command']) + except: + raise Exception('Error installing %s sdist', app_name) + + path(tmp_dir).rmtree(path(tmp_dir)) + + +def upload(): + app_name, dry_run, new_version = config.common_arguments() + pypi = arguments['--pypi'] + + upload = 'python setup.py clean sdist upload' + if pypi: + upload = upload + '-r %s' % pypi + + if not shell.execute(upload, dry_run=dry_run): + raise Exception('Error uploading') + else: + log.info('Succesfully uploaded %s %s', app_name, new_version) + + +def pypi(): + app_name, dry_run, _ = config.common_arguments() + pypi = arguments['--pypi'] + package_index = 'pypi' + + tmp_dir = make_virtualenv() + install = '%s/bin/pip install %s' % (tmp_dir, app_name) + + if pypi: + install = install + '-i %s' % pypi + package_index = pypi + + try: + result = shell.execute(install, dry_run=dry_run) + if result: + log.info('Successfully installed %s from %s', + app_name, package_index) + else: + log.error('Failed to install %s from %s', + app_name, package_index) + + testing.run_test_command() + except: + raise Exception('Error installing %s from %s', app_name, package_index) + + path(tmp_dir).rmtree(path(tmp_dir)) diff --git a/tests/test_packaging.py b/tests/test_packaging.py new file mode 100644 index 0000000..f318b32 --- /dev/null +++ b/tests/test_packaging.py @@ -0,0 +1,10 @@ +from changes import config, packaging +from . import BaseTestCase + + +class PackagingTestCase(BaseTestCase): + # def make_virtualenv(): + # def install(): + # def upload(): + # def pypi(): + pass \ No newline at end of file From 17d5f9fd8c7c849af668f7190c2e93d8b2358be5 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 16:45:39 +0200 Subject: [PATCH 13/30] Refactor bump_version --- changes/cli.py | 12 +----------- changes/version.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index bf35051..d4754dc 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -54,22 +54,12 @@ from changes.packaging import install, upload, pypi from changes.testing import run_tests +from changes.version import bump_version from changes.vcs import tag, commit_version_change log = logging.getLogger(__name__) -def bump_version(): - app_name, dry_run, new_version = config.common_arguments() - - attributes.replace_attribute( - app_name, - '__version__', - new_version, - dry_run=dry_run) - - - ) def release(): try: if not arguments['--skip-changelog']: diff --git a/changes/version.py b/changes/version.py index fb3e92e..5c14d30 100644 --- a/changes/version.py +++ b/changes/version.py @@ -7,6 +7,16 @@ log = logging.getLogger(__name__) +def bump_version(): + app_name, dry_run, new_version = config.common_arguments() + + attributes.replace_attribute( + app_name, + '__version__', + new_version, + dry_run=dry_run) + + def current_version(app_name): return attributes.extract_attribute(app_name, '__version__') From 95d424d9b7b6a6bed42df698f51bc612b75a3e65 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 18 Oct 2013 16:54:01 +0200 Subject: [PATCH 14/30] Cleanup --- changes/cli.py | 8 ++------ changes/config.py | 10 +++++++--- changes/version.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index d4754dc..f8234bc 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -40,18 +40,14 @@ pypi Attempts to install your package from pypi release Runs all the previous commands """ - -import re - +import logging from docopt import docopt -import logging import changes -from changes import attributes, config, probe, shell, version +from changes import probe from changes.config import arguments from changes.changelog import changelog - from changes.packaging import install, upload, pypi from changes.testing import run_tests from changes.version import bump_version diff --git a/changes/config.py b/changes/config.py index 37a56cf..eb2164c 100644 --- a/changes/config.py +++ b/changes/config.py @@ -1,18 +1,22 @@ CHANGELOG = 'CHANGELOG.md' arguments = {} + def common_arguments(): """ Return common arguments :return: tuple of , --dry-run, new_version """ - version_prefix = arguments['--version-prefix'] + + app_name = arguments[''] + + version_prefix = arguments.get('--version-prefix') new_version = arguments['new_version'] common_arguments = ( - arguments[''], + app_name, arguments['--dry-run'], version_prefix + new_version if version_prefix else new_version, ) - return common_arguments \ No newline at end of file + return common_arguments diff --git a/changes/version.py b/changes/version.py index 5c14d30..9a1106e 100644 --- a/changes/version.py +++ b/changes/version.py @@ -2,7 +2,7 @@ import semantic_version -from changes import util, attributes +from changes import config, util, attributes log = logging.getLogger(__name__) From 0e0505b18fceef14fa8285350d3be4f6cb8ade44 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Wed, 23 Oct 2013 09:21:59 +0200 Subject: [PATCH 15/30] Use subshell to avoid changing directories --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d16f2eb..ef18791 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ Lint the project with: Generate the documentation with: - cd docs && PYTHONPATH=.. make singlehtml + (cd docs && make singlehtml) To monitor changes to Python files and execute flake8 and nosetests automatically, execute the following from the root project directory: From 7d27e3790275886fd709b4019e953adb976a397a Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Wed, 23 Oct 2013 12:56:33 +0200 Subject: [PATCH 16/30] Wrap sh calls to check for dry_run --- changes/shell.py | 14 +++++--------- tests/test_shell.py | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/test_shell.py diff --git a/changes/shell.py b/changes/shell.py index fec3908..fff2f9e 100644 --- a/changes/shell.py +++ b/changes/shell.py @@ -1,17 +1,13 @@ import logging -import iterpipes +from changes import config log = logging.getLogger(__name__) -def execute(command, dry_run=True): - log.debug('executing %s', command) - if not dry_run: - try: - return [result for result in iterpipes.linecmd(command)(None)] - except iterpipes.CalledProcessError, e: - log.debug('return code: %s, output: %s', e.returncode, e.output) - return False +def handle_dry_run(function, *args): + if not config.arguments.get('--dry-run', True): + return function(*args) else: + log.debug('dry run of %s %s, skipping' % (function, args)) return True diff --git a/tests/test_shell.py b/tests/test_shell.py new file mode 100644 index 0000000..30c9a09 --- /dev/null +++ b/tests/test_shell.py @@ -0,0 +1,25 @@ +import sh +from unittest2 import TestCase + +from changes import config, shell + + +class ShellTestCase(TestCase): + + def test_handle_dry_run(self): + self.assertEquals( + '', + shell.handle_dry_run( + sh.diff, + ('README.md', 'README.md') + ) + ) + + def test_handle_dry_run_true(self): + config.arguments['--dry-run'] = True + self.assertTrue( + shell.handle_dry_run( + sh.diff, + ('README.md', 'README.md') + ) + ) From cf36094986bf358599354e2cc7da1c022544c0a6 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:26:25 +0200 Subject: [PATCH 17/30] Rename testing to verification Don't make nosetests run itself --- changes/cli.py | 4 ++-- changes/testing.py | 24 ------------------------ changes/verification.py | 27 +++++++++++++++++++++++++++ tests/test_testing.py | 11 ----------- 4 files changed, 29 insertions(+), 37 deletions(-) delete mode 100644 changes/testing.py create mode 100644 changes/verification.py delete mode 100644 tests/test_testing.py diff --git a/changes/cli.py b/changes/cli.py index f8234bc..e5696f8 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -49,7 +49,7 @@ from changes.config import arguments from changes.changelog import changelog from changes.packaging import install, upload, pypi -from changes.testing import run_tests +from changes.verification import run_tests from changes.version import bump_version from changes.vcs import tag, commit_version_change @@ -61,7 +61,7 @@ def release(): if not arguments['--skip-changelog']: changelog() bump_version() - test() + run_tests() commit_version_change() install() upload() diff --git a/changes/testing.py b/changes/testing.py deleted file mode 100644 index e490735..0000000 --- a/changes/testing.py +++ /dev/null @@ -1,24 +0,0 @@ -import tempfile -import logging - -from changes import shell - -log = logging.getLogger(__name__) - - -def run_tests(): - command = 'nosetests' - if arguments['--tox']: - command = 'tox' - - if not shell.execute(command, dry_run=False): - raise Exception('Test command failed') - - -def run_test_command(): - if arguments['--test-command']: - test_command = arguments['--test-command'] - result = shell.execute(test_command, dry_run=arguments['--dry-run']) - log.info('Test command "%s", returned %s', test_command, result) - else: - log.warning('Test command "%s" failed', test_command) diff --git a/changes/verification.py b/changes/verification.py new file mode 100644 index 0000000..6e9f67f --- /dev/null +++ b/changes/verification.py @@ -0,0 +1,27 @@ +import logging + +import sh + +from changes import config, shell + +log = logging.getLogger(__name__) + + +def run_tests(): + if config.arguments['--tox']: + result = sh.tox() + else: + result = sh.nosetests() + + if not result: + raise Exception('Test command failed') + else: + return True + + +def run_test_command(): + if config.arguments['--test-command']: + test_command = config.arguments['--test-command'] + result = shell.execute(sh, tuple(test_command.split(' '))) + log.info('Test command "%s", returned %s', test_command, result) + return True diff --git a/tests/test_testing.py b/tests/test_testing.py deleted file mode 100644 index b4fb99a..0000000 --- a/tests/test_testing.py +++ /dev/null @@ -1,11 +0,0 @@ -from changes import config, testing -from . import BaseTestCase - - -class TestingTestCase(BaseTestCase): - """ - def test(): - def make_virtualenv(): - def run_test_command(): - """ - pass \ No newline at end of file From 9abb0cab6dc5ced331731b8f5b24e540f84fb3bd Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:27:12 +0200 Subject: [PATCH 18/30] Port attributes to sh --- changes/attributes.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/changes/attributes.py b/changes/attributes.py index 87c1c66..ee2e352 100644 --- a/changes/attributes.py +++ b/changes/attributes.py @@ -4,7 +4,7 @@ from path import path -from changes import shell +import sh log = logging.getLogger(__name__) @@ -32,10 +32,7 @@ def replace_attribute(app_name, attribute_name, new_value, dry_run=True): if not dry_run: path(tmp_file).move(init_file) else: - log.debug(shell.execute( - 'diff %s %s' % (tmp_file, init_file), - dry_run=False - )) + log.debug(sh.diff(tmp_file, init_file, _ok_code=1)) def has_attribute(app_name, attribute_name): From 95c6b719467525a173a22b67a30cf34c139d2321 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:27:27 +0200 Subject: [PATCH 19/30] Port changelog to sh --- changes/changelog.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/changes/changelog.py b/changes/changelog.py index 993dd07..9b96567 100644 --- a/changes/changelog.py +++ b/changes/changelog.py @@ -1,7 +1,8 @@ import logging import re -from changes import attributes, config, shell, version +import sh +from changes import attributes, config, version log = logging.getLogger(__name__) @@ -64,21 +65,21 @@ def changelog(): ) ] - ## vcs (todo: config templatise all these commands) - git_log = 'git log --oneline --no-merges' - version_difference = '%s..master' % version.current_version(app_name) - - git_log_content = shell.execute( - '%s %s' % (git_log, version_difference), - dry_run=False - ) + git_log_content = sh.git.log( + '--oneline', + '--no-merges', + '%s..master' % version.current_version(app_name), + _tty_out=False + ).split('\n') log.debug('content: %s' % git_log_content) if not git_log_content: - log.debug('sniffing initial release, drop tags: %s', git_log) - git_log_content = shell.execute(git_log, dry_run=False) - - ## /vcs + log.debug('sniffing initial release, drop tags') + git_log_content = sh.git.log( + '--oneline', + '--no-merges', + _tty_out=False + ).split('\n') git_log_content = replace_sha_with_commit_link(git_log_content) @@ -87,10 +88,9 @@ def changelog(): # makes change log entries into bullet points if git_log_content: [ - changelog_content.append('* %s' % line) + changelog_content.append('* %s\n' % line) if line else line for line in git_log_content[:-1] - # for all except the last line? ] write_new_changelog( From ab8bd202efdf1950a050aaaef07b748708f35666 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:30:43 +0200 Subject: [PATCH 20/30] iterpipes => sh --- requirements.txt | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7473950..202be53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ coverage < 4.0.0 docopt < 1.0.0 -iterpipes < 1.0.0 flake8 < 3.0.0 mock < 2.0.0 nose < 2.0.0 @@ -9,6 +8,7 @@ path.py < 5.0.0 pinocchio < 1.0.0 python-coveralls < 3.0.0 semantic_version < 3.0.0 +sh < 2.0.0 sphinx-bootstrap-theme < 1.0.0 sphinxcontrib-httpdomain < 2.0.0 testtube < 1.0.0 diff --git a/setup.py b/setup.py index 0c9a2b2..7a83f3c 100644 --- a/setup.py +++ b/setup.py @@ -18,10 +18,10 @@ packages=['changes'], install_requires=[ 'docopt < 1.0.0', - 'iterpipes < 1.0.0', - 'pypandoc < 1.0.0', 'path.py < 5.0.0', + 'pypandoc < 1.0.0', 'semantic_version < 3.0.0', + 'sh < 2.0.0', 'virtualenv < 2.0.0', ], entry_points={ From 5b2058868e2e70ebda98a0ff1a094897ebdd6f03 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:36:59 +0200 Subject: [PATCH 21/30] Migrate vcs to sh --- changes/vcs.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/changes/vcs.py b/changes/vcs.py index fab18fe..4465eb6 100644 --- a/changes/vcs.py +++ b/changes/vcs.py @@ -1,22 +1,35 @@ +import logging + +import sh + from changes import config, shell +log = logging.getLogger(__name__) + def commit_version_change(): app_name, dry_run, new_version = config.common_arguments() - command = 'git commit -m %s %s/__init__.py %s' % ( - new_version, app_name, config.CHANGELOG + commit_result = shell.handle_dry_run( + sh.git.commit, + ('-m', new_version, '%s/__init__.py' % app_name, config.CHANGELOG) ) - if not (shell.execute(command, dry_run=dry_run) and - shell.execute('git push', dry_run=dry_run)): - raise Exception('Version change commit failed') + if commit_result: + push_result = shell.handle_dry_run(sh.git.push, ()) + if not push_result: + raise Exception('Version change commit failed') + def tag(): _, dry_run, new_version = config.common_arguments() - shell.execute( - 'git tag -a %s -m "%s"' % (new_version, new_version), - dry_run=dry_run + tag_result = shell.handle_dry_run( + sh.git.tag, + ('-a', new_version, '-m', '"%s"' % new_version) ) - shell.execute('git push --tags', dry_run=dry_run) + + if tag_result: + push_tags_result = shell.handle_dry_run(sh.git.push, ('--tags')) + if not push_tags_result: + raise Exception('Tagging failed') From 0d45807f5deea432eae814644e3bbdd067ef60d6 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:37:22 +0200 Subject: [PATCH 22/30] Migrate probe to sh --- changes/probe.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/changes/probe.py b/changes/probe.py index 1685227..3a40dd4 100644 --- a/changes/probe.py +++ b/changes/probe.py @@ -1,7 +1,9 @@ import logging from os.path import exists -from changes import attributes, shell +import sh + +from changes import attributes log = logging.getLogger(__name__) @@ -18,7 +20,7 @@ def probe_project(app_name): """ log.info('Checking project for changes requirements.') # on [github](https://github.com) - git_remotes = shell.execute('git remote -v', dry_run=False) + git_remotes = sh.git.remote('-v') on_github = any(['github.com' in remote for remote in git_remotes]) log.info('On Github? %s', on_github) @@ -45,7 +47,6 @@ def probe_project(app_name): log.info('Has module metadata? %s', has_metadata) # supports executing tests with `nosetests` or `tox` - log.debug(requirements_contents) runs_tests = ( has_requirement('nose', requirements_contents) or has_requirement('tox', requirements_contents) From 182fb0a8fe0a8b124db87fda5ca39f35e195e88a Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:39:10 +0200 Subject: [PATCH 23/30] Fix versioning --- changes/cli.py | 6 ++++-- changes/version.py | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index e5696f8..3826e95 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -45,7 +45,7 @@ from docopt import docopt import changes -from changes import probe +from changes import config, probe, util, version from changes.config import arguments from changes.changelog import changelog from changes.packaging import install, upload, pypi @@ -83,9 +83,11 @@ def initialise(): def main(): arguments = initialise() + version_arguments = ['--major', '--minor', '--patch'] commands = ['release', 'changelog', 'run_tests', 'bump_version', 'tag', 'upload', 'install', 'pypi'] suppress_version_prompt_for = ['run_tests', 'upload'] + if arguments['--new-version']: arguments['new_version'] = arguments['--new-version'] @@ -100,6 +102,6 @@ def main(): arguments['new_version'] = version.get_new_version( app_name, version.current_version(app_name), - **version.extract_version_arguments(arguments) + **util.extract_arguments(arguments, version_arguments) ) globals()[command]() diff --git a/changes/version.py b/changes/version.py index 9a1106e..28bbcbe 100644 --- a/changes/version.py +++ b/changes/version.py @@ -2,7 +2,7 @@ import semantic_version -from changes import config, util, attributes +from changes import config, attributes log = logging.getLogger(__name__) @@ -35,17 +35,15 @@ def get_new_version(app_name, current_version, 'What is the release version for "%s" ' '[Default: %s]: ' % ( app_name, proposed_new_version - **util.extract_arguments( ) ) + if not new_version: return proposed_new_version.strip() else: return new_version.strip() - - def increment(version, major=False, minor=False, patch=True): """ Increment a semantic version From e22b2d562c7ee8957660cc9a4e76f50155fb0a2a Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 10:39:36 +0200 Subject: [PATCH 24/30] Port packaging to sh --- changes/packaging.py | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/changes/packaging.py b/changes/packaging.py index faff806..e535607 100644 --- a/changes/packaging.py +++ b/changes/packaging.py @@ -1,7 +1,13 @@ -import virtualenv +import logging +import tempfile + from path import path +import sh +import virtualenv -from changes import config, shell, testing +from changes import config, shell, verification + +log = logging.getLogger(__name__) def make_virtualenv(): @@ -13,19 +19,19 @@ def make_virtualenv(): def install(): app_name, dry_run, new_version = config.common_arguments() - result = shell.execute('python setup.py clean sdist', dry_run=dry_run) + result = shell.handle_dry_run(sh.python, ('setup.py', 'clean', 'sdist')) if result: tmp_dir = make_virtualenv() try: virtualenv.install_sdist( - arguments[''], + config.arguments[''], 'dist/%s-%s.tar.gz' % (app_name, new_version), '%s/bin/python' % tmp_dir ) log.info('Successfully installed %s sdist', app_name) - if testing.run_test_command(): + if verification.run_test_command(): log.info('Successfully ran test command: %s', - arguments['--test-command']) + config.arguments['--test-command']) except: raise Exception('Error installing %s sdist', app_name) @@ -34,13 +40,14 @@ def install(): def upload(): app_name, dry_run, new_version = config.common_arguments() - pypi = arguments['--pypi'] + pypi = config.arguments['--pypi'] - upload = 'python setup.py clean sdist upload' + upload_args = 'setup.py clean sdist upload'.split(' ') if pypi: - upload = upload + '-r %s' % pypi + upload_args.extend(['-r', pypi]) - if not shell.execute(upload, dry_run=dry_run): + upload_result = shell.handle_dry_run(sh.python, tuple(upload_args)) + if not upload_result: raise Exception('Error uploading') else: log.info('Succesfully uploaded %s %s', app_name, new_version) @@ -48,18 +55,20 @@ def upload(): def pypi(): app_name, dry_run, _ = config.common_arguments() - pypi = arguments['--pypi'] - package_index = 'pypi' tmp_dir = make_virtualenv() - install = '%s/bin/pip install %s' % (tmp_dir, app_name) + install_args = ('%s/bin/pip install %s' % (tmp_dir, app_name)).split(' ') + package_index = 'pypi' + pypi = config.arguments['--pypi'] if pypi: - install = install + '-i %s' % pypi + install_args.extend('-i', pypi) package_index = pypi + install_args = tuple(install_args) + log.debug(install_args) try: - result = shell.execute(install, dry_run=dry_run) + result = shell.handle_dry_run(sh, install_args) if result: log.info('Successfully installed %s from %s', app_name, package_index) @@ -67,8 +76,9 @@ def pypi(): log.error('Failed to install %s from %s', app_name, package_index) - testing.run_test_command() + verification.run_test_command() except: + log.exception('error installing %s from %s', app_name, package_index) raise Exception('Error installing %s from %s', app_name, package_index) path(tmp_dir).rmtree(path(tmp_dir)) From 756cd631c792922e89e32f78a05cd4c1e8a3e1a5 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 11:02:07 +0200 Subject: [PATCH 25/30] Add subprocess execution for testing installation in a virtualenv --- changes/compat.py | 23 +++++++++++++++++++++++ changes/packaging.py | 8 +++----- changes/shell.py | 13 +++++++++++++ tests/test_packaging.py | 19 +++++++++++++------ 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 changes/compat.py diff --git a/changes/compat.py b/changes/compat.py new file mode 100644 index 0000000..b161f39 --- /dev/null +++ b/changes/compat.py @@ -0,0 +1,23 @@ +from subprocess import Popen, PIPE, CalledProcessError + + +def check_output(*popenargs, **kwargs): + """ + Run command with arguments and return its output as a byte string. + + Backported from Python 2.7 as it's implemented as pure python on stdlib. + + >>> check_output(['/usr/bin/python', '--version']) + Python 2.6.2 + """ + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + error = CalledProcessError(retcode, cmd) + error.output = output + raise error + return output diff --git a/changes/packaging.py b/changes/packaging.py index e535607..74e4ef8 100644 --- a/changes/packaging.py +++ b/changes/packaging.py @@ -57,18 +57,16 @@ def pypi(): app_name, dry_run, _ = config.common_arguments() tmp_dir = make_virtualenv() - install_args = ('%s/bin/pip install %s' % (tmp_dir, app_name)).split(' ') + install_cmd = '%s/bin/pip install %s' % (tmp_dir, app_name) package_index = 'pypi' pypi = config.arguments['--pypi'] if pypi: - install_args.extend('-i', pypi) + install_cmd += '-i %s' % pypi package_index = pypi - install_args = tuple(install_args) - log.debug(install_args) try: - result = shell.handle_dry_run(sh, install_args) + result = shell.execute(install_cmd, dry_run=dry_run) if result: log.info('Successfully installed %s from %s', app_name, package_index) diff --git a/changes/shell.py b/changes/shell.py index fff2f9e..acd1412 100644 --- a/changes/shell.py +++ b/changes/shell.py @@ -1,6 +1,7 @@ import logging from changes import config +from changes.compat import check_output, CalledProcessError log = logging.getLogger(__name__) @@ -11,3 +12,15 @@ def handle_dry_run(function, *args): else: log.debug('dry run of %s %s, skipping' % (function, args)) return True + + +def execute(command, dry_run=True): + if not dry_run: + try: + return check_output(command.split(' ')).split('\n') + except CalledProcessError as e: + log.debug('return code: %s, output: %s', e.returncode, e.output) + return False + else: + log.debug('dry run of %s, skipping' % command) + return True diff --git a/tests/test_packaging.py b/tests/test_packaging.py index f318b32..1699bc9 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -1,10 +1,17 @@ -from changes import config, packaging +from changes import packaging from . import BaseTestCase class PackagingTestCase(BaseTestCase): - # def make_virtualenv(): - # def install(): - # def upload(): - # def pypi(): - pass \ No newline at end of file + + def test_install(self): + packaging.install() + + def test_make_virtualenv(self): + packaging.make_virtualenv() + + def test_upload(self): + packaging.upload() + + def test_pypi(self): + packaging.pypi() From 7561d38aeeba74b7aebada94c0e65dead3146f0e Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 12:00:40 +0200 Subject: [PATCH 26/30] Dry runs --- tests/test_packaging.py | 5 ++++- tests/test_shell.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_packaging.py b/tests/test_packaging.py index 1699bc9..91d1bca 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -1,17 +1,20 @@ -from changes import packaging +from changes import config, packaging from . import BaseTestCase class PackagingTestCase(BaseTestCase): def test_install(self): + config.arguments['--dry-run'] = True packaging.install() def test_make_virtualenv(self): packaging.make_virtualenv() def test_upload(self): + config.arguments['--dry-run'] = True packaging.upload() def test_pypi(self): + config.arguments['--dry-run'] = True packaging.pypi() diff --git a/tests/test_shell.py b/tests/test_shell.py index 30c9a09..a952cbb 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -7,6 +7,7 @@ class ShellTestCase(TestCase): def test_handle_dry_run(self): + config.arguments['--dry-run'] = False self.assertEquals( '', shell.handle_dry_run( From e2e97d1a1e53ed46e0ce789d10054b059012f23e Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 12:13:04 +0200 Subject: [PATCH 27/30] Linting --- changes/__init__.py | 2 +- changes/util.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changes/__init__.py b/changes/__init__.py index 88e555f..f21a308 100644 --- a/changes/__init__.py +++ b/changes/__init__.py @@ -7,4 +7,4 @@ from .cli import main # noqa -from .pandoc import md2rst +from .pandoc import md2rst # noqa diff --git a/changes/util.py b/changes/util.py index 3486b2f..5d596ea 100644 --- a/changes/util.py +++ b/changes/util.py @@ -10,11 +10,13 @@ def extract(dictionary, keys): (k, dictionary[k]) for k in keys if k in dictionary ) + def extract_arguments(arguments, long_keys, key_prefix='--'): long_arguments = extract( arguments, long_keys, ) return dict([ - (key.replace(key_prefix, ''), value) for key, value in long_arguments.items() + (key.replace(key_prefix, ''), value) + for key, value in long_arguments.items() ]) From 2426764d36781a42f76865326706a1446c57ddab Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 12:20:33 +0200 Subject: [PATCH 28/30] abc --- changes/cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index 3826e95..7c42ff1 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -46,12 +46,13 @@ import changes from changes import config, probe, util, version -from changes.config import arguments from changes.changelog import changelog +from changes.config import arguments from changes.packaging import install, upload, pypi +from changes.vcs import tag, commit_version_change from changes.verification import run_tests from changes.version import bump_version -from changes.vcs import tag, commit_version_change + log = logging.getLogger(__name__) From 79361eedeec2d518ad77d57f936e356273942fe3 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 24 Oct 2013 12:20:55 +0200 Subject: [PATCH 29/30] Add noinput option --- changes/cli.py | 4 ++++ changes/version.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/changes/cli.py b/changes/cli.py index 7c42ff1..da795a0 100644 --- a/changes/cli.py +++ b/changes/cli.py @@ -28,6 +28,9 @@ --tox Use `tox` instead of the default: `nosetests` --test-command= Command to use to test the newly installed package --version-prefix= Specify a prefix for version number tags + --noinput To be used in conjuction with one of the increment + options above, this option stops `changes` from + confirming the new version number. --debug Debug output. The commands do the following: @@ -103,6 +106,7 @@ def main(): arguments['new_version'] = version.get_new_version( app_name, version.current_version(app_name), + arguments.get('--noinput', False), **util.extract_arguments(arguments, version_arguments) ) globals()[command]() diff --git a/changes/version.py b/changes/version.py index 28bbcbe..38af088 100644 --- a/changes/version.py +++ b/changes/version.py @@ -21,7 +21,7 @@ def current_version(app_name): return attributes.extract_attribute(app_name, '__version__') -def get_new_version(app_name, current_version, +def get_new_version(app_name, current_version, no_input, major=False, minor=False, patch=True): proposed_new_version = increment( @@ -31,17 +31,17 @@ def get_new_version(app_name, current_version, patch=patch ) - new_version = raw_input( - 'What is the release version for "%s" ' - '[Default: %s]: ' % ( - app_name, proposed_new_version + if no_input: + new_version = proposed_new_version.strip() + else: + new_version = raw_input( + 'What is the release version for "%s" ' + '[Default: %s]: ' % ( + app_name, proposed_new_version + ) ) - ) - if not new_version: - return proposed_new_version.strip() - else: - return new_version.strip() + return new_version.strip() def increment(version, major=False, minor=False, patch=True): From d958a9fe363281c889087636bfd403451400ebf8 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 25 Oct 2013 10:39:02 +0200 Subject: [PATCH 30/30] Stop defaulting patch to true --- changes/version.py | 2 +- tests/test_version.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changes/version.py b/changes/version.py index 38af088..8674620 100644 --- a/changes/version.py +++ b/changes/version.py @@ -22,7 +22,7 @@ def current_version(app_name): def get_new_version(app_name, current_version, no_input, - major=False, minor=False, patch=True): + major=False, minor=False, patch=False): proposed_new_version = increment( current_version, diff --git a/tests/test_version.py b/tests/test_version.py index c77b0be..f00c480 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -40,6 +40,7 @@ def test_get_new_version(self): version.get_new_version( self.module_name, '0.0.1', - minor=True + True, + minor=True, ) )