mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-07-07 14:17:57 +02:00
fix: block Podman on macOS and remove ghcr.io default
- Add platform check in PodmanCLI.__init__() that raises FuzzForgeError on macOS with instructions to use Docker instead - Change RegistrySettings.url default from "ghcr.io/fuzzinglabs" to "" (empty string) for local-only mode since no images are published yet - Update _ensure_module_image() to show helpful error when image not found locally and no registry configured - Update tests to mock Linux platform for Podman tests - Add root ruff.toml to fix broken configuration in fuzzforge-runner
This commit is contained in:
@@ -95,8 +95,8 @@ class DockerCLI(AbstractFuzzForgeSandboxEngine):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
reference = f"{repo}:{tag}"
|
reference = f"{repo}:{tag}"
|
||||||
|
|
||||||
if filter_prefix and not reference.startswith(filter_prefix):
|
if filter_prefix and filter_prefix not in reference:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
images.append(
|
images.append(
|
||||||
|
|||||||
@@ -31,14 +31,15 @@ def get_logger() -> BoundLogger:
|
|||||||
|
|
||||||
def _is_running_under_snap() -> bool:
|
def _is_running_under_snap() -> bool:
|
||||||
"""Check if running under Snap environment.
|
"""Check if running under Snap environment.
|
||||||
|
|
||||||
VS Code installed via Snap sets XDG_DATA_HOME to a version-specific path,
|
VS Code installed via Snap sets XDG_DATA_HOME to a version-specific path,
|
||||||
causing Podman to look for storage in non-standard locations. When SNAP
|
causing Podman to look for storage in non-standard locations. When SNAP
|
||||||
is set, we use custom storage paths to ensure consistency.
|
is set, we use custom storage paths to ensure consistency.
|
||||||
|
|
||||||
Note: Snap only exists on Linux, so this also handles macOS implicitly.
|
Note: Snap only exists on Linux, so this also handles macOS implicitly.
|
||||||
"""
|
"""
|
||||||
import os # noqa: PLC0415
|
import os # noqa: PLC0415
|
||||||
|
|
||||||
return os.getenv("SNAP") is not None
|
return os.getenv("SNAP") is not None
|
||||||
|
|
||||||
|
|
||||||
@@ -48,11 +49,11 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
|||||||
This implementation uses subprocess calls to the Podman CLI with --root
|
This implementation uses subprocess calls to the Podman CLI with --root
|
||||||
and --runroot flags when running under Snap, providing isolation from
|
and --runroot flags when running under Snap, providing isolation from
|
||||||
system Podman storage.
|
system Podman storage.
|
||||||
|
|
||||||
The custom storage is only used when:
|
The custom storage is only used when:
|
||||||
1. Running under Snap (SNAP env var is set) - to fix XDG_DATA_HOME issues
|
1. Running under Snap (SNAP env var is set) - to fix XDG_DATA_HOME issues
|
||||||
2. Custom paths are explicitly provided
|
2. Custom paths are explicitly provided
|
||||||
|
|
||||||
Otherwise, uses default Podman storage which works for:
|
Otherwise, uses default Podman storage which works for:
|
||||||
- Native Linux installations
|
- Native Linux installations
|
||||||
- macOS (where Podman runs in a VM via podman machine)
|
- macOS (where Podman runs in a VM via podman machine)
|
||||||
@@ -69,16 +70,24 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
|||||||
:param runroot: Path to container runtime state.
|
:param runroot: Path to container runtime state.
|
||||||
|
|
||||||
Custom storage is used when running under Snap AND paths are provided.
|
Custom storage is used when running under Snap AND paths are provided.
|
||||||
|
|
||||||
|
:raises FuzzForgeError: If running on macOS (Podman not supported).
|
||||||
"""
|
"""
|
||||||
|
import sys # noqa: PLC0415
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
msg = (
|
||||||
|
"Podman is not supported on macOS. Please use Docker instead:\n"
|
||||||
|
" brew install --cask docker\n"
|
||||||
|
" # Or download from https://docker.com/products/docker-desktop"
|
||||||
|
)
|
||||||
|
raise FuzzForgeError(msg)
|
||||||
|
|
||||||
AbstractFuzzForgeSandboxEngine.__init__(self)
|
AbstractFuzzForgeSandboxEngine.__init__(self)
|
||||||
|
|
||||||
# Use custom storage only under Snap (to fix XDG_DATA_HOME issues)
|
# Use custom storage only under Snap (to fix XDG_DATA_HOME issues)
|
||||||
self.__use_custom_storage = (
|
self.__use_custom_storage = _is_running_under_snap() and graphroot is not None and runroot is not None
|
||||||
_is_running_under_snap()
|
|
||||||
and graphroot is not None
|
|
||||||
and runroot is not None
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.__use_custom_storage:
|
if self.__use_custom_storage:
|
||||||
self.__graphroot = graphroot
|
self.__graphroot = graphroot
|
||||||
self.__runroot = runroot
|
self.__runroot = runroot
|
||||||
@@ -98,8 +107,10 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
|||||||
if self.__use_custom_storage and self.__graphroot and self.__runroot:
|
if self.__use_custom_storage and self.__graphroot and self.__runroot:
|
||||||
return [
|
return [
|
||||||
"podman",
|
"podman",
|
||||||
"--root", str(self.__graphroot),
|
"--root",
|
||||||
"--runroot", str(self.__runroot),
|
str(self.__graphroot),
|
||||||
|
"--runroot",
|
||||||
|
str(self.__runroot),
|
||||||
]
|
]
|
||||||
return ["podman"]
|
return ["podman"]
|
||||||
|
|
||||||
|
|||||||
@@ -2,32 +2,41 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from fuzzforge_common.exceptions import FuzzForgeError
|
||||||
from fuzzforge_common.sandboxes.engines.podman.cli import PodmanCLI, _is_running_under_snap
|
from fuzzforge_common.sandboxes.engines.podman.cli import PodmanCLI, _is_running_under_snap
|
||||||
|
|
||||||
|
|
||||||
|
# Helper to mock Linux platform for testing (since Podman is Linux-only)
|
||||||
|
def _mock_linux_platform() -> mock._patch[str]:
|
||||||
|
"""Context manager to mock sys.platform as 'linux'."""
|
||||||
|
return mock.patch.object(sys, "platform", "linux")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def podman_cli_engine() -> PodmanCLI:
|
def podman_cli_engine() -> PodmanCLI:
|
||||||
"""Create a PodmanCLI engine with temporary storage.
|
"""Create a PodmanCLI engine with temporary storage.
|
||||||
|
|
||||||
Uses short paths in /tmp to avoid podman's 50-char runroot limit.
|
Uses short paths in /tmp to avoid podman's 50-char runroot limit.
|
||||||
Simulates Snap environment to test custom storage paths.
|
Simulates Snap environment to test custom storage paths.
|
||||||
|
Mocks Linux platform since Podman is Linux-only.
|
||||||
"""
|
"""
|
||||||
short_id = str(uuid.uuid4())[:8]
|
short_id = str(uuid.uuid4())[:8]
|
||||||
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
||||||
runroot = Path(f"/tmp/ff-{short_id}/run")
|
runroot = Path(f"/tmp/ff-{short_id}/run")
|
||||||
|
|
||||||
# Simulate Snap environment for testing
|
# Simulate Snap environment for testing on Linux
|
||||||
with mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
with _mock_linux_platform(), mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
||||||
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
||||||
|
|
||||||
yield engine
|
yield engine
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
parent = graphroot.parent
|
parent = graphroot.parent
|
||||||
if parent.exists():
|
if parent.exists():
|
||||||
@@ -48,21 +57,30 @@ def test_snap_detection_when_snap_not_set() -> None:
|
|||||||
assert _is_running_under_snap() is False
|
assert _is_running_under_snap() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_podman_cli_blocks_macos() -> None:
|
||||||
|
"""Test that PodmanCLI raises error on macOS."""
|
||||||
|
with mock.patch.object(sys, "platform", "darwin"):
|
||||||
|
with pytest.raises(FuzzForgeError) as exc_info:
|
||||||
|
PodmanCLI()
|
||||||
|
assert "Podman is not supported on macOS" in str(exc_info.value)
|
||||||
|
assert "Docker" in str(exc_info.value)
|
||||||
|
|
||||||
|
|
||||||
def test_podman_cli_creates_storage_directories_under_snap() -> None:
|
def test_podman_cli_creates_storage_directories_under_snap() -> None:
|
||||||
"""Test that PodmanCLI creates storage directories when under Snap."""
|
"""Test that PodmanCLI creates storage directories when under Snap."""
|
||||||
short_id = str(uuid.uuid4())[:8]
|
short_id = str(uuid.uuid4())[:8]
|
||||||
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
||||||
runroot = Path(f"/tmp/ff-{short_id}/run")
|
runroot = Path(f"/tmp/ff-{short_id}/run")
|
||||||
|
|
||||||
assert not graphroot.exists()
|
assert not graphroot.exists()
|
||||||
assert not runroot.exists()
|
assert not runroot.exists()
|
||||||
|
|
||||||
with mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
with _mock_linux_platform(), mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
||||||
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
PodmanCLI(graphroot=graphroot, runroot=runroot)
|
||||||
|
|
||||||
assert graphroot.exists()
|
assert graphroot.exists()
|
||||||
assert runroot.exists()
|
assert runroot.exists()
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
shutil.rmtree(graphroot.parent, ignore_errors=True)
|
shutil.rmtree(graphroot.parent, ignore_errors=True)
|
||||||
|
|
||||||
@@ -72,15 +90,15 @@ def test_podman_cli_base_cmd_under_snap() -> None:
|
|||||||
short_id = str(uuid.uuid4())[:8]
|
short_id = str(uuid.uuid4())[:8]
|
||||||
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
||||||
runroot = Path(f"/tmp/ff-{short_id}/run")
|
runroot = Path(f"/tmp/ff-{short_id}/run")
|
||||||
|
|
||||||
with mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
with _mock_linux_platform(), mock.patch.dict(os.environ, {"SNAP": "/snap/code/123"}):
|
||||||
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
||||||
base_cmd = engine._base_cmd()
|
base_cmd = engine._base_cmd()
|
||||||
|
|
||||||
assert "podman" in base_cmd
|
assert "podman" in base_cmd
|
||||||
assert "--root" in base_cmd
|
assert "--root" in base_cmd
|
||||||
assert "--runroot" in base_cmd
|
assert "--runroot" in base_cmd
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
shutil.rmtree(graphroot.parent, ignore_errors=True)
|
shutil.rmtree(graphroot.parent, ignore_errors=True)
|
||||||
|
|
||||||
@@ -90,25 +108,26 @@ def test_podman_cli_base_cmd_without_snap() -> None:
|
|||||||
short_id = str(uuid.uuid4())[:8]
|
short_id = str(uuid.uuid4())[:8]
|
||||||
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
graphroot = Path(f"/tmp/ff-{short_id}/storage")
|
||||||
runroot = Path(f"/tmp/ff-{short_id}/run")
|
runroot = Path(f"/tmp/ff-{short_id}/run")
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env.pop("SNAP", None)
|
env.pop("SNAP", None)
|
||||||
with mock.patch.dict(os.environ, env, clear=True):
|
with _mock_linux_platform(), mock.patch.dict(os.environ, env, clear=True):
|
||||||
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
engine = PodmanCLI(graphroot=graphroot, runroot=runroot)
|
||||||
base_cmd = engine._base_cmd()
|
base_cmd = engine._base_cmd()
|
||||||
|
|
||||||
assert base_cmd == ["podman"]
|
assert base_cmd == ["podman"]
|
||||||
assert "--root" not in base_cmd
|
assert "--root" not in base_cmd
|
||||||
|
|
||||||
# Directories should NOT be created when not under Snap
|
# Directories should NOT be created when not under Snap
|
||||||
assert not graphroot.exists()
|
assert not graphroot.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_podman_cli_default_mode() -> None:
|
def test_podman_cli_default_mode() -> None:
|
||||||
"""Test PodmanCLI without custom storage paths."""
|
"""Test PodmanCLI without custom storage paths."""
|
||||||
engine = PodmanCLI() # No paths provided
|
with _mock_linux_platform():
|
||||||
base_cmd = engine._base_cmd()
|
engine = PodmanCLI() # No paths provided
|
||||||
|
base_cmd = engine._base_cmd()
|
||||||
|
|
||||||
assert base_cmd == ["podman"]
|
assert base_cmd == ["podman"]
|
||||||
assert "--root" not in base_cmd
|
assert "--root" not in base_cmd
|
||||||
|
|
||||||
@@ -116,7 +135,7 @@ def test_podman_cli_default_mode() -> None:
|
|||||||
def test_podman_cli_list_images_returns_list(podman_cli_engine: PodmanCLI) -> None:
|
def test_podman_cli_list_images_returns_list(podman_cli_engine: PodmanCLI) -> None:
|
||||||
"""Test that list_images returns a list (even if empty)."""
|
"""Test that list_images returns a list (even if empty)."""
|
||||||
images = podman_cli_engine.list_images()
|
images = podman_cli_engine.list_images()
|
||||||
|
|
||||||
assert isinstance(images, list)
|
assert isinstance(images, list)
|
||||||
|
|
||||||
|
|
||||||
@@ -125,6 +144,6 @@ def test_podman_cli_can_pull_and_list_image(podman_cli_engine: PodmanCLI) -> Non
|
|||||||
"""Test pulling an image and listing it."""
|
"""Test pulling an image and listing it."""
|
||||||
# Pull a small image
|
# Pull a small image
|
||||||
podman_cli_engine._run(["pull", "docker.io/library/alpine:latest"])
|
podman_cli_engine._run(["pull", "docker.io/library/alpine:latest"])
|
||||||
|
|
||||||
images = podman_cli_engine.list_images()
|
images = podman_cli_engine.list_images()
|
||||||
assert any("alpine" in img.identifier for img in images)
|
assert any("alpine" in img.identifier for img in images)
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ class ModuleExecutor:
|
|||||||
# - fuzzforge-module-{name}:{tag} (OSS local builds with module prefix)
|
# - fuzzforge-module-{name}:{tag} (OSS local builds with module prefix)
|
||||||
# - localhost/fuzzforge-module-{name}:{tag} (standard convention)
|
# - localhost/fuzzforge-module-{name}:{tag} (standard convention)
|
||||||
# - localhost/{name}:{tag} (legacy/short form)
|
# - localhost/{name}:{tag} (legacy/short form)
|
||||||
|
|
||||||
# For OSS local builds (no localhost/ prefix)
|
# For OSS local builds (no localhost/ prefix)
|
||||||
for tag in tags_to_check:
|
for tag in tags_to_check:
|
||||||
# Check direct module name (fuzzforge-cargo-fuzzer:0.1.0)
|
# Check direct module name (fuzzforge-cargo-fuzzer:0.1.0)
|
||||||
@@ -146,7 +146,7 @@ class ModuleExecutor:
|
|||||||
if not module_identifier.startswith("fuzzforge-"):
|
if not module_identifier.startswith("fuzzforge-"):
|
||||||
if engine.image_exists(f"fuzzforge-{module_identifier}:{tag}"):
|
if engine.image_exists(f"fuzzforge-{module_identifier}:{tag}"):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# For registry-style naming (localhost/ prefix)
|
# For registry-style naming (localhost/ prefix)
|
||||||
name_prefixes = [f"fuzzforge-module-{module_identifier}", module_identifier]
|
name_prefixes = [f"fuzzforge-module-{module_identifier}", module_identifier]
|
||||||
|
|
||||||
@@ -166,17 +166,17 @@ class ModuleExecutor:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
engine = self._get_engine()
|
engine = self._get_engine()
|
||||||
|
|
||||||
# Try common tags
|
# Try common tags
|
||||||
tags_to_check = ["latest", "0.1.0", "0.0.1"]
|
tags_to_check = ["latest", "0.1.0", "0.0.1"]
|
||||||
|
|
||||||
# Check OSS local builds first (no localhost/ prefix)
|
# Check OSS local builds first (no localhost/ prefix)
|
||||||
for tag in tags_to_check:
|
for tag in tags_to_check:
|
||||||
# Direct module name (fuzzforge-cargo-fuzzer:0.1.0)
|
# Direct module name (fuzzforge-cargo-fuzzer:0.1.0)
|
||||||
direct_name = f"{module_identifier}:{tag}"
|
direct_name = f"{module_identifier}:{tag}"
|
||||||
if engine.image_exists(direct_name):
|
if engine.image_exists(direct_name):
|
||||||
return direct_name
|
return direct_name
|
||||||
|
|
||||||
# With fuzzforge- prefix if not already present
|
# With fuzzforge- prefix if not already present
|
||||||
if not module_identifier.startswith("fuzzforge-"):
|
if not module_identifier.startswith("fuzzforge-"):
|
||||||
prefixed_name = f"fuzzforge-{module_identifier}:{tag}"
|
prefixed_name = f"fuzzforge-{module_identifier}:{tag}"
|
||||||
@@ -189,7 +189,7 @@ class ModuleExecutor:
|
|||||||
prefixed_name = f"localhost/fuzzforge-module-{module_identifier}:{tag}"
|
prefixed_name = f"localhost/fuzzforge-module-{module_identifier}:{tag}"
|
||||||
if engine.image_exists(prefixed_name):
|
if engine.image_exists(prefixed_name):
|
||||||
return prefixed_name
|
return prefixed_name
|
||||||
|
|
||||||
# Legacy short form: localhost/{name}:{tag}
|
# Legacy short form: localhost/{name}:{tag}
|
||||||
short_name = f"localhost/{module_identifier}:{tag}"
|
short_name = f"localhost/{module_identifier}:{tag}"
|
||||||
if engine.image_exists(short_name):
|
if engine.image_exists(short_name):
|
||||||
@@ -198,7 +198,7 @@ class ModuleExecutor:
|
|||||||
# Default fallback
|
# Default fallback
|
||||||
return f"localhost/{module_identifier}:latest"
|
return f"localhost/{module_identifier}:latest"
|
||||||
|
|
||||||
def _pull_module_image(self, module_identifier: str, registry_url: str = "ghcr.io/fuzzinglabs", tag: str = "latest") -> None:
|
def _pull_module_image(self, module_identifier: str, registry_url: str, tag: str = "latest") -> None:
|
||||||
"""Pull a module image from the container registry.
|
"""Pull a module image from the container registry.
|
||||||
|
|
||||||
:param module_identifier: Name/identifier of the module to pull.
|
:param module_identifier: Name/identifier of the module to pull.
|
||||||
@@ -238,21 +238,30 @@ class ModuleExecutor:
|
|||||||
)
|
)
|
||||||
raise SandboxError(message) from exc
|
raise SandboxError(message) from exc
|
||||||
|
|
||||||
def _ensure_module_image(self, module_identifier: str, registry_url: str = "ghcr.io/fuzzinglabs", tag: str = "latest") -> None:
|
def _ensure_module_image(self, module_identifier: str, registry_url: str = "", tag: str = "latest") -> None:
|
||||||
"""Ensure module image exists, pulling it if necessary.
|
"""Ensure module image exists, pulling it if necessary.
|
||||||
|
|
||||||
:param module_identifier: Name/identifier of the module image.
|
:param module_identifier: Name/identifier of the module image.
|
||||||
:param registry_url: Container registry URL to pull from.
|
:param registry_url: Container registry URL to pull from (empty = local-only mode).
|
||||||
:param tag: Image tag to pull.
|
:param tag: Image tag to pull.
|
||||||
:raises SandboxError: If image check or pull fails.
|
:raises SandboxError: If image not found locally and no registry configured.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
if self._check_image_exists(module_identifier):
|
if self._check_image_exists(module_identifier):
|
||||||
logger.debug("module image exists locally", module=module_identifier)
|
logger.debug("module image exists locally", module=module_identifier)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# If no registry configured, we're in local-only mode
|
||||||
|
if not registry_url:
|
||||||
|
raise SandboxError(
|
||||||
|
f"Module image '{module_identifier}' not found locally.\n"
|
||||||
|
"Build it with: make build-modules\n"
|
||||||
|
"\n"
|
||||||
|
"Or configure a registry URL via FUZZFORGE_REGISTRY__URL environment variable."
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"module image not found locally, pulling from registry",
|
"module image not found locally, pulling from registry",
|
||||||
module=module_identifier,
|
module=module_identifier,
|
||||||
@@ -260,7 +269,7 @@ class ModuleExecutor:
|
|||||||
info="This may take a moment on first run",
|
info="This may take a moment on first run",
|
||||||
)
|
)
|
||||||
self._pull_module_image(module_identifier, registry_url, tag)
|
self._pull_module_image(module_identifier, registry_url, tag)
|
||||||
|
|
||||||
# Verify image now exists
|
# Verify image now exists
|
||||||
if not self._check_image_exists(module_identifier):
|
if not self._check_image_exists(module_identifier):
|
||||||
message = (
|
message = (
|
||||||
@@ -332,6 +341,7 @@ class ModuleExecutor:
|
|||||||
try:
|
try:
|
||||||
# Create temporary directory - caller must clean it up after container finishes
|
# Create temporary directory - caller must clean it up after container finishes
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
temp_path = Path(mkdtemp(prefix="fuzzforge-input-"))
|
temp_path = Path(mkdtemp(prefix="fuzzforge-input-"))
|
||||||
|
|
||||||
# Copy assets to temp directory
|
# Copy assets to temp directory
|
||||||
@@ -341,16 +351,19 @@ class ModuleExecutor:
|
|||||||
if assets_path.suffix == ".gz" or assets_path.name.endswith(".tar.gz"):
|
if assets_path.suffix == ".gz" or assets_path.name.endswith(".tar.gz"):
|
||||||
# Extract archive contents
|
# Extract archive contents
|
||||||
import tarfile
|
import tarfile
|
||||||
|
|
||||||
with tarfile.open(assets_path, "r:gz") as tar:
|
with tarfile.open(assets_path, "r:gz") as tar:
|
||||||
tar.extractall(path=temp_path)
|
tar.extractall(path=temp_path)
|
||||||
logger.debug("extracted tar.gz archive", archive=str(assets_path))
|
logger.debug("extracted tar.gz archive", archive=str(assets_path))
|
||||||
else:
|
else:
|
||||||
# Single file - copy it
|
# Single file - copy it
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
shutil.copy2(assets_path, temp_path / assets_path.name)
|
shutil.copy2(assets_path, temp_path / assets_path.name)
|
||||||
else:
|
else:
|
||||||
# Directory - copy all files (including subdirectories)
|
# Directory - copy all files (including subdirectories)
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
for item in assets_path.iterdir():
|
for item in assets_path.iterdir():
|
||||||
if item.is_file():
|
if item.is_file():
|
||||||
shutil.copy2(item, temp_path / item.name)
|
shutil.copy2(item, temp_path / item.name)
|
||||||
@@ -363,19 +376,23 @@ class ModuleExecutor:
|
|||||||
if item.name == "input.json":
|
if item.name == "input.json":
|
||||||
continue
|
continue
|
||||||
if item.is_file():
|
if item.is_file():
|
||||||
resources.append({
|
resources.append(
|
||||||
"name": item.stem,
|
{
|
||||||
"description": f"Input file: {item.name}",
|
"name": item.stem,
|
||||||
"kind": "unknown",
|
"description": f"Input file: {item.name}",
|
||||||
"path": f"/data/input/{item.name}",
|
"kind": "unknown",
|
||||||
})
|
"path": f"/data/input/{item.name}",
|
||||||
|
}
|
||||||
|
)
|
||||||
elif item.is_dir():
|
elif item.is_dir():
|
||||||
resources.append({
|
resources.append(
|
||||||
"name": item.name,
|
{
|
||||||
"description": f"Input directory: {item.name}",
|
"name": item.name,
|
||||||
"kind": "unknown",
|
"description": f"Input directory: {item.name}",
|
||||||
"path": f"/data/input/{item.name}",
|
"kind": "unknown",
|
||||||
})
|
"path": f"/data/input/{item.name}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Create input.json with settings and resources
|
# Create input.json with settings and resources
|
||||||
input_data = {
|
input_data = {
|
||||||
@@ -461,6 +478,7 @@ class ModuleExecutor:
|
|||||||
try:
|
try:
|
||||||
# Create temporary directory for results
|
# Create temporary directory for results
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
temp_dir = Path(mkdtemp(prefix="fuzzforge-results-"))
|
temp_dir = Path(mkdtemp(prefix="fuzzforge-results-"))
|
||||||
|
|
||||||
# Copy entire output directory from container
|
# Copy entire output directory from container
|
||||||
@@ -489,6 +507,7 @@ class ModuleExecutor:
|
|||||||
|
|
||||||
# Clean up temp directory
|
# Clean up temp directory
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||||
|
|
||||||
logger.info("results pulled successfully", sandbox=sandbox, archive=str(archive_path))
|
logger.info("results pulled successfully", sandbox=sandbox, archive=str(archive_path))
|
||||||
@@ -571,6 +590,7 @@ class ModuleExecutor:
|
|||||||
self.terminate_sandbox(sandbox)
|
self.terminate_sandbox(sandbox)
|
||||||
if input_dir and input_dir.exists():
|
if input_dir and input_dir.exists():
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
shutil.rmtree(input_dir, ignore_errors=True)
|
shutil.rmtree(input_dir, ignore_errors=True)
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -669,4 +689,5 @@ class ModuleExecutor:
|
|||||||
self.terminate_sandbox(container_id)
|
self.terminate_sandbox(container_id)
|
||||||
if input_dir:
|
if input_dir:
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
shutil.rmtree(input_dir, ignore_errors=True)
|
shutil.rmtree(input_dir, ignore_errors=True)
|
||||||
|
|||||||
@@ -60,10 +60,15 @@ class ProjectSettings(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class RegistrySettings(BaseModel):
|
class RegistrySettings(BaseModel):
|
||||||
"""Container registry configuration for module images."""
|
"""Container registry configuration for module images.
|
||||||
|
|
||||||
#: Registry URL for pulling module images.
|
By default, registry URL is empty (local-only mode). When empty,
|
||||||
url: str = Field(default="ghcr.io/fuzzinglabs")
|
modules must be built locally with `make build-modules`.
|
||||||
|
Set via FUZZFORGE_REGISTRY__URL environment variable if needed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: Registry URL for pulling module images (empty = local-only mode).
|
||||||
|
url: str = Field(default="")
|
||||||
|
|
||||||
#: Default tag to use when pulling images.
|
#: Default tag to use when pulling images.
|
||||||
default_tag: str = Field(default="latest")
|
default_tag: str = Field(default="latest")
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
line-length = 120
|
||||||
|
|
||||||
|
[lint]
|
||||||
|
select = [ "ALL" ]
|
||||||
|
ignore = [
|
||||||
|
"COM812", # conflicts with the formatter
|
||||||
|
"D100", # ignoring missing docstrings in public modules
|
||||||
|
"D104", # ignoring missing docstrings in public packages
|
||||||
|
"D203", # conflicts with 'D211'
|
||||||
|
"D213", # conflicts with 'D212'
|
||||||
|
"TD002", # ignoring missing author in 'TODO' statements
|
||||||
|
"TD003", # ignoring missing issue link in 'TODO' statements
|
||||||
|
]
|
||||||
|
|
||||||
|
[lint.per-file-ignores]
|
||||||
|
"tests/*" = [
|
||||||
|
"ANN401", # allowing 'typing.Any' to be used to type function parameters in tests
|
||||||
|
"PLR2004", # allowing comparisons using unamed numerical constants in tests
|
||||||
|
"S101", # allowing 'assert' statements in tests
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user