mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 17:12:03 +00:00
Add --dry-run option for release.py
This commit is contained in:
parent
1ec9b60c27
commit
81d419e4a2
@ -71,25 +71,34 @@ class Release:
|
||||
repo: Repo,
|
||||
release_commit: str,
|
||||
release_type: Literal["major", "minor", "patch"],
|
||||
dry_run: bool,
|
||||
):
|
||||
self.repo = repo
|
||||
self._release_commit = ""
|
||||
self.release_commit = release_commit
|
||||
self.dry_run = dry_run
|
||||
assert release_type in self.BIG + self.SMALL
|
||||
self.release_type = release_type
|
||||
self._git = Git()
|
||||
self._version = get_version_from_repo(git=self._git)
|
||||
self.release_version = self.version
|
||||
self._release_branch = ""
|
||||
self._rollback_stack = [] # type: List[str]
|
||||
|
||||
def run(self, cmd: str, cwd: Optional[str] = None, **kwargs: Any) -> str:
|
||||
def run(
|
||||
self, cmd: str, cwd: Optional[str] = None, dry_run: bool = False, **kwargs: Any
|
||||
) -> str:
|
||||
cwd_text = ""
|
||||
if cwd:
|
||||
cwd_text = f" (CWD='{cwd}')"
|
||||
if dry_run:
|
||||
logging.info("Would run command%s:\n %s", cwd_text, cmd)
|
||||
return ""
|
||||
|
||||
logging.info("Running command%s:\n %s", cwd_text, cmd)
|
||||
return self._git.run(cmd, cwd, **kwargs)
|
||||
|
||||
def set_release_branch(self):
|
||||
def set_release_info(self):
|
||||
# Fetch release commit and tags in case they don't exist locally
|
||||
self.run(f"git fetch {self.repo.url} {self.release_commit}")
|
||||
self.run(f"git fetch {self.repo.url} --tags")
|
||||
@ -97,6 +106,8 @@ class Release:
|
||||
# Get the actual version for the commit before check
|
||||
with self._checkout(self.release_commit, True):
|
||||
self.release_branch = f"{self.version.major}.{self.version.minor}"
|
||||
self.release_version = get_version_from_repo(git=self._git)
|
||||
self.release_version.with_description(self.get_stable_release_type())
|
||||
|
||||
self.read_version()
|
||||
|
||||
@ -171,17 +182,18 @@ class Release:
|
||||
if self._git.branch != "master":
|
||||
raise Exception("the script must be launched only from master")
|
||||
|
||||
self.set_release_branch()
|
||||
self.set_release_info()
|
||||
|
||||
if check_branch:
|
||||
self.check_branch()
|
||||
|
||||
with self._checkout(self.release_commit, True):
|
||||
if self.release_type in self.BIG:
|
||||
if self._version.minor >= 12 and self.release_type != "major":
|
||||
raise ValueError(
|
||||
"The relese type must be 'major' for minor versions>=12"
|
||||
)
|
||||
if self.release_type in self.BIG:
|
||||
if self._version.minor >= 12 and self.release_type != "major":
|
||||
raise ValueError(
|
||||
"The relese type must be 'major' for minor versions>=12"
|
||||
)
|
||||
|
||||
with self._checkout(self.release_commit, True):
|
||||
# Checkout to the commit, it will provide the correct current version
|
||||
if with_release_branch:
|
||||
with self.create_release_branch():
|
||||
@ -200,10 +212,19 @@ class Release:
|
||||
self.run(cmd)
|
||||
raise
|
||||
|
||||
elif self.release_type in self.SMALL:
|
||||
elif self.release_type in self.SMALL:
|
||||
with self._checkout(self.release_commit, True):
|
||||
with self.stable():
|
||||
logging.info("Stable part of the releasing is done")
|
||||
|
||||
if self.dry_run:
|
||||
logging.info("Dry running, clean out possible changes")
|
||||
rollback = self._rollback_stack.copy()
|
||||
rollback.reverse()
|
||||
for cmd in rollback:
|
||||
self.run(cmd)
|
||||
return
|
||||
|
||||
self.log_post_workflows()
|
||||
self.log_rollback()
|
||||
|
||||
@ -240,6 +261,21 @@ class Release:
|
||||
f"for {self.release_type} release"
|
||||
)
|
||||
|
||||
def _commit_cmake_contributors(self, version: ClickHouseVersion) -> None:
|
||||
update_cmake_version(version)
|
||||
update_contributors(raise_error=True)
|
||||
if self.dry_run:
|
||||
logging.info(
|
||||
"Dry running, resetting the following changes in the repo:\n%s",
|
||||
self.run(f"git diff '{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'"),
|
||||
)
|
||||
self.run(f"git checkout '{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'")
|
||||
self.run(
|
||||
f"git commit -m 'Update version to {version.string}' "
|
||||
f"'{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
|
||||
def log_rollback(self):
|
||||
if self._rollback_stack:
|
||||
rollback = self._rollback_stack.copy()
|
||||
@ -265,7 +301,6 @@ class Release:
|
||||
self.read_version()
|
||||
with self._create_branch(self.release_branch, self.release_commit):
|
||||
with self._checkout(self.release_branch, True):
|
||||
self.version.with_description(self.get_stable_release_type())
|
||||
with self._create_gh_release(False):
|
||||
with self._bump_release_branch():
|
||||
# At this point everything will rollback automatically
|
||||
@ -282,6 +317,15 @@ class Release:
|
||||
self.version.with_description(version_type)
|
||||
update_cmake_version(self.version)
|
||||
update_contributors(raise_error=True)
|
||||
if self.dry_run:
|
||||
logging.info(
|
||||
"Dry running, resetting the following changes in the repo:\n%s",
|
||||
self.run(
|
||||
f"git diff '{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'"
|
||||
),
|
||||
)
|
||||
self.run(f"git checkout '{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'")
|
||||
|
||||
# Checkouting the commit of the branch and not the branch itself,
|
||||
# then we are able to skip rollback
|
||||
with self._checkout(f"{self.release_branch}^0", False):
|
||||
@ -289,7 +333,8 @@ class Release:
|
||||
self.run(
|
||||
f"git commit -m "
|
||||
f"'Update version to {self.version.string}' "
|
||||
f"'{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'"
|
||||
f"'{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
with self._push(
|
||||
"HEAD", with_rollback_on_fail=False, remote_ref=self.release_branch
|
||||
@ -297,7 +342,7 @@ class Release:
|
||||
# DO NOT PUT ANYTHING ELSE HERE
|
||||
# The push must be the last action and mean the successful release
|
||||
self._rollback_stack.append(
|
||||
f"git push {self.repo.url} "
|
||||
f"{self.dry_run_prefix}git push {self.repo.url} "
|
||||
f"+{current_commit}:{self.release_branch}"
|
||||
)
|
||||
yield
|
||||
@ -339,6 +384,12 @@ class Release:
|
||||
def release_commit(self, release_commit: str) -> None:
|
||||
self._release_commit = commit(release_commit)
|
||||
|
||||
@property
|
||||
def dry_run_prefix(self) -> str:
|
||||
if self.dry_run:
|
||||
return "# "
|
||||
return ""
|
||||
|
||||
@contextmanager
|
||||
def _bump_release_branch(self):
|
||||
# Update only git, origal version stays the same
|
||||
@ -349,12 +400,7 @@ class Release:
|
||||
if version_type == VersionType.LTS:
|
||||
pr_labels += " --label release-lts"
|
||||
new_version.with_description(version_type)
|
||||
update_cmake_version(new_version)
|
||||
update_contributors(raise_error=True)
|
||||
self.run(
|
||||
f"git commit -m 'Update version to {new_version.string}' "
|
||||
f"'{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'"
|
||||
)
|
||||
self._commit_cmake_contributors(new_version)
|
||||
with self._push(self.release_branch):
|
||||
with self._create_gh_label(
|
||||
f"v{self.release_branch}-must-backport", "10dbed"
|
||||
@ -362,14 +408,16 @@ class Release:
|
||||
with self._create_gh_label(
|
||||
f"v{self.release_branch}-affected", "c2bfff"
|
||||
):
|
||||
# The following command is rolled back by self._push
|
||||
# The following command is rolled back by deleting branch
|
||||
# in self._push
|
||||
self.run(
|
||||
f"gh pr create --repo {self.repo} --title "
|
||||
f"'Release pull request for branch {self.release_branch}' "
|
||||
f"--head {self.release_branch} {pr_labels} "
|
||||
"--body 'This PullRequest is a part of ClickHouse release "
|
||||
"cycle. It is used by CI system only. Do not perform any "
|
||||
"changes with it.'"
|
||||
"changes with it.'",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
# Here the release branch part is done
|
||||
yield
|
||||
@ -379,18 +427,15 @@ class Release:
|
||||
self.read_version()
|
||||
self.version = self.version.update(self.release_type)
|
||||
self.version.with_description(VersionType.TESTING)
|
||||
update_cmake_version(self.version)
|
||||
update_contributors(raise_error=True)
|
||||
self.run(
|
||||
f"git commit -m 'Update version to {self.version.string}' "
|
||||
f"'{self.CMAKE_PATH}' '{self.CONTRIBUTORS_PATH}'"
|
||||
)
|
||||
self._commit_cmake_contributors(self.version)
|
||||
with self._push(helper_branch):
|
||||
body_file = get_abs_path(".github/PULL_REQUEST_TEMPLATE.md")
|
||||
# The following command is rolled back by deleting branch in self._push
|
||||
self.run(
|
||||
f"gh pr create --repo {self.repo} --title 'Update version after "
|
||||
f"release' --head {helper_branch} --body-file '{body_file}' "
|
||||
"--label 'do not test' --assignee @me"
|
||||
"--label 'do not test' --assignee @me",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
# Here the testing part is done
|
||||
yield
|
||||
@ -419,6 +464,7 @@ class Release:
|
||||
@contextmanager
|
||||
def _create_branch(self, name: str, start_point: str = "") -> Iterator[None]:
|
||||
self.run(f"git branch {name} {start_point}")
|
||||
|
||||
rollback_cmd = f"git branch -D {name}"
|
||||
self._rollback_stack.append(rollback_cmd)
|
||||
try:
|
||||
@ -432,9 +478,12 @@ class Release:
|
||||
def _create_gh_label(self, label: str, color_hex: str) -> Iterator[None]:
|
||||
# API call, https://docs.github.com/en/rest/reference/issues#create-a-label
|
||||
self.run(
|
||||
f"gh api repos/{self.repo}/labels -f name={label} -f color={color_hex}"
|
||||
f"gh api repos/{self.repo}/labels -f name={label} -f color={color_hex}",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
rollback_cmd = (
|
||||
f"{self.dry_run_prefix}gh api repos/{self.repo}/labels/{label} -X DELETE"
|
||||
)
|
||||
rollback_cmd = f"gh api repos/{self.repo}/labels/{label} -X DELETE"
|
||||
self._rollback_stack.append(rollback_cmd)
|
||||
try:
|
||||
yield
|
||||
@ -447,15 +496,19 @@ class Release:
|
||||
def _create_gh_release(self, as_prerelease: bool) -> Iterator[None]:
|
||||
with self._create_tag():
|
||||
# Preserve tag if version is changed
|
||||
tag = self.version.describe
|
||||
tag = self.release_version.describe
|
||||
prerelease = ""
|
||||
if as_prerelease:
|
||||
prerelease = "--prerelease"
|
||||
self.run(
|
||||
f"gh release create {prerelease} --repo {self.repo} "
|
||||
f"--title 'Release {tag}' '{tag}'"
|
||||
f"--title 'Release {tag}' '{tag}'",
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
rollback_cmd = (
|
||||
f"{self.dry_run_prefix}gh release delete --yes "
|
||||
f"--repo {self.repo} '{tag}'"
|
||||
)
|
||||
rollback_cmd = f"gh release delete --yes --repo {self.repo} '{tag}'"
|
||||
self._rollback_stack.append(rollback_cmd)
|
||||
try:
|
||||
yield
|
||||
@ -466,12 +519,12 @@ class Release:
|
||||
|
||||
@contextmanager
|
||||
def _create_tag(self):
|
||||
tag = self.version.describe
|
||||
self.run(f"git tag -a -m 'Release {tag}' '{tag}'")
|
||||
rollback_cmd = f"git tag -d '{tag}'"
|
||||
tag = self.release_version.describe
|
||||
self.run(f"git tag -a -m 'Release {tag}' '{tag}'", dry_run=self.dry_run)
|
||||
rollback_cmd = f"{self.dry_run_prefix}git tag -d '{tag}'"
|
||||
self._rollback_stack.append(rollback_cmd)
|
||||
try:
|
||||
with self._push(f"'{tag}'"):
|
||||
with self._push(tag):
|
||||
yield
|
||||
except (Exception, KeyboardInterrupt):
|
||||
logging.warning("Rolling back tag %s", tag)
|
||||
@ -485,9 +538,11 @@ class Release:
|
||||
if remote_ref == "":
|
||||
remote_ref = ref
|
||||
|
||||
self.run(f"git push {self.repo.url} {ref}:{remote_ref}")
|
||||
self.run(f"git push {self.repo.url} {ref}:{remote_ref}", dry_run=self.dry_run)
|
||||
if with_rollback_on_fail:
|
||||
rollback_cmd = f"git push -d {self.repo.url} {remote_ref}"
|
||||
rollback_cmd = (
|
||||
f"{self.dry_run_prefix}git push -d {self.repo.url} {remote_ref}"
|
||||
)
|
||||
self._rollback_stack.append(rollback_cmd)
|
||||
|
||||
try:
|
||||
@ -562,6 +617,11 @@ def parse_args() -> argparse.Namespace:
|
||||
"works only for a release branches, that name "
|
||||
"should be the same as '$MAJOR.$MINOR' version, e.g. 22.2",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="do not make any actual changes in the repo, just show what will be done",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@ -570,7 +630,7 @@ def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse_args()
|
||||
repo = Repo(args.repo, args.remote_protocol)
|
||||
release = Release(repo, args.commit, args.release_type)
|
||||
release = Release(repo, args.commit, args.release_type, args.dry_run)
|
||||
|
||||
release.do(args.check_dirty, args.check_branch, args.with_release_branch)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user