Add script for change version. (#8443)

- Replace jvm regex replacement script with mvn command.
- Replace cmake script for python version with python script.
- Automate rest of the manual steps.

The script can handle dev branch, rc release, and formal release version.
This commit is contained in:
Jiaming Yuan
2022-11-24 00:06:39 +08:00
committed by GitHub
parent 5f1a6fca0d
commit 284dcf8d22
8 changed files with 175 additions and 85 deletions

View File

@@ -0,0 +1,141 @@
"""
1. Modify ``CMakeLists.txt`` in source tree and ``python-package/xgboost/VERSION`` if
needed, run CMake .
If this is a RC release, the Python version has the form <major>.<minor>.<patch>rc1
2. Modify ``DESCRIPTION`` and ``configure.ac`` in R-package. Run ``autoreconf``.
3. Run ``mvn`` in ``jvm-packages``
If this is a RC release, the version for JVM packages has the form
<major>.<minor>.<patch>-RC1
"""
import argparse
import os
import re
import subprocess
import sys
import tempfile
from test_utils import JVM_PACKAGES, PY_PACKAGE, R_PACKAGE, ROOT, cd
@cd(ROOT)
def cmake(major: int, minor: int, patch: int) -> None:
version = f"{major}.{minor}.{patch}"
with open("CMakeLists.txt", "r") as fd:
cmakelist = fd.read()
pattern = r"project\(xgboost LANGUAGES .* VERSION ([0-9]+\.[0-9]+\.[0-9]+)\)"
matched = re.search(pattern, cmakelist)
assert matched, "Couldn't find the version string in CMakeLists.txt."
print(matched.start(1), matched.end(1))
cmakelist = cmakelist[: matched.start(1)] + version + cmakelist[matched.end(1) :]
with open("CMakeLists.txt", "w") as fd:
fd.write(cmakelist)
with tempfile.TemporaryDirectory() as tmpdir:
subprocess.call(["cmake", "-S", ".", "-B", tmpdir])
@cd(PY_PACKAGE)
def pypkg(
major: int, minor: int, patch: int, rc: int, is_rc: bool, is_dev: bool
) -> None:
version = f"{major}.{minor}.{patch}"
pyver_path = os.path.join("xgboost", "VERSION")
pyver = version
if is_rc:
pyver = pyver + f"rc{rc}"
if is_dev:
pyver = pyver + "-dev"
with open(pyver_path, "w") as fd:
fd.write(pyver)
@cd(R_PACKAGE)
def rpkg(major: int, minor: int, patch: int) -> None:
version = f"{major}.{minor}.{patch}.1"
# Version: 2.0.0.1
desc_path = "DESCRIPTION"
with open(desc_path, "r") as fd:
description = fd.read()
pattern = r"Version:\ ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"
matched = re.search(pattern, description)
assert matched, "Couldn't find version string in DESCRIPTION."
description = (
description[: matched.start(1)] + version + description[matched.end(1) :]
)
with open(desc_path, "w") as fd:
fd.write(description)
config_path = "configure.ac"
# AC_INIT([xgboost],[2.0.0],[],[xgboost],[])
version = f"{major}.{minor}.{patch}"
with open(config_path, "r") as fd:
config = fd.read()
pattern = (
r"AC_INIT\(\[xgboost\],\[([0-9]+\.[0-9]+\.[0-9]+)\],\[\],\[xgboost\],\[\]\)"
)
matched = re.search(pattern, config)
assert matched, "Couldn't find version string in configure.ac"
config = config[: matched.start(1)] + version + config[matched.end(1) :]
with open(config_path, "w") as fd:
fd.write(config)
subprocess.check_call(["autoreconf"])
@cd(JVM_PACKAGES)
def jvmpkgs(
major: int, minor: int, patch: int, rc: int, is_rc: bool, is_dev: bool
) -> None:
version = f"{major}.{minor}.{patch}"
if is_dev:
version += "-SNAPSHOT"
if is_rc:
version += f"-RC{rc}"
subprocess.check_call(["mvn", "versions:set", f"-DnewVersion={version}"])
@cd(ROOT)
def main(args: argparse.Namespace) -> None:
major = args.major
minor = args.minor
patch = args.patch
rc = args.rc
is_rc = args.is_rc == 1
is_dev = args.is_dev == 1
if is_rc and is_dev:
raise ValueError("It cannot be both a rc and a dev branch.")
if is_rc:
assert rc >= 1, "RC version starts from 1."
else:
assert rc == 0, "RC is not used."
cmake(major, minor, patch)
pypkg(major, minor, patch, rc, is_rc, is_dev)
rpkg(major, minor, patch)
jvmpkgs(major, minor, patch, rc, is_rc, is_dev)
print(
"""
Please examine the changes and commit. Be aware that mvn might leave backup files in the
source tree.
"""
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--major", type=int)
parser.add_argument("--minor", type=int)
parser.add_argument("--patch", type=int)
parser.add_argument("--rc", type=int, default=0)
parser.add_argument("--is-rc", type=int, choices=[0, 1])
parser.add_argument("--is-dev", type=int, choices=[0, 1])
args = parser.parse_args()
try:
main(args)
except Exception as e:
print("Error:", e, file=sys.stderr)
exit(-1)

View File

@@ -6,10 +6,9 @@ from multiprocessing import Pool, cpu_count
from typing import Dict, Tuple
from pylint import epylint
from test_utils import DirectoryExcursion, print_time, record_time
from test_utils import PY_PACKAGE, ROOT, cd, print_time, record_time
CURDIR = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
PROJECT_ROOT = os.path.normpath(os.path.join(CURDIR, os.path.pardir, os.path.pardir))
@record_time
@@ -45,20 +44,20 @@ Please run the following command on your machine to address the formatting error
@record_time
@cd(PY_PACKAGE)
def run_mypy(rel_path: str) -> bool:
with DirectoryExcursion(os.path.join(PROJECT_ROOT, "python-package")):
path = os.path.join(PROJECT_ROOT, rel_path)
ret = subprocess.run(["mypy", path])
if ret.returncode != 0:
return False
return True
path = os.path.join(ROOT, rel_path)
ret = subprocess.run(["mypy", path])
if ret.returncode != 0:
return False
return True
class PyLint:
"""A helper for running pylint, mostly copied from dmlc-core/scripts."""
def __init__(self) -> None:
self.pypackage_root = os.path.join(PROJECT_ROOT, "python-package/")
self.pypackage_root = os.path.join(ROOT, "python-package/")
self.pylint_cats = set(["error", "warning", "convention", "refactor"])
self.pylint_opts = [
"--extension-pkg-whitelist=numpy",
@@ -147,9 +146,6 @@ def main(args: argparse.Namespace) -> None:
"tests/python/test_data_iterator.py",
"tests/python/test_quantile_dmatrix.py",
"tests/python-gpu/test_gpu_data_iterator.py",
"tests/ci_build/lint_python.py",
"tests/ci_build/test_r_package.py",
"tests/ci_build/test_utils.py",
"tests/test_distributed/test_with_spark/",
"tests/test_distributed/test_gpu_with_spark/",
# demo
@@ -157,6 +153,11 @@ def main(args: argparse.Namespace) -> None:
"demo/guide-python/cat_in_the_dat.py",
"demo/guide-python/categorical.py",
"demo/guide-python/spark_estimator_examples.py",
# CI
"tests/ci_build/lint_python.py",
"tests/ci_build/test_r_package.py",
"tests/ci_build/test_utils.py",
"tests/ci_build/change_version.py",
]
]
if not all(black_results):
@@ -195,14 +196,17 @@ def main(args: argparse.Namespace) -> None:
# tests
"tests/python/test_data_iterator.py",
"tests/python-gpu/test_gpu_data_iterator.py",
"tests/ci_build/lint_python.py",
"tests/ci_build/test_r_package.py",
"tests/ci_build/test_utils.py",
"tests/test_distributed/test_with_spark/test_data.py",
"tests/test_distributed/test_gpu_with_spark/test_data.py",
"tests/test_distributed/test_gpu_with_dask/test_gpu_with_dask.py",
# CI
"tests/ci_build/lint_python.py",
"tests/ci_build/test_r_package.py",
"tests/ci_build/test_utils.py",
"tests/ci_build/change_version.py",
]
):
subprocess.check_call(["mypy", "--version"])
sys.exit(-1)
if args.pylint == 1:

View File

@@ -6,14 +6,7 @@ import subprocess
from pathlib import Path
from platform import system
from test_utils import DirectoryExcursion, cd, print_time, record_time
ROOT = os.path.normpath(
os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir
)
)
r_package = os.path.join(ROOT, "R-package")
from test_utils import R_PACKAGE, ROOT, DirectoryExcursion, cd, print_time, record_time
def get_mingw_bin() -> str:
@@ -153,7 +146,7 @@ def check_rpackage(path: str) -> None:
raise ValueError("Suspicious NOTE.")
@cd(r_package)
@cd(R_PACKAGE)
@record_time
def check_rmarkdown() -> None:
assert system() != "Windows", "Document test doesn't support Windows."
@@ -171,7 +164,7 @@ def check_rmarkdown() -> None:
)
@cd(r_package)
@cd(R_PACKAGE)
@record_time
def test_with_autotools() -> None:
"""Windows only test. No `--as-cran` check, only unittests. We don't want to manage
@@ -240,7 +233,7 @@ def test_with_cmake(args: argparse.Namespace) -> None:
)
else:
raise ValueError("Wrong compiler")
with DirectoryExcursion(r_package):
with DirectoryExcursion(R_PACKAGE):
subprocess.check_call(
[
R,

View File

@@ -70,3 +70,13 @@ def print_time() -> None:
"Elapsed:",
f"{v['total'].seconds} secs",
)
ROOT = os.path.normpath(
os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir
)
)
R_PACKAGE = os.path.join(ROOT, "R-package")
JVM_PACKAGES = os.path.join(ROOT, "jvm-packages")
PY_PACKAGE = os.path.join(ROOT, "python-package")