| 
 | 1 | +import argparse  | 
 | 2 | +import re  | 
 | 3 | +import sys  | 
 | 4 | +from pathlib import Path  | 
 | 5 | + | 
 | 6 | +BASE_VERSION_PATTERN = re.compile(r"^BASE_VERSION\s*:=.*?(\d+\.\d+\.\d+)", re.MULTILINE)  | 
 | 7 | +BASE_VERSION_NIGHTLY_PATTERN = re.compile(  | 
 | 8 | +    r"^BASE_VERSION_NIGHTLY\s*:=.*?(\d+\.\d+\.\d+-SNAPSHOT)",  | 
 | 9 | +    re.MULTILINE,  | 
 | 10 | +)  | 
 | 11 | + | 
 | 12 | + | 
 | 13 | +def find_makefile():  | 
 | 14 | +    cwd = Path.cwd()  | 
 | 15 | +    candidate = cwd / "Makefile"  | 
 | 16 | +    if candidate.exists():  | 
 | 17 | +        return candidate  | 
 | 18 | + | 
 | 19 | +    script_dir = Path(__file__).parent  | 
 | 20 | +    if script_dir != cwd:  | 
 | 21 | +        candidate = script_dir / "Makefile"  | 
 | 22 | +        if candidate.exists():  | 
 | 23 | +            return candidate  | 
 | 24 | + | 
 | 25 | +    return None  | 
 | 26 | + | 
 | 27 | + | 
 | 28 | +def parse_versions(content: str):  | 
 | 29 | +    base_match = BASE_VERSION_PATTERN.search(content)  | 
 | 30 | +    nightly_match = BASE_VERSION_NIGHTLY_PATTERN.search(content)  | 
 | 31 | + | 
 | 32 | +    if not base_match:  | 
 | 33 | +        raise ValueError("BASE_VERSION not found in Makefile")  | 
 | 34 | +    if not nightly_match:  | 
 | 35 | +        raise ValueError("BASE_VERSION_NIGHTLY not found in Makefile")  | 
 | 36 | + | 
 | 37 | +    return base_match.group(1), nightly_match.group(1)  | 
 | 38 | + | 
 | 39 | + | 
 | 40 | +def bump_minor(version: str) -> str:  | 
 | 41 | +    parts = version.split(".")  | 
 | 42 | +    if len(parts) != 3:  | 
 | 43 | +        raise ValueError(f"Unsupported version format: {version}")  | 
 | 44 | + | 
 | 45 | +    major, minor, _ = parts  | 
 | 46 | + | 
 | 47 | +    try:  | 
 | 48 | +        minor_number = int(minor)  | 
 | 49 | +    except ValueError as exc:  | 
 | 50 | +        raise ValueError(f"Minor version is not an integer in '{version}'") from exc  | 
 | 51 | + | 
 | 52 | +    return f"{major}.{minor_number + 1}.0"  | 
 | 53 | + | 
 | 54 | + | 
 | 55 | +def update_versions(makefile_path: Path, expected_base_version: str | None) -> bool:  | 
 | 56 | +    content = makefile_path.read_text()  | 
 | 57 | +    current_base, current_nightly = parse_versions(content)  | 
 | 58 | + | 
 | 59 | +    if expected_base_version and current_base == expected_base_version:  | 
 | 60 | +        print(  | 
 | 61 | +            "BASE_VERSION already matches expected value "  | 
 | 62 | +            f"({expected_base_version}); skipping updates."  | 
 | 63 | +        )  | 
 | 64 | +        return False  | 
 | 65 | + | 
 | 66 | +    nightly_base = current_nightly.removesuffix("-SNAPSHOT")  | 
 | 67 | +    new_base = nightly_base  | 
 | 68 | +    new_nightly = bump_minor(nightly_base) + "-SNAPSHOT"  | 
 | 69 | + | 
 | 70 | +    if new_base == current_base and new_nightly == current_nightly:  | 
 | 71 | +        print("Makefile already uses the desired versions; nothing to update.")  | 
 | 72 | +        return False  | 
 | 73 | + | 
 | 74 | +    updated_content = content.replace(current_base, new_base)  | 
 | 75 | +    updated_content = updated_content.replace(current_nightly, new_nightly)  | 
 | 76 | + | 
 | 77 | +    makefile_path.write_text(updated_content)  | 
 | 78 | + | 
 | 79 | +    print(f"Updated BASE_VERSION from {current_base} to {new_base}")  | 
 | 80 | +    print(f"Updated BASE_VERSION_NIGHTLY from {current_nightly} to {new_nightly}")  | 
 | 81 | +    return True  | 
 | 82 | + | 
 | 83 | + | 
 | 84 | +def main(argv: list[str]) -> int:  | 
 | 85 | +    parser = argparse.ArgumentParser(description="Bump Selenium Grid versions in Makefile")  | 
 | 86 | +    parser.add_argument(  | 
 | 87 | +        "makefile",  | 
 | 88 | +        nargs="?",  | 
 | 89 | +        help="Optional path to the Makefile (default: search in CWD then script directory)",  | 
 | 90 | +    )  | 
 | 91 | +    parser.add_argument(  | 
 | 92 | +        "--expected-base-version",  | 
 | 93 | +        dest="expected_base_version",  | 
 | 94 | +        help="If provided and Makefile already contains this BASE_VERSION, skip updating.",  | 
 | 95 | +    )  | 
 | 96 | +    args = parser.parse_args(argv)  | 
 | 97 | + | 
 | 98 | +    if args.makefile:  | 
 | 99 | +        makefile_path = Path(args.makefile)  | 
 | 100 | +    else:  | 
 | 101 | +        found = find_makefile()  | 
 | 102 | +        if not found:  | 
 | 103 | +            print("Error: Could not locate Makefile", file=sys.stderr)  | 
 | 104 | +            return 1  | 
 | 105 | +        makefile_path = found  | 
 | 106 | + | 
 | 107 | +    if not makefile_path.exists():  | 
 | 108 | +        print(f"Error: {makefile_path} does not exist", file=sys.stderr)  | 
 | 109 | +        return 1  | 
 | 110 | + | 
 | 111 | +    try:  | 
 | 112 | +        changed = update_versions(makefile_path, args.expected_base_version)  | 
 | 113 | +    except ValueError as exc:  | 
 | 114 | +        print(f"Error: {exc}", file=sys.stderr)  | 
 | 115 | +        return 1  | 
 | 116 | + | 
 | 117 | +    return 0 if changed else 0  | 
 | 118 | + | 
 | 119 | + | 
 | 120 | +if __name__ == "__main__":  | 
 | 121 | +    sys.exit(main(sys.argv[1:]))  | 
0 commit comments