def current_version_option_validator(ctx, param, value): """If a version string is provided, validates it. Otherwise it tries to determine the current version from the last Git tag that matches ``tag_pattern`` option. Return a :class:`~braulio.version.Version` object or **None**. """ current_version = None if value: try: current_version = Version(value) except ValueError: ctx.fail(f"{value} is not a valid version string") # Look for the last git tag for the curren version git = Git() tag_pattern = ctx.params["tag_pattern"] versions = tag_analyzer(git.tags, tag_pattern, Version) # User provided current version. Try to find a tag that match it. if current_version: for version in versions: if version == current_version: current_version = version break elif versions: current_version = versions[0] ctx.params[ "current_tag"] = current_version.tag if current_version else None ctx.params["versions"] = versions return current_version
def test_pass_files_to_commit(self, mocked_git_add, mocked_run_command): git = Git() git.commit("A good message", files=["file1.py", "file2.py"]) # When autospec is used on a class method, the self parameter # must be passed to too. git.add.assert_called_with(git, "file1.py", "file2.py") command = ["git", "commit", "-m", '"A good message"'] mocked_run_command.assert_called_with(command)
def test_log_all_commits(self, mocked_run_command, fake_git_log_output): mocked_run_command.return_value = fake_git_log_output git = Git() commits = git.log() mocked_run_command.assert_called_with(["git", "log"]) assert len(commits) == 12 assert isinstance(commits[0], Commit) first = commits[0] assert first.header == "Fix lorem ipsum dolor sit amet" last = commits[-1] assert last.header == "Add additional information (#26)"
def test_get_tags(self, mocked_run_command, command_output, expected): mocked_run_command.return_value = command_output git = Git() lst = git.tag() mocked_run_command.assert_called_with([ "git", "tag", "-l", "--sort=creatordate", "--format=%(creatordate:short)%09%(refname:strip=2)", ]) assert type(lst) == list assert len(lst) == expected if expected > 0: for _tag in lst: assert type(_tag) == Tag assert lst[0].name == "v0.0.6" assert lst[5].name == "v0.0.1"
def release( ctx, bump, bump_type, commit_flag, message, tag_flag, confirm_flag, changelog_file, files, label_pattern, label_position, tag_pattern, current_version, stage, merge_pre, current_tag=None, versions=None, ): """Release a new version. Determines the next version by inspecting commit messages, updates the changelog, commit the changes and tag the repository with the new version. """ # If there isn't a current version, assume version 0.0.0 current_version = current_version or Version() git = Git() from_tag = current_tag.name if current_tag else None # Delimiter of the block to be removed from the changelog file remove_pre_chglog = None # Look for the last final release version if the user want it if merge_pre and current_version.stage != "final": remove_pre_chglog = [current_version.string] for version in versions: if version.stage == "final": from_tag = version.tag.name remove_pre_chglog.append(version.string) break commit_list = git.log(_from=from_tag) msg(f'{label("Current version")} {current_version}') msg(f'{label("Commits found")} {len(commit_list)} since last release') if not commit_list: click.echo(" › Nothing to release.") ctx.exit() semantic_commits = commit_analyzer(commit_list, label_pattern, label_position) release_data = ReleaseDataTree(semantic_commits) bump_version_to = None # --bump, --major, --minor, --patch or commit message based version # are taken into account only if the current version is in final stage. if current_version.stage == "final": # --bump have precedence over any of --major, --minor or --patch bump_version_to = bump.string if bump else bump_type # Any manual bump have precedence over commit message based versions. bump_version_to = bump_version_to or release_data.bump_version_to try: new_version = get_next_version(current_version, bump_version_to, stage) except ValueError as e: ctx.fail(e) if not new_version: msg("The release of a lower versions is not supported for now.") ctx.abort() new_tag_name = tag_pattern.format(version=new_version.string) msg(f'{label("New version")} {new_version}') msg(f'{label("Changelog file")} {changelog_file.name}') # Messages about what tasks will be performed msg("Braulio will perform the next tasks :") msg(f" Update {len(files) + 1} files.", prefix="") msg(" Add a release commit.", prefix="", silence=not commit_flag) msg( f" Tag the repository with {new_tag_name}", prefix="", silence=not tag_flag, ) msg("", prefix="") # Print just a new line if confirm_flag or click.confirm(f"{prefix_mark}Continue?"): msg("Update changelog ", nl=False) update_chglog( changelog_file, new_version=new_version, current_version=current_version, release_data=release_data, remove=remove_pre_chglog, ) msg(check_mark, prefix="") try: update_files(files, str(current_version), str(new_version)) except ValueError as e: click.echo(e) ctx.abort() if commit_flag: message_args = {"new_version": new_version.string} if "{current_version}" in message: message_args["current_version"] = current_version.string commit_message = message.format(**message_args) msg(f"Add commit: {commit_message}", nl=False) files = [str(changelog_file)] + list(files) git.commit(commit_message, files=files) msg(f" {check_mark}", prefix="") if tag_flag: msg(f"Add tag {new_tag_name}", nl=False) git.tag(new_tag_name) msg(f" {check_mark}", prefix="") if "current_version" in ctx.obj.cfg_file_options: update_config_file("current_version", new_version.string) msg(f"Version {new_version} released successfully", suffix=" 🎉")
def test_add_tag(self, mocked_run_command): git = Git() git.tag("tagname") mocked_run_command.assert_called_with( ["git", "tag", "-a", "tagname", "-m", '""'])
def test_log_range(self, mocked_run_command, f, t, revision_range): git = Git() git.log(_from=f, to=t) mocked_run_command.assert_called_with(["git", "log"] + revision_range)
def test_call_to_run_command(self, mocked_run_command): git = Git() git.commit("A message") command = ["git", "commit", "-m", '"A message"'] mocked_run_command.assert_called_with(command)
def test_when_provided_files_does_not_exits(self): git = Git() error = "No such file or directory: this_does_not_exist.py" with pytest.raises(FileNotFoundError, match=error): git.add("this_does_not_exist.py")
def test_run_command_call(self, mocked_run_command, files, expected): git = Git() git.add(*files) command = ("git", "add") mocked_run_command.assert_called_with(command + expected)