diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml index 56d8f52e3..632537387 100644 --- a/.github/workflows/_build.yml +++ b/.github/workflows/_build.yml @@ -238,55 +238,16 @@ jobs: cp target/${{ matrix.arch == 'aarch64' && 'aarch64-unknown-linux-gnu' || 'x86_64-unknown-linux-gnu' }}/release/ov openviking/bin/ chmod +x openviking/bin/ov - - name: Build ragfs-python and extract into openviking/lib/ (Linux) - shell: bash - run: | - uv pip install maturin - TMPDIR=$(mktemp -d) - cd crates/ragfs-python - maturin build --release \ - --target ${{ matrix.arch == 'aarch64' && 'aarch64-unknown-linux-gnu' || 'x86_64-unknown-linux-gnu' }} \ - --out "$TMPDIR" - cd ../.. - mkdir -p openviking/lib - python3 < {dst}") - sys.exit(0) - - print("ERROR: No ragfs_python .so/.pyd found in wheel") - sys.exit(1) - PY - rm -rf "$TMPDIR" - echo "Contents of openviking/lib/:" - ls -la openviking/lib/ - name: Clean workspace (force ignore dirty) shell: bash run: | # Back up pre-built artifacts before cleaning cp -a openviking/bin /tmp/_ov_bin || true - cp -a openviking/lib /tmp/_ov_lib || true git reset --hard HEAD git clean -fd rm -rf openviking/_version.py openviking.egg-info # Restore pre-built artifacts cp -a /tmp/_ov_bin openviking/bin || true - cp -a /tmp/_ov_lib openviking/lib || true # Ignore uv.lock changes to avoid dirty state in setuptools_scm git update-index --assume-unchanged uv.lock || true @@ -302,7 +263,7 @@ jobs: echo "=== Check openviking/_version.py ===" if [ -f openviking/_version.py ]; then cat openviking/_version.py; else echo "Not found"; fi echo "=== Verify pre-built artifacts survived clean ===" - ls -la openviking/bin/ openviking/lib/ || true + ls -la openviking/bin/ || true - name: Build package (Wheel Only) run: uv build --wheel @@ -449,57 +410,16 @@ jobs: chmod +x openviking/bin/ov fi - - name: Build ragfs-python and extract into openviking/lib/ (macOS/Windows) - shell: bash - run: | - uv pip install maturin - TMPDIR=$(mktemp -d) - cd crates/ragfs-python - if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - maturin build --release --target x86_64-pc-windows-msvc --out "$TMPDIR" - else - maturin build --release --out "$TMPDIR" - fi - cd ../.. - mkdir -p openviking/lib - python3 < {dst}") - sys.exit(0) - - print("ERROR: No ragfs_python .so/.pyd found in wheel") - sys.exit(1) - PY - rm -rf "$TMPDIR" - echo "Contents of openviking/lib/:" - ls -la openviking/lib/ - name: Clean workspace (force ignore dirty) shell: bash run: | # Back up pre-built artifacts before cleaning cp -a openviking/bin /tmp/_ov_bin || true - cp -a openviking/lib /tmp/_ov_lib || true git reset --hard HEAD git clean -fd rm -rf openviking/_version.py openviking.egg-info # Restore pre-built artifacts cp -a /tmp/_ov_bin openviking/bin || true - cp -a /tmp/_ov_lib openviking/lib || true # Ignore uv.lock changes to avoid dirty state in setuptools_scm git update-index --assume-unchanged uv.lock || true @@ -515,7 +435,7 @@ jobs: echo "=== Check openviking/_version.py ===" if [ -f openviking/_version.py ]; then cat openviking/_version.py; else echo "Not found"; fi echo "=== Verify pre-built artifacts survived clean ===" - ls -la openviking/bin/ openviking/lib/ || true + ls -la openviking/bin/ || true - name: Build package (Wheel Only) run: uv build --wheel diff --git a/Cargo.lock b/Cargo.lock index d4554e4d2..3dd5e4775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2949,11 +2949,10 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.23.5" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" +checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" dependencies = [ - "cfg-if", "indoc", "libc", "memoffset", @@ -2967,19 +2966,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.23.5" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" +checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.23.5" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" +checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" dependencies = [ "libc", "pyo3-build-config", @@ -2987,9 +2985,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.23.5" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" +checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -2999,9 +2997,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.23.5" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" +checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" dependencies = [ "heck", "proc-macro2", @@ -4147,9 +4145,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" diff --git a/crates/ragfs-python/Cargo.toml b/crates/ragfs-python/Cargo.toml index c132835cf..6506f20a3 100644 --- a/crates/ragfs-python/Cargo.toml +++ b/crates/ragfs-python/Cargo.toml @@ -11,6 +11,6 @@ crate-type = ["cdylib"] [dependencies] ragfs = { path = "../ragfs" } -pyo3 = { version = "0.23", features = ["extension-module"] } +pyo3 = { version = "0.27", features = ["extension-module"] } tokio = { version = "1", features = ["full"] } serde_json = "1.0" diff --git a/crates/ragfs-python/src/lib.rs b/crates/ragfs-python/src/lib.rs index 9998a69eb..6be96f4ed 100644 --- a/crates/ragfs-python/src/lib.rs +++ b/crates/ragfs-python/src/lib.rs @@ -138,8 +138,8 @@ impl RAGFSBindingClient { } /// Get client capabilities. - fn get_capabilities(&self) -> PyResult> { - Python::with_gil(|py| { + fn get_capabilities(&self) -> PyResult>> { + Python::attach(|py| { let mut m = HashMap::new(); m.insert("version".to_string(), "ragfs-python".into_pyobject(py)?.into_any().unbind()); let features = vec!["memfs", "kvfs", "queuefs", "sqlfs"]; @@ -152,13 +152,13 @@ impl RAGFSBindingClient { /// /// Returns a list of file info dicts with keys: /// name, size, mode, modTime, isDir - fn ls(&self, path: String) -> PyResult { + fn ls(&self, path: String) -> PyResult> { let fs = self.fs.clone(); let entries = self.rt.block_on(async move { fs.read_dir(&path).await }).map_err(to_py_err)?; - Python::with_gil(|py| { + Python::attach(|py| { let list = PyList::empty(py); for entry in &entries { let dict = file_info_to_py_dict(py, entry)?; @@ -176,7 +176,7 @@ impl RAGFSBindingClient { /// size: Number of bytes to read (default: -1, read all) /// stream: Not supported in binding mode #[pyo3(signature = (path, offset=0, size=-1, stream=false))] - fn read(&self, path: String, offset: i64, size: i64, stream: bool) -> PyResult { + fn read(&self, path: String, offset: i64, size: i64, stream: bool) -> PyResult> { if stream { return Err(PyRuntimeError::new_err( "Streaming not supported in binding mode", @@ -191,14 +191,14 @@ impl RAGFSBindingClient { fs.read(&path, off, sz).await }).map_err(to_py_err)?; - Python::with_gil(|py| { + Python::attach(|py| { Ok(PyBytes::new(py, &data).into()) }) } /// Read file content (alias for read). #[pyo3(signature = (path, offset=0, size=-1, stream=false))] - fn cat(&self, path: String, offset: i64, size: i64, stream: bool) -> PyResult { + fn cat(&self, path: String, offset: i64, size: i64, stream: bool) -> PyResult> { self.read(path, offset, size, stream) } @@ -265,13 +265,13 @@ impl RAGFSBindingClient { } /// Get file/directory information. - fn stat(&self, path: String) -> PyResult { + fn stat(&self, path: String) -> PyResult> { let fs = self.fs.clone(); let info = self.rt.block_on(async move { fs.stat(&path).await }).map_err(to_py_err)?; - Python::with_gil(|py| { + Python::attach(|py| { let dict = file_info_to_py_dict(py, &info)?; Ok(dict.into()) }) @@ -432,7 +432,7 @@ impl RAGFSBindingClient { case_insensitive: bool, stream: bool, node_limit: Option, - ) -> PyResult { + ) -> PyResult> { let _ = (path, pattern, recursive, case_insensitive, stream, node_limit); Err(PyRuntimeError::new_err( "grep not yet implemented in ragfs-python", diff --git a/pyproject.toml b/pyproject.toml index d518e847e..07093717f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ requires = [ "setuptools>=61.0", "setuptools-scm>=8.0", "cmake>=3.15", + "maturin>=1.0,<2.0", "wheel", ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 2a062b553..3b1275f8f 100644 --- a/setup.py +++ b/setup.py @@ -393,8 +393,7 @@ def build_ragfs_python_artifact(self): print("[OK] Skipping ragfs-python build (OV_SKIP_RAGFS_BUILD=1)") return - maturin_cmd = shutil.which("maturin") - if not maturin_cmd: + if importlib.util.find_spec("maturin") is None: print( "[SKIP] maturin not found. ragfs-python (Rust binding) will not be built.\n" " Install maturin to enable: pip install maturin\n" @@ -409,7 +408,15 @@ def build_ragfs_python_artifact(self): try: print("Building ragfs-python (Rust AGFS binding) via maturin...") env = os.environ.copy() - build_args = [maturin_cmd, "build", "--release", "--out", tmpdir] + build_args = [ + sys.executable, + "-m", + "maturin", + "build", + "--release", + "--out", + tmpdir, + ] # Respect CARGO_BUILD_TARGET for cross-compilation target = env.get("CARGO_BUILD_TARGET") if target: diff --git a/tests/misc/test_root_docker_image_packaging.py b/tests/misc/test_root_docker_image_packaging.py new file mode 100644 index 000000000..312ce4d19 --- /dev/null +++ b/tests/misc/test_root_docker_image_packaging.py @@ -0,0 +1,45 @@ +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def _read_text(relative_path: str) -> str: + return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + + +def test_root_dockerfile_copies_bot_sources_into_build_context(): + dockerfile = _read_text("Dockerfile") + + assert "COPY bot/ bot/" in dockerfile + + +def test_openviking_package_includes_console_static_assets(): + pyproject = _read_text("pyproject.toml") + setup_py = _read_text("setup.py") + + assert '"console/static/**/*"' in pyproject + assert '"console/static/**/*"' in pyproject.split("vikingbot = [", maxsplit=1)[0] + assert '"console/static/**/*"' in setup_py + + +def test_build_workflow_invokes_maturin_via_python_module(): + workflow = _read_text(".github/workflows/_build.yml") + + assert "Build ragfs-python and extract into openviking/lib/" not in workflow + assert "uv run python -m maturin build --release" not in workflow + assert "uv run python <=1.0,<2.0",' in pyproject + assert '[sys.executable, "-m", "maturin", "build", "--release", "--out", tmpdir]' in setup_py + assert 'shutil.which("maturin")' not in setup_py diff --git a/uv.lock b/uv.lock index 64bcafe5b..3c8b05997 100644 --- a/uv.lock +++ b/uv.lock @@ -3496,6 +3496,7 @@ dependencies = [ { name = "tree-sitter-go" }, { name = "tree-sitter-java" }, { name = "tree-sitter-javascript" }, + { name = "tree-sitter-lua" }, { name = "tree-sitter-php" }, { name = "tree-sitter-python" }, { name = "tree-sitter-rust" }, @@ -3755,6 +3756,7 @@ requires-dist = [ { name = "tree-sitter-go", specifier = ">=0.23.0" }, { name = "tree-sitter-java", specifier = ">=0.23.0" }, { name = "tree-sitter-javascript", specifier = ">=0.23.0" }, + { name = "tree-sitter-lua", specifier = ">=0.1.0" }, { name = "tree-sitter-php", specifier = ">=0.23.0" }, { name = "tree-sitter-python", specifier = ">=0.23.0" }, { name = "tree-sitter-rust", specifier = ">=0.23.0" }, @@ -6136,6 +6138,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2e/1f/f9eba1038b7d4394410f3c0a6ec2122b590cd7acb03f196e52fa57ebbe72/tree_sitter_javascript-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:622a69d677aa7f6ee2931d8c77c981a33f0ebb6d275aa9d43d3397c879a9bb0b", size = 61668, upload-time = "2025-09-01T07:13:43.803Z" }, ] +[[package]] +name = "tree-sitter-lua" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/07/98d7c5f60c9a79a1d40f85e59b7c25a0102d2eebcc5a83608c7c308edf22/tree_sitter_lua-0.5.0.tar.gz", hash = "sha256:0e46356038ccb8ce1049289104c56230003448309a335f2e353f1edc7b373552", size = 36829, upload-time = "2026-02-26T17:07:33.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/b2/d1ffd919692b217d257222cbfa1705268dfea073b91ffb81726da0e27fe8/tree_sitter_lua-0.5.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cc4f2eb734dc9223bf96c0eeffa78a9485db207d00841e27e52c8b036f2164f7", size = 22781, upload-time = "2026-02-26T17:07:26.412Z" }, + { url = "https://files.pythonhosted.org/packages/de/0c/6bc3228d01419e8b5af664bf328d174b02a64736ffa23a335c778c8cda68/tree_sitter_lua-0.5.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c14714ad395c4166566f3e4dd0cc0979411684cbcd23702e3c631c3e6eae84fd", size = 23437, upload-time = "2026-02-26T17:07:27.504Z" }, + { url = "https://files.pythonhosted.org/packages/45/2b/1edfd9bef9a1cc11047cd87ca9c60707b8425080cfc0498a7d3bc762d783/tree_sitter_lua-0.5.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ec448c854fea32414a0449147d648bc5baddf7a0357008c4abe3269db35370a", size = 41743, upload-time = "2026-02-26T17:07:28.433Z" }, + { url = "https://files.pythonhosted.org/packages/bf/7f/53bbfde347e5d9a34e0a9ed367d340dd876cf987c6ce8478c0597e1cf608/tree_sitter_lua-0.5.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b02f057a997e618c5b1b03a5cef9dd6c2673043d396ca86edba372728f17ef53", size = 44405, upload-time = "2026-02-26T17:07:29.662Z" }, + { url = "https://files.pythonhosted.org/packages/f9/63/989c0bcde97280cb7938aa2797ce310735c907ad372f6adc4645ef8dfb86/tree_sitter_lua-0.5.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a048571f55a3dd30c94e2313091274338284cab23e757c181e4961c185ba9d0", size = 43208, upload-time = "2026-02-26T17:07:30.612Z" }, + { url = "https://files.pythonhosted.org/packages/6d/da/d9ce9a35c3042b2fd7453ba69d543d32c5d09563277a099b0859ce53d919/tree_sitter_lua-0.5.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:922a5a3d0fec8af373cab504cbcd9abeeebb212d454f54163591c50c183466be", size = 41357, upload-time = "2026-02-26T17:07:31.408Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/8973f4049d81b2920ef496cf61b9b947ccee63dfb1aa89cb73810cb22784/tree_sitter_lua-0.5.0-cp310-abi3-win_amd64.whl", hash = "sha256:ace3dd61218124ee08410a55601cb5fbbb00be3ee004b30e705cef9ef25165a9", size = 24755, upload-time = "2026-02-26T17:07:32.128Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/3104ecfa3c34320411bcad9b4f2823956487b6e222edcc83689819badc9d/tree_sitter_lua-0.5.0-cp310-abi3-win_arm64.whl", hash = "sha256:8488f3bea40779896f5771bcfcdc26900eb21e94f6658eb68a848fc37dd39221", size = 23506, upload-time = "2026-02-26T17:07:32.775Z" }, +] + [[package]] name = "tree-sitter-php" version = "0.24.1"