Skip to content

Add version tracking to bin/install.py to skip reinstalls when versions unchanged#19132

Merged
harupy merged 5 commits intomasterfrom
copilot/update-install-tool-version-tracking
Dec 1, 2025
Merged

Add version tracking to bin/install.py to skip reinstalls when versions unchanged#19132
harupy merged 5 commits intomasterfrom
copilot/update-install-tool-version-tracking

Conversation

Copy link
Contributor

Copilot AI commented Dec 1, 2025

Related Issues/PRs

#xxx

What changes are proposed in this pull request?

bin/install.py now tracks installed tool versions in .installed_versions.json and only reinstalls when versions change or --force-reinstall is used.

Changes:

  • Added version field to Tool dataclass with explicit versions for all tools (taplo 0.9.3, typos 1.39.2, conftest 0.63.0, regal 0.36.1, buf 1.59.0)
  • Implemented version tracking functions: get_expected_versions(), load_installed_versions(), save_installed_versions(), get_tools_needing_update()
  • Updated main() to compare installed vs expected versions and skip tools already at correct version
  • Added informative messages showing which tools need updates

Behavior:

  • First run: installs all tools, saves versions
  • Subsequent runs: only reinstalls if version changed
  • --force-reinstall: reinstalls everything regardless of version

How is this PR tested?

  • Manual tests

Verified all scenarios:

  1. First install with no .installed_versions.json - all tools installed
  2. All tools current - skips installation
  3. Some tools outdated - only those reinstalled
  4. Force reinstall - all tools reinstalled

Does this PR require documentation update?

  • No. You can skip the rest of this section.

Release Notes

Is this a user-facing change?

  • No. You can skip the rest of this section.

What component(s), interfaces, languages, and integrations does this PR affect?

Components

  • area/build: Build and test infrastructure for MLflow

How should the PR be classified in the release notes? Choose one:

  • rn/none - No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" section

Should this PR be included in the next patch release?

  • Yes (this PR will be cherry-picked and included in the next patch release)
  • No (this PR will be included in the next minor release)
Original prompt

Implement this to update bin/install.py so that it tracks installed tool versions and only reinstalls tools when their versions change or when a force reinstall is requested.

diff --git a/bin/install.py b/bin/install.py
index 63928a3ae7..779cff3b8f 100644
--- a/bin/install.py
+++ b/bin/install.py
@@ -5,6 +5,7 @@ Install binary tools for MLflow development.
 # ruff: noqa: T201
 import argparse
 import gzip
+import json
 import platform
 import subprocess
 import tarfile
@@ -13,6 +14,8 @@ from dataclasses import dataclass
 from pathlib import Path
 from typing import Literal

+INSTALLED_VERSIONS_FILE = ".installed_versions.json"
+
 # Type definitions
 PlatformKey = tuple[
     Literal["linux", "darwin"],
@@ -24,6 +27,7 @@ ExtractType = Literal["gzip", "tar", "binary"]
 @dataclass
 class Tool:
     name: str
+    version: str
     urls: dict[PlatformKey, str]  # platform -> URL mapping
     version_args: list[str] | None = None  # Custom version check args (default: ["--version"])

@@ -31,11 +35,9 @@ class Tool:
         return self.urls.get(platform_key)

     def get_version_args(self) -> list[str]:
-        """Get version check arguments, defaulting to --version."""
         return self.version_args or ["--version"]

     def get_extract_type(self, url: str) -> ExtractType:
-        """Infer extract type from URL file extension."""
         if url.endswith(".gz") and not url.endswith(".tar.gz"):
             return "gzip"
         elif url.endswith((".tar.gz", ".tgz")):
@@ -52,6 +54,7 @@ class Tool:
 TOOLS = [
     Tool(
         name="taplo",
+        version="0.9.3",
         urls={
             (
                 "linux",
@@ -65,6 +68,7 @@ TOOLS = [
     ),
     Tool(
         name="typos",
+        version="1.39.2",
         urls={
             (
                 "linux",
@@ -78,6 +82,7 @@ TOOLS = [
     ),
     Tool(
         name="conftest",
+        version="0.63.0",
         urls={
             (
                 "linux",
@@ -91,6 +96,7 @@ TOOLS = [
     ),
     Tool(
         name="regal",
+        version="0.36.1",
         urls={
             (
                 "linux",
@@ -105,6 +111,7 @@ TOOLS = [
     ),
     Tool(
         name="buf",
+        version="1.59.0",
         urls={
             (
                 "linux",
@@ -223,6 +230,33 @@ def install_tool(tool: Tool, dest_dir: Path, force: bool = False) -> None:
     print(f"Successfully installed {tool.name} to {binary_path}")


+def get_expected_versions() -> dict[str, str]:
+    return {tool.name: tool.version for tool in TOOLS}
+
+
+def load_installed_versions(dest_dir: Path) -> dict[str, str]:
+    versions_file = dest_dir / INSTALLED_VERSIONS_FILE
+    if versions_file.exists():
+        return json.loads(versions_file.read_text())
+    return {}
+
+
+def save_installed_versions(dest_dir: Path, versions: dict[str, str]) -> None:
+    versions_file = dest_dir / INSTALLED_VERSIONS_FILE
+    versions_file.write_text(json.dumps(versions, indent=2) + "\n")
+
+
+def get_tools_needing_update(dest_dir: Path) -> list[str]:
+    installed = load_installed_versions(dest_dir)
+    expected = get_expected_versions()
+    outdated = []
+    for name, expected_version in expected.items():
+        installed_version = installed.get(name)
+        if installed_version != expected_version:
+            outdated.append(name)
+    return outdated
+
+
 def main() -> None:
     parser = argparse.ArgumentParser(description="Install binary tools for MLflow development")
     parser.add_argument(
@@ -233,17 +267,33 @@ def main() -> None:
     )
     args = parser.parse_args()

-    if args.force_reinstall:
+    dest_dir = Path(__file__).resolve().parent
+    dest_dir.mkdir(parents=True, exist_ok=True)
+
+    # Determine which tools need to be installed/updated
+    tools_to_update = set(get_tools_needing_update(dest_dir))
+    force_all = args.force_reinstall
+
+    if force_all:
         print("Force reinstall: removing existing tools and reinstalling...")
+    elif tools_to_update:
+        print(f"Version changes detected for: {', '.join(sorted(tools_to_update))}")
     else:
         print("Installing all tools to bin/ directory...")

-    dest_dir = Path(__file__).resolve().parent
-    dest_dir.mkdir(parents=True, exist_ok=True)
+    installed_versions = load_installed_versions(dest_dir)

     for tool in TOOLS:
+        # Force reinstall if globally forced or if this tool's version changed
+        force = force_all or tool.name in tools_to_update
         print(f"\nInstalling {tool.name}...")
-        install_tool(tool, dest_dir, force=args.force_reinstall)
+        install_tool(tool, dest_dir, force=force)
+
+        # Update installed version after successful install
+        installed_versions[tool.name] = tool.version
+
+    # Save installed versions after successful installation
+    save_installed_versions(dest_dir, installed_versions)

     print("\nAll tools installed successfully!")

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@harupy harupy marked this pull request as ready for review December 1, 2025 12:25
- Add json import and INSTALLED_VERSIONS_FILE constant
- Add version field to Tool dataclass with versions for all tools
- Remove docstring comments from get_version_args and get_extract_type
- Implement get_expected_versions() function
- Implement load_installed_versions() function
- Implement save_installed_versions() function
- Implement get_tools_needing_update() function
- Update main() to check versions and only reinstall when needed
- Update main() to save installed versions after installation

Co-authored-by: harupy <17039389+harupy@users.noreply.github.com>
Copilot AI changed the title [WIP] Update install.py to track tool versions and handle reinstalls Add version tracking to bin/install.py to skip reinstalls when versions unchanged Dec 1, 2025
Copilot AI requested a review from harupy December 1, 2025 12:47
@github-actions github-actions bot added area/build Build and test infrastructure for MLflow rn/none List under Small Changes in Changelogs. labels Dec 1, 2025
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
@harupy harupy enabled auto-merge December 1, 2025 13:37
@harupy harupy disabled auto-merge December 1, 2025 13:40
@harupy harupy merged commit f2ba7bf into master Dec 1, 2025
57 of 66 checks passed
@harupy harupy deleted the copilot/update-install-tool-version-tracking branch December 1, 2025 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build Build and test infrastructure for MLflow rn/none List under Small Changes in Changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants