Coverage for src/hatch_ci/script.py: 84%
66 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-08 07:57 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-08 07:57 +0000
1"""create a beta branch or release a beta branch
3This script will either create a new beta branch:
5 hatch-ci make-beta ./src/package_name/__init__.py
7Or will release the beta branch and will move inot the next minor
9 hatch-ci {major|minor|micro} ./src/package_name/__init__.py
11"""
12from __future__ import annotations
14import argparse
15import logging
16import re
17import sys
18from pathlib import Path
20from . import cli, scm, tools
22log = logging.getLogger(__name__)
25def add_arguments(parser: argparse.ArgumentParser):
26 parser.add_argument("--master", help="the 'master' branch")
27 parser.add_argument(
28 "-w",
29 "--workdir",
30 help="git working dir",
31 default=Path("."),
32 type=Path,
33 )
34 parser.add_argument("mode", choices=["micro", "minor", "major", "make-beta"])
35 parser.add_argument("initfile", metavar="__init__.py", type=Path)
38def process_options(
39 options: argparse.Namespace, error: cli.ErrorFn
40) -> argparse.Namespace:
41 try:
42 options.repo = repo = scm.GitRepo(options.workdir)
43 repo.status()
44 except scm.GitError:
45 error(
46 "no git directory",
47 "It looks the repository is not a git repo",
48 hint="init the git directory",
49 )
50 log.info("working dir set to '%s'", options.workdir)
51 try:
52 branch = repo.head.shorthand
53 log.info("current branch set to '%s'", branch)
54 except scm.GitError:
55 error(
56 "invalid git repository",
57 """
58 It looks the repository doesn't have any branch,
59 you should:
60 git checkout --orphan <branch-name>
61 """,
62 hint="create a git branch",
63 )
64 return options
67@cli.cli(add_arguments, process_options, __doc__)
68def main(options) -> None:
69 # master branch
70 master = options.master or (
71 options.repo.config["init.defaultbranch"]
72 if "init.defaultbranch" in options.repo.config
73 else "master"
74 )
76 if options.repo.status(untracked_files="no", ignored=False): 76 ↛ 77line 76 didn't jump to line 77, because the condition on line 76 was never true
77 options.error(f"modified files in {options.repo.workdir}")
78 if not options.initfile.exists():
79 options.error(f"cannot find version file {options.initfile}")
81 version = tools.get_module_var(options.initfile, "__version__")
82 log.info("got version %s for branch '%s'", version, options.repo.head.shorthand)
83 if not version: 83 ↛ 84line 83 didn't jump to line 84, because the condition on line 83 was never true
84 raise tools.InvalidVersionError(f"cannot find a version in {options.initfile}")
86 # fetching all remotes
87 options.repo(["fetch", "--all"])
89 if options.mode == "make-beta":
90 if options.repo.head.name != f"refs/heads/{master}": 90 ↛ 91line 90 didn't jump to line 91, because the condition on line 90 was never true
91 options.error(
92 f"wrong branch '{options.repo.head.name}', expected '{master}'"
93 )
95 for branch in [*options.repo.branches.local, *options.repo.branches.remote]:
96 if not branch.endswith(f"beta/{version}"):
97 continue
98 options.error(f"branch '{branch}' already present")
99 log.info("creating branch '%s'", f"/beta/{version}")
100 options.repo.branch(f"beta/{version}", master)
101 print( # noqa: T201
102 tools.indent(
103 f"""
104 The release branch beta/{version} has been created.
106 To complete the release:
107 git push origin beta/{version}
109 To revert this beta branch:
110 git branch -D beta/{version}
111 """
112 ),
113 file=sys.stderr,
114 )
115 elif options.mode in {"micro", "minor", "major"}: 115 ↛ 165line 115 didn't jump to line 165, because the condition on line 115 was never false
116 # we need to be in the beta/N.M.O branch
117 expr = re.compile(r"refs/heads/beta/(?P<beta>\d+([.]\d+)*)$")
118 if not (match := expr.search(options.repo.head.name)):
119 options.error(
120 f"wrong branch '{options.repo.head.shorthand}'",
121 f"expected to be in 'beta/{version}' branch",
122 f"git checkout beta/{version}",
123 )
124 return
125 local = match.group("beta")
126 if local != version: 126 ↛ 127line 126 didn't jump to line 127, because the condition on line 126 was never true
127 options.error(f"wrong version file {version=} != {local}")
129 # create an empty commit to mark the release
130 options.repo(["commit", "--allow-empty", "-m", f"released {version}"])
132 # tag
133 options.repo(["tag", "-a", f"release/{version}", "-m", f"released {version}"])
135 # switch to master (and incorporate the commit message)
136 options.repo(["checkout", master])
137 options.repo(["merge", f"beta/{version}"])
139 # bump version
140 new_version = tools.bump_version(version, options.mode)
141 tools.set_module_var(options.initfile, "__version__", new_version)
143 # commit
144 options.repo.commit(
145 options.initfile, f"version bump {version} -> {new_version}"
146 )
148 print( # noqa: T201
149 tools.indent(
150 f"""
151 The release is almost complete.
153 To complete the release:
154 git push origin release/{version}
155 git push origin master
157 To revert this release:
158 git reset --hard HEAD~1
159 git tag -d release/{version}
160 """
161 ),
162 file=sys.stderr,
163 )
164 else:
165 options.error(f"unsupported mode {options.mode=}")
166 raise RuntimeError(f"unsupported mode {options.mode=}")
169if __name__ == "__main__":
170 main()