Skip to content
Merged
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
2 changes: 1 addition & 1 deletion desktop/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "umbod-gateway"
version = "0.2.0"
version = "0.2.1"
edition = "2024"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ OAuth supports native public clients with loopback callbacks, discovery, PKCE an

The [upstream Windows run](https://github.com/kasperjunge/umbod-desktop/actions/runs/37907078176) is historical source evidence supplied for this import. It is **not** a CI result for this target repository or these integration changes.

Release packaging collects dependency inventories and notices through `scripts/dependency-notices.py`; pinned supplemental sources are documented in `licenses/README.md`. `scripts/install-smoke.py` installs the Mac DMG into disposable storage and verifies the packaged connector/agent flow, native window lifecycle, and signature integrity before and after execution. It never uses the login Keychain.
Release packaging collects dependency inventories and notices through `scripts/dependency-notices.py`; pinned supplemental sources are documented in `licenses/README.md`. `scripts/verify-python-bundle.py` rejects editable installs and imports outside the bundled interpreter before signing and again after installation. `scripts/install-smoke.py` installs the Mac DMG into disposable storage and verifies the packaged connector/agent flow, native window lifecycle, and signature integrity before and after execution. It never uses the login Keychain.
2 changes: 1 addition & 1 deletion desktop/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.2.1
2 changes: 1 addition & 1 deletion desktop/native/Sources/UmbodCore/Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import Darwin
guard let ready = startupResult,
let management = ready["management"] as? String, let gateway = ready["gateway"] as? String,
Self.isLoopbackAddress(management), Self.isLoopbackAddress(gateway) else {
if child.isRunning { child.terminate() }; stop(); throw ContractError.message("Gateway could not start. Check Keychain access and configuration, then retry.")
if child.isRunning { child.terminate() }; stop(); throw ContractError.message("The bundled backend could not start. Restart it in Settings; if this continues, reinstall the latest Umbod release.")
}
guard let startupToken = ready["admin_token"] as? String else { throw ContractError.message("Missing backend authentication") }
token = startupToken
Expand Down
4 changes: 3 additions & 1 deletion desktop/scripts/bundle-python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ printf '%s %s\n' '1a98bbe7e63547d2f764bc9119f6d604dfb333f21afab5f4885dc362b4f3a
tar -xzf "$ARCHIVE" -C "$DEST"
printf '20260310\n' > "$DEST/python/BUILD"
uv export --quiet --project "$ROOT/desktop/python" --frozen --no-dev --no-editable --no-hashes --output-file "$DEST/requirements.txt"
(cd "$ROOT/desktop/python" && uv pip install --python "$DEST/python/bin/python3" --reinstall-package umbod-desktop --reinstall-package umbod-core --reinstall-package umbod --break-system-packages --requirements "$DEST/requirements.txt")
# Export covers the full lock; development tool.uv.sources must not restore editable installs.
(cd "$ROOT/desktop/python" && uv pip install --python "$DEST/python/bin/python3" --reinstall-package umbod-desktop --reinstall-package umbod-core --reinstall-package umbod --break-system-packages --no-editable --no-sources --requirements "$DEST/requirements.txt")
rm "$DEST/requirements.txt"
"$DEST/python/bin/python3" -I -B "$ROOT/desktop/scripts/verify-python-bundle.py"
"$DEST/python/bin/python3" -I -B "$ROOT/desktop/scripts/dependency-notices.py" python "$DEST"
# Compile before signing; runtime must not mutate the signed bundle.
"$DEST/python/bin/python3" -m compileall -q -s "$DEST/python" -p /umbod-runtime "$DEST/python/lib"
1 change: 1 addition & 0 deletions desktop/scripts/install-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def main():
finally:
subprocess.run(["hdiutil", "detach", str(mount)], check=True)
subprocess.run(["codesign", "--verify", "--deep", "--strict", str(app)], check=True)
subprocess.run([str(app / "Contents/Resources/python/bin/python3"), "-I", "-B", str(desktop / "scripts/verify-python-bundle.py")], check=True)
environment = {**os.environ, "UMBOD_TEST_APP": str(app)}
# Swift drives the tests; every backend and agent process uses the installed interpreter.
subprocess.run(["swift", "run", "--package-path", str(desktop / "native"), "UmbodTests"], env=environment, check=True)
Expand Down
2 changes: 1 addition & 1 deletion desktop/scripts/ui-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ APP="${1:-dist/Umbod.app}"
test -x "$APP/Contents/MacOS/Umbod"
test -x "$APP/Contents/MacOS/umbod-desktop"
test -x "$APP/Contents/Resources/python/bin/python3"
env -i PATH=/usr/bin:/bin "$APP/Contents/Resources/python/bin/python3" -I -B -c 'import umbod_desktop, umbod.rest.main, umbod.mcp.public_app'
env -i PATH=/usr/bin:/bin "$APP/Contents/Resources/python/bin/python3" -I -B "$(dirname "$0")/verify-python-bundle.py"
/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$APP/Contents/Info.plist"
45 changes: 45 additions & 0 deletions desktop/scripts/verify-python-bundle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Run with the bundled Python (-I -B) to reject checkout-dependent builds."""
import importlib
import importlib.metadata
import json
from pathlib import Path
import sys


def require_bundled(path, prefix):
if not Path(path).resolve().is_relative_to(prefix.resolve()):
raise ValueError(f"Python bundle references an external path: {path}")


def verify_installation(prefix, paths, distributions):
for path in paths:
require_bundled(path, prefix)
for distribution in distributions:
direct_url = distribution.read_text("direct_url.json")
if direct_url and json.loads(direct_url).get("dir_info", {}).get("editable"):
raise ValueError(f"Editable distribution in Python bundle: {distribution.metadata['Name']}")
# Nonexistent checkout paths are silently omitted from sys.path by site.py.
# Inspect them too, so a broken release fails even on a clean build machine.
for pth in prefix.glob("lib/python*/site-packages/*.pth"):
for line in pth.read_text().splitlines():
line = line.strip()
if line and not line.startswith(("#", "import ", "import\t")):
require_bundled(pth.parent / line, prefix)


def main():
prefix = Path(sys.prefix)
verify_installation(prefix, sys.path, importlib.metadata.distributions())
# Exercise the real CLI import graph as well as REST/MCP entry points.
for name in ("umbod_desktop.main", "umbod.core", "umbod.rest.main", "umbod.mcp.public_app", "umbod_sdk"):
module = importlib.import_module(name)
if module.__file__:
require_bundled(module.__file__, prefix)
for path in getattr(module, "__path__", ()):
require_bundled(path, prefix)
print("PASS: bundled Python imports without editable or external source paths")


if __name__ == "__main__":
main()
37 changes: 37 additions & 0 deletions desktop/tests/test_python_bundle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import importlib.util
from pathlib import Path
import tempfile
import unittest
from unittest.mock import Mock

SCRIPT = Path(__file__).resolve().parents[1] / "scripts/verify-python-bundle.py"
spec = importlib.util.spec_from_file_location("verify_python_bundle", SCRIPT)
bundle = importlib.util.module_from_spec(spec)
spec.loader.exec_module(bundle)


class PythonBundleTests(unittest.TestCase):
def test_accepts_bundled_paths(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
bundle.verify_installation(root, [root / "lib/python314.zip"], [])

def test_rejects_checkout_on_import_path(self):
with tempfile.TemporaryDirectory() as directory:
with self.assertRaisesRegex(ValueError, "external path"):
bundle.verify_installation(Path(directory), [SCRIPT.parent], [])

def test_rejects_editable_distribution_even_with_import_hook(self):
distribution = Mock(metadata={"Name": "umbod-core"})
distribution.read_text.return_value = '{"dir_info": {"editable": true}}'
with self.assertRaisesRegex(ValueError, "Editable distribution"):
bundle.verify_installation(SCRIPT.parent, [], [distribution])

def test_rejects_pth_pointing_to_missing_ci_checkout(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
site = root / "lib/python3.14/site-packages"
site.mkdir(parents=True)
(site / "_umbod_core.pth").write_text("/Users/runner/work/umbod/umbod/umbod/api/src\n")
with self.assertRaisesRegex(ValueError, "external path"):
bundle.verify_installation(root, [], [])
Loading