mirror of
https://github.com/razzant/ouroboros.git
synced 2026-08-05 00:29:46 +00:00
Branch `ouroboros` was seeded from an `.app` bundle snapshot
(`6700358 Initial commit from app bundle`), not from `main`, so the
release pipeline (build scripts, PyInstaller spec, launcher runtime,
vendored Python framework, CI workflow) never landed on this branch.
Import those files verbatim from `origin/main` (76bf13c) so a tagged
push can trigger the existing tag-driven release pipeline.
Imported verbatim from `origin/main`:
- .github/workflows/ci.yml Three-tier CI (push -> quick
tests, stable/tag -> full matrix,
v* tag -> build + GitHub Release
with `prerelease: contains(...,
-rc|-alpha|-beta)`).
- build.sh / build_linux.sh / PyInstaller + Playwright Chromium
build_windows.ps1 / Dockerfile bundling for each target (the four
files `tests/test_build_scripts.py`
assertively requires).
- Ouroboros.spec PyInstaller entry spec.
- entitlements.plist macOS codesign entitlements.
- launcher.py Native launcher that bootstraps
the embedded python-standalone
interpreter and runs the agent as
a subprocess.
- scripts/download_python_standalone.sh / .ps1 / pyi_rth_pythonnet.py
- Python, Python.framework/ Vendored macOS launcher-side
Python 3.10 framework.
- certifi/, jsonschema/, Marker + data files consumed by
jsonschema_specifications/ the runtime-loaded agent deps.
Adapted for Phase 1-6:
- Ouroboros.spec datas now contains ('skills', 'skills') so the
bundled Phase 5 weather reference skill under `skills/weather/`
actually ships inside the .app / .exe / tarball. Without this the
Skills page on a packaged build would show "no skills discovered"
even with the bundled reference in source. Docstring updated to
document the new bundled payload.
Verification:
- `pytest tests/test_build_scripts.py tests/test_release_sync.py`
passes locally (47 asserts; all Playwright + PyInstaller ordering
invariants + release-sync carrier logic hold after the import).
- `python -c "import ast; ast.parse(open('Ouroboros.spec').read())"`
parses.
Not imported from main (intentional):
- `.pytest-tmp-run`, pytest-cache dirs, and other transient files.
- No existing file was overwritten by the checkout; the import is
purely additive on top of the Phase 1-6 tree.
83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
"""PyInstaller runtime hook: configure pythonnet for frozen bundles.
|
|
|
|
Must run before any ``import clr`` / ``import webview`` so that
|
|
Python.Runtime.dll can locate the bundled pythonXY.dll.
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
if sys.platform == "win32":
|
|
_base = getattr(sys, "_MEIPASS", os.path.dirname(sys.executable))
|
|
_exe_dir = os.path.dirname(sys.executable)
|
|
_dll_name = f"python{sys.version_info[0]}{sys.version_info[1]}.dll"
|
|
_runtime_dir = os.path.join(_base, "pythonnet", "runtime")
|
|
_webview_lib_dir = os.path.join(_base, "webview", "lib")
|
|
|
|
os.environ["PYTHONNET_RUNTIME"] = "netfx"
|
|
|
|
_candidates = [
|
|
os.path.join(_base, _dll_name),
|
|
os.path.join(_exe_dir, _dll_name),
|
|
]
|
|
if _base != _exe_dir:
|
|
for _root, _dirs, _files in os.walk(_base):
|
|
if _dll_name in _files:
|
|
_candidates.append(os.path.join(_root, _dll_name))
|
|
if len(_candidates) > 5:
|
|
break
|
|
|
|
def _unblock_file(_path: str) -> None:
|
|
try:
|
|
os.remove(f"{_path}:Zone.Identifier")
|
|
except OSError:
|
|
pass
|
|
|
|
def _unblock_tree(_root: str) -> None:
|
|
if not os.path.isdir(_root):
|
|
return
|
|
for _dirpath, _dirnames, _filenames in os.walk(_root):
|
|
for _filename in _filenames:
|
|
if os.path.splitext(_filename)[1].lower() in {".dll", ".exe", ".pyd"}:
|
|
_unblock_file(os.path.join(_dirpath, _filename))
|
|
|
|
_selected_pydll = None
|
|
for _path in _candidates:
|
|
if os.path.isfile(_path):
|
|
os.environ["PYTHONNET_PYDLL"] = _path
|
|
_unblock_file(_path)
|
|
_selected_pydll = _path
|
|
break
|
|
|
|
_runtime_dll = os.path.join(_runtime_dir, "Python.Runtime.dll")
|
|
if not os.path.isfile(_runtime_dll):
|
|
for _root, _dirs, _files in os.walk(_base):
|
|
if "Python.Runtime.dll" in _files:
|
|
_runtime_dll = os.path.join(_root, "Python.Runtime.dll")
|
|
break
|
|
if os.path.isfile(_runtime_dll):
|
|
_unblock_file(_runtime_dll)
|
|
_unblock_tree(os.path.dirname(_runtime_dll))
|
|
_unblock_tree(_webview_lib_dir)
|
|
|
|
_search_dirs = []
|
|
for _path in (
|
|
_base,
|
|
_exe_dir,
|
|
_runtime_dir,
|
|
os.path.dirname(_runtime_dll) if os.path.isfile(_runtime_dll) else None,
|
|
os.path.dirname(_selected_pydll) if _selected_pydll else None,
|
|
_webview_lib_dir,
|
|
):
|
|
if os.path.isdir(_path) and _path not in _search_dirs:
|
|
_search_dirs.append(_path)
|
|
os.environ["PATH"] = os.pathsep.join(
|
|
_search_dirs + [p for p in os.environ.get("PATH", "").split(os.pathsep) if p and p not in _search_dirs]
|
|
)
|
|
|
|
if hasattr(os, "add_dll_directory"):
|
|
_dll_dir_handles = []
|
|
for _path in _search_dirs:
|
|
try:
|
|
_dll_dir_handles.append(os.add_dll_directory(_path))
|
|
except (FileNotFoundError, OSError):
|
|
pass
|