11
11
import click
12
12
import shellingham # type: ignore[import]
13
13
from click_option_group import MutuallyExclusiveOptionGroup , optgroup
14
+ from git import Repo
14
15
from git .exc import GitCommandError
15
16
from requests import HTTPError
16
17
25
26
from semantic_release .enums import LevelBump
26
27
from semantic_release .errors import BuildDistributionsError , UnexpectedResponse
27
28
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
+ )
29
35
30
36
if TYPE_CHECKING : # pragma: no cover
37
+ from pathlib import Path
31
38
from typing import ContextManager , Iterable , Mapping
32
39
33
- from git import Repo
34
40
from git .refs .tag import Tag
35
41
36
42
from semantic_release .cli .cli_context import CliContextObj
37
- from semantic_release .version import VersionTranslator
38
43
from semantic_release .version .declaration import VersionDeclarationABC
39
44
40
45
@@ -64,17 +69,20 @@ def is_forced_prerelease(
64
69
)
65
70
66
71
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
+
71
78
return ts_and_vs [0 ] if ts_and_vs else None
72
79
73
80
74
81
def version_from_forced_level (
75
- repo : Repo , forced_level_bump : LevelBump , translator : VersionTranslator
82
+ repo_dir : Path , forced_level_bump : LevelBump , translator : VersionTranslator
76
83
) -> 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 )
78
86
79
87
# If we have no tags, return the default version
80
88
if not ts_and_vs :
@@ -110,15 +118,13 @@ def version_from_forced_level(
110
118
111
119
112
120
def apply_version_to_source_files (
113
- repo : Repo ,
121
+ repo_dir : Path ,
114
122
version_declarations : Iterable [VersionDeclarationABC ],
115
123
version : Version ,
116
124
noop : bool = False ,
117
125
) -> list [str ]:
118
- working_dir = os .getcwd () if repo .working_dir is None else repo .working_dir
119
-
120
126
paths = [
121
- str (declaration .path .resolve ().relative_to (working_dir ))
127
+ str (declaration .path .resolve ().relative_to (repo_dir ))
122
128
for declaration in version_declarations
123
129
]
124
130
@@ -405,12 +411,12 @@ def version( # noqa: C901
405
411
"""
406
412
ctx = click .get_current_context ()
407
413
runtime = cli_ctx .runtime_ctx
408
- repo = runtime .repo
409
414
translator = runtime .version_translator
410
415
411
416
# We can short circuit updating the release if we are only printing the last released version
412
417
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 )):
414
420
log .warning ("No release tags found." )
415
421
return
416
422
@@ -425,7 +431,6 @@ def version( # noqa: C901
425
431
prerelease = runtime .prerelease ,
426
432
)
427
433
hvcs_client = runtime .hvcs_client
428
- changelog_excluded_commit_patterns = runtime .changelog_excluded_commit_patterns
429
434
assets = runtime .assets
430
435
commit_author = runtime .commit_author
431
436
commit_message = runtime .commit_message
@@ -454,14 +459,15 @@ def version( # noqa: C901
454
459
make_vcs_release &= push_changes
455
460
456
461
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
+ )
465
471
else :
466
472
log .warning (
467
473
"Forcing a '%s' release due to '--%s' command-line flag" ,
@@ -474,7 +480,9 @@ def version( # noqa: C901
474
480
)
475
481
476
482
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 ,
478
486
)
479
487
480
488
# We only turn the forced version into a prerelease if the user has specified
@@ -514,10 +522,11 @@ def version( # noqa: C901
514
522
# Print the new version so that command-line output capture will work
515
523
click .echo (version_to_print )
516
524
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
+ }
521
530
522
531
# If the new version has already been released, we fail and abort if strict;
523
532
# otherwise we exit with 0.
@@ -540,12 +549,13 @@ def version( # noqa: C901
540
549
if print_only or print_only_tag :
541
550
return
542
551
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
+ )
549
559
550
560
rprint (f"[bold green]The next version is: [white]{ new_version !s} [/white]! :rocket:" )
551
561
@@ -578,7 +588,7 @@ def version( # noqa: C901
578
588
579
589
# Apply the new version to the source files
580
590
files_with_new_version_written = apply_version_to_source_files (
581
- repo = repo ,
591
+ repo_dir = runtime . repo_dir ,
582
592
version_declarations = runtime .version_declarations ,
583
593
version = new_version ,
584
594
noop = opts .noop ,
@@ -623,11 +633,12 @@ def version( # noqa: C901
623
633
# repo.index.add(all_paths_to_add, force=False) # noqa: ERA001
624
634
# but since 'force' is deliberately ineffective (as in docstring) in gitpython 3.1.18
625
635
# 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 )
631
642
632
643
def custom_git_environment () -> ContextManager [None ]:
633
644
"""
@@ -636,21 +647,25 @@ def custom_git_environment() -> ContextManager[None]:
636
647
we need to throw it away and re-create it in
637
648
order to use it again
638
649
"""
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
+ )
647
660
)
648
- )
649
661
650
662
# If we haven't modified any source code then we skip trying to make a commit
651
663
# and any tag that we apply will be to the HEAD commit (made outside of
652
664
# 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 :
654
669
log .info ("No local changes to add to any commit, skipping" )
655
670
656
671
elif commit_changes and opts .noop :
@@ -686,8 +701,8 @@ def custom_git_environment() -> ContextManager[None]:
686
701
)
687
702
688
703
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 (
691
706
m = commit_message .format (version = new_version ),
692
707
date = int (commit_date .timestamp ()),
693
708
no_verify = no_verify ,
@@ -709,14 +724,17 @@ def custom_git_environment() -> ContextManager[None]:
709
724
)
710
725
)
711
726
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 ())
714
729
715
730
if push_changes :
716
731
remote_url = runtime .hvcs_client .remote_url (
717
732
use_token = not runtime .ignore_token_for_push
718
733
)
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
+
720
738
if commit_changes and opts .noop :
721
739
noop_report (
722
740
indented (
@@ -727,7 +745,8 @@ def custom_git_environment() -> ContextManager[None]:
727
745
)
728
746
)
729
747
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 )
731
750
732
751
if create_tag and opts .noop :
733
752
noop_report (
@@ -744,7 +763,8 @@ def custom_git_environment() -> ContextManager[None]:
744
763
# Resolves issue #803 where a tag that already existed was pushed and caused
745
764
# a failure. Its not clear why there was an incorrect tag (likely user error change)
746
765
# 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 ())
748
768
749
769
# Update GitHub Actions output value now that release has occurred
750
770
gha_output .released = True
0 commit comments