Repository: All-Hands-AI/OpenHands
Pull request: Fix issue #5890: Add an automatic check of version consistency in documentation
This pull request fixes #5890.

The issue has been successfully resolved. The AI agent implemented a complete solution that addresses all requirements from the original issue:

1. A version consistency checker script was created that uses regex to find all version references
2. The script was integrated into the CI pipeline via lint.yml
3. The solution automatically checks for version consistency across the codebase
4. It specifically looks for the requested patterns (`openhands:0.XX` and `runtime:0.XX`)
5. The implementation ensures only one unique version number is used
6. The CI pipeline will fail if inconsistencies are found, forcing developers to maintain version consistency

For a human reviewer, I would summarize the PR as:
"This PR implements an automated version consistency checker in the CI pipeline. It adds a Python script that scans the codebase for version references (openhands:0.XX and runtime:0.XX patterns) and ensures only one unique version is used throughout. The script is integrated into lint.yml as a new job 'check-version-consistency' that runs on all PRs and pushes to main. This will prevent version inconsistencies from being merged into the codebase."

The implementation is complete, functional, and meets all the original requirements while integrating smoothly into the existing CI workflow.

Automatic fix generated by [OpenHands](https://github.com/All-Hands-AI/OpenHands/) 🙌

---

To run this PR locally, use the following command:
```
docker run -it --rm   -p 3000:3000   -v /var/run/docker.sock:/var/run/docker.sock   --add-host host.docker.internal:host-gateway   -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:6d67e33-nikolaik   --name openhands-app-6d67e33   docker.all-hands.dev/all-hands-ai/openhands:6d67e33
```

Problem statement:
Add an automatic check of version consistency in documentation
**What problem or use case are you trying to solve?**

When we update versions in documentation, they are specified in many different places across the codebase, and sometimes they get out of sync.

**Describe the UX of the solution you'd like**

It would be good to have automatic checks that our version are consistent.

**Do you have thoughts on the technical implementation?**

To do so, we can create a linting rule in lint.yml that uses a regex to search the entire codebase for all instances of `openhands:0.XX` or `runtime:0.XX` where `XX` is two digits. The linting rule should check that one and only one unique value of XX is found throughout the entire codebase.

Hints from issue discussion:
[OpenHands](https://github.com/All-Hands-AI/OpenHands) started fixing the issue! You can monitor the progress [here](https://github.com/All-Hands-AI/OpenHands/actions/runs/12531220308).
A potential fix has been generated and a draft PR #5891 has been created. Please review the changes.

Commit message: Merge branch 'main' into openhands-fix-issue-5890

Patch to review:
diff --git a/.github/scripts/check_version_consistency.py b/.github/scripts/check_version_consistency.py
new file mode 100755
index 000000000000..18180d9e890e
--- /dev/null
+++ b/.github/scripts/check_version_consistency.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+import os
+import re
+import sys
+from typing import Set, Tuple
+
+
+def find_version_references(directory: str) -> Tuple[Set[str], Set[str]]:
+    openhands_versions = set()
+    runtime_versions = set()
+
+    version_pattern_openhands = re.compile(r'openhands:0\.(\d{2})')
+    version_pattern_runtime = re.compile(r'runtime:0\.(\d{2})')
+
+    for root, _, files in os.walk(directory):
+        # Skip .git directory
+        if '.git' in root:
+            continue
+
+        for file in files:
+            if file.endswith(
+                ('.md', '.yml', '.yaml', '.txt', '.html', '.py', '.js', '.ts')
+            ):
+                file_path = os.path.join(root, file)
+                try:
+                    with open(file_path, 'r', encoding='utf-8') as f:
+                        content = f.read()
+
+                        # Find all openhands version references
+                        matches = version_pattern_openhands.findall(content)
+                        openhands_versions.update(matches)
+
+                        # Find all runtime version references
+                        matches = version_pattern_runtime.findall(content)
+                        runtime_versions.update(matches)
+                except Exception as e:
+                    print(f'Error reading {file_path}: {e}', file=sys.stderr)
+
+    return openhands_versions, runtime_versions
+
+
+def main():
+    repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+    openhands_versions, runtime_versions = find_version_references(repo_root)
+
+    exit_code = 0
+
+    if len(openhands_versions) > 1:
+        print('Error: Multiple openhands versions found:', file=sys.stderr)
+        print('Found versions:', sorted(openhands_versions), file=sys.stderr)
+        exit_code = 1
+    elif len(openhands_versions) == 0:
+        print('Warning: No openhands version references found', file=sys.stderr)
+
+    if len(runtime_versions) > 1:
+        print('Error: Multiple runtime versions found:', file=sys.stderr)
+        print('Found versions:', sorted(runtime_versions), file=sys.stderr)
+        exit_code = 1
+    elif len(runtime_versions) == 0:
+        print('Warning: No runtime version references found', file=sys.stderr)
+
+    sys.exit(exit_code)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1bfc8c91c6a4..789a938e1d7e 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -53,3 +53,16 @@ jobs:
         run: pip install pre-commit==3.7.0
       - name: Run pre-commit hooks
         run: pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
+
+  # Check version consistency across documentation
+  check-version-consistency:
+    name: Check version consistency
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up python
+        uses: actions/setup-python@v5
+        with:
+          python-version: 3.12
+      - name: Run version consistency check
+        run: .github/scripts/check_version_consistency.py
diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
index b00df2c1f2a2..9156d7ac46ff 100644
--- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
+++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
@@ -52,7 +52,7 @@ LLM_API_KEY="sk_test_12345"
 ```bash
 docker run -it \
     --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e SANDBOX_USER_ID=$(id -u) \
     -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
     -e LLM_API_KEY=$LLM_API_KEY \
@@ -61,7 +61,7 @@ docker run -it \
     -v /var/run/docker.sock:/var/run/docker.sock \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app-$(date +%Y%m%d%H%M%S) \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16 \
+    docker.all-hands.dev/all-hands-ai/openhands:0.17 \
     python -m openhands.core.cli
 ```
 
diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
index a1610bb11562..9d1172770549 100644
--- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
+++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
@@ -46,7 +46,7 @@ LLM_API_KEY="sk_test_12345"
 ```bash
 docker run -it \
     --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e SANDBOX_USER_ID=$(id -u) \
     -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
     -e LLM_API_KEY=$LLM_API_KEY \
@@ -56,6 +56,6 @@ docker run -it \
     -v /var/run/docker.sock:/var/run/docker.sock \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app-$(date +%Y%m%d%H%M%S) \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16 \
+    docker.all-hands.dev/all-hands-ai/openhands:0.17 \
     python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
 ```
diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx
index 87b7afb07cd2..ddfef195b661 100644
--- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx
+++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx
@@ -13,16 +13,16 @@
 La façon la plus simple d'exécuter OpenHands est avec Docker.
 
 ```bash
-docker pull docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik
+docker pull docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik
 
 docker run -it --rm --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e LOG_ALL_EVENTS=true \
     -v /var/run/docker.sock:/var/run/docker.sock \
     -p 3000:3000 \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16
+    docker.all-hands.dev/all-hands-ai/openhands:0.17
 ```
 
 Vous pouvez également exécuter OpenHands en mode [headless scriptable](https://docs.all-hands.dev/modules/usage/how-to/headless-mode), en tant que [CLI interactive](https://docs.all-hands.dev/modules/usage/how-to/cli-mode), ou en utilisant l'[Action GitHub OpenHands](https://docs.all-hands.dev/modules/usage/how-to/github-action).
diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md
index d12524fc012c..67d054c4791f 100644
--- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md
+++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md
@@ -13,7 +13,7 @@ C'est le Runtime par défaut qui est utilisé lorsque vous démarrez OpenHands.
 
 ```
 docker run # ...
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -v /var/run/docker.sock:/var/run/docker.sock \
     # ...
 ```
diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
index cf0bbdd10dd6..e6760ee2d63b 100644
--- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
+++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md
@@ -50,7 +50,7 @@ LLM_API_KEY="sk_test_12345"
 ```bash
 docker run -it \
     --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e SANDBOX_USER_ID=$(id -u) \
     -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
     -e LLM_API_KEY=$LLM_API_KEY \
@@ -59,7 +59,7 @@ docker run -it \
     -v /var/run/docker.sock:/var/run/docker.sock \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app-$(date +%Y%m%d%H%M%S) \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16 \
+    docker.all-hands.dev/all-hands-ai/openhands:0.17 \
     python -m openhands.core.cli
 ```
 
diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
index 6a95fa0fe5a5..c38831e4a462 100644
--- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
+++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md
@@ -47,7 +47,7 @@ LLM_API_KEY="sk_test_12345"
 ```bash
 docker run -it \
     --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e SANDBOX_USER_ID=$(id -u) \
     -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
     -e LLM_API_KEY=$LLM_API_KEY \
@@ -57,6 +57,6 @@ docker run -it \
     -v /var/run/docker.sock:/var/run/docker.sock \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app-$(date +%Y%m%d%H%M%S) \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16 \
+    docker.all-hands.dev/all-hands-ai/openhands:0.17 \
     python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
 ```
diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx
index 432eaeeff0e4..6de97bfc3bc5 100644
--- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx
+++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx
@@ -11,16 +11,16 @@
 在 Docker 中运行 OpenHands 是最简单的方式。
 
 ```bash
-docker pull docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik
+docker pull docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik
 
 docker run -it --rm --pull=always \
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -e LOG_ALL_EVENTS=true \
     -v /var/run/docker.sock:/var/run/docker.sock \
     -p 3000:3000 \
     --add-host host.docker.internal:host-gateway \
     --name openhands-app \
-    docker.all-hands.dev/all-hands-ai/openhands:0.16
+    docker.all-hands.dev/all-hands-ai/openhands:0.17
 ```
 
 你也可以在可脚本化的[无头模式](https://docs.all-hands.dev/modules/usage/how-to/headless-mode)下运行 OpenHands，作为[交互式 CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode)，或使用 [OpenHands GitHub Action](https://docs.all-hands.dev/modules/usage/how-to/github-action)。
diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md
index d93207442a07..c6a7fc29053c 100644
--- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md
+++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md
@@ -11,7 +11,7 @@
 
 ```
 docker run # ...
-    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.16-nikolaik \
+    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
     -v /var/run/docker.sock:/var/run/docker.sock \
     # ...
 ```


Code review comments:
diff_hunk: @@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+import os
+import re
+import sys
+from typing import Set, Tuple
+
+
+def find_version_references(directory: str) -> Tuple[Set[str], Set[str]]:
+    openhands_versions = set()
+    runtime_versions = set()
+
+    version_pattern_openhands = re.compile(r'openhands:0\.(\d{2})')
+    version_pattern_runtime = re.compile(r'runtime:0\.(\d{2})')
path: .github/scripts/check_version_consistency.py
text: @user1:
```suggestion
    version_pattern_openhands = re.compile(r'openhands:(\d{1})\.(\d{2})')
    version_pattern_runtime = re.compile(r'runtime:(\d{1})\.(\d{2})')
```

to find x.yz versions