Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e06204d6e0 | ||
|
|
8209d4844d | ||
|
|
dc84a2b053 | ||
|
|
3944f688f4 | ||
|
|
96e2de60ae | ||
|
|
b9de2eb346 | ||
|
|
3830544e13 | ||
|
|
913542c19f | ||
|
|
f603a0dfff | ||
|
|
5e47e055a2 | ||
|
|
d1136d74cb | ||
|
|
54f598789e | ||
|
|
5171eaf31e |
No files matched your search
@@ -0,0 +1,20 @@
|
||||
version: 2
|
||||
build:
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.13"
|
||||
jobs:
|
||||
build:
|
||||
html:
|
||||
- uv run --no-project --with-requirements .github/docs/requirements.txt sphinx-build -W --keep-going -b html -c .github/docs docs "$READTHEDOCS_OUTPUT/html"
|
||||
submodules:
|
||||
include:
|
||||
- docs
|
||||
python:
|
||||
install:
|
||||
- method: uv
|
||||
command: pip
|
||||
requirements: .github/docs/requirements.txt
|
||||
sphinx:
|
||||
configuration: .github/docs/conf.py
|
||||
fail_on_warning: true
|
||||
@@ -0,0 +1,43 @@
|
||||
# Building the 0.7.4 documentation
|
||||
|
||||
The `docs` submodule remains pinned to the wiki revision shipped with 0.7.4.
|
||||
This configuration renders that historical user guide without installing
|
||||
ArchiveBox, importing Django, or updating the wiki checkout.
|
||||
|
||||
Prerequisites: [uv](https://docs.astral.sh/uv/getting-started/installation/) 0.11.3 and Python 3.13, matching CI and Read the Docs. `uv` can install Python when needed.
|
||||
|
||||
```sh
|
||||
git submodule update --init docs
|
||||
uv run --no-project --python 3.13 --with-requirements .github/docs/requirements.txt \
|
||||
sphinx-build -W --keep-going -b html -c .github/docs docs /tmp/archivebox-docs
|
||||
```
|
||||
|
||||
`conf.py` adapts GitHub wiki links, heading levels, and obsolete local anchors
|
||||
in memory. Configuration options without a prose section link to their exact
|
||||
0.7.4 source definition. The original user documentation is not rewritten.
|
||||
Generated Python autodoc scaffolding is outside this standalone user guide;
|
||||
its legacy Django-dependent builder is not loaded.
|
||||
|
||||
Read the Docs uses `.github/.readthedocs.yaml` and the same strict HTML command.
|
||||
Keep its project configuration-file setting pointed at that path.
|
||||
|
||||
`requirements.txt` pins the complete documentation dependency set. To update it,
|
||||
edit `requirements.in` and run:
|
||||
|
||||
```sh
|
||||
uv pip compile --universal --python-version 3.13 .github/docs/requirements.in \
|
||||
--output-file .github/docs/requirements.txt
|
||||
```
|
||||
|
||||
## Live version picker
|
||||
|
||||
Read the Docs loads `https://docs.archivebox.io/docs-live-picker/_static/archivebox-versions.js`
|
||||
through Addons > Custom script. The hidden `docs-live-picker` version keeps this
|
||||
asset available independently of future main or release documentation builds. This applies the release policy and puts `dev`
|
||||
after `latest` across existing versions without rebuilding them. The script uses
|
||||
the published version inventory and preserves existing navigation URLs.
|
||||
|
||||
The hosting checkout command checks out the original main/tag commit, then overlays
|
||||
only the historical Sphinx build files from pinned docs repair commits. Application
|
||||
source, release tags, and pinned wiki revisions remain those of the selected release.
|
||||
Keep the docs repair branches available for fetching these configuration commits.
|
||||
@@ -0,0 +1,200 @@
|
||||
"""Build the pinned 0.7.4 user guide without importing ArchiveBox or Django."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
from markdown_it import MarkdownIt
|
||||
from myst_parser.mdit_to_docutils.base import default_slugify
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
DOCS = ROOT / "docs"
|
||||
project = "ArchiveBox"
|
||||
author = "ArchiveBox contributors"
|
||||
copyright = "ArchiveBox contributors"
|
||||
html_show_sphinx = False
|
||||
release = json.loads((ROOT / "package.json").read_text())["version"]
|
||||
version = release
|
||||
extensions = ["myst_parser", "sphinx_rtd_theme"]
|
||||
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
|
||||
root_doc = "index"
|
||||
# These are generated autodoc scaffolding, not standalone user documentation.
|
||||
exclude_patterns = [
|
||||
"archivebox*.rst",
|
||||
"modules.rst",
|
||||
"_Sidebar.md",
|
||||
"_Footer.md",
|
||||
".git",
|
||||
]
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_options = {
|
||||
"navigation_depth": 3,
|
||||
"collapse_navigation": False,
|
||||
"version_selector": True,
|
||||
"language_selector": False,
|
||||
}
|
||||
html_context = {"current_version": os.environ.get("READTHEDOCS_VERSION", release)}
|
||||
html_logo = str(DOCS / "logo.png")
|
||||
html_static_path = [str(DOCS / "_static"), str(ROOT / ".github/docs/static")]
|
||||
html_title = f"ArchiveBox {release} documentation"
|
||||
myst_heading_anchors = 6
|
||||
|
||||
|
||||
def rewrite_prose(text, transform):
|
||||
"""Rewrite navigation outside fenced and indented code blocks."""
|
||||
lines = text.splitlines(keepends=True)
|
||||
output = []
|
||||
start = 0
|
||||
for token in MarkdownIt().parse(text):
|
||||
if token.type not in {"fence", "code_block"}:
|
||||
continue
|
||||
first, last = token.map
|
||||
output.append(transform("".join(lines[start:first])))
|
||||
output.append("".join(lines[first:last]))
|
||||
start = last
|
||||
output.append(transform("".join(lines[start:])))
|
||||
return "".join(output)
|
||||
|
||||
|
||||
def prepare_source(app, docname, source):
|
||||
"""Adapt GitHub wiki navigation in memory, preserving the pinned sources."""
|
||||
text = source[0]
|
||||
if docname == "Contents":
|
||||
text = re.sub(
|
||||
r"API Reference\n#############.*?(?=Meta\n####)", "", text, flags=re.DOTALL
|
||||
)
|
||||
text += "\n.. toctree::\n :hidden:\n\n Home\n"
|
||||
if docname == "index":
|
||||
text = text.replace("archivebox info", "archivebox status")
|
||||
text = text.replace(
|
||||
"pip install archivebox", f"pip install archivebox=={release}"
|
||||
)
|
||||
text = text.replace(
|
||||
"ArchiveBox/ArchiveBox/tree/master",
|
||||
f"ArchiveBox/ArchiveBox/tree/v{release}",
|
||||
)
|
||||
text = text.replace(
|
||||
"`Github <https://github.com/ArchiveBox/ArchiveBox/issues>`_",
|
||||
"`GitHub issues <https://github.com/ArchiveBox/ArchiveBox/issues>`_",
|
||||
)
|
||||
text = text.replace(
|
||||
"==========\nArchiveBox\n==========",
|
||||
f"ArchiveBox {release}\n" + "=" * (11 + len(release)),
|
||||
)
|
||||
if Path(app.env.doc2path(docname)).suffix == ".md":
|
||||
|
||||
def wiki_link(match):
|
||||
parts = match.group(1).split("|", 1)
|
||||
label, target = (parts[0], parts[-1])
|
||||
page, separator, anchor = unquote(target).partition("#")
|
||||
page = page.replace(" ", "-")
|
||||
suffix = f"#{anchor}" if separator else ""
|
||||
return f"[{label}]({page}.md{suffix})"
|
||||
|
||||
text = rewrite_prose(
|
||||
text, lambda prose: re.sub(r"\[\[([^\]\n]+)\]\]", wiki_link, prose)
|
||||
)
|
||||
source[0] = text
|
||||
|
||||
|
||||
# GitHub wiki headings allow skipped levels; normalize their hierarchy before MyST
|
||||
# parses them, without changing fenced code blocks or the historical source files.
|
||||
def normalize_markdown(app, docname, source):
|
||||
if Path(app.env.doc2path(docname)).suffix != ".md":
|
||||
return
|
||||
text = source[0]
|
||||
if docname in {"Home", "README", "Donations", "Upgrading-or-Merging-Archives"}:
|
||||
text = (
|
||||
"# "
|
||||
+ (
|
||||
"ArchiveBox overview"
|
||||
if docname == "README"
|
||||
else docname.replace("-", " ")
|
||||
)
|
||||
+ "\n\n"
|
||||
+ text
|
||||
)
|
||||
lines = text.splitlines()
|
||||
stack = []
|
||||
for token in MarkdownIt().parse(text):
|
||||
if token.type != "heading_open":
|
||||
continue
|
||||
original = int(token.tag[1])
|
||||
while stack and stack[-1] >= original:
|
||||
stack.pop()
|
||||
stack.append(original)
|
||||
line = token.map[0]
|
||||
lines[line] = re.sub(r"^#{1,6}(?=\s)", "#" * len(stack), lines[line]).replace(
|
||||
"\ufe0f", ""
|
||||
)
|
||||
text = "\n".join(lines) + "\n"
|
||||
|
||||
def link(match):
|
||||
target = match.group(1)
|
||||
if "://" in target or target.startswith("mailto:"):
|
||||
return match.group(0)
|
||||
page, sep, anchor = target.partition("#")
|
||||
if page and (DOCS / (page + ".md")).exists():
|
||||
page += ".md"
|
||||
# MyST resolves Markdown GitHub slugs (e.g. background--motivation)
|
||||
# to rendered section IDs; raw HTML links below need explicit rewrites.
|
||||
anchor = anchor.lower()
|
||||
if (docname == "Configuration" and not page) or page == "Configuration.md":
|
||||
other = (DOCS / "Configuration.md").read_text()
|
||||
for heading in re.findall(r"^#+\s+(.+)$", other, re.MULTILINE):
|
||||
if anchor in [
|
||||
word.lower() for word in re.findall(r"`([^`]+)`", heading)
|
||||
]:
|
||||
anchor = default_slugify(heading.replace("`", ""))
|
||||
break
|
||||
aliases = {"templates_dir": "custom_templates_dir"}
|
||||
anchor = aliases.get(anchor, anchor)
|
||||
if anchor in {
|
||||
"search_backend_timeout",
|
||||
"search_backend_engine",
|
||||
"use_curl",
|
||||
}:
|
||||
code = (ROOT / "archivebox/config.py").read_text().splitlines()
|
||||
number = next(
|
||||
i + 1 for i, line in enumerate(code) if anchor.upper() in line
|
||||
)
|
||||
return (
|
||||
"](https://github.com/ArchiveBox/ArchiveBox/blob/v0.7.4/archivebox/config.py#L"
|
||||
+ str(number)
|
||||
+ ")"
|
||||
)
|
||||
if (
|
||||
docname == "README"
|
||||
and anchor == "saves-lots-of-useful-stuff-for-each-imported-link"
|
||||
):
|
||||
anchor = "output-formats"
|
||||
if docname == "Web-Archiving-Community":
|
||||
anchor = {
|
||||
"blogs": "blogs-friends-of-archivebox",
|
||||
"articles": "articles-we-like-about-internet-archiving",
|
||||
}.get(anchor, anchor)
|
||||
return "](" + page + ("#" + anchor if sep else "") + ")"
|
||||
|
||||
def rewrite_links(prose):
|
||||
prose = re.sub(r"\]\(([^)\s]+)\)", link, prose)
|
||||
# Raw HTML links are outside MyST's cross-reference resolver.
|
||||
html_anchors = {
|
||||
"background--motivation": "background-motivation",
|
||||
"Caveats": "caveats",
|
||||
"contents": "web-archiving-community",
|
||||
"%EF%B8%8F-cli-usage": "cli-usage",
|
||||
}
|
||||
for old, new in html_anchors.items():
|
||||
prose = prose.replace(f'href="#{old}"', f'href="#{new}"')
|
||||
prose = prose.replace("#️-cli-usage", "#cli-usage")
|
||||
return prose
|
||||
|
||||
text = rewrite_prose(text, rewrite_links)
|
||||
source[0] = text
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.connect("source-read", prepare_source)
|
||||
app.connect("source-read", normalize_markdown)
|
||||
@@ -0,0 +1,3 @@
|
||||
Sphinx==8.2.3
|
||||
myst-parser==4.0.1
|
||||
sphinx-rtd-theme==3.1.0
|
||||
@@ -0,0 +1,75 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --universal --python-version 3.13 .github/docs/requirements.in --output-file .github/docs/requirements.txt
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
babel==2.18.0
|
||||
# via sphinx
|
||||
certifi==2026.7.22
|
||||
# via requests
|
||||
charset-normalizer==3.5.1
|
||||
# via requests
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via sphinx
|
||||
docutils==0.21.2
|
||||
# via
|
||||
# myst-parser
|
||||
# sphinx
|
||||
# sphinx-rtd-theme
|
||||
idna==3.20
|
||||
# via requests
|
||||
imagesize==2.0.1
|
||||
# via sphinx
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# myst-parser
|
||||
# sphinx
|
||||
markdown-it-py==3.0.0
|
||||
# via
|
||||
# mdit-py-plugins
|
||||
# myst-parser
|
||||
markupsafe==3.0.3
|
||||
# via jinja2
|
||||
mdit-py-plugins==0.6.1
|
||||
# via myst-parser
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
myst-parser==4.0.1
|
||||
# via -r .github/docs/requirements.in
|
||||
packaging==26.3
|
||||
# via sphinx
|
||||
pygments==2.21.0
|
||||
# via sphinx
|
||||
pyyaml==6.0.3
|
||||
# via myst-parser
|
||||
requests==2.34.2
|
||||
# via sphinx
|
||||
roman-numerals==4.1.0
|
||||
# via roman-numerals-py
|
||||
roman-numerals-py==4.1.0
|
||||
# via sphinx
|
||||
snowballstemmer==3.1.1
|
||||
# via sphinx
|
||||
sphinx==8.2.3
|
||||
# via
|
||||
# -r .github/docs/requirements.in
|
||||
# myst-parser
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx-rtd-theme==3.1.0
|
||||
# via -r .github/docs/requirements.in
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
# via sphinx
|
||||
sphinxcontrib-jquery==4.1
|
||||
# via sphinx-rtd-theme
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
urllib3==2.8.0
|
||||
# via requests
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ArchiveBox RTD picker policy. No requests, credentials, or documentation edits.
|
||||
* RTD event is read-only input: internal flyout rendering precedes the public
|
||||
* event, so changing its payload is not a supported rendering hook.
|
||||
* Targets: addons readthedocs-flyout shadow dl.versions, RTD theme 3.1 select,
|
||||
* and the legacy theme's Versions definition list. Other navigation is untouched.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function parse(slug) {
|
||||
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-?rc(\d*))?$/i.exec(slug);
|
||||
if (!match) return null;
|
||||
return { slug, major: +match[1], minor: +match[2], patch: +match[3],
|
||||
rc: match[4] === undefined ? null : +(match[4] || 0) };
|
||||
}
|
||||
function compare(a, b) {
|
||||
return a.major - b.major || a.minor - b.minor || a.patch - b.patch ||
|
||||
(a.rc === null ? Infinity : a.rc) - (b.rc === null ? Infinity : b.rc) ||
|
||||
a.slug.localeCompare(b.slug);
|
||||
}
|
||||
function select(versions) {
|
||||
const published = versions.filter(v => v.active !== false && v.built !== false && !v.hidden);
|
||||
const parsed = published.map(v => parse(v.slug)).filter(Boolean);
|
||||
const minors = [...new Set(parsed.sort((a, b) => compare(b, a))
|
||||
.map(v => `${v.major}.${v.minor}`))];
|
||||
const winners = minors.map((minor, index) => parsed.filter(v =>
|
||||
`${v.major}.${v.minor}` === minor && (index < 2 ? v.rc !== null : v.rc === null))
|
||||
.sort((a, b) => compare(b, a))[0]).filter(Boolean).map(v => v.slug);
|
||||
return ["latest", "dev"].filter(slug => published.some(v => v.slug === slug)).concat(winners);
|
||||
}
|
||||
|
||||
// Export only the pure policy for Node verification; browser has no new globals.
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = { parse, select };
|
||||
return;
|
||||
}
|
||||
const project = document.querySelector('meta[name="readthedocs-project-slug"]')?.content;
|
||||
if (project && project !== "archivebox") return;
|
||||
if (!document.querySelector('meta[name="readthedocs-addons-api-version"]')) {
|
||||
const meta = document.createElement("meta");
|
||||
meta.name = "readthedocs-addons-api-version";
|
||||
meta.content = "1";
|
||||
document.head.append(meta);
|
||||
}
|
||||
let wanted = null;
|
||||
const watched = new WeakSet();
|
||||
function reconcile(container) {
|
||||
if (!wanted) return;
|
||||
const isSelect = container.tagName === "SELECT";
|
||||
const children = [...container.children].filter(n => isSelect ? n.tagName === "OPTION" : n.tagName === "DD");
|
||||
const slug = n => isSelect ? n.value : n.querySelector("a")?.textContent.trim();
|
||||
const selected = isSelect ? container.value : null;
|
||||
const kept = wanted.map(s => children.find(n => slug(n) === s)).filter(Boolean);
|
||||
const current = children.filter(n => wanted.includes(slug(n)));
|
||||
for (const node of children) if (!wanted.includes(slug(node))) node.remove();
|
||||
if (kept.some((node, i) => node !== current[i])) {
|
||||
for (const node of kept) container.append(node);
|
||||
}
|
||||
if (isSelect) {
|
||||
container.value = selected;
|
||||
if (!kept.some(n => n.value === selected)) container.selectedIndex = -1;
|
||||
}
|
||||
}
|
||||
function watch(container) {
|
||||
reconcile(container);
|
||||
if (watched.has(container)) return;
|
||||
watched.add(container);
|
||||
new MutationObserver(() => reconcile(container)).observe(container, { childList: true, subtree: true });
|
||||
}
|
||||
function scan() {
|
||||
if (!wanted) return;
|
||||
document.querySelectorAll("div.switch-menus > div.version-switch select").forEach(watch);
|
||||
document.querySelectorAll(".rst-other-versions dl").forEach(dl => {
|
||||
if (dl.querySelector("dt")?.textContent.trim() === "Versions") watch(dl);
|
||||
});
|
||||
document.querySelectorAll("readthedocs-flyout").forEach(host => {
|
||||
const root = host.shadowRoot;
|
||||
if (!root) return;
|
||||
root.querySelectorAll("dl.versions").forEach(watch);
|
||||
if (!watched.has(root)) {
|
||||
watched.add(root);
|
||||
new MutationObserver(() => root.querySelectorAll("dl.versions").forEach(watch))
|
||||
.observe(root, { childList: true, subtree: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
customElements.whenDefined("readthedocs-flyout").then(scan);
|
||||
let discovery;
|
||||
let stopDiscovery;
|
||||
function ready(eventData) {
|
||||
const config = eventData.data();
|
||||
if (config.projects?.current?.slug !== "archivebox" || !Array.isArray(config.versions?.active)) return;
|
||||
wanted = select(config.versions.active);
|
||||
if (!wanted.length) return;
|
||||
scan();
|
||||
// Discover asynchronously inserted picker containers for at most 10 seconds.
|
||||
// Afterwards, observers remain scoped to known picker/shadow containers only.
|
||||
discovery?.disconnect();
|
||||
clearTimeout(stopDiscovery);
|
||||
discovery = new MutationObserver(scan);
|
||||
discovery.observe(document.body, { childList: true, subtree: true });
|
||||
stopDiscovery = setTimeout(() => discovery.disconnect(), 10000);
|
||||
}
|
||||
document.addEventListener("readthedocs-addons-data-ready", event => ready(event.detail));
|
||||
if (globalThis.ReadTheDocsEventData) ready(globalThis.ReadTheDocsEventData);
|
||||
})();
|
||||
@@ -3,8 +3,9 @@ name: Build Docker image
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
branches-ignore:
|
||||
- docs
|
||||
- 'docs/**'
|
||||
tags:
|
||||
- 'v*'
|
||||
# pull_request:
|
||||
@@ -14,6 +15,8 @@ env:
|
||||
|
||||
jobs:
|
||||
buildx:
|
||||
# Also prevent a manual dispatch on a docs repair branch from publishing.
|
||||
if: github.ref != 'refs/heads/docs' && !startsWith(github.ref, 'refs/heads/docs/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Historical Sphinx documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ['.github/docs/**', '.github/.readthedocs.yaml', '.github/workflows/docs.yml', 'docs', 'README.md', 'package.json', 'archivebox/config.py']
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['.github/docs/**', '.github/.readthedocs.yaml', '.github/workflows/docs.yml', 'docs', 'README.md', 'package.json', 'archivebox/config.py']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
- run: git submodule update --init docs
|
||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: '0.11.3'
|
||||
python-version: '3.13'
|
||||
- name: Build the pinned historical user guide
|
||||
env:
|
||||
READTHEDOCS: 'True'
|
||||
READTHEDOCS_VERSION: latest
|
||||
run: uv run --no-project --with-requirements .github/docs/requirements.txt sphinx-build -W --keep-going -b html -c .github/docs docs "$RUNNER_TEMP/docs-html"
|
||||
- name: Verify the version selector
|
||||
run: |
|
||||
grep -q 'class="version-switch"' "$RUNNER_TEMP/docs-html/index.html"
|
||||
grep -q 'readthedocs-addons-data-ready' "$RUNNER_TEMP/docs-html/_static/js/versions.js"
|
||||
+19
-6
@@ -45,7 +45,10 @@ ENV TZ=UTC \
|
||||
|
||||
# Version config
|
||||
ENV PYTHON_VERSION=3.11 \
|
||||
NODE_VERSION=20
|
||||
NODE_VERSION=24 \
|
||||
PLAYWRIGHT_VERSION=1.59.0 \
|
||||
YTDLP_VERSION=2026.3.17 \
|
||||
GOSU_VERSION=1.19
|
||||
|
||||
# User config
|
||||
ENV ARCHIVEBOX_USER="archivebox" \
|
||||
@@ -113,10 +116,20 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$TARGETARCH$T
|
||||
# 1. packaging dependencies
|
||||
apt-transport-https ca-certificates apt-utils gnupg2 curl wget \
|
||||
# 2. docker and init system dependencies
|
||||
zlib1g-dev dumb-init gosu cron unzip grep \
|
||||
zlib1g-dev dumb-init cron unzip grep \
|
||||
# 3. frivolous CLI helpers to make debugging failed archiving easier
|
||||
# nano iputils-ping dnsutils htop procps jq yq
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& case "$TARGETARCH/$TARGETVARIANT" in \
|
||||
amd64/) GOSU_ARCH=amd64; GOSU_SHA256=52c8749d0142edd234e9d6bd5237dff2d81e71f43537e2f4f66f75dd4b243dd0 ;; \
|
||||
arm64/) GOSU_ARCH=arm64; GOSU_SHA256=3a8ef022d82c0bc4a98bcb144e77da714c25fcfa64dccc57f6aba7ae47ff1a44 ;; \
|
||||
arm/v7) GOSU_ARCH=armhf; GOSU_SHA256=8457a0bfd28e016c2c7d8ea6e5f7eed1376033ffbd36491bb455094c8b1dc9fd ;; \
|
||||
*) echo "Unsupported gosu platform: $TARGETARCH/$TARGETVARIANT"; exit 1 ;; \
|
||||
esac \
|
||||
&& curl -fsSL -o /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$GOSU_ARCH" \
|
||||
&& echo "$GOSU_SHA256 /usr/local/bin/gosu" | sha256sum -c - \
|
||||
&& chmod +x /usr/local/bin/gosu \
|
||||
&& gosu --version | head -n1 | tee -a /VERSION.txt
|
||||
|
||||
######### Language Environments ####################################
|
||||
|
||||
@@ -164,7 +177,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$TARGETARCH$T
|
||||
echo "[+] Installing APT extractor dependencies globally using apt..." \
|
||||
&& apt-get update -qq \
|
||||
&& apt-get install -qq -y -t bookworm-backports --no-install-recommends \
|
||||
curl wget git yt-dlp ffmpeg ripgrep \
|
||||
curl wget git ffmpeg ripgrep \
|
||||
# Packages we have also needed in the past:
|
||||
# youtube-dl wget2 aria2 python3-pyxattr rtmpdump libfribidi-bin mpv \
|
||||
# fontconfig fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-symbola fonts-noto fonts-freefont-ttf \
|
||||
@@ -173,7 +186,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$TARGETARCH$T
|
||||
&& ( \
|
||||
which curl && curl --version | head -n1 \
|
||||
&& which wget && wget --version 2>&1 | head -n1 \
|
||||
&& which yt-dlp && yt-dlp --version 2>&1 | head -n1 \
|
||||
&& which git && git --version 2>&1 | head -n1 \
|
||||
&& which rg && rg --version 2>&1 | head -n1 \
|
||||
&& echo -e '\n\n' \
|
||||
@@ -185,7 +197,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$TARGETARCH$T
|
||||
&& apt-get update -qq \
|
||||
&& if [[ "$TARGETPLATFORM" == *amd64* || "$TARGETPLATFORM" == *arm64* ]]; then \
|
||||
# install Chromium using playwright
|
||||
pip install playwright \
|
||||
pip install "playwright==$PLAYWRIGHT_VERSION" \
|
||||
&& cp -r /root/.cache/ms-playwright "$PLAYWRIGHT_BROWSERS_PATH" \
|
||||
&& playwright install --with-deps chromium \
|
||||
&& export CHROME_BINARY="$(python -c 'from playwright.sync_api import sync_playwright; print(sync_playwright().start().chromium.executable_path)')"; \
|
||||
@@ -238,6 +250,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-$TARGETARCH$T
|
||||
# && pdm export -o requirements.txt --without-hashes \
|
||||
# && source $GLOBAL_VENV/bin/activate \
|
||||
&& pip install -r requirements.txt \
|
||||
&& pip install --upgrade "yt-dlp==$YTDLP_VERSION" \
|
||||
&& apt-get purge -y \
|
||||
build-essential \
|
||||
&& apt-get autoremove -y \
|
||||
|
||||
@@ -836,7 +836,10 @@ def bin_version(binary: Optional[str]) -> Optional[str]:
|
||||
if not version_str:
|
||||
version_str = run([abspath, "--version"], stdout=PIPE).stdout.strip().decode()
|
||||
# take first 3 columns of first line of version info
|
||||
return ' '.join(version_str.split('\n')[0].strip().split()[:3])
|
||||
version_parts = version_str.split('\n')[0].strip().split()
|
||||
if version_parts[:4] == ['Google', 'Chrome', 'for', 'Testing']:
|
||||
return ' '.join(version_parts[:5])
|
||||
return ' '.join(version_parts[:3])
|
||||
except OSError:
|
||||
pass
|
||||
# stderr(f'[X] Unable to find working version of dependency: {binary}', color='red')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "archivebox",
|
||||
"version": "0.7.2",
|
||||
"version": "0.7.4",
|
||||
"description": "ArchiveBox: The self-hosted internet archive",
|
||||
"author": "Nick Sweeting <archivebox-npm@sweeting.me>",
|
||||
"repository": "github:ArchiveBox/ArchiveBox",
|
||||
@@ -8,6 +8,6 @@
|
||||
"dependencies": {
|
||||
"@postlight/parser": "^2.2.3",
|
||||
"readability-extractor": "github:ArchiveBox/readability-extractor",
|
||||
"single-file-cli": "^1.1.46"
|
||||
"single-file-cli": "^2.0.83"
|
||||
}
|
||||
}
|
||||
@@ -155,6 +155,9 @@ def save_text_as_source(raw_text: str, filename: str='{ts}-stdin.txt', out_dir:
|
||||
referenced_texts = ''
|
||||
|
||||
for entry in raw_text.split():
|
||||
# Skip URLs - Path() can't handle them and they aren't local files
|
||||
if any(entry.startswith(s) for s in ('http://', 'https://', 'ftp://')):
|
||||
continue
|
||||
try:
|
||||
if Path(entry).exists():
|
||||
referenced_texts += Path(entry).read_text()
|
||||
|
||||
+5
-2
@@ -73,7 +73,7 @@ check_platforms || (recreate_builder && check_platforms) || exit 1
|
||||
|
||||
# Build python package lists
|
||||
echo "[+] Generating requirements.txt and pdm.lock from pyproject.toml..."
|
||||
pdm lock --group=':all' --strategy="cross_platform" --production
|
||||
pdm lock --group=':all' --strategy="inherit_metadata" --production
|
||||
pdm export --group=':all' --production --without-hashes -o requirements.txt
|
||||
|
||||
|
||||
@@ -88,14 +88,17 @@ docker buildx build --platform "$SELECTED_PLATFORMS" --load . \
|
||||
-t archivebox/archivebox:$SHORT_VERSION \
|
||||
-t archivebox/archivebox:$GIT_SHA \
|
||||
-t archivebox/archivebox:latest \
|
||||
-t archivebox/archivebox:stable \
|
||||
-t nikisweeting/archivebox \
|
||||
-t nikisweeting/archivebox:$TAG_NAME \
|
||||
-t nikisweeting/archivebox:$VERSION \
|
||||
-t nikisweeting/archivebox:$SHORT_VERSION \
|
||||
-t nikisweeting/archivebox:$GIT_SHA \
|
||||
-t nikisweeting/archivebox:latest \
|
||||
-t nikisweeting/archivebox:stable \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$TAG_NAME \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$VERSION \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$SHORT_VERSION \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$GIT_SHA \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:latest
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:latest \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:stable
|
||||
@@ -26,8 +26,8 @@ SELECTED_PLATFORMS="${2:-$SUPPORTED_PLATFORMS}"
|
||||
# docker login --username=nikisweeting
|
||||
# docker login ghcr.io --username=pirate
|
||||
|
||||
echo "[^] Building docker image"
|
||||
./bin/build_docker.sh "$TAG_NAME" "$SELECTED_PLATFORMS"
|
||||
# echo "[^] Building docker image"
|
||||
# ./bin/build_docker.sh "$TAG_NAME" "$SELECTED_PLATFORMS"
|
||||
|
||||
echo "[^] Uploading docker image"
|
||||
docker buildx build --platform "$SELECTED_PLATFORMS" --push . \
|
||||
@@ -37,14 +37,18 @@ docker buildx build --platform "$SELECTED_PLATFORMS" --push . \
|
||||
-t archivebox/archivebox:$SHORT_VERSION \
|
||||
-t archivebox/archivebox:$GIT_SHA \
|
||||
-t archivebox/archivebox:latest \
|
||||
-t archivebox/archivebox:stable \
|
||||
-t nikisweeting/archivebox \
|
||||
-t nikisweeting/archivebox:$TAG_NAME \
|
||||
-t nikisweeting/archivebox:$VERSION \
|
||||
-t nikisweeting/archivebox:$SHORT_VERSION \
|
||||
-t nikisweeting/archivebox:$GIT_SHA \
|
||||
-t nikisweeting/archivebox:latest \
|
||||
-t nikisweeting/archivebox:stable \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$TAG_NAME \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$VERSION \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$SHORT_VERSION \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$GIT_SHA
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:$GIT_SHA \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:latest \
|
||||
-t ghcr.io/archivebox/archivebox/archivebox:stable
|
||||
|
||||
+6
-11
@@ -8,12 +8,10 @@
|
||||
# Documentation:
|
||||
# https://github.com/ArchiveBox/ArchiveBox/wiki/Docker#docker-compose
|
||||
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
archivebox:
|
||||
#image: ${DOCKER_IMAGE:-archivebox/archivebox:dev}
|
||||
image: archivebox/archivebox:dev
|
||||
image: archivebox/archivebox:stable
|
||||
command: server --quick-init 0.0.0.0:8000
|
||||
ports:
|
||||
- 8000:8000
|
||||
@@ -41,7 +39,7 @@ services:
|
||||
# ...
|
||||
# add further configuration options from archivebox/config.py as needed (to apply them only to this container)
|
||||
# or set using `docker compose run archivebox config --set SOME_KEY=someval` (to persist config across all containers)
|
||||
|
||||
|
||||
# For ad-blocking during archiving, uncomment this section and pihole service section below
|
||||
# networks:
|
||||
# - dns
|
||||
@@ -51,13 +49,10 @@ services:
|
||||
|
||||
######## Optional Addons: tweak examples below as needed for your specific use case ########
|
||||
|
||||
### Example: To run the Sonic full-text search backend, first download the config file to sonic.cfg
|
||||
# $ curl -O https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/master/etc/sonic.cfg
|
||||
# After starting, backfill any existing Snapshots into the full-text index:
|
||||
# $ docker-compose run archivebox update --index-only
|
||||
### Example: To run the Sonic full-text search backend, uncomment this block and the sonic config above
|
||||
|
||||
# sonic:
|
||||
# image: valeriansaliou/sonic:latest
|
||||
# image: archivebox/sonic:latest
|
||||
# expose:
|
||||
# - 1491
|
||||
# environment:
|
||||
@@ -65,8 +60,8 @@ services:
|
||||
# volumes:
|
||||
# - ./sonic.cfg:/etc/sonic.cfg:ro
|
||||
# - ./data/sonic:/var/lib/sonic/store
|
||||
|
||||
|
||||
|
||||
|
||||
### Example: To run pihole in order to block ad/tracker requests during archiving,
|
||||
# uncomment this block and set up pihole using its admin interface
|
||||
|
||||
|
||||
Generated
+32
-1060
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "archivebox",
|
||||
"version": "0.7.2",
|
||||
"version": "0.7.4",
|
||||
"description": "ArchiveBox: The self-hosted internet archive",
|
||||
"author": "Nick Sweeting <archivebox-npm@sweeting.me>",
|
||||
"repository": "github:ArchiveBox/ArchiveBox",
|
||||
@@ -8,6 +8,6 @@
|
||||
"dependencies": {
|
||||
"@postlight/parser": "^2.2.3",
|
||||
"readability-extractor": "github:ArchiveBox/readability-extractor",
|
||||
"single-file-cli": "^1.1.46"
|
||||
"single-file-cli": "^2.0.83"
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -1,21 +1,21 @@
|
||||
[project]
|
||||
name = "archivebox"
|
||||
version = "0.7.2"
|
||||
version = "0.7.4"
|
||||
description = "Self-hosted internet archiving solution."
|
||||
authors = [
|
||||
{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"},
|
||||
]
|
||||
dependencies = [
|
||||
"croniter>=0.3.34",
|
||||
"dateparser>=1.0.0",
|
||||
"croniter>=6.2.2",
|
||||
"dateparser>=1.2.2",
|
||||
"django-extensions>=3.0.3",
|
||||
"django>=3.1.3,<3.2",
|
||||
"ipython>5.0.0",
|
||||
"mypy-extensions>=0.4.3",
|
||||
"python-crontab>=2.5.1",
|
||||
"requests>=2.24.0",
|
||||
"w3lib>=1.22.0",
|
||||
"yt-dlp>=2023.10.13",
|
||||
"mypy-extensions>=1.1.0",
|
||||
"python-crontab>=3.3.0",
|
||||
"requests>=2.32.5",
|
||||
"w3lib>=2.3.1",
|
||||
"yt-dlp>=2025.10.14",
|
||||
# "playwright>=1.39.0; platform_machine != 'armv7l'",
|
||||
]
|
||||
requires-python = ">=3.9,<3.12"
|
||||
@@ -92,7 +92,7 @@ sonic = [
|
||||
]
|
||||
ldap = [
|
||||
# apt install libldap2-dev libsasl2-dev
|
||||
"python-ldap>=3.4.3",
|
||||
"python-ldap>=3.4.6",
|
||||
"django-auth-ldap>=4.1.0",
|
||||
]
|
||||
# playwright = [
|
||||
@@ -119,4 +119,3 @@ Community = "https://github.com/ArchiveBox/ArchiveBox/wiki/Web-Archiving-Communi
|
||||
Demo = "https://demo.archivebox.io"
|
||||
Donate = "https://github.com/ArchiveBox/ArchiveBox/wiki/Donations"
|
||||
|
||||
|
||||
+35
-42
@@ -1,54 +1,47 @@
|
||||
# This file is @generated by PDM.
|
||||
# Please do not edit it manually.
|
||||
|
||||
asgiref==3.7.2
|
||||
asttokens==2.4.1
|
||||
brotli==1.1.0; implementation_name == "cpython"
|
||||
brotlicffi==1.1.0.0; implementation_name != "cpython"
|
||||
certifi==2023.11.17
|
||||
cffi==1.16.0; implementation_name != "cpython"
|
||||
charset-normalizer==3.3.2
|
||||
asgiref==3.11.1
|
||||
asttokens==3.0.1
|
||||
certifi==2026.4.22
|
||||
charset-normalizer==3.4.7
|
||||
colorama==0.4.6; sys_platform == "win32"
|
||||
croniter==2.0.1
|
||||
dateparser==1.2.0
|
||||
decorator==5.1.1
|
||||
croniter==6.2.2
|
||||
dateparser==1.2.2
|
||||
decorator==5.3.1
|
||||
django==3.1.14
|
||||
django-auth-ldap==4.1.0
|
||||
django-extensions==3.1.5
|
||||
exceptiongroup==1.2.0; python_version < "3.11"
|
||||
executing==2.0.1
|
||||
idna==3.6
|
||||
exceptiongroup==1.3.1; python_version < "3.11"
|
||||
executing==2.2.1
|
||||
idna==3.15
|
||||
ipython==8.18.1
|
||||
jedi==0.19.1
|
||||
matplotlib-inline==0.1.6
|
||||
mutagen==1.47.0
|
||||
mypy-extensions==1.0.0
|
||||
parso==0.8.3
|
||||
jedi==0.19.2
|
||||
matplotlib-inline==0.2.2
|
||||
mypy-extensions==1.1.0
|
||||
parso==0.8.7
|
||||
pexpect==4.9.0; sys_platform != "win32"
|
||||
prompt-toolkit==3.0.43
|
||||
prompt-toolkit==3.0.52
|
||||
ptyprocess==0.7.0; sys_platform != "win32"
|
||||
pure-eval==0.2.2
|
||||
pyasn1==0.5.1
|
||||
pyasn1-modules==0.3.0
|
||||
pycparser==2.21; implementation_name != "cpython"
|
||||
pycryptodomex==3.19.1
|
||||
pygments==2.17.2
|
||||
python-crontab==3.0.0
|
||||
python-dateutil==2.8.2
|
||||
python-ldap==3.4.4
|
||||
pytz==2023.3.post1
|
||||
regex==2023.12.25
|
||||
requests==2.31.0
|
||||
six==1.16.0
|
||||
pure-eval==0.2.3
|
||||
pyasn1==0.6.3
|
||||
pyasn1-modules==0.4.2
|
||||
pygments==2.20.0
|
||||
python-crontab==3.3.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-ldap==3.4.6
|
||||
pytz==2026.2
|
||||
regex==2026.1.15
|
||||
requests==2.32.5
|
||||
six==1.17.0
|
||||
sonic-client==1.0.0
|
||||
sqlparse==0.4.4
|
||||
sqlparse==0.5.5
|
||||
stack-data==0.6.3
|
||||
traitlets==5.14.1
|
||||
typing-extensions==4.9.0; python_version < "3.11"
|
||||
tzdata==2023.4; platform_system == "Windows"
|
||||
tzlocal==5.2
|
||||
urllib3==2.1.0
|
||||
w3lib==2.1.2
|
||||
wcwidth==0.2.12
|
||||
websockets==12.0
|
||||
yt-dlp==2023.12.30
|
||||
traitlets==5.15.0
|
||||
typing-extensions==4.15.0; python_version < "3.11"
|
||||
tzdata==2026.2; platform_system == "Windows"
|
||||
tzlocal==5.3.1
|
||||
urllib3==2.6.3
|
||||
w3lib==2.3.1
|
||||
wcwidth==0.7.0
|
||||
yt-dlp==2025.10.14
|
||||
Reference in new issue
Block a user