release: archivebox 0.9.31rc27

This commit is contained in:
Nick Sweeting 2026-05-24 01:24:50 -07:00
parent 02a6cb91cd
commit 035132ded5
No known key found for this signature in database
5 changed files with 90 additions and 5 deletions

View File

@ -205,7 +205,7 @@ RUN --mount=type=cache,target=/root/.npm,sharing=locked,id=npm-$TARGETARCH$TARGE
# Set up uv and main app /venv
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/bin sh
ENV UV_COMPILE_BYTECODE=1 \
ENV UV_COMPILE_BYTECODE=0 \
UV_PYTHON_PREFERENCE=managed \
UV_PYTHON_INSTALL_DIR=/opt/uv/python \
UV_LINK_MODE=copy \
@ -268,6 +268,7 @@ RUN --mount=type=bind,source=pyproject.toml,target=/app/pyproject.toml \
&& apt-get install -qq -y --no-install-recommends build-essential gcc python3-dev \
&& uv sync \
--no-cache \
--no-dev \
--inexact \
--all-extras \
--no-install-project \
@ -275,6 +276,8 @@ RUN --mount=type=bind,source=pyproject.toml,target=/app/pyproject.toml \
--no-sources \
&& apt-get purge -y python3-dev build-essential gcc \
&& apt-get autoremove -y \
&& find /venv -type d -name __pycache__ -prune -exec rm -rf {} + \
&& find /venv -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete \
&& rm -rf /var/lib/apt/lists/*
# installs the pip packages that archivebox depends on, defined in pyproject.toml dependencies
@ -289,7 +292,9 @@ RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked,id=uv-$TARGETARCH$T
pip show archivebox \
&& which archivebox \
&& echo -e '\n\n' \
) | tee -a /VERSION.txt
) | tee -a /VERSION.txt \
&& find /venv "$CODE_DIR" -type d -name __pycache__ -prune -exec rm -rf {} + \
&& find /venv "$CODE_DIR" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete
# installs archivebox itself, and any other vendored packages in pkgs/*, defined in pyproject.toml workspaces
####################################################
@ -319,6 +324,9 @@ RUN openssl rand -hex 16 > /etc/machine-id \
# returned to the runtime archivebox user.
RUN echo "[+] Initializing image collection and installing plugin runtime dependencies into $LIB_DIR..." \
&& PUID=0 PGID=0 archivebox init --install \
&& find /venv "$CODE_DIR" "$LIB_DIR" "$DATA_DIR" -type d -name __pycache__ -prune -exec rm -rf {} + \
&& find /venv "$CODE_DIR" "$LIB_DIR" "$DATA_DIR" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete \
&& rm -rf /root/.cache /var/cache/apt/* /var/lib/apt/lists/* \
&& (chown "$DEFAULT_PUID:$DEFAULT_PGID" "$DATA_DIR" "$DATA_DIR"/logs "$DATA_DIR"/sources "$DATA_DIR"/archive "$DATA_DIR"/archive/users "$DATA_DIR"/personas "$DATA_DIR"/index.sqlite3 "$DATA_DIR"/ArchiveBox.conf 2>/dev/null || true) \
&& chown -R "$DEFAULT_PUID:$DEFAULT_PGID" "$LIB_DIR"
@ -330,7 +338,10 @@ RUN (echo -e "\n\n[√] Finished Docker build successfully. Saving build summary
# Verify ArchiveBox is installed and write full version/dependency info.
RUN chmod +x "$CODE_DIR"/bin/*.sh \
&& gosu "$ARCHIVEBOX_USER" archivebox version 2>&1 | tee -a /VERSION.txt
&& gosu "$ARCHIVEBOX_USER" archivebox version 2>&1 | tee -a /VERSION.txt \
&& find /venv "$CODE_DIR" "$LIB_DIR" "$DATA_DIR" -type d -name __pycache__ -prune -exec rm -rf {} + \
&& find /venv "$CODE_DIR" "$LIB_DIR" "$DATA_DIR" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete \
&& rm -rf /root/.cache /var/cache/apt/* /var/lib/apt/lists/*
####################################################

View File

@ -3,6 +3,7 @@ __package__ = "archivebox.core"
from typing import TYPE_CHECKING, Any
from django.contrib import admin
from django.db import DatabaseError, connection
from admin_data_views.admin import (
admin_data_index_view as adv_admin_data_index_view,
get_admin_data_urls as adv_get_admin_data_urls,
@ -31,6 +32,51 @@ class ArchiveBoxAdmin(admin.AdminSite):
def admin_data_index_view(self, request: "HttpRequest", **kwargs: Any) -> "TemplateResponse":
return adv_admin_data_index_view(self, request, **kwargs)
def index(self, request: "HttpRequest", extra_context: dict[str, Any] | None = None) -> "TemplateResponse":
response = super().index(request, extra_context)
if connection.vendor != "sqlite":
return response
models_by_table: dict[str, list[dict[str, Any]]] = {}
for app in response.context_data.get("app_list", []):
for model in app.get("models", []):
model_class = model.get("model")
if not model_class or not model.get("perms", {}).get("view"):
continue
models_by_table.setdefault(model_class._meta.db_table, []).append(model)
if not models_by_table:
return response
try:
with connection.cursor() as cursor:
cursor.execute("SELECT tbl, stat FROM sqlite_stat1")
for table, stat in cursor.fetchall():
models = models_by_table.get(table)
if not models:
continue
try:
count = int(str(stat).split()[0])
except (IndexError, TypeError, ValueError):
continue
if count >= 1_000_000_000:
count_label = f"~{count / 1_000_000_000:.1f}B"
elif count >= 1_000_000:
count_label = f"~{count / 1_000_000:.1f}M"
elif count >= 1_000:
count_label = f"~{count / 1_000:.1f}K"
else:
count_label = f"~{count:,}"
count_label = count_label.replace(".0", "")
for model in models:
model["object_count"] = count
model["object_count_label"] = count_label
model["object_count_title"] = f"Approximate count from SQLite stats: {count:,}"
models_by_table.pop(table, None)
except DatabaseError:
pass
return response
def get_admin_data_urls(self) -> list["URLResolver | URLPattern"]:
return adv_get_admin_data_urls(self)

View File

@ -271,6 +271,13 @@
min-width: 0;
}
.abx-admin-card__title-row {
display: flex;
gap: 7px;
align-items: baseline;
min-width: 0;
}
#content .abx-admin-card__title {
color: var(--abx-ink) !important;
font-size: 15px;
@ -279,6 +286,22 @@
text-decoration: none !important;
}
.abx-admin-card__count {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
min-height: 16px;
padding: 0 5px;
color: var(--abx-ink-4);
background: rgba(28, 24, 20, .035);
border: 1px solid rgba(28, 24, 20, .08);
border-radius: 999px;
font-family: var(--abx-font-mono);
font-size: 10px;
font-weight: 650;
line-height: 1;
}
.abx-admin-card__text {
color: var(--abx-ink-3);
font-size: 12px;

View File

@ -50,7 +50,12 @@
</svg>
</span>
<span class="abx-admin-card__copy">
<strong class="abx-admin-card__title">{{ model.name }}</strong>
<span class="abx-admin-card__title-row">
<strong class="abx-admin-card__title">{{ model.name }}</strong>
{% if model.object_count_label %}
<span class="abx-admin-card__count" title="{{ model.object_count_title }}">{{ model.object_count_label }}</span>
{% endif %}
</span>
<span class="abx-admin-card__text">
{% if model.object_name == "Crawl" %}
Create and monitor URL collection jobs.

View File

@ -1,6 +1,6 @@
[project]
name = "archivebox"
version = "0.9.31rc26"
version = "0.9.31rc27"
requires-python = ">=3.13"
description = "Self-hosted internet archiving solution."
authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}]