Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ To restart the Omega Docker container:
docker start omega
```

The `scripts/omega` launcher and the Docker image must be the same version.
`scripts/omega start` (and the interactive bootstrap) compare the host script
version with the version baked into the image and abort when both are known
and differ. A `curl | bash` install has no checkout, so that path warns and
continues. Use the same git tag for a checkout and its image.

### Memory portability

Memory export is disabled by default. See the [memory portability reference](./docs/reference-memory-portability.md)
Expand Down
119 changes: 111 additions & 8 deletions scripts/omega
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ if error:
PY

bootstrap() {
ensure_launcher_matches_image || return 1

# Create temporary files
tmp_config_file="$(mktemp)"
Expand Down Expand Up @@ -623,16 +624,117 @@ start

}

version() {
local script_dir
local repo_root
_trim() {
local value="${1-}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}

bare_omega_version() {
local script_path script_dir repo_root

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# A piped `curl | bash` has no script file. Do not treat the working
# directory, or a helper.py above it, as the checkout.
script_path="${BASH_SOURCE[0]}"
if [[ ! -f "$script_path" ]]; then
return 0
fi
script_dir="$(cd -- "$(dirname -- "$script_path")" && pwd)"
repo_root="$(cd -- "${script_dir}/.." && pwd)"
(
cd -- "$repo_root"
python3 -c 'from src.helper import omega_version; print(omega_version())'
)
if [[ -e "${repo_root}/.git" ]]; then
git -C "$repo_root" describe --tags --dirty --always 2>/dev/null || true
return 0
fi
if [[ -f "${repo_root}/version" ]]; then
cat "${repo_root}/version"
fi
}

strip_legacy_version_prefix() {
local version="$1"
local prefix
for prefix in "Omega version=" "OmegaClaw version="; do
if [[ "$version" == "$prefix"* ]]; then
version="${version#"$prefix"}"
version="$(_trim "$version")"
break
fi
done
printf '%s' "$version"
}

version() {
local bare
bare="$(_trim "$(bare_omega_version 2>/dev/null || true)")"
bare="${bare%$'\r'}"
bare="$(strip_legacy_version_prefix "$bare")"
if [[ -z "$bare" || "$bare" == "unknown" || "$bare" == "Omega unknown" ]]; then
printf '%s\n' "Omega unknown"
else
printf 'Omega version=%s\n' "$bare"
fi
}

strip_dirty_suffix() {
local version="$1"
if [[ "$version" == *-dirty ]]; then
version="${version%-dirty}"
fi
printf '%s' "$version"
}

comparable_omega_version() {
local version
version="$(_trim "${1-}")"
version="${version%$'\r'}"
version="$(strip_legacy_version_prefix "$version")"
if [[ -z "$version" || "$version" == "unknown" || "$version" == "Omega unknown" ]]; then
printf ''
return 0
fi
strip_dirty_suffix "$version"
}

read_image_omega_version() {
local raw="" path
for path in /PeTTa/repos/Omega/version /PeTTa/repos/OmegaClaw-Core/version; do
raw="$(docker run --rm --entrypoint cat "${image}" "$path" 2>/dev/null || true)"
raw="$(_trim "$raw")"
if [ -n "$raw" ]; then
printf '%s' "$raw"
return 0
fi
done
}

ensure_launcher_matches_image() {
local script_version image_version
script_version="$(comparable_omega_version "$(bare_omega_version 2>/dev/null || true)")"
if [ -z "$script_version" ]; then
echo "Could not determine the launcher script version; skipping version check." >&2
echo "Use the same tag for scripts/omega and the Docker image." >&2
return 0
fi

image_version="$(comparable_omega_version "$(read_image_omega_version)")"
if [ -z "$image_version" ]; then
echo "Could not determine the Docker image version; skipping version check." >&2
echo "Use the same tag for scripts/omega and the Docker image." >&2
return 0
fi

if [[ "$script_version" != "$image_version" ]]; then
cat >&2 <<EOF
The launcher script and Docker image versions do not match.
Script: Omega version=${script_version}
Image (${image}): Omega version=${image_version}

Update the script or the image so both are the same tag/commit. For example:
curl -fsSL https://github.com/singnet/Omega/raw/refs/tags/<tag>/scripts/omega | bash -s -- singularitynet/omega:<tag>
EOF
return 1
fi
}

help() {
Expand Down Expand Up @@ -956,6 +1058,7 @@ start() {
fi

docker pull "${image}" 2>/dev/null || true
ensure_launcher_matches_image || return 1

if [ -n "${memory_transfer_dir}" ]; then
if ! docker run --rm --user 65534:65534 --group-add "${memory_transfer_gid}" --entrypoint /bin/sh \
Expand Down
32 changes: 8 additions & 24 deletions src/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,19 +286,8 @@ def projectRootDirectory():
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _format_omega_version(version: str) -> str | None:
version = version.strip()
if not version:
return None
if version.startswith("Omega version="):
return version
if version.startswith("Omega "):
version = version[len("Omega "):]
return f"Omega version={version}"


def omega_version(repo_root: str | os.PathLike | None = None) -> str:
"""Return the checkout version, falling back to the baked version file."""
"""Return the bare checkout version, or "unknown" when it cannot be determined."""
root = Path(repo_root) if repo_root is not None else Path(projectRootDirectory())

try:
Expand All @@ -315,34 +304,29 @@ def omega_version(repo_root: str | os.PathLike | None = None) -> str:
timeout=3,
)
if result.returncode == 0:
version = _format_omega_version(result.stdout)
if version is not None:
version = result.stdout.strip()
if version:
return version

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@janaina-senna Is the bare version format intended for all existing omega_version() consumers? In particular, the MeTTa (version) skill and memory-export metadata now receive v… instead of the previous Omega version=v… format.
If not, should display-facing call sites format the value explicitly?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omega_version() is meant to return the bare version, or an empty string, for every caller. That follows @vsbogd's review: keep the bare value in the function, and add the Omega version= prefix only where the version is printed.

The launcher does that for --version and for the mismatch message, which keeps the existing Omega version=$(git describe …) output. The MeTTa (version) skill calls omega_version() directly, so it now returns the version number (v…). Memory export stores that same bare string in OMEGA_VERSION. Neither of those sites adds the prefix.

except (OSError, subprocess.TimeoutExpired):
pass

try:
version = _format_omega_version(
(root / "version").read_text(encoding="utf-8")
)
if version is not None:
version = (root / "version").read_text(encoding="utf-8").strip()
if version:
return version
except OSError:
pass

return "Omega unknown"
return "unknown"


def test_omega_version():
with TemporaryDirectory() as directory:
root = Path(directory)
assert omega_version(root) == "Omega unknown"
assert omega_version(root) == "unknown"

(root / "version").write_text("v1.2.3-4-g1234567\n", encoding="utf-8")
assert omega_version(root) == "Omega version=v1.2.3-4-g1234567"

(root / "version").write_text("Omega v1.2.3\n", encoding="utf-8")
assert omega_version(root) == "Omega version=v1.2.3"
assert omega_version(root) == "v1.2.3-4-g1234567"


def test_balance_parenthesis():
Expand Down
6 changes: 3 additions & 3 deletions tests/test_memory_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def __init__(self, **kwargs):
package.MemoryTransfer = FakeTransfer
monkeypatch.setitem(sys.modules, "memory_portability", package)
monkeypatch.setattr(handler, "create_memory_store", lambda: "configured-store")
monkeypatch.setattr(handler, "omega_version", lambda: "Omega version=v1.2.3")
monkeypatch.setattr(handler, "omega_version", lambda: "v1.2.3")
handler._transfer = None

transfer = handler._get_transfer()
Expand All @@ -159,7 +159,7 @@ def __init__(self, **kwargs):
assert created == [{
"transfer_dir": handler._TRANSFER_DIR,
"store": "configured-store",
"omega_version": "Omega version=v1.2.3",
"omega_version": "v1.2.3",
}]


Expand Down Expand Up @@ -352,7 +352,7 @@ def test_export_is_allowed_for_asicloud_embeddings(handler, monkeypatch):
monkeypatch.setitem(sys.modules, "memory_portability", package)
monkeypatch.setenv("EMBEDDING_PROVIDER", "Local")
monkeypatch.setenv("OMEGA_VERSION", "unset")
monkeypatch.setattr(handler, "omega_version", lambda: "Omega version=test")
monkeypatch.setattr(handler, "omega_version", lambda: "test")
monkeypatch.setattr(handler, "create_memory_store", lambda: "configured-store")
monkeypatch.setattr(
handler,
Expand Down
103 changes: 95 additions & 8 deletions tests/test_omegaclaw_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,45 @@
REPO_ROOT = Path(__file__).resolve().parents[1]
LAUNCHER = REPO_ROOT / "scripts" / "omega"
CONTAINER_TEST_IMAGE = os.environ.get("OMEGA_LAUNCHER_TEST_IMAGE", "")
FAKE_DOCKER = """\
#!/bin/sh
version_path=""
for arg in "$@"; do
case "$arg" in
/PeTTa/repos/Omega/version|/PeTTa/repos/OmegaClaw-Core/version)
version_path="$arg"
;;
esac
done
if [ -n "$version_path" ]; then
case "$version_path" in
/PeTTa/repos/Omega/version)
if [ -n "${OMEGA_TEST_OMEGA_VERSION+x}" ]; then
printf '%s\\n' "${OMEGA_TEST_OMEGA_VERSION}"
else
printf '%s\\n' "${OMEGA_TEST_IMAGE_VERSION}"
fi
;;
*)
printf '%s\\n' "${OMEGA_TEST_LEGACY_VERSION}"
;;
esac
exit 0
fi
printf 'docker'
printf ' <%s>' "$@"
printf '\\n'
"""


def _host_omega_version() -> str:
result = subprocess.run(
["git", "-C", str(REPO_ROOT), "describe", "--tags", "--dirty", "--always"],
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()


def _load_installer_namespace():
Expand All @@ -31,14 +70,7 @@ def _stub_docker_environment(
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
docker = bin_dir / "docker"
docker.write_text(
"#!/bin/sh\n"
"printf 'docker'\n"
"printf ' <%s>' \"$@\"\n"
"printf '\\n'\n",
encoding="utf-8",
)

docker.write_text(FAKE_DOCKER, encoding="utf-8")
docker.chmod(0o755)
uname = bin_dir / "uname"
uname.write_text("#!/bin/sh\necho Linux\n", encoding="utf-8")
Expand Down Expand Up @@ -67,6 +99,8 @@ def _run_launcher(
transfer_gid: int | None = None,
memory_import: bool = True,
stub_runtime_validator: bool = True,
image_version: str | None = None,
legacy_version: str | None = None,
) -> subprocess.CompletedProcess:
if transfer_dir is None:
transfer_dir = tmp_path
Expand All @@ -86,6 +120,13 @@ def _run_launcher(
environment = _stub_docker_environment(
tmp_path, transfer_gid, stub_runtime_validator=stub_runtime_validator
)
if legacy_version is not None:
environment["OMEGA_TEST_OMEGA_VERSION"] = ""
environment["OMEGA_TEST_LEGACY_VERSION"] = legacy_version
else:
environment["OMEGA_TEST_IMAGE_VERSION"] = (
image_version if image_version is not None else _host_omega_version()
)

return subprocess.run(
[
Expand Down Expand Up @@ -389,3 +430,49 @@ def test_removed_component_options_are_rejected(tmp_path, removed_option):
assert result.returncode != 0
assert "Usage:" in result.stdout
assert "docker <" not in result.stdout


def test_matching_image_version_allows_start(tmp_path):
result = _run_launcher(tmp_path)

assert result.returncode == 0, result.stderr
assert "docker <rm>" in result.stdout
assert "docker <run>" in result.stdout
assert "The launcher script and Docker image versions do not match." not in result.stderr


def test_mismatched_image_version_aborts_before_container_replace(tmp_path):
result = _run_launcher(tmp_path, image_version="v0.0.0-test")

assert result.returncode != 0
assert "The launcher script and Docker image versions do not match." in result.stderr
assert "Omega version=v0.0.0-test" in result.stderr
assert "docker <rm>" not in result.stdout
assert "<--name>" not in result.stdout


def test_legacy_prefixed_image_version_prints_prefix_once(tmp_path):
result = _run_launcher(tmp_path, image_version="Omega version=v0.0.0-test")

assert result.returncode != 0
assert "Omega version=v0.0.0-test" in result.stderr
assert "Omega version=Omega version=" not in result.stderr
assert "docker <rm>" not in result.stdout


def test_legacy_omegaclaw_prefix_prints_once(tmp_path):
result = _run_launcher(tmp_path, image_version="OmegaClaw version=v0.1.19")

assert result.returncode != 0
assert "Image (singularitynet/omega:latest): Omega version=v0.1.19" in result.stderr
assert "OmegaClaw version=" not in result.stderr
assert "docker <rm>" not in result.stdout


def test_legacy_image_path_version_is_compared(tmp_path):
result = _run_launcher(tmp_path, legacy_version="v0.0.0-test")

assert result.returncode != 0
assert "Omega version=v0.0.0-test" in result.stderr
assert "Could not determine the Docker image version" not in result.stderr
assert "docker <rm>" not in result.stdout
Loading