Compare commits
102
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
803388ef42 | ||
|
|
d9381db20c | ||
|
|
50cb7cab33 | ||
|
|
71fc3f8c62 | ||
|
|
a42df9e289 | ||
|
|
728f3da055 | ||
|
|
69f7e8f606 | ||
|
|
7d1c7870ab | ||
|
|
04f3dba4b2 | ||
|
|
fe3aee8392 | ||
|
|
0d37c0d380 | ||
|
|
3f266a3eb5 | ||
|
|
4d54241b1c | ||
|
|
29aa07ba87 | ||
|
|
89e204e0e7 | ||
|
|
8760b0d020 | ||
|
|
9852b01f41 | ||
|
|
6eea1c470d | ||
|
|
aca67773df | ||
|
|
f0e53508a5 | ||
|
|
cad9768b5e | ||
|
|
4b6b097a87 | ||
|
|
d692077433 | ||
|
|
d020f58445 | ||
|
|
d6f076de42 | ||
|
|
23d4ef8f18 | ||
|
|
02691f21bd | ||
|
|
81e178bd01 | ||
|
|
013221a54d | ||
|
|
5ec6478b12 | ||
|
|
b1dd33a541 | ||
|
|
82403e78db | ||
|
|
ceb056263c | ||
|
|
79d47de611 | ||
|
|
f2467197fa | ||
|
|
f10a7084ba | ||
|
|
754db79b04 | ||
|
|
b38102e23a | ||
|
|
19a2eb0846 | ||
|
|
01101f6b78 | ||
|
|
63b539253f | ||
|
|
91c02bb8c3 | ||
|
|
f1984cd87a | ||
|
|
f7822088f8 | ||
|
|
b9c563eb5b | ||
|
|
c7b356aa73 | ||
|
|
4c4cb309b3 | ||
|
|
169d991dbc | ||
|
|
2b194c48cd | ||
|
|
99e863f997 | ||
|
|
78469f388a | ||
|
|
38ceec1e35 | ||
|
|
100420446e | ||
|
|
4d28c21a55 | ||
|
|
dd736356d3 | ||
|
|
8f766ace6f | ||
|
|
c10579bad1 | ||
|
|
d2f3b3a65b | ||
|
|
defcb91642 | ||
|
|
03bd92f07a | ||
|
|
3dc4804899 | ||
|
|
f76091d5cb | ||
|
|
6586dade7a | ||
|
|
28bdc5902d | ||
|
|
1014badebc | ||
|
|
7d8cc42cb1 | ||
|
|
ec1fa05ed5 | ||
|
|
248246a49e | ||
|
|
cf796bbe26 | ||
|
|
a473debb87 | ||
|
|
e16092b564 | ||
|
|
dd2f6d6742 | ||
|
|
d4a5e9ab32 | ||
|
|
f222dc2cb5 | ||
|
|
370753e672 | ||
|
|
a3671a707e | ||
|
|
eeb5d88e15 | ||
|
|
b702384e79 | ||
|
|
24f4aaa4f8 | ||
|
|
c8f7a77430 | ||
|
|
34ab9e66dd | ||
|
|
bb2205cd9f | ||
|
|
a5baa3f0cd | ||
|
|
be73b36391 | ||
|
|
08e7a8db2d | ||
|
|
9433eafccc | ||
|
|
482f71f3d8 | ||
|
|
9fe8be7c85 | ||
|
|
13f0a197cb | ||
|
|
b10c482919 | ||
|
|
06ce2d8cea | ||
|
|
e04cbdd547 | ||
|
|
968d97de04 | ||
|
|
5f9c36eb29 | ||
|
|
2dec55ecac | ||
|
|
ef5ee039b1 | ||
|
|
ee1fd3cc48 | ||
|
|
6d32129007 | ||
|
|
afa6b78fff | ||
|
|
619449e05c | ||
|
|
a5db75fd15 | ||
|
|
1430aaffe0 |
No files matched your search
@@ -259,7 +259,7 @@ jobs:
|
||||
PY
|
||||
|
||||
"$DOCKER_BINARY" run --rm --user 911:911 --platform '${{ matrix.platform }}' --entrypoint /venv/bin/python3 "$IMAGE" -c \
|
||||
'import json, pathlib, stat; cache_file = "/opt/archivebox/lib/env/derived.env"; raw = open(cache_file).read().strip(); records = json.loads(raw[len("ABXPKG_DERIVED_CACHE=\x27"):-1]); plans = [plan for record in records.values() for plan in record.get("script_exec_plans", {}).values()]; contexts = [json.loads(plan["run_context"])["base"]["abxpkg_env"] for plan in plans]; fingerprints = [fingerprint for plan in plans for fingerprint in plan["fingerprint"]]; actual = lambda fingerprint: (lambda path, value: {"path": str(path.resolve(strict=False)), "size": value.st_size, "mtime_ns": value.st_mtime_ns, "mode": stat.S_IMODE(value.st_mode), "euid": value.st_uid})(pathlib.Path(fingerprint["path"]), pathlib.Path(fingerprint["path"]).stat()); mismatches = [(fingerprint, actual(fingerprint)) for fingerprint in fingerprints if fingerprint != actual(fingerprint)]; assert plans and {plan.get("euid") for plan in plans} == {911}; assert all("ABXPKG_TMP_CACHE_DIR" not in context for context in contexts); assert not mismatches, mismatches[:3]'
|
||||
'import json, pathlib, stat; cache_file = "/opt/archivebox/lib/env/derived.env"; raw = open(cache_file).read().strip(); records = json.loads(raw[len("ABXPKG_DERIVED_CACHE=\x27"):-1]); projections = [projection for record in records.values() for projection in record.get("request_exec_projections", {}).values()]; plans = [projection["validation"] for projection in projections]; fingerprints = [fingerprint for plan in plans for fingerprint in plan["fingerprint"]]; actual = lambda fingerprint: (lambda path, value: {"path": str(path.resolve(strict=False)), "size": value.st_size, "mtime_ns": value.st_mtime_ns, "mode": stat.S_IMODE(value.st_mode), "euid": value.st_uid})(pathlib.Path(fingerprint["path"]), pathlib.Path(fingerprint["path"]).stat()); mismatches = [(fingerprint, actual(fingerprint)) for fingerprint in fingerprints if fingerprint != actual(fingerprint)]; assert projections and {plan.get("euid") for plan in plans} == {911}; assert all(not record.get("script_exec_plans") for record in records.values()); assert "ABXPKG_TMP_CACHE_DIR" not in json.dumps(projections); assert not mismatches, mismatches[:3]'
|
||||
|
||||
- name: Resolve real Docker CLI test dependencies through abxpkg
|
||||
if: matrix.platform == 'linux/amd64'
|
||||
@@ -384,7 +384,7 @@ jobs:
|
||||
'${{ runner.temp }}/archivebox-image.tar' \
|
||||
'${{ steps.docker_build.outputs.digest }}' \
|
||||
"$LOCAL_IMAGE_ID" \
|
||||
"$((780 * 1024 * 1024))" <<'PY'
|
||||
"$((910 * 1024 * 1024))" <<'PY'
|
||||
from hashlib import sha256
|
||||
import json
|
||||
import os
|
||||
|
||||
@@ -189,5 +189,33 @@ jobs:
|
||||
echo "Root-owned files remain in $root_tool_dir/data" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo mkdir "$root_tool_dir/external"
|
||||
sudo ln -s "$root_tool_dir/external" "$root_tool_dir/data/external-link"
|
||||
sudo env PATH="$root_tool_dir/bin:$PATH" \
|
||||
bash -c "cd '$root_tool_dir/data' && archivebox status"
|
||||
sudo stat -c %U "$root_tool_dir/external" | grep -qx root
|
||||
|
||||
sudo mkdir "$root_tool_dir/data-universal-init"
|
||||
sudo env PATH="$root_tool_dir/bin:$PATH" \
|
||||
bash -c "cd '$root_tool_dir/data-universal-init' && archivebox status --init"
|
||||
sudo stat -c %U "$root_tool_dir/data-universal-init" | grep -qx archivebox
|
||||
|
||||
sudo mkdir "$root_tool_dir/data-install"
|
||||
sudo env PATH="$root_tool_dir/bin:$PATH" \
|
||||
bash -c "cd '$root_tool_dir/data-install' && archivebox install --dry-run"
|
||||
sudo stat -c %U "$root_tool_dir/data-install" | grep -qx archivebox
|
||||
|
||||
sudo useradd --create-home archivebox-smoke-user
|
||||
smoke_user_group="$(id -gn archivebox-smoke-user)"
|
||||
sudo mkdir "$root_tool_dir/data-mixed-owner"
|
||||
sudo chown archivebox-smoke-user:root "$root_tool_dir/data-mixed-owner"
|
||||
sudo env PATH="$root_tool_dir/bin:$PATH" \
|
||||
bash -c "cd '$root_tool_dir/data-mixed-owner' && archivebox init"
|
||||
sudo stat -c %U:%G "$root_tool_dir/data-mixed-owner" | grep -qx "archivebox-smoke-user:$smoke_user_group"
|
||||
if sudo find "$root_tool_dir/data-mixed-owner" -xdev -user root -print -quit | grep -q .; then
|
||||
echo "Root-owned files remain in $root_tool_dir/data-mixed-owner" >&2
|
||||
exit 1
|
||||
fi
|
||||
sudo rm -rf "$root_tool_dir"
|
||||
fi
|
||||
@@ -38,7 +38,7 @@ uv run --project .. archivebox run
|
||||
Recommended CLI install:
|
||||
|
||||
```bash
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade 'git+https://github.com/ArchiveBox/ArchiveBox.git@dev'
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'git+https://github.com/ArchiveBox/ArchiveBox.git@dev'
|
||||
mkdir -p ~/archivebox/data
|
||||
cd ~/archivebox/data
|
||||
archivebox init --install
|
||||
|
||||
+7
-7
@@ -10,10 +10,10 @@
|
||||
# --build-context abx-plugins=../abx-plugins \
|
||||
# -t archivebox/abx-dl:dev
|
||||
# docker buildx build . -f Dockerfile \
|
||||
# --build-arg ABX_DL_IMAGE=archivebox/abx-dl:1.11.272 \
|
||||
# --build-arg ABX_DL_IMAGE=archivebox/abx-dl:1.12.158 \
|
||||
# -t archivebox:multistage
|
||||
|
||||
ARG ABX_DL_IMAGE=archivebox/abx-dl:1.11.272
|
||||
ARG ABX_DL_IMAGE=archivebox/abx-dl:1.12.158
|
||||
|
||||
FROM archivebox/sonic:1.4.9 AS sonic
|
||||
FROM ${ABX_DL_IMAGE} AS archivebox-runtime-base
|
||||
@@ -85,8 +85,8 @@ RUN cp /VERSION.txt /ABX-DL-VERSION.txt \
|
||||
&& echo "BUILD_START_TIME=$(date +"%Y-%m-%d %H:%M:%S %s") TZ=${TZ} LANG=${LANG}" \
|
||||
&& uname -a \
|
||||
&& sed -n '1,7p' /etc/os-release \
|
||||
&& abxpkg load --binproviders=env node \
|
||||
&& abxpkg load --binproviders=env uv \
|
||||
&& setpriv --reuid="$ARCHIVEBOX_USER" --regid="$ARCHIVEBOX_USER" --init-groups abxpkg load --binproviders=env node \
|
||||
&& setpriv --reuid="$ARCHIVEBOX_USER" --regid="$ARCHIVEBOX_USER" --init-groups abxpkg load --binproviders=env uv \
|
||||
) | tee -a /VERSION.txt
|
||||
|
||||
FROM archivebox-runtime-base AS archivebox-builder
|
||||
@@ -155,7 +155,7 @@ PY
|
||||
--no-install-workspace \
|
||||
--no-sources
|
||||
ABXPKG_NO_CACHE=True abxpkg env --install --binproviders=env,apt --lib="$ABXPKG_LIB_DIR" --overrides='{"apt":{"install_args":["binutils"]}}' strip >/dev/null
|
||||
"$ABXPKG_LIB_DIR/env/bin/find" /venv/lib/python3.*/site-packages -type f -name '*.so' -print0 > /tmp/archivebox-native-libraries
|
||||
/usr/bin/find /venv/lib/python3.*/site-packages -type f -name '*.so' -print0 > /tmp/archivebox-native-libraries
|
||||
while IFS= read -r -d '' native_library; do
|
||||
magic=''
|
||||
if IFS= read -r -N 4 magic < "$native_library" && [[ "$magic" == $'\x7fELF' ]]; then
|
||||
@@ -166,7 +166,7 @@ rm -f /tmp/archivebox-native-libraries
|
||||
rm -f /venv/bin/uv /venv/bin/uvx
|
||||
abxpkg run --binproviders=env --lib="$ABXPKG_LIB_DIR" apt-get purge -y binutils build-essential gcc libldap2-dev libsasl2-dev libssl-dev
|
||||
abxpkg run --binproviders=env --lib="$ABXPKG_LIB_DIR" apt-get autoremove -y
|
||||
"$ABXPKG_LIB_DIR/env/bin/find" "$ABXPKG_LIB_DIR/env/bin" -maxdepth 1 -type l -name strip -delete
|
||||
/usr/bin/find "$ABXPKG_LIB_DIR/env/bin" -maxdepth 1 -type l -name strip -delete
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
|
||||
@@ -221,7 +221,7 @@ RUN echo "[*] Setting up $ARCHIVEBOX_USER user uid=${DEFAULT_ARCHIVEBOX_UID}..."
|
||||
&& usermod --append --groups audio,video "$ARCHIVEBOX_USER" \
|
||||
&& [[ "$(id -u "$ARCHIVEBOX_USER")" == "$DEFAULT_ARCHIVEBOX_UID" ]] || usermod -u "$DEFAULT_ARCHIVEBOX_UID" "$ARCHIVEBOX_USER" \
|
||||
&& [[ "$(id -g "$ARCHIVEBOX_USER")" == "$DEFAULT_ARCHIVEBOX_GID" ]] || groupmod -g "$DEFAULT_ARCHIVEBOX_GID" "$ARCHIVEBOX_USER" \
|
||||
&& abxpkg load --binproviders=env sonic | tee -a /VERSION.txt \
|
||||
&& setpriv --reuid="$ARCHIVEBOX_USER" --regid="$ARCHIVEBOX_USER" --init-groups abxpkg load --binproviders=env sonic | tee -a /VERSION.txt \
|
||||
&& install -d -o "$DEFAULT_ARCHIVEBOX_UID" -g "$DEFAULT_ARCHIVEBOX_GID" "$DATA_DIR" "$TMP_DIR" "$CONFIG_DIR" "$ABXPKG_LIB_DIR" "$XDG_CACHE_HOME" "$PLAYWRIGHT_BROWSERS_PATH" \
|
||||
&& install -d -o "$DEFAULT_ARCHIVEBOX_UID" -g "$DEFAULT_ARCHIVEBOX_GID" "/home/$ARCHIVEBOX_USER" \
|
||||
&& chown "$DEFAULT_ARCHIVEBOX_UID:$DEFAULT_ARCHIVEBOX_GID" "$DATA_DIR" "$TMP_DIR" \
|
||||
|
||||
@@ -23,7 +23,7 @@ Without active preservation effort, everything on the internet eventually disapp
|
||||
*ArchiveBox is an open source tool that lets organizations & individuals archive both public & private web content while retaining control over their data. It can be used to save copies of bookmarks, preserve evidence for legal cases, backup photos from FB/Insta/Flickr or media from YT/Soundcloud/etc., save research papers, and more...*
|
||||
<br/>
|
||||
|
||||
> ➡️ Get ArchiveBox with `uv tool install --python 3.13 --prerelease allow --upgrade archivebox` on [Linux](#quickstart)/[macOS](#quickstart), or via **[Docker](#quickstart)** ⭐️ on any OS.
|
||||
> ➡️ Get ArchiveBox with `uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'` on [Linux](#quickstart)/[macOS](#quickstart), or via **[Docker](#quickstart)** ⭐️ on Linux/macOS.
|
||||
|
||||
*Once installed, you can interact with it through the: [Browser Extension](https://github.com/ArchiveBox/archivebox-browser-extension), [CLI](#usage), [self-hosted web interface](https://github.com/ArchiveBox/ArchiveBox/wiki/Publishing-Your-Archive), [Python API](https://github.com/ArchiveBox/ArchiveBox/wiki/Usage#python-shell-usage), or [filesystem](#static-archive-exporting).*
|
||||
|
||||
@@ -70,27 +70,26 @@ The goal is to sleep soundly knowing the part of the internet you care about wil
|
||||
<br/>
|
||||
<pre lang="bash"><code style="white-space: pre-line"># Option A: Get ArchiveBox with Docker Compose (recommended):
|
||||
mkdir -p ~/archivebox/data && cd ~/archivebox
|
||||
curl -fsSL 'https://docker-compose.archivebox.io' > docker-compose.yml # edit options in this file as-needed
|
||||
curl -fsSL 'https://docker-compose.archivebox.io' > docker-compose.yml
|
||||
docker compose pull
|
||||
docker compose run --rm archivebox init
|
||||
docker compose run --rm archivebox install
|
||||
docker compose run --rm archivebox manage createsuperuser
|
||||
docker compose up -d --wait # initializes new collections automatically
|
||||
docker compose exec archivebox archivebox manage createsuperuser # create the first Web UI user
|
||||
# docker compose run --rm archivebox add 'https://example.com'
|
||||
# docker compose run --rm archivebox help
|
||||
# docker compose up
|
||||
<br/>
|
||||
<br/>
|
||||
# Option B: Or use it as a plain Docker container:
|
||||
mkdir -p ~/archivebox/data && cd ~/archivebox/data
|
||||
docker run -it -v $PWD:/data archivebox/archivebox:dev init
|
||||
docker run -it -v $PWD:/data archivebox/archivebox:dev install
|
||||
docker run --rm -it -v "$PWD:/data" archivebox/archivebox:dev init
|
||||
docker run --rm -it -v "$PWD:/data" archivebox/archivebox:dev install
|
||||
docker run --rm -it -v "$PWD:/data" archivebox/archivebox:dev manage createsuperuser
|
||||
docker run -d --name archivebox -v "$PWD:/data" -p 8000:8000 archivebox/archivebox:dev
|
||||
# docker run -it -v $PWD:/data archivebox/archivebox:dev add 'https://example.com'
|
||||
# docker run -it -v $PWD:/data archivebox/archivebox:dev help
|
||||
# docker run -it -v $PWD:/data -p 8000:8000 archivebox/archivebox:dev
|
||||
<br/>
|
||||
<br/>
|
||||
# Option C: Or install it with uv (see Quickstart below for apt, brew, and more)
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
mkdir -p ~/archivebox/data && cd ~/archivebox/data
|
||||
archivebox init
|
||||
archivebox install
|
||||
@@ -176,7 +175,7 @@ ArchiveBox is free for everyone to self-host, but we also provide support, secur
|
||||
#### ✳️ Easy Setup
|
||||
|
||||
<details>
|
||||
<summary><b><img src="https://user-images.githubusercontent.com/511499/117447182-29758200-af0b-11eb-97bd-58723fee62ab.png" alt="Docker" height="28px" align="top"/> <code>docker-compose</code></b> (macOS/Linux/Windows) <b>👈 recommended</b> <i>(click to expand)</i></summary>
|
||||
<summary><b><img src="https://user-images.githubusercontent.com/511499/117447182-29758200-af0b-11eb-97bd-58723fee62ab.png" alt="Docker" height="28px" align="top"/> <code>docker-compose</code></b> (macOS/Linux) <b>👈 recommended</b> <i>(click to expand)</i></summary>
|
||||
<br/>
|
||||
<i>👍 Docker Compose is recommended for the easiest install/update UX + best security + all <a href="#dependencies">extras</a> out-of-the-box.</i>
|
||||
<br/><br/>
|
||||
@@ -184,21 +183,18 @@ ArchiveBox is free for everyone to self-host, but we also provide support, secur
|
||||
<li>Install <a href="https://docs.docker.com/get-docker/">Docker</a> on your system (if not already installed).</li>
|
||||
<li>Download the <a href="https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/dev/docker-compose.yml" download><code>docker-compose.yml</code></a> file into a new empty directory (can be anywhere).
|
||||
<pre lang="bash"><code style="white-space: pre-line">mkdir -p ~/archivebox/data && cd ~/archivebox
|
||||
# Read and edit docker-compose.yml options as-needed after downloading
|
||||
curl -fsSL 'https://docker-compose.archivebox.io' > docker-compose.yml
|
||||
docker compose pull
|
||||
</code></pre></li>
|
||||
<li>Initialize the collection, then create an admin user (or set ADMIN_USERNAME/ADMIN_PASSWORD in docker-compose.yml)
|
||||
<pre lang="bash"><code style="white-space: pre-line">docker compose run --rm archivebox init
|
||||
docker compose run --rm archivebox install
|
||||
docker compose run --rm archivebox manage createsuperuser
|
||||
<li>Start the server, which initializes a new collection automatically, then create the first admin user.
|
||||
<pre lang="bash"><code style="white-space: pre-line">docker compose up -d --wait
|
||||
docker compose exec archivebox archivebox manage createsuperuser
|
||||
</code></pre></li>
|
||||
<li>Next steps: Start the server then login to the Web UI <a href="http://archivebox.localhost:8000">http://archivebox.localhost:8000</a> ⇢ Admin.
|
||||
<pre lang="bash"><code style="white-space: pre-line">docker compose up -d
|
||||
# completely optional, CLI can always be used without running a server
|
||||
# docker compose run --rm [-T] archivebox [subcommand] [--help]
|
||||
docker compose run --rm archivebox add 'https://example.com'
|
||||
docker compose run --rm archivebox help
|
||||
<li>Next steps: Log in to the Admin UI at <a href="http://admin.archivebox.localhost:8000">http://admin.archivebox.localhost:8000</a>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">
|
||||
# run CLI commands inside the server container started above
|
||||
docker compose exec archivebox archivebox add 'https://example.com'
|
||||
docker compose exec archivebox archivebox help
|
||||
</code></pre>
|
||||
<i>For more info, see <a href="https://github.com/ArchiveBox/ArchiveBox/wiki/Install#option-a-docker--docker-compose-setup-%EF%B8%8F">Install: Docker Compose</a> in the Wiki. ➡️</i>
|
||||
</li>
|
||||
@@ -209,17 +205,17 @@ See <a href="#%EF%B8%8F-cli-usage">below</a> for more usage examples using the C
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b><img src="https://user-images.githubusercontent.com/511499/117447182-29758200-af0b-11eb-97bd-58723fee62ab.png" alt="Docker" height="28px" align="top"/> <code>docker run</code></b> (macOS/Linux/Windows)</summary>
|
||||
<summary><b><img src="https://user-images.githubusercontent.com/511499/117447182-29758200-af0b-11eb-97bd-58723fee62ab.png" alt="Docker" height="28px" align="top"/> <code>docker run</code></b> (macOS/Linux)</summary>
|
||||
<br/>
|
||||
<ol>
|
||||
<li>Install <a href="https://docs.docker.com/get-docker/">Docker</a> on your system (if not already installed).</li>
|
||||
<li>Create a new empty directory and initialize your collection (can be anywhere).
|
||||
<pre lang="bash"><code style="white-space: pre-line">mkdir -p ~/archivebox/data && cd ~/archivebox/data
|
||||
docker run -v $PWD:/data -it archivebox/archivebox:dev init
|
||||
docker run -v $PWD:/data -it archivebox/archivebox:dev install
|
||||
docker run --rm -v $PWD:/data -it archivebox/archivebox:dev init
|
||||
docker run --rm -v $PWD:/data -it archivebox/archivebox:dev install
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>Optional: Start the server then login to the Web UI <a href="http://archivebox.localhost:8000">http://archivebox.localhost:8000</a> ⇢ Admin.
|
||||
<li>Optional: Start the server then log in to the Admin UI at <a href="http://admin.archivebox.localhost:8000">http://admin.archivebox.localhost:8000</a>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">docker run -v $PWD:/data -p 8000:8000 archivebox/archivebox:dev
|
||||
# completely optional, CLI can always be used without running a server
|
||||
# docker run -v $PWD:/data -it archivebox/archivebox:dev [subcommand] [--help]
|
||||
@@ -264,7 +260,7 @@ See <a href="https://docs.sweeting.me/s/against-curl-sh">"Against curl | sh as a
|
||||
|
||||
<li>Install <a href="https://docs.astral.sh/uv/getting-started/installation/">uv</a> on your system (if not already installed).</li>
|
||||
<li>Install the ArchiveBox package using <code>uv</code>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
<pre lang="bash"><code style="white-space: pre-line">uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
archivebox version
|
||||
</code></pre>
|
||||
<i>See the <a href="https://github.com/ArchiveBox/ArchiveBox/wiki/Install">Install: Bare Metal</a> Wiki for full install instructions for each OS...</i>
|
||||
@@ -275,7 +271,7 @@ archivebox init # initialize a new collection
|
||||
archivebox install # install all the runtime dependencies (e.g. chrome, single-file, yt-dlp, etc.)
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>Create an admin account, then optionally start the server and log in to the Web UI at <a href="http://archivebox.localhost:8000">http://archivebox.localhost:8000</a> ⇢ Admin.
|
||||
<li>Create an admin account, then optionally start the server and log in to the Admin UI at <a href="http://admin.archivebox.localhost:8000">http://admin.archivebox.localhost:8000</a>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">archivebox manage createsuperuser
|
||||
archivebox server 0.0.0.0:8000
|
||||
# completely optional, CLI can always be used without running a server
|
||||
@@ -312,7 +308,7 @@ archivebox add 'https://example.com'
|
||||
</code></pre>
|
||||
<br/>
|
||||
</li>
|
||||
<li>Create an admin account, then optionally start the server and log in to the Web UI at <a href="http://archivebox.localhost:8000">http://archivebox.localhost:8000</a> ⇢ Admin.
|
||||
<li>Create an admin account, then optionally start the server and log in to the Admin UI at <a href="http://admin.archivebox.localhost:8000">http://admin.archivebox.localhost:8000</a>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">archivebox manage createsuperuser
|
||||
archivebox server 0.0.0.0:8000
|
||||
# completely optional, CLI can always be used without running a server
|
||||
@@ -336,7 +332,7 @@ Run Homebrew as your normal non-root user on both macOS and Linux; do not use <c
|
||||
<pre lang="bash"><code style="white-space: pre-line">brew tap archivebox/archivebox
|
||||
brew trust archivebox/archivebox
|
||||
brew install archivebox
|
||||
archivebox version # make sure all dependencies are installed
|
||||
archivebox version # verify the installed version
|
||||
</code></pre>
|
||||
<i>See the <a href="https://github.com/ArchiveBox/ArchiveBox/wiki/Install#option-c-bare-metal-setup">Install: Bare Metal</a> Wiki for more granular instructions for macOS... ➡️</i>
|
||||
</li>
|
||||
@@ -346,7 +342,7 @@ archivebox init
|
||||
archivebox install
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>Create an admin account, then optionally start the server and log in to the Web UI at <a href="http://archivebox.localhost:8000">http://archivebox.localhost:8000</a> ⇢ Admin.
|
||||
<li>Create an admin account, then optionally start the server and log in to the Admin UI at <a href="http://admin.archivebox.localhost:8000">http://admin.archivebox.localhost:8000</a>.
|
||||
<pre lang="bash"><code style="white-space: pre-line">archivebox manage createsuperuser
|
||||
archivebox server 0.0.0.0:8000
|
||||
# completely optional, CLI can always be used without running a server
|
||||
@@ -405,7 +401,7 @@ See <a href="#%EF%B8%8F-cli-usage">below</a> for usage examples using the CLI, W
|
||||
<summary><img src="https://github.com/ArchiveBox/ArchiveBox/assets/511499/0c46e949-00fe-49c8-a613-ee14501c014c" alt="Self-hosting Platforms" height="28px" align="top"/><b> TrueNAS / UNRAID / YunoHost / Cloudron / etc.</b> (self-hosting solutions)</summary>
|
||||
<br/>
|
||||
|
||||
> *Warning: These are contributed by external volunteers and may lag behind the official `pip` channel.*
|
||||
> *Warning: These are contributed by external volunteers and may lag behind the official Docker and `uv` channels.*
|
||||
|
||||
<ul>
|
||||
<li><s>TrueNAS: <a href="https://truecharts.org/charts/stable/archivebox/">Official ArchiveBox TrueChart</a> / <a href="https://dev.to/finloop/setting-up-archivebox-on-truenas-scale-1788">Custom App Guide</a></s> (<a href="https://truecharts.org/news/scale-deprecation/">TrueCharts is discontinued</a>, wait for <a href="https://forums.truenas.com/t/the-future-of-electric-eel-and-apps/5409/">Electric Eel</a>)</li>
|
||||
@@ -584,12 +580,12 @@ find ./archive/users -path '*/snapshots/*/*/*/index.html' # inspect snapshot da
|
||||
<details>
|
||||
<summary><b>🖥 Web UI & API Usage</b></summary>
|
||||
<pre lang="bash"><code style="white-space: pre-line">
|
||||
# Start the server on bare metal (pip/apt/brew/etc):
|
||||
# Start the server on bare metal (uv/apt/brew):
|
||||
archivebox manage createsuperuser # create a new admin user via CLI
|
||||
archivebox server 0.0.0.0:8000 # start the server
|
||||
<br/>
|
||||
# Or with Docker Compose:
|
||||
nano docker-compose.yml # setup initial ADMIN_USERNAME & ADMIN_PASSWORD
|
||||
docker compose run --rm archivebox manage createsuperuser
|
||||
docker compose up # start the server
|
||||
<br/>
|
||||
# Or with a Docker container:
|
||||
|
||||
@@ -303,33 +303,29 @@ def version(
|
||||
continue
|
||||
plugin_requested = bool(requested_names)
|
||||
plugin_enabled = plugin_name in enabled_plugin_names
|
||||
logical_records = get_required_binary_requests(
|
||||
binary_records = get_required_binary_requests(
|
||||
plugin,
|
||||
plugin.config.required_binaries,
|
||||
overrides=runtime_config,
|
||||
derived_overrides=derived_config,
|
||||
run_output_dir=CONSTANTS.DATA_DIR,
|
||||
)
|
||||
actual_records = get_required_binary_requests(
|
||||
plugin,
|
||||
plugin.config.required_binaries,
|
||||
overrides=runtime_config,
|
||||
derived_overrides=derived_config,
|
||||
run_output_dir=CONSTANTS.DATA_DIR,
|
||||
logical_names=False,
|
||||
)
|
||||
for logical_record, actual_record in zip(logical_records, actual_records, strict=False):
|
||||
logical_name = str(logical_record["name"])
|
||||
actual_name = str(actual_record["name"])
|
||||
display_name = Path(actual_name).expanduser().name if ("/" in actual_name or actual_name.startswith("~")) else logical_name
|
||||
for binary_record in binary_records:
|
||||
actual_name = str(binary_record["name"])
|
||||
logical_name = (
|
||||
Path(actual_name).expanduser().name
|
||||
if ("/" in actual_name or "\\" in actual_name or actual_name.startswith("~"))
|
||||
else actual_name
|
||||
)
|
||||
display_name = logical_name
|
||||
if not plugin_requested and not binary_is_requested(logical_name, actual_name, display_name):
|
||||
continue
|
||||
if not plugin_enabled and not requested_names:
|
||||
continue
|
||||
if _binary_record_matches_runtime(db_binaries.get(logical_name), config.ABXPKG_LIB_DIR):
|
||||
continue
|
||||
signature = json.dumps(actual_record, sort_keys=True, default=str)
|
||||
declared_binary_specs.setdefault(signature, actual_record)
|
||||
signature = json.dumps(binary_record, sort_keys=True, default=str)
|
||||
declared_binary_specs.setdefault(signature, binary_record)
|
||||
|
||||
loaded_binaries: dict[str, BinaryEvent | None] = {}
|
||||
if declared_binary_specs:
|
||||
@@ -365,25 +361,21 @@ def version(
|
||||
continue
|
||||
plugin_requested = bool(requested_names)
|
||||
plugin_enabled = plugin_name in enabled_plugin_names
|
||||
logical_records = get_required_binary_requests(
|
||||
binary_records = get_required_binary_requests(
|
||||
plugin,
|
||||
plugin.config.required_binaries,
|
||||
overrides=runtime_config,
|
||||
derived_overrides=derived_config,
|
||||
run_output_dir=CONSTANTS.DATA_DIR,
|
||||
)
|
||||
actual_records = get_required_binary_requests(
|
||||
plugin,
|
||||
plugin.config.required_binaries,
|
||||
overrides=runtime_config,
|
||||
derived_overrides=derived_config,
|
||||
run_output_dir=CONSTANTS.DATA_DIR,
|
||||
logical_names=False,
|
||||
)
|
||||
for logical_record, actual_record in zip(logical_records, actual_records, strict=False):
|
||||
logical_name = str(logical_record["name"])
|
||||
actual_name = str(actual_record["name"])
|
||||
display_name = Path(actual_name).expanduser().name if ("/" in actual_name or actual_name.startswith("~")) else logical_name
|
||||
for binary_record in binary_records:
|
||||
actual_name = str(binary_record["name"])
|
||||
logical_name = (
|
||||
Path(actual_name).expanduser().name
|
||||
if ("/" in actual_name or "\\" in actual_name or actual_name.startswith("~"))
|
||||
else actual_name
|
||||
)
|
||||
display_name = logical_name
|
||||
if not plugin_requested and not binary_is_requested(logical_name, actual_name, display_name):
|
||||
continue
|
||||
|
||||
@@ -401,7 +393,7 @@ def version(
|
||||
# providers the current collection will never execute.
|
||||
continue
|
||||
else:
|
||||
loaded = loaded_binaries[json.dumps(actual_record, sort_keys=True, default=str)]
|
||||
loaded = loaded_binaries[json.dumps(binary_record, sort_keys=True, default=str)]
|
||||
abspath = loaded.abspath if loaded is not None else ""
|
||||
version_str = str(loaded.version or "unknown")[:15] if loaded is not None else "unknown"
|
||||
provider = str(loaded.binprovider or "env")[:8] if loaded is not None else "env"
|
||||
|
||||
@@ -59,10 +59,10 @@ def setup_django(check_db=False) -> None:
|
||||
with SudoPermission(uid=0):
|
||||
# running as root is a special case where it's ok to be a bit slower
|
||||
# make sure data dir is always owned by the correct user
|
||||
os.chown(CONSTANTS.DATA_DIR, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP)
|
||||
os.chown(CONSTANTS.DATA_DIR, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, follow_symlinks=False)
|
||||
if CONSTANTS.DATA_DIR.exists():
|
||||
for child in CONSTANTS.DATA_DIR.iterdir():
|
||||
os.chown(child, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP)
|
||||
os.chown(child, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, follow_symlinks=False)
|
||||
|
||||
# Suppress the "database access during app initialization" warning
|
||||
# This warning can be triggered during django.setup() but is safe to ignore
|
||||
|
||||
@@ -32,11 +32,13 @@ def select_archivebox_user(
|
||||
data_dir_gid: int,
|
||||
account_uid: int | None,
|
||||
account_gid: int | None,
|
||||
data_dir_owner_gid: int | None = None,
|
||||
data_dir_owner_exists: bool = True,
|
||||
) -> tuple[int, int]:
|
||||
if running_uid == 0:
|
||||
if data_dir_uid != 0 and data_dir_owner_exists:
|
||||
return data_dir_uid, data_dir_gid
|
||||
owner_gid = data_dir_owner_gid if data_dir_gid == 0 and data_dir_owner_gid is not None else data_dir_gid
|
||||
return data_dir_uid, owner_gid
|
||||
if account_uid is not None and account_gid is not None:
|
||||
return account_uid, account_gid
|
||||
|
||||
@@ -57,9 +59,10 @@ except PermissionError:
|
||||
DATA_DIR_GID = 0
|
||||
|
||||
try:
|
||||
pwd.getpwuid(DATA_DIR_UID)
|
||||
DATA_DIR_OWNER_GID = pwd.getpwuid(DATA_DIR_UID).pw_gid
|
||||
DATA_DIR_OWNER_EXISTS = True
|
||||
except KeyError:
|
||||
DATA_DIR_OWNER_GID = None
|
||||
DATA_DIR_OWNER_EXISTS = False
|
||||
|
||||
DEFAULT_UID = 911
|
||||
@@ -138,6 +141,7 @@ ARCHIVEBOX_USER, ARCHIVEBOX_GROUP = select_archivebox_user(
|
||||
sudo_gid=SUDO_GID,
|
||||
data_dir_uid=DATA_DIR_UID,
|
||||
data_dir_gid=DATA_DIR_GID,
|
||||
data_dir_owner_gid=DATA_DIR_OWNER_GID,
|
||||
account_uid=ARCHIVEBOX_ACCOUNT.pw_uid if ARCHIVEBOX_ACCOUNT is not None else None,
|
||||
account_gid=ARCHIVEBOX_ACCOUNT.pw_gid if ARCHIVEBOX_ACCOUNT is not None else None,
|
||||
data_dir_owner_exists=DATA_DIR_OWNER_EXISTS,
|
||||
@@ -205,7 +209,7 @@ def root_data_dir_handoff_paths(data_dir: Path, argv: list[str]) -> tuple[Path,
|
||||
except (FileNotFoundError, PermissionError):
|
||||
return ()
|
||||
|
||||
is_init = "init" in argv[1:]
|
||||
is_init = any(arg in ("init", "install", "--init", "--quick-init") for arg in argv[1:])
|
||||
collection_exists = any((data_dir / marker).exists() for marker in (".archivebox_id", "ArchiveBox.conf", "index.sqlite3"))
|
||||
if not collection_exists and not (is_init and not children):
|
||||
return ()
|
||||
@@ -297,7 +301,8 @@ def drop_privileges():
|
||||
# archivebox service account when the data dir is root-owned.
|
||||
if os.geteuid() == 0 and ARCHIVEBOX_USER != 0 and ARCHIVEBOX_USER_EXISTS:
|
||||
pw_record = pwd.getpwuid(ARCHIVEBOX_USER)
|
||||
os.initgroups(pw_record.pw_name, ARCHIVEBOX_GROUP)
|
||||
supplemental_gid = ARCHIVEBOX_ACCOUNT.pw_gid if ARCHIVEBOX_ACCOUNT is not None else ARCHIVEBOX_GROUP
|
||||
os.initgroups(pw_record.pw_name, supplemental_gid)
|
||||
if os.getegid() != ARCHIVEBOX_GROUP:
|
||||
os.setegid(ARCHIVEBOX_GROUP)
|
||||
if os.geteuid() != ARCHIVEBOX_USER:
|
||||
|
||||
@@ -1801,7 +1801,11 @@ class HealthCheckView(View):
|
||||
"""
|
||||
Handle a GET request
|
||||
"""
|
||||
return HttpResponse("OK", content_type="text/plain", status=200)
|
||||
response = HttpResponse("OK", content_type="text/plain", status=200)
|
||||
response["Access-Control-Allow-Origin"] = "*"
|
||||
response["Access-Control-Expose-Headers"] = "X-ArchiveBox-Health"
|
||||
response["X-ArchiveBox-Health"] = "OK"
|
||||
return response
|
||||
|
||||
|
||||
@render_with_table_view
|
||||
|
||||
+21
-14
@@ -232,12 +232,21 @@ def iter_query_search_ids(
|
||||
|
||||
backends = get_available_backends()
|
||||
configured_backend = normalize_search_backend_name(config.SEARCH_BACKEND_ENGINE)
|
||||
fallback_enabled = search_mode_base == "deep" and (not forced_backend or forced_backend == configured_backend == "sonic")
|
||||
if forced_backend:
|
||||
if forced_backend not in backends:
|
||||
raise RuntimeError(
|
||||
f'Search backend "{forced_backend}" not found. Available backends: {list(backends) or "none"}',
|
||||
)
|
||||
backend_names = [forced_backend]
|
||||
backend_names = (
|
||||
[
|
||||
forced_backend,
|
||||
*(name for name in backends if name not in {forced_backend, "ripgrep"}),
|
||||
*(["ripgrep"] if "ripgrep" in backends and forced_backend != "ripgrep" else []),
|
||||
]
|
||||
if fallback_enabled
|
||||
else [forced_backend]
|
||||
)
|
||||
elif search_mode_base == "deep":
|
||||
backend_names = [
|
||||
*([configured_backend] if configured_backend in backends and configured_backend != "ripgrep" else []),
|
||||
@@ -252,18 +261,6 @@ def iter_query_search_ids(
|
||||
get_backend()
|
||||
return
|
||||
|
||||
if "sonic" in backend_names:
|
||||
import sys
|
||||
from contextlib import redirect_stdout
|
||||
|
||||
from archivebox.core.takeover_util import ensure_daemon_stack
|
||||
|
||||
# Search commands can produce structured stdout such as --csv/--json.
|
||||
# Daemon bootstrap progress is diagnostic output and must not corrupt
|
||||
# that user-facing data stream.
|
||||
with redirect_stdout(sys.stderr):
|
||||
ensure_daemon_stack(reason="search query")
|
||||
|
||||
errors: list[Exception] = []
|
||||
successful_backends = 0
|
||||
seen: set[str] = set()
|
||||
@@ -271,6 +268,16 @@ def iter_query_search_ids(
|
||||
for backend_name in backend_names:
|
||||
backend = backends[backend_name]
|
||||
try:
|
||||
if backend_name == "sonic":
|
||||
import sys
|
||||
from contextlib import redirect_stdout
|
||||
|
||||
from archivebox.core.takeover_util import ensure_daemon_stack
|
||||
|
||||
# Search commands can produce structured stdout such as
|
||||
# --csv/--json. Keep daemon diagnostics on stderr.
|
||||
with redirect_stdout(sys.stderr):
|
||||
ensure_daemon_stack(reason="search query")
|
||||
with search_backend_env(config=config):
|
||||
if backend_name == "ripgrep":
|
||||
ids = backend.iter_search(query, search_mode=search_mode_base)
|
||||
@@ -286,7 +293,7 @@ def iter_query_search_ids(
|
||||
successful_backends += 1
|
||||
except Exception as err:
|
||||
errors.append(err)
|
||||
if search_mode_base != "deep" or forced_backend:
|
||||
if not fallback_enabled:
|
||||
raise
|
||||
except Exception as err:
|
||||
stderr()
|
||||
|
||||
@@ -538,7 +538,4 @@ async def _mark_binary_queued(binary) -> None:
|
||||
def _persisted_overrides_for_request(request: BinaryRequestEvent | None) -> dict[str, Any]:
|
||||
if request is None:
|
||||
return {}
|
||||
raw_overrides = request.extra_context.get("raw_overrides")
|
||||
if isinstance(raw_overrides, Mapping):
|
||||
return dict(raw_overrides)
|
||||
return dict(request.overrides or {})
|
||||
@@ -46,7 +46,6 @@ from abx_dl.orchestrator import (
|
||||
setup_services as setup_abx_services,
|
||||
)
|
||||
from abx_dl.services.process_service import ProcessService as HookProcessService
|
||||
from abx_dl.services.binary_service import split_abxpkg_binary_request_overrides
|
||||
from abx_dl.services.snapshot_service import SnapshotService as HookSnapshotService
|
||||
from abx_dl.cli import LiveBusUI
|
||||
from abxbus import BaseEvent
|
||||
@@ -873,13 +872,10 @@ class CrawlRunner:
|
||||
snapshot_phase_timeout=crawl_setup_phase_timeout,
|
||||
snapshot_cleanup_phase_timeout=crawl_setup_phase_timeout,
|
||||
crawl_cleanup_phase_timeout=crawl_setup_phase_timeout,
|
||||
persist_derived=False,
|
||||
auto_install=True,
|
||||
emit_jsonl=False,
|
||||
abort_requested=self.crawl_is_cancelled,
|
||||
MachineService=None,
|
||||
PluginBinariesService=None,
|
||||
BinaryCacheService=None,
|
||||
BinaryService=None,
|
||||
ProcessService=None,
|
||||
ArchiveResultService=None,
|
||||
@@ -1338,7 +1334,7 @@ async def _run_binary(binary_id: str) -> None:
|
||||
process_service = PersistedProcessService(bus)
|
||||
binary_process_service = ArchiveBoxBinaryService(bus)
|
||||
BinaryCacheService(bus, backend=ArchiveBoxDBBinaryCacheBackend())
|
||||
BinaryService(bus)
|
||||
BinaryService(bus, lib_dir=Path(config["ABXPKG_LIB_DIR"]))
|
||||
TagService(bus)
|
||||
ArchiveResultService(bus)
|
||||
MachineService(bus)
|
||||
@@ -1350,29 +1346,24 @@ async def _run_binary(binary_id: str) -> None:
|
||||
crawl_start_enabled=False,
|
||||
snapshot_cleanup_enabled=False,
|
||||
crawl_cleanup_enabled=False,
|
||||
persist_derived=False,
|
||||
auto_install=True,
|
||||
emit_jsonl=False,
|
||||
BinaryCacheService=None,
|
||||
BinaryService=None,
|
||||
)
|
||||
await _emit_machine_config(bus, config=config, derived_config=derived_config)
|
||||
|
||||
native_overrides, override_extra_context = split_abxpkg_binary_request_overrides(binary.overrides or None)
|
||||
|
||||
try:
|
||||
await bus.emit(
|
||||
BinaryRequestEvent(
|
||||
name=binary.name,
|
||||
binproviders=binary.binproviders,
|
||||
overrides=native_overrides or None,
|
||||
overrides=binary.overrides or None,
|
||||
extra_context={
|
||||
"plugin_name": "archivebox",
|
||||
"hook_name": "archivebox_binary_run",
|
||||
"output_dir": str(binary.output_dir),
|
||||
"binary_id": str(binary.id),
|
||||
"machine_id": str(binary.machine_id),
|
||||
**override_extra_context,
|
||||
},
|
||||
),
|
||||
).now(first_result=True)
|
||||
@@ -1952,8 +1943,6 @@ async def _run_install(plugin_names: list[str] | None = None) -> None:
|
||||
derived_config_overrides=derived_config,
|
||||
emit_jsonl=False,
|
||||
bus=bus,
|
||||
MachineService=None,
|
||||
BinaryCacheService=None,
|
||||
BinaryService=None,
|
||||
)
|
||||
finally:
|
||||
@@ -1962,8 +1951,6 @@ async def _run_install(plugin_names: list[str] | None = None) -> None:
|
||||
finally:
|
||||
await bus.destroy(clear=False)
|
||||
bus_destroyed = True
|
||||
if live_ui is not None:
|
||||
live_ui.print_summary(output_dir=output_dir, archive_results=[])
|
||||
finally:
|
||||
if not bus_destroyed:
|
||||
await bus.destroy(clear=False)
|
||||
|
||||
@@ -73,8 +73,13 @@
|
||||
<br/>
|
||||
To create a new admin user or reset a password, run:<br/>
|
||||
<pre>
|
||||
cd data/ # run commands inside your data folder
|
||||
archivebox manage createsuperuser <username>
|
||||
# Docker Compose
|
||||
docker compose exec archivebox archivebox manage createsuperuser --username <username>
|
||||
docker compose exec archivebox archivebox manage changepassword <username>
|
||||
|
||||
# Native installs (run inside your data folder)
|
||||
cd data/
|
||||
archivebox manage createsuperuser --username <username>
|
||||
archivebox manage changepassword <username>
|
||||
</pre>
|
||||
<pre>
|
||||
|
||||
@@ -1577,7 +1577,7 @@
|
||||
<span class="tag-pill permission-pill">{{ snapshot_permissions_icon }} {{ snapshot_permissions }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<a href="#" class="header-toggle header-toggle-trigger">▾</a>
|
||||
<a href="#" class="header-toggle">▾</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-col header-meta">
|
||||
@@ -2120,7 +2120,8 @@
|
||||
hideSnapshotHeader()
|
||||
}
|
||||
}
|
||||
function handleSnapshotHeaderToggle() {
|
||||
function handleSnapshotHeaderToggle(event) {
|
||||
event.preventDefault()
|
||||
if (jQuery('.header-toggle').text().includes('▾')) {
|
||||
hideSnapshotHeader()
|
||||
} else {
|
||||
@@ -2129,8 +2130,7 @@
|
||||
return true
|
||||
}
|
||||
|
||||
// hide header when collapse icon is clicked
|
||||
jQuery('.header-toggle').on('click', handleSnapshotHeaderToggle)
|
||||
// Hide or show the header once when its title row or collapse icon is clicked.
|
||||
jQuery('.header-toggle-trigger').on('click', handleSnapshotHeaderToggle)
|
||||
|
||||
// check URL for hash e.g. #git and load relevant preview
|
||||
|
||||
@@ -262,21 +262,23 @@
|
||||
document.getElementById('archivebox-setup-wildcard-help').hidden = dnsMode === 'localhost';
|
||||
}
|
||||
|
||||
function probeUrl(url, generation) {
|
||||
function probeUrl(url, generation, requireArchiveBoxHealth) {
|
||||
var controller = new AbortController();
|
||||
var timeout = window.setTimeout(function() { controller.abort(); }, 5000);
|
||||
var target = new URL(url);
|
||||
target.searchParams.set('archivebox_setup_probe', String(generation));
|
||||
return fetch(target.toString(), {
|
||||
method: 'GET',
|
||||
mode: 'no-cors',
|
||||
mode: requireArchiveBoxHealth ? 'cors' : 'no-cors',
|
||||
credentials: 'omit',
|
||||
cache: 'no-store',
|
||||
redirect: 'follow',
|
||||
signal: controller.signal,
|
||||
}).then(function() {
|
||||
}).then(function(response) {
|
||||
window.clearTimeout(timeout);
|
||||
return true;
|
||||
return requireArchiveBoxHealth
|
||||
? response.ok && response.headers.get('X-ArchiveBox-Health') === 'OK'
|
||||
: true;
|
||||
}).catch(function() {
|
||||
window.clearTimeout(timeout);
|
||||
return false;
|
||||
@@ -378,9 +380,9 @@
|
||||
admin: probeUrl(preview.adminUrl, generation),
|
||||
api: probeUrl(preview.apiUrl, generation),
|
||||
index: probeUrl(preview.indexUrl, generation),
|
||||
web: probeUrl(preview.webHealthUrl, generation),
|
||||
snapshot: probeUrl(preview.snapshotHealthUrl, generation),
|
||||
original: probeUrl(preview.originalHealthUrl, generation),
|
||||
web: probeUrl(preview.webHealthUrl, generation, true),
|
||||
snapshot: probeUrl(preview.snapshotHealthUrl, generation, true),
|
||||
original: probeUrl(preview.originalHealthUrl, generation, true),
|
||||
};
|
||||
Promise.all(Object.keys(checks).map(function(key) { return checks[key].then(function(ok) { return [key, ok]; }); })).then(function(entries) {
|
||||
if (generation !== probeGeneration || preview !== currentPreview) return;
|
||||
|
||||
@@ -990,6 +990,7 @@ def seed_0_8_data(db_path: Path) -> dict[str, list[dict]]:
|
||||
"urls": uri,
|
||||
"max_depth": max_depth,
|
||||
"label": "",
|
||||
"status": "queued",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -39,23 +39,22 @@ def _run_real_binary_state_machine(data_dir: Path, *, name: str, binproviders: s
|
||||
)
|
||||
|
||||
|
||||
def test_binary_request_preserves_raw_overrides_in_db_while_using_native_event():
|
||||
def test_binary_request_preserves_native_overrides_in_db():
|
||||
from abxpkg.binary_service import BinaryCacheService, BinaryEvent, BinaryRequestEvent, BinaryService
|
||||
from abx_dl.orchestrator import create_bus
|
||||
from archivebox.services.binary_service import ArchiveBoxDBBinaryCacheBackend
|
||||
|
||||
machine = Machine.current()
|
||||
raw_overrides = {
|
||||
overrides = {
|
||||
"pip": {
|
||||
"install_args": ["imagesize>=2.0.0"],
|
||||
"module_name": "imagesize",
|
||||
},
|
||||
}
|
||||
binary = Binary.objects.create(
|
||||
machine=machine,
|
||||
name="python3",
|
||||
binproviders="env,pip",
|
||||
overrides=raw_overrides,
|
||||
overrides=overrides,
|
||||
status=Binary.StatusChoices.QUEUED,
|
||||
retry_at=timezone.now(),
|
||||
)
|
||||
@@ -63,7 +62,7 @@ def test_binary_request_preserves_raw_overrides_in_db_while_using_native_event()
|
||||
binary.refresh_from_db()
|
||||
assert binary.status == Binary.StatusChoices.INSTALLED
|
||||
assert Path(binary.abspath).resolve() == Path(sys.executable).resolve()
|
||||
bus = create_bus(name=f"test_binary_raw_overrides_{uuid.uuid4().hex[:8]}")
|
||||
bus = create_bus(name=f"test_binary_native_overrides_{uuid.uuid4().hex[:8]}")
|
||||
BinaryCacheService(bus, backend=ArchiveBoxDBBinaryCacheBackend())
|
||||
BinaryService(bus)
|
||||
binary_events: list[BinaryEvent] = []
|
||||
@@ -78,11 +77,7 @@ def test_binary_request_preserves_raw_overrides_in_db_while_using_native_event()
|
||||
BinaryRequestEvent(
|
||||
name="python3",
|
||||
binproviders="env,pip",
|
||||
overrides={"pip": {"install_args": ["imagesize>=2.0.0"]}},
|
||||
extra_context={
|
||||
"raw_overrides": raw_overrides,
|
||||
"provider_metadata": {"pip": {"module_name": "imagesize"}},
|
||||
},
|
||||
overrides=overrides,
|
||||
),
|
||||
).now()
|
||||
await bus.wait_until_idle()
|
||||
@@ -91,10 +86,9 @@ def test_binary_request_preserves_raw_overrides_in_db_while_using_native_event()
|
||||
|
||||
binary.refresh_from_db()
|
||||
assert binary.status == Binary.StatusChoices.INSTALLED
|
||||
assert binary.overrides == raw_overrides
|
||||
assert binary.overrides == overrides
|
||||
assert binary_events
|
||||
assert binary_events[-1].overrides == {"pip": {"install_args": ["imagesize>=2.0.0"]}}
|
||||
assert binary_events[-1].extra_context["raw_overrides"] == raw_overrides
|
||||
assert binary_events[-1].overrides == overrides
|
||||
|
||||
|
||||
def test_binary_request_installs_env_binary_and_recovers_stale_cache(initialized_archive, tmp_path):
|
||||
|
||||
@@ -204,6 +204,7 @@ def test_install_updates_binary_table(initialized_archive):
|
||||
|
||||
output = result.stdout + result.stderr
|
||||
assert result.returncode == 0, output
|
||||
assert "0 succeeded, 0 noresult, 0 failed, 0 skipped" not in output
|
||||
|
||||
with use_archivebox_db(initialized_archive):
|
||||
binary_counts = {
|
||||
|
||||
@@ -1660,7 +1660,7 @@ class TestRecoverOrchestratorState:
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
@pytest.mark.timeout(300)
|
||||
@pytest.mark.parametrize("chrome_isolation", ["crawl", "snapshot"])
|
||||
def test_resume_queued_chrome_wait_reruns_background_prerequisites(
|
||||
def test_resume_queued_chrome_navigate_reruns_background_prerequisites(
|
||||
self,
|
||||
initialized_archive,
|
||||
recursive_test_site,
|
||||
@@ -1700,21 +1700,21 @@ class TestRecoverOrchestratorState:
|
||||
)
|
||||
assert list_process.returncode == 0, list_process.stderr or list_process.stdout
|
||||
chrome_results = parse_jsonl_output(list_process.stdout)
|
||||
wait_record = next(record for record in chrome_results if record["hook_name"] == "on_Snapshot__11_chrome_wait")
|
||||
snapshot_id = wait_record["snapshot_id"]
|
||||
navigate_record = next(record for record in chrome_results if record["hook_name"] == "on_Snapshot__30_chrome_navigate")
|
||||
snapshot_id = navigate_record["snapshot_id"]
|
||||
|
||||
with use_archivebox_db(initialized_archive):
|
||||
tab_result = ArchiveResult.objects.get(
|
||||
snapshot_id=snapshot_id,
|
||||
plugin="chrome",
|
||||
hook_name="on_Snapshot__10_chrome_tab.daemon.bg",
|
||||
hook_name="on_Snapshot__01_chrome_tab.daemon.bg",
|
||||
)
|
||||
first_tab_process_id = tab_result.process_id
|
||||
assert first_tab_process_id is not None
|
||||
|
||||
update_process = run_archivebox_cmd(
|
||||
["archiveresult", "update", "--status=queued"],
|
||||
stdin=next(line for line in list_process.stdout.splitlines() if wait_record["id"] in line) + "\n",
|
||||
stdin=next(line for line in list_process.stdout.splitlines() if navigate_record["id"] in line) + "\n",
|
||||
cwd=initialized_archive,
|
||||
env=env,
|
||||
timeout=60,
|
||||
@@ -1741,19 +1741,19 @@ class TestRecoverOrchestratorState:
|
||||
cleanup_process_group(run_process.pid)
|
||||
|
||||
with use_archivebox_db(initialized_archive):
|
||||
wait_result = ArchiveResult.objects.get(
|
||||
navigate_result = ArchiveResult.objects.get(
|
||||
snapshot_id=snapshot_id,
|
||||
plugin="chrome",
|
||||
hook_name="on_Snapshot__11_chrome_wait",
|
||||
hook_name="on_Snapshot__30_chrome_navigate",
|
||||
)
|
||||
tab_result = ArchiveResult.objects.get(
|
||||
snapshot_id=snapshot_id,
|
||||
plugin="chrome",
|
||||
hook_name="on_Snapshot__10_chrome_tab.daemon.bg",
|
||||
hook_name="on_Snapshot__01_chrome_tab.daemon.bg",
|
||||
)
|
||||
|
||||
assert run_process.returncode == 0
|
||||
assert wait_result.status == ArchiveResult.StatusChoices.SUCCEEDED
|
||||
assert navigate_result.status == ArchiveResult.StatusChoices.SUCCEEDED
|
||||
assert tab_result.process_id is not None
|
||||
assert tab_result.process_id != first_tab_process_id
|
||||
|
||||
|
||||
@@ -719,7 +719,8 @@ def test_run_pending_crawls_processes_queued_crawl_and_real_binary(tmp_path):
|
||||
assert Snapshot.objects.filter(crawl=crawl, status=Snapshot.StatusChoices.SEALED).count() == 1
|
||||
assert binary.status == Binary.StatusChoices.INSTALLED
|
||||
assert binary.retry_at is None
|
||||
assert Path(binary.abspath).is_symlink()
|
||||
assert Path(binary.abspath).is_file()
|
||||
assert binary.version
|
||||
assert binary.binprovider == "env"
|
||||
|
||||
|
||||
@@ -877,7 +878,8 @@ def test_run_pending_crawls_resolves_real_binary_through_abxpkg(tmp_path):
|
||||
assert result == 0
|
||||
assert binary.status == Binary.StatusChoices.INSTALLED
|
||||
assert binary.retry_at is None
|
||||
assert Path(binary.abspath).is_symlink()
|
||||
assert Path(binary.abspath).is_file()
|
||||
assert binary.version
|
||||
assert binary.binprovider == "env"
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class TestBackgroundHookDetection:
|
||||
assert foreground_hooks
|
||||
assert all(".bg." in hook.name for hook in background_hooks)
|
||||
assert all(".bg." not in hook.name for hook in foreground_hooks)
|
||||
assert any(hook.name == "on_Snapshot__10_chrome_tab.daemon.bg.js" for hook in background_hooks)
|
||||
assert any(hook.name == "on_Snapshot__01_chrome_tab.daemon.bg.js" for hook in background_hooks)
|
||||
assert any(hook.name == "on_Snapshot__06_wget.finite.bg.py" for hook in background_hooks)
|
||||
assert any(hook.name == "on_Snapshot__93_hashes.py" for hook in foreground_hooks)
|
||||
|
||||
@@ -227,7 +227,7 @@ class TestHookDiscovery:
|
||||
hooks = discover_hooks("Snapshot", filter_disabled=False)
|
||||
|
||||
hook_names = [h.name for h in hooks]
|
||||
assert "on_Snapshot__10_chrome_tab.daemon.bg.js" in hook_names
|
||||
assert "on_Snapshot__01_chrome_tab.daemon.bg.js" in hook_names
|
||||
assert "on_Snapshot__21_consolelog.daemon.bg.js" in hook_names
|
||||
assert "on_Snapshot__06_wget.finite.bg.py" in hook_names
|
||||
assert all(hook.is_file() for hook in hooks)
|
||||
|
||||
@@ -667,11 +667,12 @@ def test_crawl_data_preserved_after_migration(migration_08_data):
|
||||
|
||||
# Check each crawl's data is preserved
|
||||
for crawl in original_data["crawls"]:
|
||||
cursor.execute("SELECT urls, label FROM crawls_crawl WHERE id = ?", (crawl["id"],))
|
||||
cursor.execute("SELECT urls, label, status FROM crawls_crawl WHERE id = ?", (crawl["id"],))
|
||||
row = cursor.fetchone()
|
||||
assert row is not None, f"Crawl {crawl['id']} not found after migration"
|
||||
assert row[0] == crawl["urls"], f"URLs mismatch for crawl {crawl['id']}"
|
||||
assert row[1] == crawl["label"], f"Label mismatch for crawl {crawl['id']}"
|
||||
assert row[2] == crawl["status"], f"Status mismatch for crawl {crawl['id']}"
|
||||
|
||||
conn.close()
|
||||
|
||||
@@ -1061,7 +1062,7 @@ def test_each_declared_filesystem_hop_preserves_outputs(migration_08_data, fs_ve
|
||||
(source_dir / "unknown-empty-dir" / "nested").mkdir(parents=True)
|
||||
expected_tree = filesystem_manifest(source_dir)
|
||||
|
||||
result = run_archivebox_migration_cmd(work_dir, ["update"], timeout=180)
|
||||
result = run_archivebox_migration_cmd(work_dir, ["update", "--migrate-only"], timeout=180)
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
migrated_dir = source_dir.resolve()
|
||||
|
||||
@@ -45,6 +45,22 @@ def test_root_preserves_existing_non_root_data_dir_owner():
|
||||
) == (1001, 1002)
|
||||
|
||||
|
||||
def test_root_uses_owner_primary_group_for_mixed_user_root_data_dir():
|
||||
assert select_archivebox_user(
|
||||
running_uid=0,
|
||||
running_gid=0,
|
||||
effective_uid=0,
|
||||
effective_gid=0,
|
||||
sudo_uid=1001,
|
||||
sudo_gid=1002,
|
||||
data_dir_uid=1001,
|
||||
data_dir_gid=0,
|
||||
data_dir_owner_gid=1002,
|
||||
account_uid=911,
|
||||
account_gid=911,
|
||||
) == (1001, 1002)
|
||||
|
||||
|
||||
def test_root_uses_archivebox_account_for_unknown_data_dir_owner():
|
||||
assert select_archivebox_user(
|
||||
running_uid=0,
|
||||
@@ -105,14 +121,23 @@ def test_root_hands_off_root_or_archivebox_owned_collection_boundaries():
|
||||
)
|
||||
|
||||
|
||||
def test_root_init_hands_off_only_an_empty_data_dir(tmp_path):
|
||||
def test_root_setup_commands_hand_off_only_an_empty_data_dir(tmp_path):
|
||||
from archivebox.config.permissions import root_data_dir_handoff_paths
|
||||
|
||||
assert root_data_dir_handoff_paths(tmp_path, ["archivebox", "init"]) == (tmp_path,)
|
||||
setup_commands = (
|
||||
["archivebox", "init"],
|
||||
["archivebox", "install"],
|
||||
["archivebox", "server", "--init"],
|
||||
["archivebox", "server", "--quick-init"],
|
||||
["archivebox", "add", "--init", "https://example.com"],
|
||||
)
|
||||
for argv in setup_commands:
|
||||
assert root_data_dir_handoff_paths(tmp_path, argv) == (tmp_path,)
|
||||
|
||||
unrelated = tmp_path / "unrelated.txt"
|
||||
unrelated.write_text("keep root ownership")
|
||||
assert root_data_dir_handoff_paths(tmp_path, ["archivebox", "init"]) == ()
|
||||
for argv in setup_commands:
|
||||
assert root_data_dir_handoff_paths(tmp_path, argv) == ()
|
||||
|
||||
|
||||
def test_existing_collection_handoff_is_bounded_to_known_top_level_paths(tmp_path):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -34,14 +35,16 @@ def test_setup_script_gives_archivebox_user_ownership_of_runtime_parent_dirs():
|
||||
|
||||
def test_setup_script_bootstraps_locked_abxpkg_version():
|
||||
script = SETUP_SCRIPT.read_text()
|
||||
lock = tomllib.loads((SETUP_SCRIPT.parents[1] / "uv.lock").read_text())
|
||||
locked_abxpkg_version = next(package["version"] for package in lock["package"] if package["name"] == "abxpkg")
|
||||
prepare_function = script.partition("prepare_abxpkg_environment() {")[2].partition("\n}")[0]
|
||||
install_function = script.partition("install_archivebox_with_uv() {")[2].partition("\n}")[0]
|
||||
|
||||
assert 'ABXPKG_PACKAGE="${ABXPKG_PACKAGE:-abxpkg==1.12.61}"' in script
|
||||
assert 'ARCHIVEBOX_PACKAGE="${ARCHIVEBOX_PACKAGE:-archivebox}"' in script
|
||||
assert '--prerelease allow --upgrade "$ARCHIVEBOX_PACKAGE"' in install_function
|
||||
assert f'ABXPKG_PACKAGE="${{ABXPKG_PACKAGE:-abxpkg=={locked_abxpkg_version}}}"' in script
|
||||
assert 'ARCHIVEBOX_PACKAGE="${ARCHIVEBOX_PACKAGE:-archivebox>=0.9.0rc0,<0.10}"' in script
|
||||
assert '--prerelease explicit --upgrade "$ARCHIVEBOX_PACKAGE"' in install_function
|
||||
assert "fix_root_install_ownership" in prepare_function
|
||||
assert "resolve_setup_binary git env,brew,apt true" in install_function
|
||||
assert "resolve_setup_binary git env,brew,apt true" not in install_function
|
||||
|
||||
|
||||
def test_setup_script_preserves_collection_and_compose_ownership_boundaries():
|
||||
@@ -59,12 +62,14 @@ def test_setup_script_prints_root_safe_runtime_commands():
|
||||
|
||||
assert 'echo " cd $ARCHIVEBOX_DATA_DIR' in script
|
||||
assert 'FOLLOWUP_SUDO="sudo "' in script
|
||||
assert "${FOLLOWUP_SUDO}docker compose run --rm archivebox add" in script
|
||||
assert "${FOLLOWUP_SUDO}docker compose exec archivebox archivebox add" in script
|
||||
assert "${FOLLOWUP_SUDO}archivebox add" in script
|
||||
assert "at ~/archivebox/data" not in script
|
||||
assert "Server started on http://0.0.0.0" not in script
|
||||
assert "server --daemonize 0.0.0.0:8000" in script
|
||||
assert 'nohup "$ARCHIVEBOX_BINARY" server' not in script
|
||||
assert "pkill -f 'archivebox server'" in script
|
||||
assert "pkill -f archivebox " not in script
|
||||
assert '"$DOCKER_BINARY" rm -f archivebox' in script
|
||||
assert "--connect-timeout 1 --max-time 2" in script
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from archivebox.config.common import get_config
|
||||
from archivebox.core.middleware import AdminCookieIsolationMiddleware
|
||||
from archivebox.core.setup_wizard import get_base_url_mismatch_context, get_setup_wizard_context
|
||||
from archivebox.core.templatetags.core_tags import system_warnings_banner
|
||||
from archivebox.core.views import HealthCheckView
|
||||
|
||||
STATIC_DIR = Path(__file__).parents[1] / "templates" / "static"
|
||||
SETUP_WIZARD_CSS = (STATIC_DIR / "setup_wizard.css").read_text()
|
||||
@@ -213,13 +214,28 @@ def test_setup_wizard_assets_enforce_selection_and_access_requirements():
|
||||
assert "Waiting for a matching browser URL and valid setup options" in SETUP_WIZARD_JS
|
||||
assert "Finish the selected DNS, ingress, and TLS setup" in SETUP_WIZARD_JS
|
||||
|
||||
for target in ("adminUrl", "apiUrl", "indexUrl", "webHealthUrl", "snapshotHealthUrl", "originalHealthUrl"):
|
||||
for target in ("adminUrl", "apiUrl", "indexUrl"):
|
||||
assert f"probeUrl(preview.{target}" in SETUP_WIZARD_JS
|
||||
assert "wildcardHealthUrl" not in SETUP_WIZARD_JS
|
||||
assert "results.wildcard" not in SETUP_WIZARD_JS
|
||||
assert "var coreReachable = results.admin && results.api && results.index && results.web && results.snapshot;" in SETUP_WIZARD_JS
|
||||
assert "probeUrl(webOrigin + '/web/https://example.com'" not in SETUP_WIZARD_JS
|
||||
assert "credentials: 'omit'" in SETUP_WIZARD_JS
|
||||
assert "function probeUrl(url, generation, requireArchiveBoxHealth)" in SETUP_WIZARD_JS
|
||||
assert "mode: requireArchiveBoxHealth ? 'cors' : 'no-cors'" in SETUP_WIZARD_JS
|
||||
assert "response.ok && response.headers.get('X-ArchiveBox-Health') === 'OK'" in SETUP_WIZARD_JS
|
||||
for target in ("webHealthUrl", "snapshotHealthUrl", "originalHealthUrl"):
|
||||
assert f"probeUrl(preview.{target}, generation, true)" in SETUP_WIZARD_JS
|
||||
|
||||
|
||||
def test_health_check_is_identifiable_across_ingress_origins():
|
||||
response = HealthCheckView.as_view()(RequestFactory().get("/health/"))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.content == b"OK"
|
||||
assert response["Access-Control-Allow-Origin"] == "*"
|
||||
assert response["Access-Control-Expose-Headers"] == "X-ArchiveBox-Health"
|
||||
assert response["X-ArchiveBox-Health"] == "OK"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -696,6 +696,9 @@ class TestSnapshotProgressStats:
|
||||
assert "index.jsonl?preview=1" in rendered
|
||||
assert "height: calc(100vh - 210px)" not in rendered
|
||||
assert "iframe-large" not in rendered
|
||||
assert 'class="header-toggle header-toggle-trigger"' not in rendered
|
||||
assert "event.preventDefault()" in rendered
|
||||
assert rendered.count(".on('click', handleSnapshotHeaderToggle)") == 1
|
||||
|
||||
|
||||
class TestAdminSnapshotListView:
|
||||
|
||||
@@ -215,6 +215,7 @@ def _login_admin_session_over_http(port: int, host: str) -> requests.Session:
|
||||
timeout=10,
|
||||
)
|
||||
assert login_page.status_code == 200, login_page.text[:500]
|
||||
assert "docker compose exec archivebox archivebox manage createsuperuser" in login_page.text
|
||||
csrf_match = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', login_page.text)
|
||||
assert csrf_match, login_page.text[:500]
|
||||
login_response = session.post(
|
||||
|
||||
+1
-1
@@ -21,6 +21,6 @@ cd "$REPO_DIR"
|
||||
./bin/build_docker.sh
|
||||
|
||||
echo "[√] Done. Install the built package by running:"
|
||||
echo " pip install archivebox"
|
||||
echo " uv tool install --force dist/archivebox-*.whl"
|
||||
echo " # or"
|
||||
echo " brew tap archivebox/archivebox && brew install archivebox"
|
||||
+17
-12
@@ -78,10 +78,10 @@ fi
|
||||
ARCHIVEBOX_BRANCH="${ARCHIVEBOX_BRANCH:-dev}"
|
||||
ARCHIVEBOX_IMAGE="${ARCHIVEBOX_IMAGE:-archivebox/archivebox:dev}"
|
||||
ARCHIVEBOX_PYTHON="${ARCHIVEBOX_PYTHON:-3.13}"
|
||||
ARCHIVEBOX_PACKAGE="${ARCHIVEBOX_PACKAGE:-archivebox}"
|
||||
ARCHIVEBOX_PACKAGE="${ARCHIVEBOX_PACKAGE:-archivebox>=0.9.0rc0,<0.10}"
|
||||
ARCHIVEBOX_PLATFORM="${ARCHIVEBOX_PLATFORM:-}"
|
||||
ARCHIVEBOX_COMPOSE_URL="${ARCHIVEBOX_COMPOSE_URL:-https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/${ARCHIVEBOX_BRANCH}/docker-compose.yml}"
|
||||
ABXPKG_PACKAGE="${ABXPKG_PACKAGE:-abxpkg==1.12.61}"
|
||||
ABXPKG_PACKAGE="${ABXPKG_PACKAGE:-abxpkg==1.12.91}"
|
||||
ABXPKG_LIB_DIR="${ABXPKG_LIB_DIR:-$HOME/.cache/archivebox/setup-abxpkg}"
|
||||
ARCHIVEBOX_HOME_DIR="$HOME/archivebox"
|
||||
ARCHIVEBOX_DATA_DIR="$ARCHIVEBOX_HOME_DIR/data"
|
||||
@@ -299,10 +299,9 @@ resolve_setup_curl() {
|
||||
}
|
||||
|
||||
install_archivebox_with_uv() {
|
||||
resolve_setup_binary git env,brew,apt true
|
||||
echo
|
||||
echo "[+] Installing ArchiveBox python tool using uv from $ARCHIVEBOX_PACKAGE..."
|
||||
"$UV_BINARY" --no-config tool install --python "$ARCHIVEBOX_PYTHON" --prerelease allow --upgrade "$ARCHIVEBOX_PACKAGE"
|
||||
"$UV_BINARY" --no-config tool install --python "$ARCHIVEBOX_PYTHON" --prerelease explicit --upgrade "$ARCHIVEBOX_PACKAGE"
|
||||
|
||||
uv_tool_bin_dir="$("$UV_BINARY" --no-config tool dir --bin)"
|
||||
ARCHIVEBOX_BINARY="$uv_tool_bin_dir/archivebox"
|
||||
@@ -361,7 +360,12 @@ if [ "$DOCKER_IMAGE_READY" = "true" ] && "$DOCKER_BINARY" compose version > /dev
|
||||
cd "$ARCHIVEBOX_HOME_DIR"
|
||||
migrate_legacy_collection_dir
|
||||
if [ ! -f docker-compose.yml ]; then
|
||||
"$CURL_BINARY" -fsSL "$ARCHIVEBOX_COMPOSE_URL" > docker-compose.yml
|
||||
compose_download="$(mktemp "$ARCHIVEBOX_HOME_DIR/.docker-compose.yml.XXXXXX")"
|
||||
if ! "$CURL_BINARY" -fsSL "$ARCHIVEBOX_COMPOSE_URL" -o "$compose_download"; then
|
||||
rm -f "$compose_download"
|
||||
exit 1
|
||||
fi
|
||||
mv "$compose_download" docker-compose.yml
|
||||
if [ "$RUNNING_AS_ROOT" = "true" ]; then
|
||||
chown "$ARCHIVEBOX_SYSTEM_UID:$ARCHIVEBOX_SYSTEM_GID" docker-compose.yml
|
||||
fi
|
||||
@@ -381,10 +385,10 @@ if [ "$DOCKER_IMAGE_READY" = "true" ] && "$DOCKER_BINARY" compose version > /dev
|
||||
echo " ${FOLLOWUP_SUDO}docker compose down"
|
||||
echo " ${FOLLOWUP_SUDO}env ARCHIVEBOX_IMAGE=$ARCHIVEBOX_IMAGE docker compose pull"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose up"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose run --rm archivebox manage createsuperuser"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose run --rm archivebox add 'https://example.com'"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose run --rm archivebox list"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose run --rm archivebox help"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose exec archivebox archivebox manage createsuperuser"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose exec archivebox archivebox add 'https://example.com'"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose exec archivebox archivebox list"
|
||||
echo " ${FOLLOWUP_SUDO}docker compose exec archivebox archivebox help"
|
||||
exit 0
|
||||
elif [ "$DOCKER_IMAGE_READY" = "true" ]; then
|
||||
resolve_setup_curl
|
||||
@@ -419,7 +423,8 @@ echo
|
||||
echo "[!] It's highly recommended to use ArchiveBox with Docker, but Docker is unavailable (not installed, not running, or not accessible to this user)."
|
||||
echo
|
||||
echo " ⚠️ If you want to use Docker, press [Ctrl-C] to cancel now. ⚠️"
|
||||
echo " Check 'docker info'. Start Docker Desktop on macOS, or rerun this script with sudo on Ubuntu if Docker reports a socket permission error."
|
||||
echo " Check 'docker info'. Start Docker Desktop on macOS, or fix Docker socket access and rerun as your normal Ubuntu user."
|
||||
echo " Rerunning with sudo intentionally creates a system-owned deployment under /var/lib/archivebox/archivebox instead of ~/archivebox."
|
||||
echo " Install Docker if needed: https://docs.docker.com/get-docker/"
|
||||
echo
|
||||
echo "Otherwise, install will continue with uv in 12s... (press [Ctrl+C] to cancel)"
|
||||
@@ -469,10 +474,10 @@ open_archivebox
|
||||
echo
|
||||
echo "[√] Server started on http://127.0.0.1:8000 and data directory initialized in $ARCHIVEBOX_DATA_DIR. Usage:"
|
||||
echo " cd $ARCHIVEBOX_DATA_DIR # see your data dir"
|
||||
echo " ${FOLLOWUP_SUDO}archivebox server --quick-init 0.0.0.0:8000 # start server process"
|
||||
echo " ${FOLLOWUP_SUDO}archivebox server 0.0.0.0:8000 # start server process"
|
||||
echo " ${FOLLOWUP_SUDO}archivebox manage createsuperuser # add an admin user+pass"
|
||||
echo " ps aux | grep archivebox # see server process pid"
|
||||
echo " ${FOLLOWUP_SUDO}pkill -f archivebox # stop the server"
|
||||
echo " ${FOLLOWUP_SUDO}pkill -f 'archivebox server' # stop the server"
|
||||
echo " curl -fsSL 'https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/dev/bin/setup.sh' | ${FOLLOWUP_SUDO}bash # update versions"
|
||||
echo " ${FOLLOWUP_SUDO}archivebox add 'https://example.com'" # archive a new URL
|
||||
echo " ${FOLLOWUP_SUDO}archivebox list # see URLs archived"
|
||||
|
||||
+6
-6
@@ -2,13 +2,13 @@
|
||||
# mkdir -p ~/archivebox/data && cd ~/archivebox
|
||||
# curl -fsSL 'https://docker-compose.archivebox.io' > docker-compose.yml
|
||||
# docker compose pull
|
||||
# docker compose run --rm archivebox init
|
||||
# docker compose run --rm archivebox install
|
||||
# docker compose up -d && open 'http://admin.archivebox.localhost:8000'
|
||||
# docker compose up -d --wait
|
||||
# docker compose exec archivebox archivebox manage createsuperuser
|
||||
# open 'http://admin.archivebox.localhost:8000'
|
||||
|
||||
# docker compose run --rm archivebox add --depth=1 'https://news.ycombinator.com'
|
||||
# docker compose run --rm -T archivebox add < ~/Downloads/bookmarks.txt
|
||||
# docker compose run --rm archivebox help
|
||||
# docker compose exec archivebox archivebox add --depth=1 'https://news.ycombinator.com'
|
||||
# docker compose exec -T archivebox archivebox add < ~/Downloads/bookmarks.txt
|
||||
# docker compose exec archivebox archivebox help
|
||||
# Documentation:
|
||||
# https://github.com/ArchiveBox/ArchiveBox/wiki/Docker#docker-compose
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ URL users are redirected to after logging out. The default `/` keeps users on Ar
|
||||
|
||||
### LDAP Settings
|
||||
|
||||
*Options for LDAP / Active Directory authentication via [django-auth-ldap](https://github.com/django-auth-ldap/django-auth-ldap). Requires `uv tool install --python 3.13 --prerelease allow --upgrade 'archivebox[ldap]'` (which also pulls in the system `libldap` / `libsasl` headers).*
|
||||
*Options for LDAP / Active Directory authentication via [django-auth-ldap](https://github.com/django-auth-ldap/django-auth-ldap). Requires `uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox[ldap]>=0.9.0rc0,<0.10'` (which also pulls in the system `libldap` / `libsasl` headers).*
|
||||
|
||||
---
|
||||
#### `LDAP_ENABLED`
|
||||
@@ -485,7 +485,7 @@ URL users are redirected to after logging out. The default `/` keeps users on Ar
|
||||
Master switch for LDAP authentication. When `True`, ArchiveBox loads the `django-auth-ldap` backend and validates that `LDAP_SERVER_URI`, `LDAP_BIND_DN`, `LDAP_BIND_PASSWORD`, and `LDAP_USER_BASE` are all set — startup fails fast otherwise.
|
||||
|
||||
```bash
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade 'archivebox[ldap]'
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox[ldap]>=0.9.0rc0,<0.10'
|
||||
```
|
||||
|
||||
Then set these configuration values:
|
||||
|
||||
+20
-21
@@ -63,18 +63,16 @@ mkdir -p ~/archivebox/data && cd ~/archivebox
|
||||
curl -fsSL 'https://docker-compose.archivebox.io' > docker-compose.yml
|
||||
# (shortcut for getting https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/dev/docker-compose.yml)
|
||||
|
||||
# pull the current image, initialize the collection, install runtime dependencies,
|
||||
# then create an admin user for the Web UI (or set ADMIN_USERNAME/ADMIN_PASSWORD env vars)
|
||||
# pull and start the current image, then create an admin user for the Web UI
|
||||
# (the server initializes a new collection automatically)
|
||||
docker compose pull
|
||||
docker compose run --rm archivebox init
|
||||
docker compose run --rm archivebox install
|
||||
docker compose run --rm archivebox manage createsuperuser
|
||||
docker compose up -d
|
||||
docker compose up -d --wait
|
||||
docker compose exec archivebox archivebox manage createsuperuser
|
||||
```
|
||||
|
||||
ArchiveBox installs and enables both ripgrep and [Sonic](https://github.com/valeriansaliou/sonic). Sonic is selected by default in the UI, while ripgrep remains available as the fallback. To select ripgrep explicitly:
|
||||
```bash
|
||||
docker compose run --rm archivebox config --set SEARCH_BACKEND_ENGINE=ripgrep
|
||||
docker compose exec archivebox archivebox config --set SEARCH_BACKEND_ENGINE=ripgrep
|
||||
```
|
||||
|
||||
<br/>
|
||||
@@ -87,40 +85,40 @@ See the wiki page on [Upgrading or Merging Archives: Upgrading with Docker Compo
|
||||
|
||||
### Usage
|
||||
|
||||
You can use `docker compose run --rm archivebox [subcommand]` just like the non-Docker `archivebox [subcommand]` CLI.
|
||||
With the server running from the setup steps above, use `docker compose exec archivebox archivebox [subcommand]` just like the non-Docker `archivebox [subcommand]` CLI. If the server is stopped, use `docker compose run --rm archivebox [subcommand]` instead.
|
||||
|
||||
First, make sure you're `cd`'ed into the same folder as your `docker-compose.yml` file (e.g. `~/archivebox`):
|
||||
```bash
|
||||
docker compose run --rm archivebox help
|
||||
docker compose exec archivebox archivebox help
|
||||
```
|
||||
|
||||
To add an individual URL, pass it in as an arg or via stdin:
|
||||
```bash
|
||||
docker compose run --rm archivebox add 'https://example.com'
|
||||
docker compose exec archivebox archivebox add 'https://example.com'
|
||||
# OR
|
||||
echo 'https://example.com' | docker compose run --rm -T archivebox add
|
||||
echo 'https://example.com' | docker compose exec -T archivebox archivebox add
|
||||
```
|
||||
|
||||
To add multiple URLs at once, pipe them in via stdin, or place them in a file inside `./data/sources` so that ArchiveBox can access it from within the container:
|
||||
```bash
|
||||
# pipe URLs in from a file outside Docker
|
||||
docker compose run --rm -T archivebox add < ~/Downloads/example_urls.txt
|
||||
docker compose exec -T archivebox archivebox add < ~/Downloads/example_urls.txt
|
||||
|
||||
# OR ingest URLs from a file mounted inside Docker
|
||||
docker compose run --rm archivebox add --depth=1 /data/sources/example_urls.txt
|
||||
docker compose exec archivebox archivebox add --depth=1 /data/sources/example_urls.txt
|
||||
|
||||
# OR pipe in URLs from a remote source
|
||||
curl 'https://example.com/some/rss/feed.xml' | docker compose run --rm -T archivebox add
|
||||
docker compose run --rm archivebox add --depth=1 'https://example.com/some/rss/feed.xml'
|
||||
curl 'https://example.com/some/rss/feed.xml' | docker compose exec -T archivebox archivebox add
|
||||
docker compose exec archivebox archivebox add --depth=1 'https://example.com/some/rss/feed.xml'
|
||||
```
|
||||
|
||||
The `--depth=1` flag tells ArchiveBox to look inside the provided source and archive all the URLs within:
|
||||
```bash
|
||||
# this archives just the RSS file itself (probably not what you want)
|
||||
docker compose run --rm archivebox add 'https://example.com/some/feed.rss'
|
||||
docker compose exec archivebox archivebox add 'https://example.com/some/feed.rss'
|
||||
|
||||
# this archives the RSS feed file + all the URLs mentioned inside of it
|
||||
docker compose run --rm archivebox add --depth=1 'https://example.com/some/feed.rss'
|
||||
docker compose exec archivebox archivebox add --depth=1 'https://example.com/some/feed.rss'
|
||||
```
|
||||
|
||||
<br/>
|
||||
@@ -146,12 +144,12 @@ ArchiveBox running with `docker compose` accepts all the same config options as
|
||||
|
||||
The recommended way configure ArchiveBox in Docker Compose is using `archivebox config --set ...` or by editing `ArchiveBox.conf`.
|
||||
```bash
|
||||
docker compose run --rm archivebox config --set TIMEOUT=120
|
||||
docker compose exec archivebox archivebox config --set TIMEOUT=120
|
||||
# OR edit ./data/ArchiveBox.conf and add this under its existing [ARCHIVING_CONFIG] section:
|
||||
TIMEOUT=120
|
||||
|
||||
# plugin-specific options work the same way (see https://archivebox.github.io/abx-plugins/)
|
||||
docker compose run --rm archivebox config --set YTDLP_MAX_SIZE=750m
|
||||
docker compose exec archivebox archivebox config --set YTDLP_MAX_SIZE=750m
|
||||
```
|
||||
This will apply the config to all containers or archivebox instances that access the collection.
|
||||
|
||||
@@ -199,8 +197,9 @@ Fetch and run the ArchiveBox Docker image to create your initial archive.
|
||||
docker pull archivebox/archivebox:dev
|
||||
|
||||
mkdir -p ~/archivebox/data && cd ~/archivebox/data
|
||||
docker run -it -v $PWD:/data archivebox/archivebox:dev init
|
||||
docker run -it -v $PWD:/data archivebox/archivebox:dev install
|
||||
docker run --rm -it -v "$PWD:/data" archivebox/archivebox:dev init
|
||||
docker run --rm -it -v "$PWD:/data" archivebox/archivebox:dev manage createsuperuser
|
||||
docker run -d --name archivebox -v "$PWD:/data" -p 8000:8000 archivebox/archivebox:dev
|
||||
```
|
||||
|
||||
*(You can create a collection in any directory you want, `~/archivebox/data` is just used as an example here)*
|
||||
|
||||
+25
-14
@@ -76,7 +76,9 @@ If you're on macOS or Ubuntu, there is an optional auto-setup script provided.
|
||||
curl -fsSL 'https://get.archivebox.io' | bash
|
||||
# shortcut to run https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/dev/bin/setup.sh
|
||||
```
|
||||
The script uses Docker automatically when a working Docker Compose installation is available. Otherwise it shows the native `uv` install plan and pauses so you can cancel before continuing. It initializes the collection, installs ArchiveBox's runtime dependencies, and starts the server; create the first admin afterward with the command printed at the end.
|
||||
The script uses Docker Compose when available, otherwise plain Docker when it can pull the released image. If Docker is unavailable, it shows the native `uv` install plan and pauses so you can cancel before continuing. It initializes the collection, installs ArchiveBox's runtime dependencies, and starts the server; create the first admin afterward with the command printed at the end.
|
||||
|
||||
Run it as your normal user unless you want a system-owned deployment. When run as root, the script creates the `archivebox` service user and places the collection under that account's home directory; it prints the exact path and follow-up commands when finished.
|
||||
|
||||
<img src="https://imgur.zervice.io/VMTzm0G.png" width="99%"/>
|
||||
|
||||
@@ -121,10 +123,13 @@ Make sure you have [Homebrew](https://brew.sh/) installed first.
|
||||
brew tap archivebox/archivebox
|
||||
brew trust archivebox/archivebox
|
||||
brew install archivebox
|
||||
```
|
||||
|
||||
# or use Homebrew to install uv, then install ArchiveBox with uv
|
||||
Or use Homebrew to install `uv`, then install ArchiveBox with `uv`:
|
||||
|
||||
```bash
|
||||
brew install uv
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
```
|
||||
|
||||
<img src="https://assets.ubuntu.com/v1/c5cb0f8e-picto-ubuntu.svg" width="30px" align="right"/>
|
||||
@@ -151,17 +156,17 @@ dependencies such as Chromium, yt-dlp, SingleFile, and other plugin-managed
|
||||
tools are installed by `archivebox install`; use `sudo archivebox install` only
|
||||
if you want it to install missing system packages via apt.
|
||||
|
||||
Alternatively, install with `uv` or [Homebrew on Linux](https://docs.brew.sh/Homebrew-on-Linux):
|
||||
|
||||
Run Homebrew as your normal non-root user; Homebrew does not support `sudo brew` or root installs.
|
||||
Alternatively, install with `uv`:
|
||||
|
||||
```bash
|
||||
# uv
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
source "$HOME/.local/bin/env"
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
```
|
||||
|
||||
# or Homebrew, after installing Linuxbrew
|
||||
Or install with [Homebrew on Linux](https://docs.brew.sh/Homebrew-on-Linux). Run Homebrew as your normal non-root user; Homebrew does not support `sudo brew` or root installs.
|
||||
|
||||
```bash
|
||||
brew tap archivebox/archivebox
|
||||
brew trust archivebox/archivebox
|
||||
brew install archivebox
|
||||
@@ -171,11 +176,11 @@ brew install archivebox
|
||||
|
||||
### 2. Install ArchiveBox using `uv`
|
||||
|
||||
If you are not using the apt package above, install ArchiveBox with `uv`.
|
||||
If you are not using the apt or Homebrew packages above, install ArchiveBox with `uv`.
|
||||
|
||||
```bash
|
||||
# get the dev version of ArchiveBox
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
|
||||
# if the optional ldap extra must compile locally on Debian/Ubuntu, install its headers and retry
|
||||
# sudo apt install build-essential libldap2-dev libsasl2-dev
|
||||
@@ -208,7 +213,7 @@ archivebox help
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Make sure the `uv`-installed version of `archivebox` is available in your `$PATH`.
|
||||
For `uv` installs, make sure the `uv`-installed version of `archivebox` is available in your `$PATH`.
|
||||
```bash
|
||||
uv tool list # show info about uv-installed tools
|
||||
|
||||
@@ -257,11 +262,17 @@ See our [[Usage]] Wiki documentation page for more examples.
|
||||
|
||||
### Next Steps: *Upgrading Archivebox to a new version*
|
||||
|
||||
Upgrade ArchiveBox itself first; `archivebox install` will then re-resolve compatible host binaries and update any managed runtime dependencies.
|
||||
Stop any running ArchiveBox processes and back up the entire collection first. Upgrade ArchiveBox with the same package manager you originally used; `archivebox install` will then re-resolve compatible host binaries and update any managed runtime dependencies.
|
||||
|
||||
```bash
|
||||
# back up the full collection before upgrading
|
||||
cd ~/archivebox
|
||||
tar -czf "archivebox-data-$(date +%s).tar.gz" data/
|
||||
|
||||
# get the dev version of ArchiveBox
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
# or: sudo apt update && sudo apt install --only-upgrade archivebox
|
||||
# or: brew update && brew upgrade archivebox
|
||||
|
||||
# run init inside any data directories to migrate the index to the latest version
|
||||
cd ~/archivebox/data
|
||||
|
||||
@@ -7,23 +7,24 @@ There are two ways to publish your archive: using the `archivebox server` or by
|
||||
## 1. Use the built-in web server
|
||||
|
||||
```bash
|
||||
# set the permissions depending on how public/locked down you want it to be
|
||||
archivebox config --set PUBLIC_INDEX=True
|
||||
archivebox config --set PUBLIC_ADD_VIEW=True
|
||||
archivebox config --set PERMISSIONS=public # default visibility of newly created snapshots (was: PUBLIC_SNAPSHOTS=True)
|
||||
archivebox config --set BASE_URL=https://archive.example.com
|
||||
archivebox config --set SERVER_SECURITY_MODE=safe-subdomains-fullreplay
|
||||
|
||||
# create an admin username and password for yourself
|
||||
archivebox manage createsuperuser
|
||||
|
||||
# then start the webserver and open the web UI in your browser
|
||||
# start the webserver and open the local admin UI
|
||||
archivebox server 0.0.0.0:8000
|
||||
open https://web.archive.example.com
|
||||
open http://admin.archivebox.localhost:8000
|
||||
```
|
||||
|
||||
This server is enabled out-of-the-box if you're using `docker-compose` to run ArchiveBox.
|
||||
If hosting publicly, place an SSL termination server in front of ArchiveBox. Start ArchiveBox normally, then follow the first-run wizard for the settings to enter in Cloudflare, Nginx Proxy Manager, Caddy, Traefik, Tailscale, or your hosting platform's ingress UI.
|
||||
If hosting publicly, complete the first-run wizard before exposing ArchiveBox. It guides you through choosing a security mode and shows the settings to enter in Cloudflare, Nginx Proxy Manager, Caddy, Traefik, Tailscale, or your hosting platform's ingress UI. Configure HTTPS there, then open the canonical URL you chose in the wizard.
|
||||
|
||||
After ingress is working, set the permissions depending on how public you want the archive to be:
|
||||
|
||||
```bash
|
||||
archivebox config --set PUBLIC_INDEX=True
|
||||
archivebox config --set PUBLIC_ADD_VIEW=True
|
||||
archivebox config --set PERMISSIONS=public
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Advanced: You can use nginx to serve a static export directly from the filesystem. Do not proxy live replay paths back onto the admin origin; use ArchiveBox's security-mode routing.
|
||||
|
||||
@@ -84,7 +84,7 @@ LOGOUT_REDIRECT_URL=https://auth.yourcompany.example.com/after/logout
|
||||
|
||||
First, install the `ldap` add-on to use this feature (not needed for Docker Archivebox).
|
||||
```bash
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade 'archivebox[ldap]'
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox[ldap]>=0.9.0rc0,<0.10'
|
||||
```
|
||||
|
||||
Then set these configuration values to finish configuring LDAP:
|
||||
|
||||
+21
-7
@@ -8,7 +8,7 @@ cd ~/archivebox
|
||||
tar -czf "archivebox-data-$(date +%s).tar.gz" data/
|
||||
|
||||
# Update ArchiveBox using the package manager you originally installed it with
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
# or: sudo apt update && sudo apt install --only-upgrade archivebox
|
||||
# or: brew update && brew upgrade archivebox
|
||||
|
||||
@@ -96,9 +96,9 @@ docker stop CONTAINER_ID
|
||||
|
||||
cd ~/archivebox/data # or wherever your existing collection is stored
|
||||
docker pull archivebox/archivebox:dev
|
||||
docker run -v $PWD:/data -it archivebox/archivebox:dev init
|
||||
docker run -v $PWD:/data -it archivebox/archivebox:dev install
|
||||
docker run -v $PWD:/data -it archivebox/archivebox:dev update --migrate-only
|
||||
docker run --rm -v $PWD:/data -it archivebox/archivebox:dev init
|
||||
docker run --rm -v $PWD:/data -it archivebox/archivebox:dev install
|
||||
docker run --rm -v $PWD:/data -it archivebox/archivebox:dev update --migrate-only
|
||||
|
||||
# restart the archivebox server container if needed
|
||||
docker run -v $PWD:/data -it -p 8000:8000 archivebox/archivebox:dev server 0.0.0.0:8000
|
||||
@@ -117,14 +117,28 @@ Package manager releases take a lot of effort to maintain ([contributions welcom
|
||||
cd ~/archivebox/data # or wherever your data folder is
|
||||
|
||||
# upgrade ArchiveBox using the package manager you originally used to install it
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
# or
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
```
|
||||
|
||||
Or, for apt:
|
||||
|
||||
```bash
|
||||
cd ~/archivebox/data # or wherever your data folder is
|
||||
sudo apt update
|
||||
sudo apt install --only-upgrade archivebox
|
||||
# or
|
||||
```
|
||||
|
||||
Or, for Homebrew:
|
||||
|
||||
```bash
|
||||
cd ~/archivebox/data # or wherever your data folder is
|
||||
brew update
|
||||
brew upgrade archivebox
|
||||
```
|
||||
|
||||
Then finish the collection upgrade:
|
||||
|
||||
```bash
|
||||
archivebox init # run init to upgrade the collection to the latest version
|
||||
archivebox install # refresh runtime dependencies if needed
|
||||
|
||||
|
||||
+64
-56
@@ -5,20 +5,20 @@ version = 2
|
||||
[snippets]
|
||||
|
||||
# README.md
|
||||
"568e2aa45f6256e9-1" = "illustration"
|
||||
"dfce88ac5547f8e1-1" = "illustration"
|
||||
"c6654ae1f15bca88-1" = "run"
|
||||
"c5440af2c171391a-1" = "illustration"
|
||||
"1754491b1b6255de-1" = "illustration"
|
||||
"72fddf697c7b0142-1" = "illustration"
|
||||
"fe1f06a0a6b02c01-1" = "illustration"
|
||||
"220882ff87e9c738-1" = "run"
|
||||
"698f2b0f88796c9b-1" = "illustration"
|
||||
"398611ab1880ef51-1" = "illustration"
|
||||
"778c693286d7b895-1" = "illustration"
|
||||
"16f90085b2eb8756-1" = "illustration"
|
||||
"50b37bfc08509639-1" = "run"
|
||||
"39bc0d99ed165be2-1" = "run"
|
||||
"d5ae3240405670bb-1" = "run"
|
||||
"4da22c86e09ce7d4-1" = "illustration"
|
||||
"a7a123ba522227be-1" = "run"
|
||||
"fcdd073a5440f5ca-1" = "run"
|
||||
"4da22c86e09ce7d4-2" = "illustration"
|
||||
"05be9e44a8ae9875-1" = "run"
|
||||
"8fe4a2c15abb0ab2-1" = "run"
|
||||
"2b08779e4d24d305-1" = "run"
|
||||
"4da22c86e09ce7d4-3" = "illustration"
|
||||
"1b9438e093866ff0-1" = "illustration"
|
||||
@@ -26,7 +26,7 @@ version = 2
|
||||
"e244b54155aa0bff-1" = "run"
|
||||
"b5176e0b5ad1bcfc-1" = "illustration"
|
||||
"13fefe6d12cab753-1" = "illustration"
|
||||
"234ffac977318d0c-1" = "illustration"
|
||||
"47bd31942b5c44c4-1" = "illustration"
|
||||
"4a595390c5fe50a4-1" = "illustration"
|
||||
"30fc18bc7b74b79c-1" = "illustration"
|
||||
"56df5d0efb8a7a7d-1" = "illustration"
|
||||
@@ -52,7 +52,7 @@ version = 2
|
||||
# AGENTS.md
|
||||
"2695777473e3a217-1" = "run"
|
||||
"16f996904f0eaa96-1" = "run"
|
||||
"b894a7c4f93e4e92-1" = "run"
|
||||
"167cab3db82a6aee-1" = "run"
|
||||
"6a6234b63cd3d0ae-1" = "run"
|
||||
"396490664ce459e9-1" = "run"
|
||||
|
||||
@@ -67,7 +67,7 @@ version = 2
|
||||
|
||||
# skills/archivebox/SKILL.md
|
||||
"ac2c1555d3b9cd8f-1" = "run"
|
||||
"6b5798897504b1c8-1" = "run"
|
||||
"11a520069efa3387-1" = "run"
|
||||
"46fe9d81940ea385-1" = "run"
|
||||
"396490664ce459e9-2" = "run"
|
||||
|
||||
@@ -98,7 +98,7 @@ version = 2
|
||||
"ce0de8ed0d7cd552-1" = "run"
|
||||
"83cb9be168d203ca-1" = "run"
|
||||
"2028f9a887105a13-1" = "run"
|
||||
"fa8a95a3d0d51b76-1" = "run"
|
||||
"70389ddb24a77820-1" = "run"
|
||||
"00c51bd82b457d50-1" = "illustration"
|
||||
"48b48da014bd91dd-1" = "illustration"
|
||||
"c4266755c0748aa0-1" = "run"
|
||||
@@ -107,16 +107,16 @@ version = 2
|
||||
|
||||
# docs/Docker.md
|
||||
"03a42395e6560eba-1" = "illustration"
|
||||
"75490fc7993139ef-1" = "illustration"
|
||||
"10f9ca01b3b7a305-1" = "run"
|
||||
"fef4f0641483ee49-1" = "run"
|
||||
"8104bfe431c6b914-1" = "run"
|
||||
"df0dbbd212722e34-1" = "illustration"
|
||||
"b10a271100fee968-1" = "illustration"
|
||||
"4deb738714bdbd33-1" = "illustration"
|
||||
"affb9c5d5980ed1c-1" = "run"
|
||||
"0e9c1f41ef579e79-1" = "run"
|
||||
"746a3e228d29f9d2-1" = "run"
|
||||
"66a7d54ea81d64dc-1" = "illustration"
|
||||
"aa4884ebe3101cf6-1" = "illustration"
|
||||
"150ae2c7f6d012ab-1" = "illustration"
|
||||
"646dda5284de16a5-1" = "run"
|
||||
"8d86727108f1dbad-1" = "run"
|
||||
"6bcb6c2e8770907a-1" = "illustration"
|
||||
"fcb2471eb154161b-1" = "illustration"
|
||||
"d093b42a06e0485e-1" = "illustration"
|
||||
"6353a7cf3014612e-1" = "illustration"
|
||||
"d66324df4c7f97c7-1" = "illustration"
|
||||
"17ed06e5b75aac1b-1" = "illustration"
|
||||
@@ -128,17 +128,19 @@ version = 2
|
||||
|
||||
# docs/Install.md
|
||||
"662be0691524555c-1" = "illustration"
|
||||
"5e4e9a2a8a9435b2-1" = "run"
|
||||
"c8df632a4f4e3100-1" = "run"
|
||||
"4d29a8d4b3902ab3-1" = "run"
|
||||
"73f9f36a369f2398-1" = "run"
|
||||
"a6837d7c229936af-1" = "illustration"
|
||||
"c2eb27c95729f2b4-1" = "run"
|
||||
"b92d8bdeb23143fc-1" = "illustration"
|
||||
"311abf57a4443a9e-1" = "illustration"
|
||||
"fcaf2b90e39272df-1" = "run"
|
||||
"77b61fd52c4c3c32-1" = "run"
|
||||
"a8f16f6be4f2b3c0-1" = "run"
|
||||
"035ecb710f7fc2a8-1" = "illustration"
|
||||
"e4e22c56cb720344-1" = "illustration"
|
||||
"2905d7f9dece0b61-1" = "run"
|
||||
"a64c69417bb98a93-1" = "illustration"
|
||||
"d62c4ceb9083ac5e-1" = "run"
|
||||
"a5bb45966ec97884-1" = "run"
|
||||
|
||||
# docs/Merging-Collections.md
|
||||
"befadadb67c37c7b-1" = "illustration"
|
||||
@@ -154,7 +156,8 @@ version = 2
|
||||
"3ecf97d15d7e8ae9-1" = "illustration"
|
||||
|
||||
# docs/Publishing-Your-Archive.md
|
||||
"a3e3e05317559025-1" = "illustration"
|
||||
"d582053c0ef82865-1" = "illustration"
|
||||
"9cf635e7d35fd992-1" = "illustration"
|
||||
"44fb1b5e4e4b8019-1" = "illustration"
|
||||
"e3fbec28244a58a0-1" = "illustration"
|
||||
|
||||
@@ -198,7 +201,7 @@ version = 2
|
||||
# docs/Setting-up-Authentication.md
|
||||
"e7e99e4304de5ac7-1" = "illustration"
|
||||
"9821835f15ad67b2-1" = "illustration"
|
||||
"fa8a95a3d0d51b76-2" = "run"
|
||||
"70389ddb24a77820-2" = "run"
|
||||
"7caaf25736e20132-1" = "illustration"
|
||||
"4f35c142a9d6a1c6-1" = "illustration"
|
||||
"38551637f2a2b4ee-1" = "illustration"
|
||||
@@ -229,10 +232,13 @@ version = 2
|
||||
"03803c44e54399c4-1" = "illustration"
|
||||
|
||||
# docs/Upgrading.md
|
||||
"91252f0b5ea69d28-1" = "illustration"
|
||||
"4c75ef58fc49ad99-1" = "illustration"
|
||||
"6678cb1b999c44ea-1" = "illustration"
|
||||
"09bbb69bbb6cf07f-1" = "illustration"
|
||||
"d4d84a74feec85a7-1" = "illustration"
|
||||
"23c840fa4bc05801-1" = "illustration"
|
||||
"3075ca963b666b72-1" = "illustration"
|
||||
"c47a6f74d71e34c2-1" = "illustration"
|
||||
"24ce21d1e112e656-1" = "illustration"
|
||||
"4a08ffc142dc82c9-1" = "illustration"
|
||||
|
||||
# docs/Usage.md
|
||||
"865eeba3c03d1e2d-1" = "run"
|
||||
@@ -249,12 +255,12 @@ version = 2
|
||||
"85f531841ec3e169-1" = "run"
|
||||
|
||||
[scenarios]
|
||||
"c6654ae1f15bca88-1" = "docker"
|
||||
"50b37bfc08509639-1" = "system"
|
||||
"220882ff87e9c738-1" = "docker"
|
||||
"39bc0d99ed165be2-1" = "system"
|
||||
"d5ae3240405670bb-1" = "collection"
|
||||
"a7a123ba522227be-1" = "system"
|
||||
"fcdd073a5440f5ca-1" = "system"
|
||||
"05be9e44a8ae9875-1" = "system"
|
||||
"8fe4a2c15abb0ab2-1" = "system"
|
||||
"2b08779e4d24d305-1" = "system"
|
||||
"6392c7b5f3593d07-1" = "collection"
|
||||
"e244b54155aa0bff-1" = "docker"
|
||||
@@ -266,7 +272,7 @@ version = 2
|
||||
"fab7ffa7e26bc3e5-1" = "collection"
|
||||
"2695777473e3a217-1" = "project"
|
||||
"16f996904f0eaa96-1" = "project"
|
||||
"b894a7c4f93e4e92-1" = "system"
|
||||
"167cab3db82a6aee-1" = "system"
|
||||
"6a6234b63cd3d0ae-1" = "collection"
|
||||
"396490664ce459e9-1" = "project"
|
||||
"d9ff84abeb97d380-1" = "collection"
|
||||
@@ -274,7 +280,7 @@ version = 2
|
||||
"0b307d4f1033723f-1" = "collection"
|
||||
"7d33d72cc96df65b-1" = "collection"
|
||||
"ac2c1555d3b9cd8f-1" = "project"
|
||||
"6b5798897504b1c8-1" = "system"
|
||||
"11a520069efa3387-1" = "system"
|
||||
"46fe9d81940ea385-1" = "collection"
|
||||
"396490664ce459e9-2" = "project"
|
||||
"201988374bff6d14-1" = "collection"
|
||||
@@ -284,20 +290,21 @@ version = 2
|
||||
"ce0de8ed0d7cd552-1" = "collection"
|
||||
"83cb9be168d203ca-1" = "collection"
|
||||
"2028f9a887105a13-1" = "collection"
|
||||
"fa8a95a3d0d51b76-1" = "system"
|
||||
"70389ddb24a77820-1" = "system"
|
||||
"c4266755c0748aa0-1" = "collection"
|
||||
"ea6d6196f0985d3c-1" = "collection"
|
||||
"10f9ca01b3b7a305-1" = "docker"
|
||||
"fef4f0641483ee49-1" = "docker"
|
||||
"8104bfe431c6b914-1" = "docker"
|
||||
"646dda5284de16a5-1" = "docker"
|
||||
"5e4e9a2a8a9435b2-1" = "system"
|
||||
"affb9c5d5980ed1c-1" = "docker"
|
||||
"0e9c1f41ef579e79-1" = "docker"
|
||||
"746a3e228d29f9d2-1" = "docker"
|
||||
"8d86727108f1dbad-1" = "docker"
|
||||
"c8df632a4f4e3100-1" = "system"
|
||||
"4d29a8d4b3902ab3-1" = "system"
|
||||
"73f9f36a369f2398-1" = "system"
|
||||
"c2eb27c95729f2b4-1" = "system"
|
||||
"fcaf2b90e39272df-1" = "system"
|
||||
"77b61fd52c4c3c32-1" = "collection"
|
||||
"a8f16f6be4f2b3c0-1" = "collection"
|
||||
"2905d7f9dece0b61-1" = "collection"
|
||||
"d62c4ceb9083ac5e-1" = "system-data"
|
||||
"a5bb45966ec97884-1" = "system-data"
|
||||
"9bbf385bf2eb1160-1" = "collection"
|
||||
"c84835e713d1c625-1" = "collection"
|
||||
"302de01a4a1490f0-1" = "docker"
|
||||
@@ -310,7 +317,7 @@ version = 2
|
||||
"d8013405262d9dee-1" = "system"
|
||||
"0fdcf228bb957933-1" = "docker-data"
|
||||
"74e75b4a84bbb636-1" = "docker"
|
||||
"fa8a95a3d0d51b76-2" = "system"
|
||||
"70389ddb24a77820-2" = "system"
|
||||
"1e5a9ff68510b35d-1" = "collection"
|
||||
"9613aa41cb49f134-1" = "collection"
|
||||
"ebebc2e7861a80e9-1" = "collection"
|
||||
@@ -328,12 +335,12 @@ version = 2
|
||||
"85f531841ec3e169-1" = "collection"
|
||||
|
||||
[environments]
|
||||
"c6654ae1f15bca88-1" = "docker"
|
||||
"50b37bfc08509639-1" = "ubuntu"
|
||||
"220882ff87e9c738-1" = "docker"
|
||||
"39bc0d99ed165be2-1" = "ubuntu"
|
||||
"d5ae3240405670bb-1" = "ubuntu"
|
||||
"a7a123ba522227be-1" = "root"
|
||||
"fcdd073a5440f5ca-1" = "root"
|
||||
"05be9e44a8ae9875-1" = "macos"
|
||||
"8fe4a2c15abb0ab2-1" = "macos"
|
||||
"2b08779e4d24d305-1" = "macos"
|
||||
"6392c7b5f3593d07-1" = "ubuntu"
|
||||
"e244b54155aa0bff-1" = "docker"
|
||||
@@ -345,7 +352,7 @@ version = 2
|
||||
"fab7ffa7e26bc3e5-1" = "ubuntu"
|
||||
"2695777473e3a217-1" = "ubuntu"
|
||||
"16f996904f0eaa96-1" = "ubuntu"
|
||||
"b894a7c4f93e4e92-1" = "ubuntu"
|
||||
"167cab3db82a6aee-1" = "ubuntu"
|
||||
"6a6234b63cd3d0ae-1" = "ubuntu"
|
||||
"396490664ce459e9-1" = "ubuntu"
|
||||
"d9ff84abeb97d380-1" = "ubuntu"
|
||||
@@ -353,7 +360,7 @@ version = 2
|
||||
"0b307d4f1033723f-1" = "ubuntu"
|
||||
"7d33d72cc96df65b-1" = "ubuntu"
|
||||
"ac2c1555d3b9cd8f-1" = "ubuntu"
|
||||
"6b5798897504b1c8-1" = "ubuntu"
|
||||
"11a520069efa3387-1" = "ubuntu"
|
||||
"46fe9d81940ea385-1" = "ubuntu"
|
||||
"396490664ce459e9-2" = "ubuntu"
|
||||
"201988374bff6d14-1" = "ubuntu"
|
||||
@@ -363,20 +370,21 @@ version = 2
|
||||
"ce0de8ed0d7cd552-1" = "ubuntu"
|
||||
"83cb9be168d203ca-1" = "ubuntu"
|
||||
"2028f9a887105a13-1" = "ubuntu"
|
||||
"fa8a95a3d0d51b76-1" = "ubuntu"
|
||||
"70389ddb24a77820-1" = "ubuntu"
|
||||
"c4266755c0748aa0-1" = "ubuntu"
|
||||
"ea6d6196f0985d3c-1" = "ubuntu"
|
||||
"10f9ca01b3b7a305-1" = "docker"
|
||||
"fef4f0641483ee49-1" = "docker"
|
||||
"8104bfe431c6b914-1" = "docker"
|
||||
"646dda5284de16a5-1" = "docker"
|
||||
"5e4e9a2a8a9435b2-1" = "macos"
|
||||
"affb9c5d5980ed1c-1" = "docker"
|
||||
"0e9c1f41ef579e79-1" = "docker"
|
||||
"746a3e228d29f9d2-1" = "docker"
|
||||
"8d86727108f1dbad-1" = "docker"
|
||||
"c8df632a4f4e3100-1" = "macos"
|
||||
"4d29a8d4b3902ab3-1" = "macos"
|
||||
"73f9f36a369f2398-1" = "root"
|
||||
"c2eb27c95729f2b4-1" = "ubuntu"
|
||||
"fcaf2b90e39272df-1" = "ubuntu"
|
||||
"77b61fd52c4c3c32-1" = "ubuntu"
|
||||
"a8f16f6be4f2b3c0-1" = "ubuntu"
|
||||
"2905d7f9dece0b61-1" = "ubuntu"
|
||||
"d62c4ceb9083ac5e-1" = "ubuntu"
|
||||
"a5bb45966ec97884-1" = "ubuntu"
|
||||
"9bbf385bf2eb1160-1" = "ubuntu"
|
||||
"c84835e713d1c625-1" = "ubuntu"
|
||||
"302de01a4a1490f0-1" = "docker"
|
||||
@@ -389,7 +397,7 @@ version = 2
|
||||
"d8013405262d9dee-1" = "root"
|
||||
"0fdcf228bb957933-1" = "docker"
|
||||
"74e75b4a84bbb636-1" = "docker"
|
||||
"fa8a95a3d0d51b76-2" = "ubuntu"
|
||||
"70389ddb24a77820-2" = "ubuntu"
|
||||
"1e5a9ff68510b35d-1" = "ubuntu"
|
||||
"9613aa41cb49f134-1" = "ubuntu"
|
||||
"ebebc2e7861a80e9-1" = "ubuntu"
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ ArchiveBox
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir my-archive; cd my-archive/
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade archivebox
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'archivebox>=0.9.0rc0,<0.10'
|
||||
|
||||
archivebox init
|
||||
archivebox install
|
||||
|
||||
@@ -479,6 +479,7 @@ def run_snippets(snippet_ids: tuple[str, ...]) -> None:
|
||||
f" chmod -R a+rX {root_checkout_venv}\n"
|
||||
"fi\n"
|
||||
f'export PYTHONPATH="{root_checkout_import_root}${{PYTHONPATH:+:${{PYTHONPATH}}}}"\n'
|
||||
f'export PATH="{root_checkout_python.parent}:$PATH"\n'
|
||||
f'exec {root_checkout_python} -m archivebox "$@"\n',
|
||||
)
|
||||
archivebox_wrapper.chmod(0o755)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "archivebox",
|
||||
"version": "0.9.35rc241",
|
||||
"version": "0.9.35rc286",
|
||||
"repository": "github:ArchiveBox/ArchiveBox",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "archivebox"
|
||||
version = "0.9.35rc241"
|
||||
version = "0.9.35rc286"
|
||||
requires-python = ">=3.13"
|
||||
description = "Self-hosted internet archiving solution."
|
||||
authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}]
|
||||
@@ -83,7 +83,7 @@ dependencies = [
|
||||
"abxbus", # direct imports only; version constrained by abx-dl -> abx-plugins
|
||||
"abxpkg", # direct imports only; version constrained by abx-dl -> abx-plugins
|
||||
"abx-plugins", # direct imports only; version constrained by abx-dl
|
||||
"abx-dl==1.12.110", # shared ArchiveBox downloader package
|
||||
"abx-dl==1.12.167", # 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
|
||||
]
|
||||
@@ -344,7 +344,7 @@ Donate = "https://github.com/ArchiveBox/ArchiveBox/wiki/Donations"
|
||||
|
||||
|
||||
[tool.bumpver]
|
||||
current_version = "v0.9.35rc241"
|
||||
current_version = "v0.9.35rc286"
|
||||
version_pattern = "vMAJOR.MINOR.PATCH[PYTAGNUM]"
|
||||
commit_message = "bump version {old_version} -> {new_version}"
|
||||
tag_message = "{new_version}"
|
||||
|
||||
@@ -34,7 +34,7 @@ uv run --project .. archivebox install
|
||||
Recommended CLI install:
|
||||
|
||||
```bash
|
||||
uv tool install --python 3.13 --prerelease allow --upgrade 'git+https://github.com/ArchiveBox/ArchiveBox.git@dev'
|
||||
uv tool install --python 3.13 --prerelease explicit --upgrade 'git+https://github.com/ArchiveBox/ArchiveBox.git@dev'
|
||||
mkdir -p ~/archivebox/data
|
||||
cd ~/archivebox/data
|
||||
archivebox init
|
||||
|
||||
@@ -13,24 +13,23 @@ supported-markers = [
|
||||
]
|
||||
|
||||
[options]
|
||||
exclude-newer = "2026-08-08T17:08:49.206372985Z"
|
||||
exclude-newer = "2026-08-11T03:34:33.381986164Z"
|
||||
exclude-newer-span = "P5D"
|
||||
|
||||
[options.exclude-newer-package]
|
||||
abxbus = { timestamp = "2026-08-13T17:08:48.206388628Z", span = "PT1S" }
|
||||
abxbus = { timestamp = "2026-08-16T03:34:32.382002568Z", 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.12.110"
|
||||
version = "1.12.167"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "abx-plugins", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "abxbus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "abxpkg", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "jambo", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
@@ -39,26 +38,25 @@ 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/a8/01/d90f9bd97cf8640f025be1c00acdb853da956636712a8103f9c3fd38ea72/abx_dl-1.12.110.tar.gz", hash = "sha256:2c4631affc31d21732e48bfade094bc5db1e469de7dcedcea22774c69de4aefb", size = 88243, upload-time = "2026-08-13T17:03:04.670869Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/28/b9/4745fe4757554fb6be5686c30cb96fbcc60a2051a2fa7c4737e419de667a/abx_dl-1.12.167.tar.gz", hash = "sha256:e4df25d5ddbde271d1afe6af6319e7486db1c6f1da68f19e6e6ee2cb171aaec2", size = 83586, upload-time = "2026-08-16T03:29:30.997059Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/ab/1e5def077aecbb0e8501fbd3c5da7bff4c14ebbe30813558b829a1149972/abx_dl-1.12.110-py3-none-any.whl", hash = "sha256:cc9c22586ccf7c3c8c0769b7427cd4f21a82a9b0c0063b1df01e6d61b44082c3", size = 92313, upload-time = "2026-08-13T17:03:05.717631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/69/999aec7d2cf6bd6817c991fb0fb85f0c38879b7eda34b3d704598d5171f9/abx_dl-1.12.167-py3-none-any.whl", hash = "sha256:64e16593ca53678e2766a8f42e16a2670c2a7e793e25b348b9a04b7d72e145f2", size = 87277, upload-time = "2026-08-16T03:29:29.741963Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abx-plugins"
|
||||
version = "1.12.98"
|
||||
version = "1.12.154"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "abxbus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "abxpkg", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "imagesize", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "jambo", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ 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/56/60/9dfb16893a1ad54279ae2f496f77af72c10a4e1da7f06798844e6dd98eae/abx_plugins-1.12.98.tar.gz", hash = "sha256:60ccfca900db653453f086daf2f615996e14487467013b283b7f2d18ad4650f6", size = 255512, upload-time = "2026-08-13T16:48:50.176Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4a/a7/ba36af9c53fe9594df08bfad53078a8e63f646eb06845b9dddd1f44db7b8/abx_plugins-1.12.154.tar.gz", hash = "sha256:a5811447d03e501edc4cab28a7bcc845c6f688560fb9a387f04c1c2ea0b77d80", size = 253870, upload-time = "2026-08-16T03:16:29.243Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/fd/83f7ab2c0b54064d21dc1bf06c24cb81940ef22e457f21605bc81148da65/abx_plugins-1.12.98-py3-none-any.whl", hash = "sha256:f4bce2858987643228fc3b1666384ca6e21fca8b3deed603bfc42c44f8a3ca91", size = 406217, upload-time = "2026-08-13T16:48:51.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/2a/4ea008be973c9721a20a1276ba00825e111630621ec4baf841ec9c213f18/abx_plugins-1.12.154-py3-none-any.whl", hash = "sha256:e208bff42d875e0d5711d6091e41b82848ec78682ae0c3b11e1438eae3234a2c", size = 405106, upload-time = "2026-08-16T03:16:27.703Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -77,7 +75,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "abxpkg"
|
||||
version = "1.12.61"
|
||||
version = "1.12.91"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
@@ -85,9 +83,9 @@ dependencies = [
|
||||
{ name = "rich-click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5d/26/217df4713c35fedf0309c28f0b8276a37ac20ffc8437917069ad5d73ba6d/abxpkg-1.12.61.tar.gz", hash = "sha256:786357fdba13df9f69a66b542fefea924c7efc063208bb3d87c210472ca442b2", size = 233362, upload-time = "2026-08-13T16:37:19.019Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/a9/39f043f58d667f48b61a9fd68ff9a92cce57ee7810495b2eb533aaeff122/abxpkg-1.12.91.tar.gz", hash = "sha256:1c8b0fe0cea603ff51251d04b02257c136d8fbc1e8255533dac5ff5f86e8a1fd", size = 238948, upload-time = "2026-08-16T01:50:16.768Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/58/7374ddf88f934427c52971fdc8a3173d2444d66c90617ffb5354f1017a99/abxpkg-1.12.61-py3-none-any.whl", hash = "sha256:52032baf7e818da26b668e697d6933e205549bd397702ba1c38b462dcb7c476f", size = 247543, upload-time = "2026-08-13T16:37:20.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/40/5e1e5fa17af317045f234cac8b3b9edbca6e3a68a2efe6cfb9343b64d226/abxpkg-1.12.91-py3-none-any.whl", hash = "sha256:06e332dd7e6deca09155ed687967aed497a4d2123d1ef92c782db00e72f494ed", size = 253111, upload-time = "2026-08-16T01:50:15.275Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -122,7 +120,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "archivebox"
|
||||
version = "0.9.35rc241"
|
||||
version = "0.9.35rc286"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "abx-dl", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
@@ -222,7 +220,7 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "abx-dl", specifier = "==1.12.110" },
|
||||
{ name = "abx-dl", specifier = "==1.12.167" },
|
||||
{ name = "abx-plugins" },
|
||||
{ name = "abxbus" },
|
||||
{ name = "abxpkg" },
|
||||
@@ -908,15 +906,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/85/c4e42d21cf748c696b8c05316bbd8e8666f17eeda0cf1743056f4cf7622b/djdt_flamegraph-0.2.13-py2.py3-none-any.whl", hash = "sha256:b3252b8cc9b586829166cc158b26952626cd6f41a3ffa92dceef2f5dbe5b99a0", size = 15256, upload-time = "2020-01-17T05:40:37.799Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.22.4"
|
||||
@@ -926,19 +915,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email-validator"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "dnspython", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.2.1"
|
||||
@@ -1094,17 +1070,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jambo"
|
||||
version = "0.1.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "email-validator", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "jsonschema", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/91/f5/74de157c7aece6a070f99f18201a0e2f46cdfd0f9e337efd411745ed9b22/jambo-0.1.7.tar.gz", hash = "sha256:df89ab8209ebdf7a6e92252ec925979cd3d32811bf4a8182a97dc35b7df58f74", size = 137822, upload-time = "2026-01-14T19:17:30.302Z" }
|
||||
|
||||
[[package]]
|
||||
name = "jedi"
|
||||
version = "0.20.0"
|
||||
@@ -1129,33 +1094,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.26.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "jsonschema-specifications", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "rpds-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema-specifications"
|
||||
version = "2025.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexid"
|
||||
version = "2021.1006"
|
||||
@@ -2124,19 +2062,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/77/ed589c75db5d02a77a1d5d2d9abc63f29676467d396c64277f98b50b79c2/recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f", size = 10214, upload-time = "2020-12-17T19:24:55.137Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.37.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "rpds-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2026.7.10"
|
||||
@@ -2260,74 +2185,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpds-py"
|
||||
version = "2026.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.21"
|
||||
|
||||
Reference in new issue
Block a user