Coverage for src/hatch_ci/script.py: 0%
67 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-07 09:36 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-07 09:36 +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):
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:
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}":
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 options.repo(["checkout", master])
102 print( # noqa: T201
103 tools.indent(
104 f"""
105 The release branch beta/{version} has been created.
107 To complete the release:
108 git push origin beta/{version}
110 To revert this beta branch:
111 git branch -D beta/{version}
112 """
113 ),
114 file=sys.stderr,
115 )
116 elif options.mode in {"micro", "minor", "major"}:
117 # we need to be in the beta/N.M.O branch
118 expr = re.compile(r"refs/heads/beta/(?P<beta>\d+([.]\d+)*)$")
119 if not (match := expr.search(options.repo.head.name)):
120 options.error(
121 f"wrong branch '{options.repo.head.shorthand}'",
122 f"expected to be in 'beta/{version}' branch",
123 f"git checkout beta/{version}",
124 )
125 return
126 local = match.group("beta")
127 if local != version:
128 options.error(f"wrong version file {version=} != {local}")
130 # create an empty commit to mark the release
131 options.repo(["commit", "--allow-empty", "-m", f"released {version}"])
133 # tag
134 options.repo(["tag", "-a", f"release/{version}", "-m", f"released {version}"])
136 # switch to master (and incorporate the commit message)
137 options.repo(["checkout", master])
138 options.repo(["merge", f"beta/{version}"])
140 # bump version
141 new_version = tools.bump_version(version, options.mode)
142 tools.set_module_var(options.initfile, "__version__", new_version)
144 # commit
145 options.repo.commit(
146 options.initfile, f"version bump {version} -> {new_version}"
147 )
149 print( # noqa: T201
150 tools.indent(
151 f"""
152 The release is almost complete.
154 To complete the release:
155 git push origin release/{version}
156 git push origin master
158 To revert this release:
159 git reset --hard HEAD~1
160 git tag -d release/{version}
161 """
162 ),
163 file=sys.stderr,
164 )
165 else:
166 options.error(f"unsupported mode {options.mode=}")
167 raise RuntimeError(f"unsupported mode {options.mode=}")
170if __name__ == "__main__":
171 main()