Skip to content

Commit

Permalink
No next version (#895)
Browse files Browse the repository at this point in the history
* Add: Make version update after release optional

Allow to deactivate the version update in the repository after a
successful release. Therefore introduce the `--no-next-version` flag for
the `pontos-release create` CLI.

* Improve formatting of help text
  • Loading branch information
bjoernricks authored Oct 5, 2023
1 parent 710fb09 commit a75043c
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 38 deletions.
75 changes: 39 additions & 36 deletions pontos/release/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from dataclasses import dataclass
from enum import IntEnum, auto
from pathlib import Path
from typing import Optional, SupportsInt
from typing import Literal, Optional, SupportsInt, Union

import httpx

Expand Down Expand Up @@ -49,7 +49,7 @@ class ReleaseInformation:
last_release_version: Optional[Version]
release_version: Version
git_release_tag: str
next_version: Version
next_version: Optional[Version]

def write_github_output(self):
with ActionIO.out() as output:
Expand All @@ -58,7 +58,7 @@ def write_github_output(self):
)
output.write("release-version", self.release_version)
output.write("git-release-tag", self.git_release_tag)
output.write("next-version", self.next_version)
output.write("next-version", self.next_version or "")


class CreateReleaseReturnValue(IntEnum):
Expand Down Expand Up @@ -142,7 +142,7 @@ async def async_run( # type: ignore[override]
versioning_scheme: VersioningScheme,
release_type: ReleaseType,
release_version: Optional[Version],
next_version: Optional[Version],
next_version: Union[Version, Literal[False], None],
git_signing_key: str,
git_remote_name: Optional[str],
git_tag_prefix: Optional[str],
Expand Down Expand Up @@ -170,7 +170,9 @@ async def async_run( # type: ignore[override]
release_version: Optional release version to use. If not set the
to be released version will be determined from the project.
next_version: Optional version to set after the release.
If not set the next development version will be set.
If set to None the next development version will be set.
If set to False the version will not be changed after the
release. Default is to update to the next development version.
git_signing_key: A GPG key ID to use for creating signatures.
git_remote_name: Name of the git remote to use.
git_tag_prefix: An optional prefix to use for creating a git tag
Expand Down Expand Up @@ -315,49 +317,50 @@ async def async_run( # type: ignore[override]
self.print_error(str(e))
return CreateReleaseReturnValue.CREATE_RELEASE_ERROR

if not next_version:
if next_version is None:
next_version = calculator.next_dev_version(release_version)

if update_project:
try:
updated = project.update_version(next_version)
self.terminal.ok(
f"Updated version after release to {next_version}"
)
except VersionError as e:
self.print_error(
f"Error while updating version after release. {e}"
)
return (
CreateReleaseReturnValue.UPDATE_VERSION_AFTER_RELEASE_ERROR
)

for f in updated.changed_files:
self.terminal.info(f"Adding changes of {f}")
self.git.add(f)

# check if files have been modified and create a commit
status = list(self.git.status())
if status:
commit_msg = f"""Automatic adjustments after release
if next_version:
if update_project:
try:
updated = project.update_version(next_version)
self.terminal.ok(
f"Updated version after release to {next_version}"
)
except VersionError as e:
self.print_error(
f"Error while updating version after release. {e}"
)
return (
CreateReleaseReturnValue.UPDATE_VERSION_AFTER_RELEASE_ERROR
)

for f in updated.changed_files:
self.terminal.info(f"Adding changes of {f}")
self.git.add(f)

# check if files have been modified and create a commit
status = list(self.git.status())
if status:
commit_msg = f"""Automatic adjustments after release
* Update to version {next_version}
"""

self.terminal.info("Committing changes after release")
self.git.commit(
commit_msg, verify=False, gpg_signing_key=git_signing_key
)
self.terminal.info("Committing changes after release")
self.git.commit(
commit_msg, verify=False, gpg_signing_key=git_signing_key
)

if not local:
self.terminal.info("Pushing changes")
self.git.push(follow_tags=True, remote=git_remote_name)
if not local:
self.terminal.info("Pushing changes")
self.git.push(follow_tags=True, remote=git_remote_name)

self.release_information = ReleaseInformation(
last_release_version=last_release_version,
release_version=release_version,
git_release_tag=git_version,
next_version=next_version,
next_version=next_version or None,
)
if ActionIO.has_output():
self.release_information.write_github_output()
Expand Down
13 changes: 11 additions & 2 deletions pontos/release/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,23 @@ def parse_args(args) -> Tuple[Optional[str], Optional[str], Namespace]:
'released version. Examples: "1.2", "2", "22.4"',
)

create_parser.add_argument(
next_version_group = create_parser.add_mutually_exclusive_group()

next_version_group.add_argument(
"--next-version",
help=(
"Sets the next version in project definition "
"after the release. Default: set to next dev version"
),
)

next_version_group.add_argument(
"--no-next-version",
help="Don't set a next version after the release.",
dest="next_version",
action="store_false",
)

create_parser.add_argument(
"--git-remote-name",
help="The git remote name to push the commits and tag to",
Expand Down Expand Up @@ -178,7 +187,7 @@ def parse_args(args) -> Tuple[Optional[str], Optional[str], Namespace]:
)
create_parser.add_argument(
"--github-pre-release",
help="Enforce uploading a release as GitHub " "pre-release. ",
help="Enforce uploading a release as GitHub pre-release. ",
action="store_true",
)

Expand Down
94 changes: 94 additions & 0 deletions tests/release/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2360,6 +2360,100 @@ def test_release_enforce_github_release(

self.assertEqual(released, CreateReleaseReturnValue.SUCCESS)

@patch("pontos.release.create.Git", autospec=True)
@patch("pontos.release.create.get_last_release_version", autospec=True)
@patch(
"pontos.release.create.CreateReleaseCommand._create_release",
autospec=True,
)
@patch(
"pontos.release.create.CreateReleaseCommand._create_changelog",
autospec=True,
)
@patch("pontos.release.create.Project._gather_commands", autospec=True)
def test_release_no_next_release(
self,
gather_commands_mock: MagicMock,
create_changelog_mock: MagicMock,
create_release_mock: AsyncMock,
get_last_release_version_mock: MagicMock,
git_mock: MagicMock,
):
current_version = PEP440Version("0.0.1")
release_version = PEP440Version("0.0.2")
next_version = PEP440Version("1.0.0.dev1")
command_mock = MagicMock(spec=GoVersionCommand)
gather_commands_mock.return_value = [command_mock]
create_changelog_mock.return_value = "A Changelog"
get_last_release_version_mock.return_value = current_version
command_mock.update_version.side_effect = [
VersionUpdate(
previous=current_version,
new=release_version,
changed_files=[Path("MyProject.conf")],
),
VersionUpdate(
previous=release_version,
new=next_version,
changed_files=[Path("MyProject.conf")],
),
]
git_instance_mock: MagicMock = git_mock.return_value
git_instance_mock.status.return_value = [
StatusEntry("M MyProject.conf")
]

_, token, args = parse_args(
[
"release",
"--project",
"foo",
"--release-type",
"patch",
"--no-next-version",
]
)

with temp_git_repository():
released = create_release(
terminal=mock_terminal(),
error_terminal=mock_terminal(),
args=args,
token=token, # type: ignore[arg-type]
)

git_instance_mock.push.assert_has_calls(
[
call(follow_tags=True, remote=None),
],
)
command_mock.update_version.assert_has_calls(
[
call(release_version, force=False),
],
)

self.assertEqual(
create_release_mock.await_args.args[1:], # type: ignore[union-attr]
(release_version, "foo", "A Changelog", False),
)

git_instance_mock.add.assert_has_calls([call(Path("MyProject.conf"))])
git_instance_mock.commit.assert_has_calls(
[
call(
"Automatic release to 0.0.2",
verify=False,
gpg_signing_key="1234",
),
]
)
git_instance_mock.tag.assert_called_once_with(
"v0.0.2", gpg_key_id="1234", message="Automatic release to 0.0.2"
)

self.assertEqual(released, CreateReleaseReturnValue.SUCCESS)


@dataclass
class Release:
Expand Down
20 changes: 20 additions & 0 deletions tests/release/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,26 @@ def test_next_version(self):

self.assertEqual(args.next_version, PEP440Version("1.2.3"))

def test_no_next_version(self):
_, _, args = parse_args(
["create", "--no-next-version", "--release-type", "patch"]
)

self.assertFalse(args.next_version)

def test_next_version_conflict(self):
with self.assertRaises(SystemExit), redirect_stderr(StringIO()):
parse_args(
[
"create",
"--release-type",
"patch",
"--no-next-version",
"--next-verson",
"1.2.3",
]
)

def test_release_type(self):
_, _, args = parse_args(["create", "--release-type", "patch"])

Expand Down

0 comments on commit a75043c

Please sign in to comment.