Coverage for src / mafw / devtools / cli / release / create.py: 99%
62 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-06-28 13:34 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-06-28 13:34 +0000
1# Copyright 2026 European Union
2# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
3# SPDX-License-Identifier: EUPL-1.2
4"""Click command for creating a new MAFw release."""
6from __future__ import annotations
8import click
10from mafw.devtools.dependencies.freeze import (
11 freeze_dependencies,
12 unfreeze_dependencies,
13 update_requirements_and_readme,
14)
15from mafw.devtools.git import (
16 check_main_branch,
17 commit_changes,
18 commit_dependency_unfreeze,
19 create_tag,
20 ensure_clean_git,
21 prevent_duplicate_tag,
22 push_changes,
23)
24from mafw.devtools.release.changelog import CHANGELOG_FILE, generate_changelog
25from mafw.devtools.release.checks import check_missing_release
26from mafw.devtools.release.notes import create_release_note
27from mafw.devtools.release.versioning import (
28 ABOUT_FILE,
29 NOTICE_FILE,
30 DocTargetVersionPrompt,
31 bump_version,
32 compute_doc_target_version,
33 next_minor_version,
34 normalize_hatch_segments,
35 read_current_version,
36 update_doc_target_version,
37 update_notice_version,
38 validate_doc_target_version,
39)
40from mafw.tools.shell_tools import CONSOLE
43@click.command(
44 name='create',
45 context_settings={'help_option_names': ['-h', '--help']},
46 help=(
47 'Prepare a new MAFw release from the main branch.\n\n'
48 'The command bumps the project version via hatch, updates the documentation target version, '
49 'updates NOTICE.txt, regenerates CHANGELOG.md, optionally generates release notes, '
50 'commits tracked release files, creates a local git tag, and optionally pushes '
51 'commits and tags to code.europa.eu/main using ssh.'
52 ),
53)
54@click.argument('segments', type=str)
55@click.option('--dry-run/--no-dry-run', default=False, show_default=True, help='Print commands without executing.')
56@click.option('--with-push/--without-push', default=True, show_default=True, help='Enable or skip pushing to remote.')
57@click.option(
58 '--with-release-note/--without-release-note',
59 default=True,
60 show_default=True,
61 help='Enable or skip release note generation.',
62)
63@click.option(
64 '--with-missing-release-check/--without-missing-release-check',
65 default=True,
66 show_default=True,
67 help='Enable or skip missing release guard.',
68)
69@click.option(
70 '--auto-doc-version/--manual-doc-version',
71 default=False,
72 show_default=True,
73 help='Auto-update doc target version without prompting.',
74)
75@click.option(
76 '--new-doc-version', default=None, type=click.STRING, help='Override documentation target version (major.minor).'
77)
78def create(
79 segments: str,
80 dry_run: bool,
81 with_push: bool,
82 with_release_note: bool,
83 with_missing_release_check: bool,
84 auto_doc_version: bool,
85 new_doc_version: str | None,
86) -> None:
87 """Prepare a new MAFw release from the main branch."""
88 current_version = read_current_version()
89 normalized_segments = normalize_hatch_segments(segments)
91 if dry_run:
92 CONSOLE.print(
93 'Dry-run mode enabled: commands are printed; git/changelog writes are skipped; '
94 f'version is resolved via hatch and {ABOUT_FILE} is restored afterwards.'
95 )
96 else:
97 check_main_branch()
98 ensure_clean_git()
99 if with_missing_release_check: 99 ↛ 102line 99 didn't jump to line 102 because the condition on line 99 was always true
100 check_missing_release(current_version)
102 original_pyproject_toml = freeze_dependencies(dry_run=dry_run)
104 version = bump_version(dry_run=dry_run, segment=normalized_segments)
106 doc_target_version = compute_doc_target_version(version, normalized_segments, new_doc_version)
107 if auto_doc_version:
108 CONSOLE.print(f'Documentation target version set to {doc_target_version}.')
109 else:
110 CONSOLE.print('Provide a new documentation target version.')
111 prompt_default = validate_doc_target_version(doc_target_version)
112 doc_target_version = DocTargetVersionPrompt(
113 'Documentation target version',
114 show_default=True,
115 min_version=next_minor_version(version),
116 )(default=prompt_default)
117 CONSOLE.print(f'Documentation target version selected: {doc_target_version}.')
119 update_doc_target_version(doc_target_version, dry_run=dry_run)
121 if not dry_run:
122 prevent_duplicate_tag(version)
124 update_notice_version(version, dry_run=dry_run)
125 generate_changelog(version, dry_run=dry_run)
126 update_requirements_and_readme(dry_run=dry_run)
128 release_note_path = None
129 if with_release_note:
130 release_note_path = create_release_note(version, dry_run=dry_run)
131 CONSOLE.print(f'Release note file: {release_note_path}')
133 commit_changes(version, dry_run=dry_run, include_changelog=True)
134 tag = create_tag(version, dry_run=dry_run)
136 unfreeze_dependencies(original_pyproject_toml, dry_run=dry_run)
137 update_requirements_and_readme(dry_run=dry_run)
138 commit_dependency_unfreeze(dry_run=dry_run)
140 if with_push:
141 push_changes(dry_run=dry_run)
142 else:
143 CONSOLE.print('Skipping remote push because --without-push is set.')
145 CONSOLE.print('')
146 CONSOLE.print('Release pipeline completed.')
147 CONSOLE.print(f'Version: v{version}')
148 CONSOLE.print(f'Documentation target: {doc_target_version}')
149 CONSOLE.print(f'Tag: {tag}')
150 if with_release_note and release_note_path is not None:
151 CONSOLE.print(f'Release note: {release_note_path} (left untracked)')
152 CONSOLE.print(f'{CHANGELOG_FILE} updated.')
153 CONSOLE.print(f'{NOTICE_FILE} updated.')