mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-09-12 19:50:57 +05:00
Fix CI runtime resolution and host policy regressions
This commit is contained in:
parent
d312c25531
commit
8254f962ba
@ -194,6 +194,12 @@ def _coerce_from_str_dict(file_config: dict[str, str]) -> dict[str, Any]:
|
||||
decoded[key] = json.loads(raw)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if not str(decoded.get("BASE_URL") or "").strip():
|
||||
from archivebox.config.common import base_url_from_legacy_server_config
|
||||
|
||||
legacy_base_url = base_url_from_legacy_server_config(decoded)
|
||||
if legacy_base_url:
|
||||
decoded["BASE_URL"] = legacy_base_url
|
||||
return decoded
|
||||
|
||||
|
||||
|
||||
@ -1245,6 +1245,10 @@ def get_config(
|
||||
)
|
||||
|
||||
config = ArchiveBoxConfig.model_validate(config_data)
|
||||
if config.SERVER_SECURITY_MODE == "auto":
|
||||
base_host = (urlparse(config.BASE_URL if "://" in config.BASE_URL else f"//{config.BASE_URL}").hostname or "").lower()
|
||||
if base_host.endswith(".localhost"):
|
||||
config = config.model_copy(update={"SERVER_SECURITY_MODE": "safe-subdomains-fullreplay"})
|
||||
for key in explicit_plugin_enabled_keys:
|
||||
if key in config_data:
|
||||
setattr(config, key, config_data[key])
|
||||
|
||||
@ -413,6 +413,7 @@ class ArchiveResultAdmin(BaseModelAdmin):
|
||||
"cmd_version",
|
||||
"pwd",
|
||||
"cmd_str",
|
||||
"admin_actions",
|
||||
"snapshot_info",
|
||||
"tags_str",
|
||||
"created_at",
|
||||
@ -438,7 +439,7 @@ class ArchiveResultAdmin(BaseModelAdmin):
|
||||
(
|
||||
"Snapshot",
|
||||
{
|
||||
"fields": ("snapshot", "snapshot_info", "tags_str"),
|
||||
"fields": ("snapshot", "snapshot_info", "tags_str", "admin_actions"),
|
||||
"classes": ("card", "wide"),
|
||||
},
|
||||
),
|
||||
@ -630,6 +631,38 @@ class ArchiveResultAdmin(BaseModelAdmin):
|
||||
self.get_output_zip_url(result),
|
||||
)
|
||||
|
||||
@admin.display(description="")
|
||||
def admin_actions(self, result):
|
||||
return format_html(
|
||||
"""
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px; align-items: center;">
|
||||
<a class="btn" style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; color: #334155; text-decoration: none; font-size: 14px; font-weight: 500;"
|
||||
href="{}">
|
||||
📄 View Output
|
||||
</a>
|
||||
<a class="btn" style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; color: #334155; text-decoration: none; font-size: 14px; font-weight: 500;"
|
||||
href="{}">
|
||||
📁 Output files
|
||||
</a>
|
||||
<a class="btn archivebox-zip-button" style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; color: #1d4ed8; text-decoration: none; font-size: 14px; font-weight: 500;"
|
||||
href="{}"
|
||||
data-loading-label="Preparing..."
|
||||
onclick="return window.archiveboxHandleZipClick(this, event);">
|
||||
<span class="archivebox-zip-spinner" aria-hidden="true"></span>
|
||||
<span class="archivebox-zip-label">⬇ Download Zip</span>
|
||||
</a>
|
||||
<a class="btn" style="display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; color: #334155; text-decoration: none; font-size: 14px; font-weight: 500;"
|
||||
href="{}">
|
||||
🗂 Snapshot
|
||||
</a>
|
||||
</div>
|
||||
""",
|
||||
self.get_output_view_url(result),
|
||||
self.get_output_files_url(result),
|
||||
self.get_output_zip_url(result),
|
||||
self.get_snapshot_view_url(result),
|
||||
)
|
||||
|
||||
@admin.display(
|
||||
description="Snapshot",
|
||||
ordering="snapshot__url",
|
||||
|
||||
@ -89,9 +89,10 @@ def AdminCookieIsolationMiddleware(get_response):
|
||||
response = get_response(request)
|
||||
|
||||
config = request.__dict__.get("archivebox_config")
|
||||
if config is None:
|
||||
config = get_config(resolve_plugins=False)
|
||||
request.archivebox_config = config
|
||||
if config is None or config.SERVER_SECURITY_MODE == "auto":
|
||||
from archivebox.config.common import get_request_config
|
||||
|
||||
config = get_request_config(request, resolve_plugins=False)
|
||||
if not config.USES_SUBDOMAIN_ROUTING:
|
||||
return response
|
||||
|
||||
@ -168,9 +169,17 @@ def ServerSecurityModeMiddleware(get_response):
|
||||
|
||||
def middleware(request):
|
||||
config = request.__dict__.get("archivebox_config")
|
||||
if config is None:
|
||||
config = get_config(resolve_plugins=False)
|
||||
request.archivebox_config = config
|
||||
if config is None or config.SERVER_SECURITY_MODE == "auto":
|
||||
base_config = config or get_config(resolve_plugins=False)
|
||||
if base_config.SERVER_SECURITY_MODE == "auto" and request.method.upper() not in allowed_methods:
|
||||
request_host, _request_port = split_host_port((request.get_host() or "").lower())
|
||||
base_host, _base_port = split_host_port(get_base_host(config=base_config))
|
||||
if base_host and request_host != base_host:
|
||||
return HttpResponseForbidden("ArchiveBox is running with the control plane disabled on this host.")
|
||||
|
||||
from archivebox.config.common import get_request_config
|
||||
|
||||
config = get_request_config(request, resolve_plugins=False)
|
||||
|
||||
if config.CONTROL_PLANE_ENABLED:
|
||||
return get_response(request)
|
||||
|
||||
@ -348,6 +348,12 @@ if not _secret_persisted:
|
||||
logger.debug("Unable to persist generated SECRET_KEY", exc_info=True)
|
||||
|
||||
ALLOWED_HOSTS = [host.strip() for host in CONFIG.ALLOWED_HOSTS.split(",") if host.strip()]
|
||||
if (CONFIG.SERVER_SECURITY_MODE == "auto" or CONFIG.USES_SUBDOMAIN_ROUTING) and "*" not in ALLOWED_HOSTS:
|
||||
# ArchiveBox owns the effective host policy at request time in auto and
|
||||
# subdomain modes: canonical hosts keep the normal app surface, while
|
||||
# alternate hosts are downgraded/rejected by get_request_config()/middleware.
|
||||
# Django has to admit the Host header first for that existing boundary to run.
|
||||
ALLOWED_HOSTS.append("*")
|
||||
CSRF_TRUSTED_ORIGINS = list({origin.strip() for origin in CONFIG.CSRF_TRUSTED_ORIGINS.split(",") if origin.strip()})
|
||||
|
||||
admin_base_url = normalize_base_url(get_admin_base_url())
|
||||
|
||||
@ -912,7 +912,7 @@ def _plugin_full_preview_response(
|
||||
response.headers["Content-Security-Policy"] = (
|
||||
"default-src 'self' data: blob:; "
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; "
|
||||
"style-src 'self' 'unsafe-inline' data: blob:; "
|
||||
"style-src 'unsafe-inline' data: blob: 'self'; "
|
||||
"connect-src 'self' data: blob:; "
|
||||
"img-src 'self' data: blob:; "
|
||||
"media-src 'self' data: blob:; "
|
||||
|
||||
@ -238,9 +238,18 @@ class Machine(ModelWithHealthStats):
|
||||
try:
|
||||
_CURRENT_MACHINE = cls.objects.get(guid=host_guid)
|
||||
except cls.DoesNotExist:
|
||||
config = {}
|
||||
try:
|
||||
from archivebox.config.collection import _coerce_from_str_dict, _load_file_config_dict
|
||||
|
||||
file_config, _file_mtime = _load_file_config_dict()
|
||||
config = _coerce_from_str_dict(file_config)
|
||||
except Exception:
|
||||
config = {}
|
||||
_CURRENT_MACHINE = cls.objects.create(
|
||||
guid=host_guid,
|
||||
hostname=socket.gethostname(),
|
||||
config=config,
|
||||
**get_os_info(),
|
||||
**get_vm_info(),
|
||||
stats=get_host_stats(),
|
||||
|
||||
@ -44,6 +44,7 @@ __package__ = "archivebox.plugins"
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Optional, Protocol, TypeGuard, runtime_checkable
|
||||
@ -326,10 +327,11 @@ def run_hook(
|
||||
if not script.is_file():
|
||||
raise FileNotFoundError(f"Hook script not found: {script}")
|
||||
|
||||
# Python hooks carry their runtime contract in the shebang
|
||||
# (usually `abxpkg run --script python3`), so execute them directly.
|
||||
# For shell/JS hooks we still dispatch through the conventional
|
||||
# interpreter because those hooks do not need per-script Python env setup.
|
||||
# ArchiveBox resolves hook dependencies before execution and exports the
|
||||
# resolved *_BINARY env values below. Run Python hooks in the current Python
|
||||
# environment so per-snapshot hook execution does not repeatedly pay the
|
||||
# abxpkg script bootstrap cost from the standalone shebang.
|
||||
# Shell/JS hooks still dispatch through abxpkg-resolved interpreters.
|
||||
ext = script.suffix.lower()
|
||||
if ext == ".sh":
|
||||
bash_projection = Path(hook_config["ABXPKG_LIB_DIR"]).expanduser() / "env" / "bin" / "bash"
|
||||
@ -337,7 +339,7 @@ def run_hook(
|
||||
raise RuntimeError(f"Bash must be resolved by abxpkg into {bash_projection}")
|
||||
cmd = [str(bash_projection), str(script)]
|
||||
elif ext == ".py":
|
||||
cmd = [str(script)]
|
||||
cmd = [sys.executable, str(script)]
|
||||
elif ext == ".js":
|
||||
node_projection = Path(hook_config["ABXPKG_LIB_DIR"]).expanduser() / "env" / "bin" / "node"
|
||||
if not node_projection.is_symlink() or not os.access(node_projection, os.X_OK):
|
||||
@ -377,7 +379,6 @@ def run_hook(
|
||||
env.pop("PLUGINS", None)
|
||||
env["DATA_DIR"] = str(CONSTANTS.DATA_DIR)
|
||||
env["LIBRARY_VERSION"] = VERSION
|
||||
env.setdefault("ABXPKG_FAST_SCRIPT", "1")
|
||||
env.setdefault("MACHINE_ID", os.environ.get("MACHINE_ID", CONSTANTS.MACHINE_ID))
|
||||
snap_dir = hook_config.get("SNAP_DIR") or _model_output_dir_from_child_path(output_dir, CONSTANTS.SNAPSHOTS_DIR_NAME)
|
||||
crawl_dir = hook_config.get("CRAWL_DIR") or _model_output_dir_from_child_path(output_dir, CONSTANTS.CRAWLS_DIR_NAME)
|
||||
|
||||
@ -16,12 +16,13 @@ def search_backend_env(config: dict[str, Any] | None = None, **config_kwargs: An
|
||||
config = config or get_config(**config_kwargs)
|
||||
updates = {}
|
||||
for key, value in config.items():
|
||||
if not str(key).startswith("SEARCH_BACKEND_"):
|
||||
key = str(key)
|
||||
if not (key.startswith("SEARCH_BACKEND_") or key.endswith("_BINARY")):
|
||||
continue
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, (str, int, float, bool, os.PathLike)):
|
||||
updates[str(key)] = str(value)
|
||||
updates[key] = str(value)
|
||||
previous = {key: os.environ.get(key) for key in updates}
|
||||
os.environ.update(updates)
|
||||
try:
|
||||
|
||||
@ -1665,7 +1665,7 @@
|
||||
{% endif %}
|
||||
<br/>
|
||||
<div class="external-links">
|
||||
<a href="{% snapshot_base_url snapshot %}/?files=1" title="Browse the full SNAP_DIR for this snapshot" target="_blank">📁 Files</a>
|
||||
<a href="{% snapshot_base_url snapshot %}/?files=1" title="Browse the full SNAP_DIR for this snapshot" target="_blank">📁 See all files...</a>
|
||||
<span class="external-links-separator">|</span>
|
||||
<a href="https://web.archive.org/web/{{url}}" title="Search for a copy of the URL saved in Archive.org" target="_blank" rel="noreferrer">🏛️ Archive.org</a>
|
||||
<!--<a href="https://archive.md/{{url}}" title="Search for a copy of the URL saved in Archive.today" target="_blank" rel="noreferrer">Archive.today</a> | -->
|
||||
|
||||
@ -234,11 +234,11 @@ def test_server_help_lists_runtime_options(initialized_archive):
|
||||
assert "--reload" in result.stdout
|
||||
|
||||
|
||||
def test_runner_worker_uses_abxpkg_projected_archivebox():
|
||||
from archivebox.workers.supervisord_util import RUNNER_WORKER, resolve_env_binary
|
||||
def test_runner_worker_uses_active_archivebox_module():
|
||||
from archivebox.workers.supervisord_util import RUNNER_WORKER, archivebox_cmd
|
||||
|
||||
worker = RUNNER_WORKER()
|
||||
assert shlex.split(worker["command"]) == [str(resolve_env_binary("archivebox")), "run", "--daemon"]
|
||||
assert shlex.split(worker["command"]) == archivebox_cmd("run", "--daemon")
|
||||
assert worker["autorestart"] == "true"
|
||||
assert 'ARCHIVEBOX_RUNNER_DAEMON="1"' in worker["environment"]
|
||||
|
||||
@ -252,26 +252,20 @@ def test_daphne_worker_uses_default_application_close_timeout():
|
||||
assert "--application-close-timeout=0" not in command
|
||||
|
||||
|
||||
def test_reload_workers_use_abxpkg_projected_archivebox():
|
||||
from archivebox.workers.supervisord_util import RUNNER_WATCH_WORKER, RUNSERVER_WORKER, resolve_env_binary
|
||||
def test_reload_workers_use_active_archivebox_module():
|
||||
from archivebox.workers.supervisord_util import RUNNER_WATCH_WORKER, RUNSERVER_WORKER, archivebox_cmd
|
||||
|
||||
runserver = RUNSERVER_WORKER("127.0.0.1", "8000", reload=True)
|
||||
watcher = RUNNER_WATCH_WORKER("http://127.0.0.1:8000")
|
||||
archivebox_binary = resolve_env_binary("archivebox")
|
||||
|
||||
assert runserver["name"] == "worker_runserver"
|
||||
assert shlex.split(runserver["command"]) == [str(archivebox_binary), "manage", "runserver", "127.0.0.1:8000"]
|
||||
assert shlex.split(runserver["command"]) == archivebox_cmd("manage", "runserver", "127.0.0.1:8000")
|
||||
assert 'ARCHIVEBOX_RUNSERVER="1"' in runserver["environment"]
|
||||
assert 'ARCHIVEBOX_AUTORELOAD="1"' in runserver["environment"]
|
||||
assert 'ARCHIVEBOX_RUNSERVER_BIND_URL="http://127.0.0.1:8000"' in runserver["environment"]
|
||||
|
||||
assert watcher["name"] == "worker_runner_watch"
|
||||
assert shlex.split(watcher["command"]) == [
|
||||
str(archivebox_binary),
|
||||
"manage",
|
||||
"runner_watch",
|
||||
"--bind-url=http://127.0.0.1:8000",
|
||||
]
|
||||
assert shlex.split(watcher["command"]) == archivebox_cmd("manage", "runner_watch", "--bind-url=http://127.0.0.1:8000")
|
||||
|
||||
|
||||
def test_server_daemon_starts_real_plugin_owned_sonic_worker(initialized_archive, archivebox_daemon_server):
|
||||
|
||||
@ -13,6 +13,7 @@ import json
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from importlib.resources import files
|
||||
from pathlib import Path
|
||||
|
||||
@ -626,14 +627,14 @@ def test_run_hook_exports_singular_node_modules_dir_with_colon_node_path(tmp_pat
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_run_hook_executes_python_hooks_through_script_shebang(tmp_path):
|
||||
"""Python hooks must execute through their abxpkg script header."""
|
||||
def test_run_hook_executes_python_hooks_with_resolved_runtime_env(tmp_path):
|
||||
"""ArchiveBox-run Python hooks use the active interpreter and resolved env."""
|
||||
from archivebox.plugins.hooks import run_hook
|
||||
|
||||
snap_dir = tmp_path / "snapshot"
|
||||
output_dir = snap_dir / "hashes"
|
||||
output_dir.mkdir(parents=True)
|
||||
(snap_dir / "source.txt").write_text("real shebang hook input", encoding="utf-8")
|
||||
(snap_dir / "source.txt").write_text("real runtime hook input", encoding="utf-8")
|
||||
hook_path = Path(str(files("abx_plugins.plugins.hashes").joinpath("on_Snapshot__93_hashes.py")))
|
||||
process = run_hook(
|
||||
hook_path,
|
||||
@ -643,18 +644,18 @@ def test_run_hook_executes_python_hooks_through_script_shebang(tmp_path):
|
||||
"SNAP_DIR": str(snap_dir),
|
||||
},
|
||||
timeout=30,
|
||||
url="https://example.com/shebang",
|
||||
url="https://example.com/runtime",
|
||||
)
|
||||
process.refresh_from_db()
|
||||
|
||||
assert process.cmd[0] == str(hook_path)
|
||||
assert process.cmd[:2] == [sys.executable, str(hook_path)]
|
||||
assert process.exit_code == 0, process.stderr
|
||||
assert process.env["ABXPKG_FAST_SCRIPT"] == "1"
|
||||
assert process.env["SNAP_DIR"] == str(snap_dir)
|
||||
records = process.parse_records_from_text(process.stdout)
|
||||
source_hash = hashlib.sha256(b"real shebang hook input").hexdigest()
|
||||
source_hash = hashlib.sha256(b"real runtime hook input").hexdigest()
|
||||
assert records == [{"type": "ArchiveResult", "status": "succeeded", "output_str": f"0.0MB {source_hash[:12]}"}]
|
||||
hashes = json.loads((output_dir / "hashes.json").read_text())
|
||||
source = hashes["files"][0]
|
||||
assert source["path"] == "source.txt"
|
||||
assert source["size"] == len("real shebang hook input")
|
||||
assert source["size"] == len("real runtime hook input")
|
||||
assert source["hash"] == source_hash
|
||||
|
||||
@ -110,7 +110,9 @@ def test_search_backend_env_exposes_resolved_runtime_config(tmp_path):
|
||||
"SEARCH_BACKEND_SONIC_HOST_NAME": "sonic",
|
||||
"SEARCH_BACKEND_SONIC_PORT": 1491,
|
||||
"SEARCH_BACKEND_SONIC_PASSWORD": "SecretPassword",
|
||||
"RIPGREP_BINARY": tmp_path / "env" / "bin" / "rg",
|
||||
"IGNORED_NONE_VALUE": None,
|
||||
"UNRELATED_BINARY_NAME": "rg",
|
||||
},
|
||||
)
|
||||
|
||||
@ -120,7 +122,9 @@ def test_search_backend_env_exposes_resolved_runtime_config(tmp_path):
|
||||
assert os.environ["SEARCH_BACKEND_SONIC_HOST_NAME"] == "sonic"
|
||||
assert os.environ["SEARCH_BACKEND_SONIC_PORT"] == "1491"
|
||||
assert os.environ["SEARCH_BACKEND_SONIC_PASSWORD"] == "SecretPassword"
|
||||
assert os.environ["RIPGREP_BINARY"] == str(tmp_path / "env" / "bin" / "rg")
|
||||
assert "IGNORED_NONE_VALUE" not in os.environ
|
||||
assert "UNRELATED_BINARY_NAME" not in os.environ
|
||||
|
||||
assert os.environ["SEARCH_BACKEND_SONIC_HOST_NAME"] == "old-host"
|
||||
finally:
|
||||
|
||||
@ -48,11 +48,19 @@ def _shell_join(args: list[str]) -> str:
|
||||
return shlex.join(args)
|
||||
|
||||
|
||||
def archivebox_cmd(*args: str) -> list[str]:
|
||||
executable = Path(sys.argv[0]).resolve() if sys.argv and sys.argv[0] else None
|
||||
if executable and executable.name == "archivebox" and executable.is_file() and os.access(executable, os.X_OK):
|
||||
return [str(executable), *args]
|
||||
return [sys.executable, "-m", "archivebox", *args]
|
||||
|
||||
|
||||
def resolve_env_binary(name: str) -> Path:
|
||||
from abxpkg import EnvProvider
|
||||
from archivebox.config.common import get_config
|
||||
|
||||
env_root = get_config().ABXPKG_LIB_DIR / "env"
|
||||
lib_dir = Path(os.environ.get("ABXPKG_LIB_DIR") or get_config().ABXPKG_LIB_DIR)
|
||||
env_root = lib_dir / "env"
|
||||
provider = EnvProvider(install_root=env_root, PATH=os.environ["PATH"])
|
||||
if name == "daphne":
|
||||
from importlib.metadata import version
|
||||
@ -179,7 +187,7 @@ def _stop_older_supervisord_processes(*, current_pid: int, current_started_at: f
|
||||
def RUNNER_WORKER():
|
||||
return {
|
||||
"name": "worker_runner",
|
||||
"command": _shell_join([str(resolve_env_binary("archivebox")), "run", "--daemon"]),
|
||||
"command": _shell_join(archivebox_cmd("run", "--daemon")),
|
||||
"autostart": "false",
|
||||
"autorestart": "true",
|
||||
"environment": 'PYTHONUNBUFFERED="1",COLUMNS="200",ARCHIVEBOX_RUNNER_DAEMON="1"',
|
||||
@ -194,7 +202,7 @@ def RUNNER_WORKER():
|
||||
RUNNER_ONCE_WORKER = lambda args, name="worker_runner_once": {
|
||||
**RUNNER_WORKER(),
|
||||
"name": name,
|
||||
"command": _shell_join([str(resolve_env_binary("archivebox")), "run", "--no-stdin", *args]),
|
||||
"command": _shell_join(archivebox_cmd("run", "--no-stdin", *args)),
|
||||
"environment": 'PYTHONUNBUFFERED="1",COLUMNS="200"',
|
||||
"autorestart": "false",
|
||||
"stopwaitsecs": "1",
|
||||
@ -203,7 +211,7 @@ RUNNER_ONCE_WORKER = lambda args, name="worker_runner_once": {
|
||||
|
||||
RUNNER_WATCH_WORKER = lambda bind_url: {
|
||||
"name": "worker_runner_watch",
|
||||
"command": _shell_join([str(resolve_env_binary("archivebox")), "manage", "runner_watch", f"--bind-url={bind_url}"]),
|
||||
"command": _shell_join(archivebox_cmd("manage", "runner_watch", f"--bind-url={bind_url}")),
|
||||
"autostart": "false",
|
||||
"autorestart": "true",
|
||||
"stdout_logfile": "logs/worker_runner_watch.log",
|
||||
@ -213,12 +221,7 @@ RUNNER_WATCH_WORKER = lambda bind_url: {
|
||||
SUPERVISORD_PARENT_WATCHDOG_WORKER = lambda supervisord_process_id: {
|
||||
"name": "worker_supervisord_parent_watchdog",
|
||||
"command": _shell_join(
|
||||
[
|
||||
str(resolve_env_binary("archivebox")),
|
||||
"manage",
|
||||
"supervisord_watchdog",
|
||||
f"--supervisord-process-id={supervisord_process_id}",
|
||||
],
|
||||
archivebox_cmd("manage", "supervisord_watchdog", f"--supervisord-process-id={supervisord_process_id}"),
|
||||
),
|
||||
"autostart": "false",
|
||||
"autorestart": "false",
|
||||
@ -250,7 +253,7 @@ SERVER_WORKER = lambda host, port: {
|
||||
|
||||
|
||||
def RUNSERVER_WORKER(host: str, port: str, *, reload: bool, nothreading: bool = False):
|
||||
command = [str(resolve_env_binary("archivebox")), "manage", "runserver", f"{host}:{port}"]
|
||||
command = archivebox_cmd("manage", "runserver", f"{host}:{port}")
|
||||
if not reload:
|
||||
command.append("--noreload")
|
||||
if nothreading:
|
||||
@ -1197,10 +1200,20 @@ def get_sonic_supervisord_worker_from_plugin(config) -> dict[str, str] | None:
|
||||
return cast(dict[str, str] | None, worker)
|
||||
|
||||
|
||||
_PROC_ACCESS_EXCEPTIONS = (
|
||||
psutil.NoSuchProcess,
|
||||
psutil.AccessDenied,
|
||||
psutil.ZombieProcess,
|
||||
PermissionError,
|
||||
SystemError,
|
||||
)
|
||||
_PROC_INFO_EXCEPTIONS = (IndexError, *_PROC_ACCESS_EXCEPTIONS)
|
||||
|
||||
|
||||
def _proc_cmdline(proc: psutil.Process) -> list[str]:
|
||||
try:
|
||||
return proc.cmdline()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
except _PROC_ACCESS_EXCEPTIONS:
|
||||
return []
|
||||
|
||||
|
||||
@ -1257,7 +1270,7 @@ def _sonic_listeners(host: str, port: int) -> list[psutil.Process]:
|
||||
continue
|
||||
try:
|
||||
connections = proc.net_connections(kind="tcp")
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
except _PROC_ACCESS_EXCEPTIONS:
|
||||
continue
|
||||
for conn in connections:
|
||||
if conn.status != psutil.CONN_LISTEN or not conn.laddr or conn.laddr.port != port:
|
||||
@ -1288,7 +1301,7 @@ def stop_stale_sonic_processes(
|
||||
if config_path is None or Path(cmdline[0]).name != "sonic" or str(config_path) not in cmdline:
|
||||
continue
|
||||
stale.append(proc)
|
||||
except (IndexError, psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
except _PROC_INFO_EXCEPTIONS:
|
||||
continue
|
||||
|
||||
if host is not None and port is not None:
|
||||
|
||||
@ -82,8 +82,8 @@ dependencies = [
|
||||
### Binary/Package Management
|
||||
"abxbus==2.5.40", # EventBus API
|
||||
"abxpkg==1.12.0", # for: detecting, versioning, and installing binaries via apt/brew/pip/npm
|
||||
"abx-plugins==1.12.0", # shared ArchiveBox plugin package with Chrome/Puppeteer dependency wiring
|
||||
"abx-dl==1.12.0", # shared ArchiveBox downloader package with blocking install preflight
|
||||
"abx-plugins==1.12.1", # shared ArchiveBox plugin package with Chrome/Puppeteer dependency wiring
|
||||
"abx-dl==1.12.2", # shared ArchiveBox downloader package with blocking install preflight
|
||||
### UUID7 backport for Python <3.14
|
||||
"uuid7>=0.1.0; python_version < '3.14'", # provides the uuid_extensions module on Python 3.13
|
||||
]
|
||||
|
||||
18
uv.lock
18
uv.lock
@ -18,13 +18,13 @@ exclude-newer-span = "P5D"
|
||||
|
||||
[options.exclude-newer-package]
|
||||
abxbus = { timestamp = "0001-01-01T00:00:00Z", span = "PT1S" }
|
||||
abx-plugins = "2100-01-01T00:00:00Z"
|
||||
abx-dl = "2100-01-01T00:00:00Z"
|
||||
abx-plugins = "2100-01-01T00:00:00Z"
|
||||
abxpkg = "2100-01-01T00:00:00Z"
|
||||
|
||||
[[package]]
|
||||
name = "abx-dl"
|
||||
version = "1.12.0"
|
||||
version = "1.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "abx-plugins" },
|
||||
@ -39,14 +39,14 @@ dependencies = [
|
||||
{ name = "rich" },
|
||||
{ name = "rich-click" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/74/5d65ad86605b41567445b9da3a08de2240a198ed2704f94539d9b58b0fbd/abx_dl-1.12.0.tar.gz", hash = "sha256:f9f336ecbc6e40e20fe2ac83255bc67358728c55705a393604097d102c4030de", size = 87129, upload-time = "2026-07-25T22:43:02.99Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/84/4842add2a2c42153f450e5ffceccb5fc844427654b4ba5b2cdb4905111e2/abx_dl-1.12.2.tar.gz", hash = "sha256:6fd532a103162af3db4c1160720df4a27ba5a270cabefc1d8ab97d0338d2e31e", size = 87413, upload-time = "2026-07-26T01:29:47.799Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/ba/34c4997c3575cda1b14c5c5dc8bc06e24b4171dd0fcab3556294063ae586/abx_dl-1.12.0-py3-none-any.whl", hash = "sha256:7fe2277fe6388389ffcf20ad5ede5cab31e533d5c9be63b1e7231bb83f8a8785", size = 90873, upload-time = "2026-07-25T22:43:01.895Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/b1/f802a0708448d10beec56b25410fcb5bb33a291056131974a7996f4854fa/abx_dl-1.12.2-py3-none-any.whl", hash = "sha256:f8d196b4b44c004e622e188e62374b201f835ff5189cd0a703be1d532b685463", size = 91176, upload-time = "2026-07-26T01:29:46.116Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abx-plugins"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "abxbus" },
|
||||
@ -56,9 +56,9 @@ dependencies = [
|
||||
{ name = "rich-click" },
|
||||
{ name = "uv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/92/976cc591ad5169e7f60b9fadfeaa0e24bf39ffa7e9ed62aebef35780631e/abx_plugins-1.12.0.tar.gz", hash = "sha256:0b411cf351b4e47b5ce03e579509411e422251209459e055f7a58d020451a8a2", size = 257427, upload-time = "2026-07-25T22:15:32.555Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/96/f7bfab6f4d04a3eb058e3efa526d4453c82f3a0d965af850a69048bc4c44/abx_plugins-1.12.1.tar.gz", hash = "sha256:c7bca936c29196a9444c5e82b2646f6e0d40b0222e7f126a5a417842baa34d11", size = 257434, upload-time = "2026-07-26T01:12:03.405Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/32/5b/88b2170493025eb056aeb4d468dfd4d4e658b469ebe848938b31ba6efcf2/abx_plugins-1.12.0-py3-none-any.whl", hash = "sha256:0bf69dc4f1311bc0e3bdbb1e234f251837cc2b31cd267b5169d74f9b1ad186f5", size = 405631, upload-time = "2026-07-25T22:15:31.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/2e/b5880e4be4b0dbb7e81654b80057e61b1cec72ae95b6a240e1b718f4d13b/abx_plugins-1.12.1-py3-none-any.whl", hash = "sha256:be41722f410872ef8b67bd729cb69728d68b2308c48bee7991d35001d3a8dadc", size = 405648, upload-time = "2026-07-26T01:12:02.199Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -223,8 +223,8 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "abx-dl", specifier = "==1.12.0" },
|
||||
{ name = "abx-plugins", specifier = "==1.12.0" },
|
||||
{ name = "abx-dl", specifier = "==1.12.2" },
|
||||
{ name = "abx-plugins", specifier = "==1.12.1" },
|
||||
{ name = "abxbus", specifier = "==2.5.40" },
|
||||
{ name = "abxpkg", specifier = "==1.12.0" },
|
||||
{ name = "archivebox", extras = ["sonic", "ldap", "debug"], marker = "extra == 'all'" },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user