Compare commits

...
7 changed files with 65 additions and 46 deletions

No files matched your search

+8
View File
@@ -659,6 +659,14 @@ def test_plugin_grid_includes_all_visible_plugins_and_timestamps_last(admin_clie
assert len(names) == len(expected)
for name in expected:
assert f'data-plugin-name="{name}"'.encode() in response.content
# The same schema drives both editors, including newly installed plugins.
# Check every plugin's category and rendered controls, not a fixed allowlist.
catalog = get_plugin_catalog()
for group in form.plugin_groups:
for card in group["plugins"]:
assert group["field_name"] == f"{catalog[card['name']].config.category}_plugins"
for field in card["config_fields"]:
assert f'name="{field["input_name"]}"'.encode() in response.content
postprocessing = next(group for group in form.plugin_groups if group["field_name"] == "postprocessing_plugins")
assert postprocessing["plugins"][-1]["name"] == "opentimestamps"
assert b'name="plugin_config__opentimestamps__OPENTIMESTAMPS_TIMEOUT"' in response.content
+20 -21
View File
@@ -156,7 +156,6 @@ if [[ "$HAS_USEFUL_SNAPSHOT" == "0" ]]; then
--depth=0 \
--overwrite \
--tag=documentation,reference \
--plugins=title,headers,wget,screenshot,pdf,dom,readability,htmltotext,hashes,dns \
https://docs.sweeting.me/s/cookie-dilemma https://archivebox.io
)
fi
@@ -267,11 +266,11 @@ VIEWS=(
add_supplementary_template_capture() {
local plugin_names="$1"
local source_url="$2"
local prepare_plugins="$3"
local capture_plugins="${prepare_plugins:-$plugin_names}"
local capture_log="$CAPTURE_ROOT/${plugin_names}-supplementary-capture.log"
local supplementary_plugins=()
IFS=',' read -r -a supplementary_plugins <<<"$plugin_names"
# These are real all-plugin captures. The recipe only selects which output
# to photograph, never which extractors run or which dependencies get saved.
echo "[*] Capturing real $plugin_names source for template coverage"
if ! (
cd "$DATA_DIR"
@@ -279,7 +278,6 @@ add_supplementary_template_capture() {
--depth=0 \
--overwrite \
--tag=screenshot-gallery \
--plugins="$capture_plugins" \
"$source_url"
) >"$capture_log" 2>&1; then
echo "[!] Supplementary $plugin_names capture failed" >&2
@@ -302,20 +300,6 @@ add_supplementary_template_capture() {
local snapshot_id snapshot_view_url snapshot_view_path
IFS=$'\t' read -r snapshot_id snapshot_view_url snapshot_view_path <<<"$snapshot_record"
if [[ -n "$prepare_plugins" ]]; then
if ! (
cd "$DATA_DIR"
uv run --no-cache --project "$REPO_DIR" archivebox extract \
--plugins="$plugin_names" \
"$snapshot_id"
) >>"$capture_log" 2>&1; then
echo "[!] $plugin_names extraction failed for the prepared snapshot" >&2
tail -100 "$capture_log" >&2
exit 1
fi
stop_background_runner
fi
local discovery_report="$CAPTURE_ROOT/${plugin_names}-output-discovery.json"
NODE_PATH="$ABXPKG_LIB_DIR/pnpm/packages/chrome/node_modules" \
CHROME_BINARY="$SCREENSHOT_CHROME_BINARY" \
@@ -360,6 +344,12 @@ while [[ "$capture_index" -lt "${#VIEWS[@]}" ]]; do
[[ "$capture_mode" != wait-text:* ]] || wait_for_text="${capture_mode#wait-text:}"
preview_wait_for_text=""
[[ "$capture_mode" != wait-preview-text:* ]] || preview_wait_for_text="${capture_mode#wait-preview-text:}"
config_selector=""
config_click_selector=""
if [[ "$capture_mode" == plugin-config:* ]]; then
config_selector="[data-plugin-name='${capture_mode#plugin-config:}']"
config_click_selector="$config_selector .plugin-config-marker"
fi
view_timing_report=""
while IFS='|' read -r profile _viewport_width _viewport_height; do
filename="$slug-$profile.png"
@@ -483,6 +473,8 @@ PY
SCREENSHOT_HEIGHT=1000 \
SCREENSHOT_VARIANTS_JSON="$output_variants" \
SCREENSHOT_COLLAPSE_FILTERS=1 \
SCREENSHOT_CLICK_SELECTOR="$config_click_selector" \
SCREENSHOT_SCROLL_SELECTOR="$config_selector" \
SCREENSHOT_SNAPSHOT_HEADER="$snapshot_header" \
SCREENSHOT_WAIT_FOR_TEXT="$wait_for_text" \
SCREENSHOT_PREVIEW_WAIT_FOR_TEXT="$preview_wait_for_text" \
@@ -655,11 +647,18 @@ PY
[[ -z "$plugin_name" ]] && continue
VIEWS+=("Snapshot View ($plugin_name)|$LIVE_SNAPSHOT_VIEW_URL#$output_path|$LIVE_SNAPSHOT_VIEW_PATH|archivebox/templates/core/snapshot.html|$output_capture_mode")
done <<<"$SNAPSHOT_OUTPUT_PLUGINS"
while IFS='|' read -r example_plugins example_url example_prepare; do
while IFS='|' read -r example_plugins example_url; do
[[ -z "$example_plugins" ]] && continue
add_supplementary_template_capture "$example_plugins" "$example_url" "$example_prepare"
add_supplementary_template_capture "$example_plugins" "$example_url"
done < <(uv run --no-cache --project "$REPO_DIR" python -c \
'import json,sys; plan=json.load(open(sys.argv[1])); print("\n".join("|".join((",".join(example["plugins"]), example["url"], ",".join(example["prepare_plugins"]))) for example in plan["examples"]))' \
'import json,sys; plan=json.load(open(sys.argv[1])); print("\n".join("|".join((",".join(example["plugins"]), example["url"])) for example in plan["examples"]))' \
"$PLUGIN_SCREENSHOT_PLAN")
while IFS='|' read -r config_name config_plugin; do
[[ -z "$config_plugin" ]] && continue
VIEWS+=("$config_name|$ADMIN_BASE_URL/add/|/add/|archivebox/templates/plugins/plugin_config_grid.html|plugin-config:$config_plugin")
done < <(uv run --no-cache --project "$REPO_DIR" python -c \
'import json,sys; print("\n".join(view["name"] + "|" + view["plugin"] for view in json.load(open(sys.argv[1]))["config_views"]))' \
"$PLUGIN_SCREENSHOT_PLAN")
VIEWS+=("Snapshot View (header collapsed)|$LIVE_SNAPSHOT_VIEW_URL|$LIVE_SNAPSHOT_VIEW_PATH|archivebox/templates/core/snapshot.html|snapshot-collapsed")
+12 -3
View File
@@ -67,6 +67,7 @@ def plugin_screenshot_plan() -> dict:
required = []
examples = {}
overrides = {}
config_views = []
enabled_plugins = []
for plugin_dir in sorted(get_plugins_dir().iterdir()):
config_path = plugin_dir / "config.json"
@@ -81,6 +82,8 @@ def plugin_screenshot_plan() -> dict:
enabled = True
if recipe.get("view"):
required.append(recipe["view"])
if recipe.get("config"):
config_views.append({"name": recipe["view"], "plugin": plugin})
elif enabled and (
recipe.get("url")
or (plugin_dir / "templates/full.html").is_file()
@@ -89,10 +92,16 @@ def plugin_screenshot_plan() -> dict:
required.append(f"Snapshot View ({plugin})")
if recipe.get("url"):
overrides[plugin] = recipe
key = (recipe["url"], tuple(recipe.get("prepare_plugins", [])))
example = examples.setdefault(key, {"url": key[0], "prepare_plugins": list(key[1]), "plugins": []})
key = recipe["url"]
example = examples.setdefault(key, {"url": key, "plugins": []})
example["plugins"].append(plugin)
return {"required": required, "examples": list(examples.values()), "overrides": overrides, "enabled": enabled_plugins}
return {
"required": required,
"examples": list(examples.values()),
"overrides": overrides,
"enabled": enabled_plugins,
"config_views": config_views,
}
def build_provenance() -> dict[str, str]:
+9 -6
View File
@@ -374,12 +374,7 @@ async function main() {
if (process.env.SCREENSHOT_AFTER_CLICK_WAIT_SELECTOR) {
await page.waitForSelector(process.env.SCREENSHOT_AFTER_CLICK_WAIT_SELECTOR, { timeout: 45000 });
}
if (process.env.SCREENSHOT_SCROLL_SELECTOR) {
await page.waitForSelector(process.env.SCREENSHOT_SCROLL_SELECTOR, { timeout: 45000 }).catch(() => {});
await page.evaluate((selector) => {
document.querySelector(selector)?.scrollIntoView({ block: 'start', inline: 'nearest' });
}, process.env.SCREENSHOT_SCROLL_SELECTOR);
}
if (requireLiveProgress) {
await waitForLiveProgress(page, liveProgressDeadline, screencastResponses);
@@ -446,6 +441,14 @@ async function main() {
const screenshotPaths = [];
if (requireLiveProgress) checks.liveProgressCaptures = [];
const capture = async (screenshotPath) => {
// Resizing can move the target to a different row/column. Re-anchor each
// gallery variant so it photographs the requested controls at every width.
if (process.env.SCREENSHOT_SCROLL_SELECTOR) {
await page.waitForSelector(process.env.SCREENSHOT_SCROLL_SELECTOR, { timeout: 45000 }).catch(() => {});
await page.evaluate((selector) => {
document.querySelector(selector)?.scrollIntoView({ block: 'start', inline: 'nearest' });
}, process.env.SCREENSHOT_SCROLL_SELECTOR);
}
await waitForVisibleCardPreviews(page, requireLiveProgress ? Math.max(1, liveProgressDeadline - Date.now()) : 45000);
if (requireLiveProgress) {
const liveProgress = await waitForLiveProgress(page, liveProgressDeadline, screencastResponses);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "archivebox",
"version": "0.9.72rc102",
"version": "0.9.72rc105",
"repository": "github:ArchiveBox/ArchiveBox",
"license": "MIT",
"dependencies": {
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "archivebox"
version = "0.9.72rc102"
version = "0.9.72rc105"
requires-python = ">=3.13"
description = "Self-hosted internet archiving solution."
authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}]
@@ -79,8 +79,8 @@ dependencies = [
### Binary/Package Management
"abxbus==2.5.74", # direct imports only; version constrained by abx-dl -> abx-plugins
"abxpkg==1.13.33", # direct imports only; version constrained by abx-dl -> abx-plugins
"abx-plugins[opencode]==1.13.156", # optional AI runtime clients are owned by the plugin
"abx-dl==1.13.124", # shared ArchiveBox downloader package
"abx-plugins[opencode]==1.13.161", # optional AI runtime clients are owned by the plugin
"abx-dl==1.13.126", # shared ArchiveBox downloader package
### UUID7 backport for Python <3.14
"uuid7>=0.1.0; python_version < '3.14'", # provides the uuid_extensions module on Python 3.13
"browser-cookie3>=0.20.1",
@@ -344,7 +344,7 @@ Donate = "https://github.com/ArchiveBox/ArchiveBox/wiki/Donations"
[tool.bumpver]
current_version = "v0.9.72rc102"
current_version = "v0.9.72rc105"
version_pattern = "vMAJOR.MINOR.PATCH[PYTAGNUM]"
commit_message = "bump version {old_version} -> {new_version}"
tag_message = "{new_version}"
Generated
+11 -11
View File
@@ -13,18 +13,18 @@ supported-markers = [
]
[options]
exclude-newer = "2026-09-30T23:07:43.739749959Z"
exclude-newer = "2026-10-01T07:11:37.418401316Z"
exclude-newer-span = "P5D"
[options.exclude-newer-package]
abxbus = { timestamp = "2026-10-05T23:07:42.739761941Z", span = "PT1S" }
abxbus = { timestamp = "2026-10-06T07:11:36.418414Z", span = "PT1S" }
abx-plugins = "2100-01-01T00:00:00Z"
abx-dl = "2100-01-01T00:00:00Z"
abxpkg = "2100-01-01T00:00:00Z"
[[package]]
name = "abx-dl"
version = "1.13.124"
version = "1.13.126"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "abx-plugins", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@@ -38,14 +38,14 @@ dependencies = [
{ name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "rich-click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/ea/b62384c3e1a90e7a0b847fbab7305aa04b74509700a0c880c560d653a749/abx_dl-1.13.124.tar.gz", hash = "sha256:8c90dff2f4c56577a61cabb5902a07b4ff5ac0e3ed416ded23bb532356dc1910", size = 98198, upload-time = "2026-10-05T23:05:57.134Z" }
sdist = { url = "https://files.pythonhosted.org/packages/b7/80/f30434e2e081d33c1b46233e9b284c98cff9eb8895b5fd546204953849b7/abx_dl-1.13.126.tar.gz", hash = "sha256:bdd29b626fba7a35f39865e5769b3b6ea1145a9953f2cf3aa14379eae192c248", size = 98185, upload-time = "2026-10-06T07:10:19.966Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/1e/b53b8961de26f9306a495563dcd69760b17b38e06eba52c70d38b3d892a2/abx_dl-1.13.124-py3-none-any.whl", hash = "sha256:11fb83a6fe125e2c002ea79d7a88c3916b4a797f9f133d2ac8096cd4de2ad39f", size = 103546, upload-time = "2026-10-05T23:05:58.671Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3e/f666f8294a31a8acd9189d790585f453e0ccecbb0a9ed9c9163c28ba6a10/abx_dl-1.13.126-py3-none-any.whl", hash = "sha256:7eb1e7295b4d11c5225d6fcd100e51069dc1022818e0c8f50abdf7d24ba4434d", size = 103547, upload-time = "2026-10-06T07:10:21.456Z" },
]
[[package]]
name = "abx-plugins"
version = "1.13.156"
version = "1.13.161"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "abxbus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@@ -54,9 +54,9 @@ dependencies = [
{ name = "rich-click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "uv", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/8f/4fdfc25513df967cbd807bfe935c9751f344a46bd4cc2115321d99748651/abx_plugins-1.13.156.tar.gz", hash = "sha256:671e83ce2816744c45023ef005337c4b1ecd0fb63780af2769fecb7c8ff14db0", size = 505856, upload-time = "2026-10-05T22:50:03.777Z" }
sdist = { url = "https://files.pythonhosted.org/packages/3a/9d/9e442f90b30eb34fe4be44444d399685f453d3e452961018f7f91eaca173/abx_plugins-1.13.161.tar.gz", hash = "sha256:eb80af30d259716d4c713c708197328ca7fe0ad0b7fe05c27ef3bcb8112913e3", size = 509069, upload-time = "2026-10-06T07:02:15.305Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/3b/334fd4853ead2518f79c0fcb51ca6ce148d1fce39c5f27f13d9daf86c7e1/abx_plugins-1.13.156-py3-none-any.whl", hash = "sha256:3af2d8bb0d9360cea179afebbd757f5c21141d5336f7e6cd6dc9af38cffd613f", size = 734956, upload-time = "2026-10-05T22:50:01.916Z" },
{ url = "https://files.pythonhosted.org/packages/76/2b/da4c917bd49e92ba059b5a93b21f49499b5d385c15db23e2bc4ced834eb9/abx_plugins-1.13.161-py3-none-any.whl", hash = "sha256:1a88ec1580fa522718cf77049fd841441bd041cea63d8e38d4d1947e8b5b7b6a", size = 739583, upload-time = "2026-10-06T07:02:12.819Z" },
]
[package.optional-dependencies]
@@ -127,7 +127,7 @@ wheels = [
[[package]]
name = "archivebox"
version = "0.9.72rc102"
version = "0.9.72rc105"
source = { editable = "." }
dependencies = [
{ name = "abx-dl", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@@ -228,8 +228,8 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "abx-dl", specifier = "==1.13.124" },
{ name = "abx-plugins", extras = ["opencode"], specifier = "==1.13.156" },
{ name = "abx-dl", specifier = "==1.13.126" },
{ name = "abx-plugins", extras = ["opencode"], specifier = "==1.13.161" },
{ name = "abxbus", specifier = "==2.5.74" },
{ name = "abxpkg", specifier = "==1.13.33" },
{ name = "archivebox", extras = ["sonic", "ldap", "debug"], marker = "extra == 'all'" },