From e41c6c3945bd75052500deec518ea190d760c082 Mon Sep 17 00:00:00 2001 From: Ondřej Nosek Date: Mar 18 2026 00:43:35 +0000 Subject: `update`: Add --notes-file option to read notes from file Users can now provide bodhi update notes from a file using --notes-file, which is useful for multiline notes with markdown formatting. Fixes: #620 Signed-off-by: Ondřej Nosek --- diff --git a/fedpkg/cli.py b/fedpkg/cli.py index b230e1e..07fcee2 100644 --- a/fedpkg/cli.py +++ b/fedpkg/cli.py @@ -181,6 +181,10 @@ class fedpkgClient(cliClient): {0} update --type bugfix --notes 'Rebuilt' --bugs 1000 1002 + Or use a file to provide multiline notes with markdown formatting: + + {0} update --type bugfix --notes-file notes.md --bugs 1000 1002 + When all lines in template editor are commented out or deleted, the creation process is aborted. If the template keeps unchanged, {0} continues on creating update. That gives user a chance to confirm the auto-generated notes from @@ -239,10 +243,15 @@ class fedpkgClient(cliClient): type=validate_bugs, help='Bug numbers. If omitted, bug numbers will be extracted from' ' change logs.') - update_parser.add_argument( + notes_group = update_parser.add_mutually_exclusive_group() + notes_group.add_argument( '--notes', help='Update description. Multiple lines of notes could be ' 'specified. If omitted, template editor will be shown.') + notes_group.add_argument( + '--notes-file', + help='Path to a file containing update notes. Useful for multiline ' + 'notes with markdown formatting.') update_parser.add_argument( '--disable-autokarma', action='store_false', @@ -1090,8 +1099,19 @@ class fedpkgClient(cliClient): if bugs: bodhi_args['bugs'] = ','.join(bugs) + notes_content = None if self.args.notes: - bodhi_args['descr'] = self.args.notes.replace('\n', '\n ') + notes_content = self.args.notes + elif hasattr(self.args, 'notes_file') and self.args.notes_file: + try: + with io.open(self.args.notes_file, encoding='utf-8') as f: + notes_content = f.read().strip() + except IOError as e: + raise rpkgError('Failed to read notes file %s: %s' % + (self.args.notes_file, e)) + + if notes_content: + bodhi_args['descr'] = notes_content.replace('\n', '\n ') bodhi_args['changelog'] = '' else: # Use clog as default message @@ -1103,7 +1123,8 @@ class fedpkgClient(cliClient): with io.open(template_file, 'w', encoding='utf-8') as f: f.write(template) - if not self.args.update_type or not self.args.notes: + has_notes = self.args.notes or (hasattr(self.args, 'notes_file') and self.args.notes_file) + if not self.args.update_type or not has_notes: # Open the template in a text editor editor = os.getenv('EDITOR', 'vi') cmd = editor.split() # EDITOR command can contain additional arguments like "emacs -nw" diff --git a/test/test_cli.py b/test/test_cli.py index a1aae35..9b5ef3a 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -404,6 +404,44 @@ class TestUpdate(CliTestCase): self.assert_bodhi_update( cli, update_type='bugfix', notes='Line 1\nLine 2\nLine 3') + def test_create_with_notes_file(self): + """Test update with --notes-file option""" + notes_content = 'Line 1\nLine 2 with [link](https://example.com)\nLine 3' + + # Create a temporary file with notes + fd, notes_file = mkstemp(suffix='.md', text=True) + try: + with os.fdopen(fd, 'w', encoding='utf-8') as f: + f.write(notes_content) + + cli_cmd = [ + 'fedpkg-stage', '--path', self.cloned_repo_path, + 'update', '--type', 'bugfix', '--notes-file', notes_file + ] + + cli = self.get_cli(cli_cmd) + + self.assert_bodhi_update( + cli, update_type='bugfix', notes=notes_content) + # editor should not be called when --notes-file is provided + self.mock_run_command.assert_not_called() + finally: + if os.path.exists(notes_file): + os.unlink(notes_file) + + def test_notes_file_not_found(self): + """Test update with --notes-file pointing to non-existent file""" + cli_cmd = [ + 'fedpkg-stage', '--path', self.cloned_repo_path, + 'update', '--type', 'bugfix', '--notes-file', '/nonexistent/file.md' + ] + + cli = self.get_cli(cli_cmd) + + with patch('os.unlink'): + with self.assertRaisesRegex(rpkgError, 'Failed to read notes file'): + cli.update() + @patch('sys.stderr', new=io.StringIO()) def test_invalid_stable_karma_option(self): with self.assertRaises(SystemExit):