Skip to content

Commit 243f0b1

Browse files
committed
refactor(runtime-ctx): change repo to repo_dir for context management
1 parent 23ce5de commit 243f0b1

File tree

10 files changed

+174
-112
lines changed

10 files changed

+174
-112
lines changed

semantic_release/cli/changelog_writer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def write_changelog_files(
111111
hvcs_client: HvcsBase,
112112
noop: bool = False,
113113
) -> list[str]:
114-
project_dir = Path(runtime_ctx.repo.working_dir)
114+
project_dir = Path(runtime_ctx.repo_dir)
115115
template_dir = runtime_ctx.template_dir
116116

117117
changelog_context = make_changelog_context(

semantic_release/cli/cli_context.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
RuntimeContext,
1515
)
1616
from semantic_release.cli.util import load_raw_config_file, rprint
17-
from semantic_release.errors import InvalidConfiguration, NotAReleaseBranch
17+
from semantic_release.errors import (
18+
DetachedHeadGitError,
19+
InvalidConfiguration,
20+
NotAReleaseBranch,
21+
)
1822

1923
if TYPE_CHECKING:
2024
from semantic_release.cli.config import GlobalCommandLineOptions
@@ -75,7 +79,7 @@ def _init_runtime_ctx(self) -> RuntimeContext:
7579
raw_config,
7680
global_cli_options=self.global_opts,
7781
)
78-
except NotAReleaseBranch as exc:
82+
except (DetachedHeadGitError, NotAReleaseBranch) as exc:
7983
rprint(f"[bold {'red' if self.global_opts.strict else 'orange1'}]{exc!s}")
8084
# If not strict, exit 0 so other processes can continue. For example, in
8185
# multibranch CI it might be desirable to run a non-release branch's pipeline

semantic_release/cli/commands/changelog.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING
55

66
import click
7+
from git import Repo
78

89
from semantic_release.changelog import ReleaseHistory
910
from semantic_release.cli.changelog_writer import (
@@ -64,12 +65,13 @@ def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None:
6465
translator = runtime.version_translator
6566
hvcs_client = runtime.hvcs_client
6667

67-
release_history = ReleaseHistory.from_git_history(
68-
repo=runtime.repo,
69-
translator=translator,
70-
commit_parser=runtime.commit_parser,
71-
exclude_commit_patterns=runtime.changelog_excluded_commit_patterns,
72-
)
68+
with Repo(str(runtime.repo_dir)) as git_repo:
69+
release_history = ReleaseHistory.from_git_history(
70+
repo=git_repo,
71+
translator=translator,
72+
commit_parser=runtime.commit_parser,
73+
exclude_commit_patterns=runtime.changelog_excluded_commit_patterns,
74+
)
7375

7476
write_changelog_files(
7577
runtime_ctx=runtime,

semantic_release/cli/commands/publish.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING
55

66
import click
7+
from git import Repo
78

89
from semantic_release.cli.util import noop_report
910
from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
@@ -57,14 +58,16 @@ def publish(cli_ctx: CliContextObj, tag: str) -> None:
5758
"""Build and publish a distribution to a VCS release."""
5859
ctx = click.get_current_context()
5960
runtime = cli_ctx.runtime_ctx
60-
repo = runtime.repo
6161
hvcs_client = runtime.hvcs_client
6262
translator = runtime.version_translator
6363
dist_glob_patterns = runtime.dist_glob_patterns
6464

65+
with Repo(str(runtime.repo_dir)) as git_repo:
66+
repo_tags = git_repo.tags
67+
6568
if tag == "latest":
6669
try:
67-
tag = str(tags_and_versions(repo.tags, translator)[0][0])
70+
tag = str(tags_and_versions(repo_tags, translator)[0][0])
6871
except IndexError:
6972
ctx.fail(
7073
str.join(
@@ -77,7 +80,7 @@ def publish(cli_ctx: CliContextObj, tag: str) -> None:
7780
)
7881
)
7982

80-
if tag not in {tag.name for tag in repo.tags}:
83+
if tag not in {tag.name for tag in repo_tags}:
8184
log.error("Tag '%s' not found in local repository!", tag)
8285
ctx.exit(1)
8386

semantic_release/cli/commands/version.py

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import click
1212
import shellingham # type: ignore[import]
1313
from click_option_group import MutuallyExclusiveOptionGroup, optgroup
14+
from git import Repo
1415
from git.exc import GitCommandError
1516
from requests import HTTPError
1617

@@ -25,16 +26,20 @@
2526
from semantic_release.enums import LevelBump
2627
from semantic_release.errors import BuildDistributionsError, UnexpectedResponse
2728
from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
28-
from semantic_release.version import Version, next_version, tags_and_versions
29+
from semantic_release.version import (
30+
Version,
31+
VersionTranslator,
32+
next_version,
33+
tags_and_versions,
34+
)
2935

3036
if TYPE_CHECKING: # pragma: no cover
37+
from pathlib import Path
3138
from typing import ContextManager, Iterable, Mapping
3239

33-
from git import Repo
3440
from git.refs.tag import Tag
3541

3642
from semantic_release.cli.cli_context import CliContextObj
37-
from semantic_release.version import VersionTranslator
3843
from semantic_release.version.declaration import VersionDeclarationABC
3944

4045

@@ -64,17 +69,20 @@ def is_forced_prerelease(
6469
)
6570

6671

67-
def last_released(
68-
repo: Repo, translator: VersionTranslator
69-
) -> tuple[Tag, Version] | None:
70-
ts_and_vs = tags_and_versions(repo.tags, translator)
72+
def last_released(repo_dir: Path, tag_format: str) -> tuple[Tag, Version] | None:
73+
with Repo(str(repo_dir)) as git_repo:
74+
ts_and_vs = tags_and_versions(
75+
git_repo.tags, VersionTranslator(tag_format=tag_format)
76+
)
77+
7178
return ts_and_vs[0] if ts_and_vs else None
7279

7380

7481
def version_from_forced_level(
75-
repo: Repo, forced_level_bump: LevelBump, translator: VersionTranslator
82+
repo_dir: Path, forced_level_bump: LevelBump, translator: VersionTranslator
7683
) -> Version:
77-
ts_and_vs = tags_and_versions(repo.tags, translator)
84+
with Repo(str(repo_dir)) as git_repo:
85+
ts_and_vs = tags_and_versions(git_repo.tags, translator)
7886

7987
# If we have no tags, return the default version
8088
if not ts_and_vs:
@@ -110,15 +118,13 @@ def version_from_forced_level(
110118

111119

112120
def apply_version_to_source_files(
113-
repo: Repo,
121+
repo_dir: Path,
114122
version_declarations: Iterable[VersionDeclarationABC],
115123
version: Version,
116124
noop: bool = False,
117125
) -> list[str]:
118-
working_dir = os.getcwd() if repo.working_dir is None else repo.working_dir
119-
120126
paths = [
121-
str(declaration.path.resolve().relative_to(working_dir))
127+
str(declaration.path.resolve().relative_to(repo_dir))
122128
for declaration in version_declarations
123129
]
124130

@@ -405,12 +411,12 @@ def version( # noqa: C901
405411
"""
406412
ctx = click.get_current_context()
407413
runtime = cli_ctx.runtime_ctx
408-
repo = runtime.repo
409414
translator = runtime.version_translator
410415

411416
# We can short circuit updating the release if we are only printing the last released version
412417
if print_last_released or print_last_released_tag:
413-
if not (last_release := last_released(repo, translator)):
418+
# TODO: get tag format a better way
419+
if not (last_release := last_released(runtime.repo_dir, translator.tag_format)):
414420
log.warning("No release tags found.")
415421
return
416422

@@ -425,7 +431,6 @@ def version( # noqa: C901
425431
prerelease=runtime.prerelease,
426432
)
427433
hvcs_client = runtime.hvcs_client
428-
changelog_excluded_commit_patterns = runtime.changelog_excluded_commit_patterns
429434
assets = runtime.assets
430435
commit_author = runtime.commit_author
431436
commit_message = runtime.commit_message
@@ -454,14 +459,15 @@ def version( # noqa: C901
454459
make_vcs_release &= push_changes
455460

456461
if not forced_level_bump:
457-
new_version = next_version(
458-
repo=repo,
459-
translator=translator,
460-
commit_parser=parser,
461-
prerelease=prerelease,
462-
major_on_zero=major_on_zero,
463-
allow_zero_version=runtime.allow_zero_version,
464-
)
462+
with Repo(str(runtime.repo_dir)) as git_repo:
463+
new_version = next_version(
464+
repo=git_repo,
465+
translator=translator,
466+
commit_parser=parser,
467+
prerelease=prerelease,
468+
major_on_zero=major_on_zero,
469+
allow_zero_version=runtime.allow_zero_version,
470+
)
465471
else:
466472
log.warning(
467473
"Forcing a '%s' release due to '--%s' command-line flag",
@@ -474,7 +480,9 @@ def version( # noqa: C901
474480
)
475481

476482
new_version = version_from_forced_level(
477-
repo=repo, forced_level_bump=forced_level_bump, translator=translator
483+
repo_dir=runtime.repo_dir,
484+
forced_level_bump=forced_level_bump,
485+
translator=translator,
478486
)
479487

480488
# We only turn the forced version into a prerelease if the user has specified
@@ -514,10 +522,11 @@ def version( # noqa: C901
514522
# Print the new version so that command-line output capture will work
515523
click.echo(version_to_print)
516524

517-
# TODO: performance improvement - cache the result of tags_and_versions (previously done in next_version())
518-
previously_released_versions = {
519-
v for _, v in tags_and_versions(repo.tags, translator)
520-
}
525+
with Repo(str(runtime.repo_dir)) as git_repo:
526+
# TODO: performance improvement - cache the result of tags_and_versions (previously done in next_version())
527+
previously_released_versions = {
528+
v for _, v in tags_and_versions(git_repo.tags, translator)
529+
}
521530

522531
# If the new version has already been released, we fail and abort if strict;
523532
# otherwise we exit with 0.
@@ -540,12 +549,13 @@ def version( # noqa: C901
540549
if print_only or print_only_tag:
541550
return
542551

543-
release_history = ReleaseHistory.from_git_history(
544-
repo=repo,
545-
translator=translator,
546-
commit_parser=parser,
547-
exclude_commit_patterns=changelog_excluded_commit_patterns,
548-
)
552+
with Repo(str(runtime.repo_dir)) as git_repo:
553+
release_history = ReleaseHistory.from_git_history(
554+
repo=git_repo,
555+
translator=translator,
556+
commit_parser=parser,
557+
exclude_commit_patterns=runtime.changelog_excluded_commit_patterns,
558+
)
549559

550560
rprint(f"[bold green]The next version is: [white]{new_version!s}[/white]! :rocket:")
551561

@@ -578,7 +588,7 @@ def version( # noqa: C901
578588

579589
# Apply the new version to the source files
580590
files_with_new_version_written = apply_version_to_source_files(
581-
repo=repo,
591+
repo_dir=runtime.repo_dir,
582592
version_declarations=runtime.version_declarations,
583593
version=new_version,
584594
noop=opts.noop,
@@ -623,11 +633,12 @@ def version( # noqa: C901
623633
# repo.index.add(all_paths_to_add, force=False) # noqa: ERA001
624634
# but since 'force' is deliberately ineffective (as in docstring) in gitpython 3.1.18
625635
# we have to do manually add each filepath, and catch the exception if it is an ignored file
626-
for updated_path in all_paths_to_add:
627-
try:
628-
repo.git.add(updated_path)
629-
except GitCommandError: # noqa: PERF203
630-
log.warning("Failed to add path (%s) to index", updated_path)
636+
with Repo(str(runtime.repo_dir)) as git_repo:
637+
for updated_path in all_paths_to_add:
638+
try:
639+
git_repo.git.add(updated_path)
640+
except GitCommandError: # noqa: PERF203
641+
log.warning("Failed to add path (%s) to index", updated_path)
631642

632643
def custom_git_environment() -> ContextManager[None]:
633644
"""
@@ -636,21 +647,25 @@ def custom_git_environment() -> ContextManager[None]:
636647
we need to throw it away and re-create it in
637648
order to use it again
638649
"""
639-
return (
640-
nullcontext()
641-
if not commit_author
642-
else repo.git.custom_environment(
643-
GIT_AUTHOR_NAME=commit_author.name,
644-
GIT_AUTHOR_EMAIL=commit_author.email,
645-
GIT_COMMITTER_NAME=commit_author.name,
646-
GIT_COMMITTER_EMAIL=commit_author.email,
650+
with Repo(str(runtime.repo_dir)) as git_repo:
651+
return (
652+
nullcontext()
653+
if not commit_author
654+
else git_repo.git.custom_environment(
655+
GIT_AUTHOR_NAME=commit_author.name,
656+
GIT_AUTHOR_EMAIL=commit_author.email,
657+
GIT_COMMITTER_NAME=commit_author.name,
658+
GIT_COMMITTER_EMAIL=commit_author.email,
659+
)
647660
)
648-
)
649661

650662
# If we haven't modified any source code then we skip trying to make a commit
651663
# and any tag that we apply will be to the HEAD commit (made outside of
652664
# running PSR
653-
if not repo.index.diff("HEAD"):
665+
with Repo(str(runtime.repo_dir)) as git_repo:
666+
curr_index_diff = git_repo.index.diff("HEAD")
667+
668+
if not curr_index_diff:
654669
log.info("No local changes to add to any commit, skipping")
655670

656671
elif commit_changes and opts.noop:
@@ -686,8 +701,8 @@ def custom_git_environment() -> ContextManager[None]:
686701
)
687702

688703
elif commit_changes:
689-
with custom_git_environment():
690-
repo.git.commit(
704+
with Repo(str(runtime.repo_dir)) as git_repo, custom_git_environment():
705+
git_repo.git.commit(
691706
m=commit_message.format(version=new_version),
692707
date=int(commit_date.timestamp()),
693708
no_verify=no_verify,
@@ -709,14 +724,17 @@ def custom_git_environment() -> ContextManager[None]:
709724
)
710725
)
711726
else:
712-
with custom_git_environment():
713-
repo.git.tag("-a", new_version.as_tag(), m=new_version.as_tag())
727+
with Repo(str(runtime.repo_dir)) as git_repo, custom_git_environment():
728+
git_repo.git.tag("-a", new_version.as_tag(), m=new_version.as_tag())
714729

715730
if push_changes:
716731
remote_url = runtime.hvcs_client.remote_url(
717732
use_token=not runtime.ignore_token_for_push
718733
)
719-
active_branch = repo.active_branch.name
734+
735+
with Repo(str(runtime.repo_dir)) as git_repo:
736+
active_branch = git_repo.active_branch.name
737+
720738
if commit_changes and opts.noop:
721739
noop_report(
722740
indented(
@@ -727,7 +745,8 @@ def custom_git_environment() -> ContextManager[None]:
727745
)
728746
)
729747
elif commit_changes:
730-
repo.git.push(remote_url, active_branch)
748+
with Repo(str(runtime.repo_dir)) as git_repo:
749+
git_repo.git.push(remote_url, active_branch)
731750

732751
if create_tag and opts.noop:
733752
noop_report(
@@ -744,7 +763,8 @@ def custom_git_environment() -> ContextManager[None]:
744763
# Resolves issue #803 where a tag that already existed was pushed and caused
745764
# a failure. Its not clear why there was an incorrect tag (likely user error change)
746765
# but we will avoid possibly pushing an separate tag that we didn't create.
747-
repo.git.push(remote_url, "tag", new_version.as_tag())
766+
with Repo(str(runtime.repo_dir)) as git_repo:
767+
git_repo.git.push(remote_url, "tag", new_version.as_tag())
748768

749769
# Update GitHub Actions output value now that release has occurred
750770
gha_output.released = True

0 commit comments

Comments
 (0)