Add option to create release note from user-provided template

This is useful when automating the release note creation (when
automating commit creation).

Change-Id: I11793aaa3232b0f2c44521a998d466f427eecd93
This commit is contained in:
Juan Antonio Osorio Robles 2017-10-13 09:13:57 +03:00
parent bbe3543f78
commit 93a063978e
5 changed files with 73 additions and 1 deletions

View File

@ -40,6 +40,20 @@ The ``--edit`` option opens the new note in a text editor.
... Opens the editor set in the EDITOR environment variable, editing the new file ... ... Opens the editor set in the EDITOR environment variable, editing the new file ...
Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
The ``--from-template`` option allows you to use a pre-defined file and use
that as the release note.
::
$ reno new slug-goes-here --from-template my-file.yaml
... Creates a release note using the provided file my-file.yaml ...
Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
.. note::
You can also combine the flags ``--edit`` and ``--from-template``
to create a release note from a specified file and immediately start an
editor to modify the new file.
By default, the new note is created under ``./releasenotes/notes``. By default, the new note is created under ``./releasenotes/notes``.
The ``--rel-notes-dir`` command-line flag changes the parent directory The ``--rel-notes-dir`` command-line flag changes the parent directory

View File

@ -0,0 +1,6 @@
---
features:
- |
The --from-template flag was added to the release note creation command.
This enables one to create a release note from a pre-defined template,
which is useful when automating the creation of commits.

View File

@ -47,6 +47,16 @@ def _edit_file(filename):
return True return True
def _get_user_template(template_file):
if not os.path.exists(template_file):
raise ValueError(
'The provided template file %s doesn\'t '
'exist' % template_file,
)
with open(template_file, 'r') as f:
return f.read()
def create_cmd(args, conf): def create_cmd(args, conf):
"Create a new release note file from the template." "Create a new release note file from the template."
# NOTE(dhellmann): There is a short race window where we might try # NOTE(dhellmann): There is a short race window where we might try
@ -57,7 +67,11 @@ def create_cmd(args, conf):
# concern. # concern.
slug = args.slug.replace(' ', '-') slug = args.slug.replace(' ', '-')
filename = _pick_note_file_name(conf.notespath, slug) filename = _pick_note_file_name(conf.notespath, slug)
_make_note_file(filename, conf.template) if args.from_template:
template = _get_user_template(args.from_template)
else:
template = conf.template
_make_note_file(filename, template)
if args.edit and not _edit_file(filename): if args.edit and not _edit_file(filename):
print('Was unable to edit the new note. EDITOR environment variable ' print('Was unable to edit the new note. EDITOR environment variable '
'is missing!') 'is missing!')

View File

@ -99,6 +99,10 @@ def main(argv=sys.argv[1:]):
action='store_true', action='store_true',
help='Edit note after its creation (require EDITOR env variable)', help='Edit note after its creation (require EDITOR env variable)',
) )
do_new.add_argument(
'--from-template',
help='Template to get the release note from.',
)
do_new.add_argument( do_new.add_argument(
'slug', 'slug',
help='descriptive title of note (keep it short)', help='descriptive title of note (keep it short)',

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
import fixtures import fixtures
import io
import mock import mock
from reno import create from reno import create
@ -45,6 +46,16 @@ class TestCreate(base.TestCase):
super(TestCreate, self).setUp() super(TestCreate, self).setUp()
self.tmpdir = self.useFixture(fixtures.TempDir()).path self.tmpdir = self.useFixture(fixtures.TempDir()).path
def _create_user_template(self, contents):
filename = create._pick_note_file_name(self.tmpdir, 'usertemplate')
with open(filename, 'w') as f:
f.write(contents)
return filename
def _get_file_path_from_output(self, output):
# Get the last consecutive word from the output and remove the newline
return output[output.rfind(" ") + 1:-1]
def test_create_from_template(self): def test_create_from_template(self):
filename = create._pick_note_file_name(self.tmpdir, 'theslug') filename = create._pick_note_file_name(self.tmpdir, 'theslug')
create._make_note_file(filename, 'i-am-a-template') create._make_note_file(filename, 'i-am-a-template')
@ -52,6 +63,29 @@ class TestCreate(base.TestCase):
body = f.read() body = f.read()
self.assertEqual('i-am-a-template', body) self.assertEqual('i-am-a-template', body)
def test_create_from_user_template(self):
args = mock.Mock()
args.from_template = self._create_user_template('i-am-a-user-template')
args.slug = 'theslug'
args.edit = False
conf = mock.Mock()
conf.notespath = self.tmpdir
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
create.create_cmd(args, conf)
filename = self._get_file_path_from_output(fake_out.getvalue())
with open(filename, 'r') as f:
body = f.read()
self.assertEqual('i-am-a-user-template', body)
def test_create_from_user_template_fails_because_unexistent_file(self):
args = mock.Mock()
args.from_template = 'some-unexistent-file.yaml'
args.slug = 'theslug'
args.edit = False
conf = mock.Mock()
conf.notespath = self.tmpdir
self.assertRaises(ValueError, create.create_cmd, args, conf)
def test_edit(self): def test_edit(self):
self.useFixture(fixtures.EnvironmentVariable('EDITOR', 'myeditor')) self.useFixture(fixtures.EnvironmentVariable('EDITOR', 'myeditor'))
with mock.patch('subprocess.call') as call_mock: with mock.patch('subprocess.call') as call_mock: