4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
199 lines
6.5 KiB
Python
199 lines
6.5 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
from config.version import get_opensre_version
|
|
|
|
_MAIN_BUILD_RELEASE_API = (
|
|
"https://api.github.com/repos/Tracer-Cloud/opensre/releases/tags/main-build"
|
|
)
|
|
_INSTALL_SCRIPT = "https://install.opensre.com"
|
|
_INSTALL_SCRIPT_PS1 = "https://install.opensre.com"
|
|
_MAIN_BUILD_RELEASE_URL = "https://github.com/Tracer-Cloud/opensre/releases/tag/main-build"
|
|
_VERSION_FROM_RELEASE_BODY = re.compile(r"Version:\s*`([^`\n]+)`", re.IGNORECASE)
|
|
_MAIN_BUILD_SHA_SUFFIX = re.compile(r"\+main\.([0-9a-f]+)$", re.IGNORECASE)
|
|
|
|
|
|
def _main_build_release_api_url() -> str:
|
|
return os.getenv("OPENSRE_RELEASES_API_URL", _MAIN_BUILD_RELEASE_API)
|
|
|
|
|
|
def _extract_main_build_version(release_body: str) -> str:
|
|
match = _VERSION_FROM_RELEASE_BODY.search(release_body)
|
|
if not match:
|
|
return ""
|
|
return match.group(1).strip()
|
|
|
|
|
|
def _extract_main_build_sha(version: str) -> str | None:
|
|
match = _MAIN_BUILD_SHA_SUFFIX.search(version.strip())
|
|
if not match:
|
|
return None
|
|
return match.group(1).lower()
|
|
|
|
|
|
def _fetch_latest_version() -> str:
|
|
import httpx
|
|
|
|
try:
|
|
resp = httpx.get(_main_build_release_api_url(), timeout=10, follow_redirects=True)
|
|
resp.raise_for_status()
|
|
except httpx.TimeoutException as exc:
|
|
raise RuntimeError("request timed out") from exc
|
|
except httpx.HTTPStatusError as exc:
|
|
if exc.response.status_code == 429:
|
|
raise RuntimeError("GitHub API rate limit exceeded, try again later") from exc
|
|
raise RuntimeError(f"GitHub API returned HTTP {exc.response.status_code}") from exc
|
|
except httpx.ConnectError as exc:
|
|
raise RuntimeError(
|
|
"could not connect to GitHub — check your network or HTTPS_PROXY settings"
|
|
) from exc
|
|
|
|
body = resp.json().get("body") or ""
|
|
return _extract_main_build_version(body)
|
|
|
|
|
|
def _is_update_available(current: str, latest: str) -> bool:
|
|
current_main_sha = _extract_main_build_sha(current)
|
|
latest_main_sha = _extract_main_build_sha(latest)
|
|
if current_main_sha is not None and latest_main_sha is not None:
|
|
# Same-day main rebuilds share a calendar prefix; compare commit SHAs
|
|
# instead of PEP 440 local segments (hex SHAs are not ordered chronologically).
|
|
return current_main_sha != latest_main_sha
|
|
|
|
try:
|
|
from packaging.version import InvalidVersion, Version
|
|
except ImportError:
|
|
return current != latest
|
|
try:
|
|
return Version(latest) > Version(current)
|
|
except InvalidVersion:
|
|
return current != latest
|
|
|
|
|
|
def _is_binary_install() -> bool:
|
|
return bool(getattr(sys, "frozen", False))
|
|
|
|
|
|
def _is_windows() -> bool:
|
|
return sys.platform == "win32"
|
|
|
|
|
|
def _is_editable_install() -> bool:
|
|
import importlib.metadata
|
|
import json
|
|
|
|
try:
|
|
dist = importlib.metadata.distribution("opensre")
|
|
direct_url_text = dist.read_text("direct_url.json")
|
|
if direct_url_text:
|
|
info = json.loads(direct_url_text)
|
|
return bool(info.get("dir_info", {}).get("editable", False))
|
|
except (importlib.metadata.PackageNotFoundError, json.JSONDecodeError, OSError):
|
|
return False
|
|
return False
|
|
|
|
|
|
def development_install_doctor_version_detail(current: str) -> str | None:
|
|
"""If this process looks like a local checkout, return the doctor line (skip release compare).
|
|
|
|
Editable installs (`pip install -e` / ``uv sync`` on a git checkout) and ``uv run``
|
|
children set signals we use so ``opensre doctor`` does not warn vs GitHub main builds.
|
|
"""
|
|
labels: list[str] = []
|
|
if _is_editable_install():
|
|
labels.append("editable install")
|
|
# uv sets this on the Python process when invoked via `uv run …`.
|
|
if os.environ.get("UV_RUN_RECURSION_DEPTH") is not None:
|
|
labels.append("uv run")
|
|
if not labels:
|
|
return None
|
|
ctx = " + ".join(labels)
|
|
return f"{current} ({ctx}; skipped comparing to latest main build)"
|
|
|
|
|
|
def _upgrade_via_install_script() -> int:
|
|
"""Download and run the official install script on the rolling main channel."""
|
|
if _is_windows():
|
|
result = subprocess.run(
|
|
[
|
|
"powershell",
|
|
"-NoProfile",
|
|
"-Command",
|
|
(
|
|
f"$env:OPENSRE_INSTALL_CHANNEL='main'; "
|
|
f"Remove-Item Env:OPENSRE_VERSION -ErrorAction SilentlyContinue; "
|
|
f"irm {_INSTALL_SCRIPT_PS1} | iex"
|
|
),
|
|
],
|
|
check=False,
|
|
)
|
|
else:
|
|
result = subprocess.run(
|
|
["bash", "-c", f"curl -fsSL {_INSTALL_SCRIPT} | bash -s -- --main"],
|
|
check=False,
|
|
)
|
|
return result.returncode
|
|
|
|
|
|
def run_update(*, check_only: bool = False, yes: bool = False) -> int:
|
|
# To skip this check in CI or automated environments, set OPENSRE_NO_UPDATE_CHECK=1.
|
|
current = get_opensre_version()
|
|
|
|
try:
|
|
latest = _fetch_latest_version()
|
|
except Exception as exc:
|
|
print(f" error: could not fetch latest version: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
if not latest:
|
|
print(
|
|
" error: could not determine latest main build version from release data.",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
if not _is_update_available(current, latest):
|
|
print(f" opensre {current} is already up to date.")
|
|
return 0
|
|
|
|
print(f" current: {current}")
|
|
print(f" latest: {latest}")
|
|
print(" main build: " + _MAIN_BUILD_RELEASE_URL)
|
|
|
|
if check_only:
|
|
return 1
|
|
|
|
if _is_editable_install():
|
|
print(
|
|
" warning: this is an editable install — upgrading will replace it with a main build."
|
|
)
|
|
|
|
if not yes:
|
|
try:
|
|
import questionary
|
|
|
|
confirmed = questionary.confirm(f" Update to main build {latest}?", default=True).ask()
|
|
except (EOFError, KeyboardInterrupt):
|
|
print("\n Aborted.")
|
|
return 1
|
|
if not confirmed:
|
|
print(" Cancelled.")
|
|
return 0
|
|
|
|
rc = _upgrade_via_install_script()
|
|
if rc == 0:
|
|
print(f" updated: {current} -> {latest}")
|
|
print(" main build release: " + _MAIN_BUILD_RELEASE_URL)
|
|
else:
|
|
print(f" install script failed (exit {rc}).", file=sys.stderr)
|
|
if _is_windows():
|
|
hint = f"$env:OPENSRE_INSTALL_CHANNEL='main'; irm {_INSTALL_SCRIPT_PS1} | iex"
|
|
else:
|
|
hint = f"curl -fsSL {_INSTALL_SCRIPT} | bash -s -- --main"
|
|
print(f" to retry manually, run:\n {hint}", file=sys.stderr)
|
|
return rc
|