|
|
|
|
|
import os |
|
import json |
|
import sys |
|
from typing import Dict, Optional |
|
import argparse |
|
|
|
VERSION_LINE_START = "__version__ = " |
|
|
|
DIRECTORIES = ["ml-agents/mlagents/trainers", "ml-agents-envs/mlagents_envs"] |
|
|
|
MLAGENTS_PACKAGE_JSON_PATH = "com.unity.ml-agents/package.json" |
|
MLAGENTS_EXTENSIONS_PACKAGE_JSON_PATH = "com.unity.ml-agents.extensions/package.json" |
|
|
|
ACADEMY_PATH = "com.unity.ml-agents/Runtime/Academy.cs" |
|
|
|
PYTHON_VERSION_FILE_TEMPLATE = """# Version of the library that will be used to upload to pypi |
|
__version__ = {version} |
|
|
|
# Git tag that will be checked to determine whether to trigger upload to pypi |
|
__release_tag__ = {release_tag} |
|
""" |
|
|
|
|
|
def _escape_non_none(s: Optional[str]) -> str: |
|
""" |
|
Returns s escaped in quotes if it is non-None, else "None" |
|
:param s: |
|
:return: |
|
""" |
|
if s is not None: |
|
return f'"{s}"' |
|
else: |
|
return "None" |
|
|
|
|
|
def extract_version_string(filename): |
|
with open(filename) as f: |
|
for line in f.readlines(): |
|
if line.startswith(VERSION_LINE_START): |
|
return line.replace(VERSION_LINE_START, "").strip() |
|
return None |
|
|
|
|
|
def check_versions() -> bool: |
|
version_by_dir: Dict[str, str] = {} |
|
for directory in DIRECTORIES: |
|
path = os.path.join(directory, "__init__.py") |
|
version = extract_version_string(path) |
|
print(f"Found version {version} for {directory}") |
|
version_by_dir[directory] = version |
|
|
|
|
|
versions = set(version_by_dir.values()) |
|
if len(versions) != 1 or None in versions: |
|
print("Each setup.py must have the same VERSION string.") |
|
return False |
|
return True |
|
|
|
|
|
def set_version( |
|
python_version: str, |
|
csharp_version: str, |
|
csharp_extensions_version: str, |
|
release_tag: Optional[str], |
|
) -> None: |
|
|
|
if release_tag and "test" in release_tag: |
|
if not ("dev" in python_version or "test" in python_version): |
|
raise RuntimeError('Test tags must use a "test" or "dev" version.') |
|
|
|
new_contents = PYTHON_VERSION_FILE_TEMPLATE.format( |
|
version=_escape_non_none(python_version), |
|
release_tag=_escape_non_none(release_tag), |
|
) |
|
for directory in DIRECTORIES: |
|
path = os.path.join(directory, "__init__.py") |
|
print(f"Setting {path} to version {python_version}") |
|
with open(path, "w") as f: |
|
f.write(new_contents) |
|
|
|
if csharp_version is not None: |
|
package_version = f"{csharp_version}-exp.1" |
|
if csharp_extensions_version is not None: |
|
|
|
|
|
extension_version = f"{csharp_extensions_version}-preview" |
|
print( |
|
f"Setting package version to {package_version} in {MLAGENTS_PACKAGE_JSON_PATH}" |
|
f" and {MLAGENTS_EXTENSIONS_PACKAGE_JSON_PATH}" |
|
) |
|
set_package_version(package_version) |
|
set_extension_package_version(package_version, extension_version) |
|
print(f"Setting package version to {package_version} in {ACADEMY_PATH}") |
|
set_academy_version_string(package_version) |
|
|
|
|
|
def set_package_version(new_version: str) -> None: |
|
with open(MLAGENTS_PACKAGE_JSON_PATH) as f: |
|
package_json = json.load(f) |
|
if "version" in package_json: |
|
package_json["version"] = new_version |
|
with open(MLAGENTS_PACKAGE_JSON_PATH, "w") as f: |
|
json.dump(package_json, f, indent=2) |
|
f.write("\n") |
|
|
|
|
|
def set_extension_package_version( |
|
new_dependency_version: str, new_extension_version |
|
) -> None: |
|
with open(MLAGENTS_EXTENSIONS_PACKAGE_JSON_PATH) as f: |
|
package_json = json.load(f) |
|
package_json["dependencies"]["com.unity.ml-agents"] = new_dependency_version |
|
if new_extension_version is not None: |
|
package_json["version"] = new_extension_version |
|
with open(MLAGENTS_EXTENSIONS_PACKAGE_JSON_PATH, "w") as f: |
|
json.dump(package_json, f, indent=2) |
|
f.write("\n") |
|
|
|
|
|
def set_academy_version_string(new_version): |
|
needle = "internal const string k_PackageVersion" |
|
found = 0 |
|
with open(ACADEMY_PATH) as f: |
|
lines = f.readlines() |
|
for i, l in enumerate(lines): |
|
if needle in l: |
|
left, right = l.split(" = ") |
|
right = f' = "{new_version}";\n' |
|
lines[i] = left + right |
|
found += 1 |
|
if found != 1: |
|
raise RuntimeError( |
|
f'Expected to find search string "{needle}" exactly once, but found it {found} times' |
|
) |
|
with open(ACADEMY_PATH, "w") as f: |
|
f.writelines(lines) |
|
|
|
|
|
def print_release_tag_commands( |
|
python_version: str, csharp_version: str, release_tag: str |
|
): |
|
python_tag = f"python-packages_{python_version}" |
|
csharp_tag = f"com.unity.ml-agents_{csharp_version}" |
|
docs_tag = f"{release_tag}_docs" |
|
print( |
|
f""" |
|
### |
|
Use these commands to create the tags after the release: |
|
### |
|
git checkout {release_tag} |
|
git tag -f latest_release |
|
git push -f origin latest_release |
|
git tag -f {docs_tag} |
|
git push -f origin {docs_tag} |
|
git tag {python_tag} |
|
git push -f origin {python_tag} |
|
git tag {csharp_tag} |
|
git push -f origin {csharp_tag} |
|
""" |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
parser = argparse.ArgumentParser() |
|
parser.add_argument("--python-version", default=None) |
|
parser.add_argument("--csharp-version", default=None) |
|
parser.add_argument("--csharp-extensions-version", default=None) |
|
parser.add_argument("--release-tag", default=None) |
|
|
|
parser.add_argument("files", nargs="*") |
|
args = parser.parse_args() |
|
|
|
if args.python_version: |
|
print(f"Updating python library to version {args.python_version}") |
|
if args.csharp_version: |
|
print(f"Updating C# package to version {args.csharp_version}") |
|
if args.csharp_extensions_version: |
|
print( |
|
f"Updating C# extensions package to version {args.csharp_extensions_version}" |
|
) |
|
set_version( |
|
args.python_version, |
|
args.csharp_version, |
|
args.csharp_extensions_version, |
|
args.release_tag, |
|
) |
|
if args.release_tag is not None: |
|
print_release_tag_commands( |
|
args.python_version, args.csharp_version, args.release_tag |
|
) |
|
else: |
|
ok = check_versions() |
|
return_code = 0 if ok else 1 |
|
sys.exit(return_code) |
|
|