Skip to content

Commit

Permalink
Merge pull request #5 from michaeljoseph/improve-changelog
Browse files Browse the repository at this point in the history
Improve changelog updating
  • Loading branch information
Michael Joseph committed Sep 2, 2013
2 parents d904918 + c93c4a1 commit 6bc0d59
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 37 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ An application wanting to use `changes` must meet these requirements:

* `setup.py`
* `CHANGELOG.md`
* `app_name/__init__.py` with `__version__`
* `app_name/__init__.py` with `__version__` and `__url__`

Install `changes`:

Expand Down
96 changes: 74 additions & 22 deletions changes/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ast
import re
import subprocess
import tempfile
from subprocess import call

from docopt import docopt
import path
Expand All @@ -13,11 +14,29 @@
log = logging.getLogger(__name__)


def extract(d, keys):
return dict((k, d[k]) for k in keys if k in d)
def extract(dictionary, keys):
"""
Extract only the specified keys from a dict
:param dictionary:
:param keys:
:return dict:
"""
return dict(
(k, dictionary[k]) for k in keys if k in dictionary
)


def increment(version, major=False, minor=False, patch=True):
"""
Increment a semantic version
:param version: str of the version to increment
:param major: bool specifying major level version increment
:param minor: bool specifying minor level version increment
:param patch: bool specifying patch level version increment
:return: str of the incremented version
"""
version = semantic_version.Version(version)
if major:
version.major += 1
Expand All @@ -32,24 +51,30 @@ def increment(version, major=False, minor=False, patch=True):
return str(version)


def write_new_changelog(app_name, filename, content, dry_run=True):
def write_new_changelog(app_name, filename, content_lines, dry_run=True):
heading_and_newline = (
'# (Changelog)[%s/releases]\n\n' %
'# (Changelog)[%s/releases]\n' %
extract_attribute(app_name, '__url__')
)

with open(filename, 'r+') as f:
existing = f.readlines()

output = existing[2:]
output.insert(0, content + '\n\n')
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.writelines(output)
f.write(output)
else:
log.info('New changelog:\n%s' % '\n'.join(output))
log.info('New changelog:\n%s' % output)


def get_new_version(app_name, current_version,
Expand Down Expand Up @@ -104,7 +129,7 @@ def current_version(app_name):

def execute(commands, dry_run=True):
if not dry_run:
return call(commands)
return subprocess.check_output(commands)
else:
log.debug('execute: %s' % commands)

Expand All @@ -116,9 +141,7 @@ def version(arguments):
replace_attribute(app_name, '__version__', new_version, dry_run=dry_run)

execute(
['git', 'ci', '-m',
'"%s"' % new_version,
'%s/__init__.py' % app_name],
['git', 'ci', '-m', '"%s"' % new_version, '%s/__init__.py' % app_name],
dry_run=dry_run
)

Expand All @@ -129,26 +152,53 @@ def changelog(arguments):
app_name = arguments['<app_name>']
new_version = arguments['new_version']

changelog_content = [
'\n## [%s](https://github.com/yola/demands/compare/%s...%s)\n\n' % (
new_version, current_version(app_name), new_version
)
]

git_log_content = execute([
'git', 'log', '--oneline', '--no-merges',
'%s..master' % current_version(app_name)],
dry_run=False
).split('\n')

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,
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]]

log.debug('content: %s' % changelog_content)

write_new_changelog(
app_name,
'CHANGELOG.md', '\n'.join([
'## [%s](https://github.com/yola/demands/compare/%s...%s)\n',
'* Fill this in.'
]) % (
new_version,
current_version(app_name),
new_version
),
'CHANGELOG.md',
changelog_content,
dry_run=dry_run
)
log.info('Added content to CHANGELOG.md')

def tag(arguments):
dry_run=arguments['--dry-run']
app_name = arguments['<app_name>']
new_version = arguments['new_version']

execute(['git', 'tag', '-a', new_version, '-m', '"%s"' % new_version], dry_run=dry_run)
# fixme: check for call error
execute(['git', 'push', '--tags'], dry_run=dry_run)

def upload(arguments):
Expand All @@ -162,6 +212,7 @@ def upload(arguments):

execute(upload, dry_run=dry_run)


cli = """
changes.
Expand Down Expand Up @@ -190,6 +241,7 @@ def main():
arguments = docopt(cli, version=changes.__version__)
debug = arguments['--debug']
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)

app_name = arguments['<app_name>']
if arguments['--new-version']:
new_version = arguments['--new-version']
Expand Down
31 changes: 17 additions & 14 deletions tests/test_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

class ChangesTestCase(TestCase):

def setUp(self):
self.tmp_file = 'test_app/__init__.py'
if not os.path.exists('test_app'):
os.mkdir('test_app')

def test_increment(self):
self.assertEquals(
'1.0.0',
Expand All @@ -25,47 +30,45 @@ def test_increment(self):
)

def test_prepend_file(self):
os.mkdir('test_app')
tmp_file = 'test_app/__init__.py'

content = [
'This is the heading\n\n',
'This is the first line\n',
]

with open(tmp_file, 'w') as existing_file:
with open(self.tmp_file, 'w') as existing_file:
existing_file.writelines(content)

write_new_changelog('test_app', tmp_file, 'Now this is')
write_new_changelog('test_app', self.tmp_file, 'Now this is')

self.assertEquals(
''.join(content),
''.join(
open(tmp_file).readlines()
open(self.tmp_file).readlines()
)
)

with open(tmp_file, 'w') as existing_file:
with open(self.tmp_file, 'w') as existing_file:
existing_file.writelines(content)

write_new_changelog(
'test_app',
tmp_file,
self.tmp_file,
'Now this is',
dry_run=False
)
expected_content = [
'# (Changelog)[None/releases]\n\n',
'Now this is\n\n',
'# (Changelog)[None/releases]\n',
'Now this is\n',
'This is the first line\n'
]
self.assertEquals(
''.join(expected_content),
''.join(
open(tmp_file).readlines()
open(self.tmp_file).readlines()
)
)

os.remove(tmp_file)
os.rmdir('test_app')

def tearDown(self):
if os.path.exists(self.tmp_file):
os.remove(self.tmp_file)
os.rmdir('test_app')

0 comments on commit 6bc0d59

Please sign in to comment.