ab4f95b166
qBittorrent's add call answers `Ok.` and nothing else — no hash, no name, no duplicate signal — so arr derives the v1 infohash from the magnet or the `.torrent` bytes before the call and looks the torrent up by it. That also drops Transmission's numeric torrent id: the hash is the only identity now. The reaper needs "stopped because a share limit was reached", and qBittorrent's state field cannot tell that apart from a hand-paused torrent. So the ratio and idle counters are checked against the limits arr set, and a torrent stopped by a global limit reads as still seeding rather than being deleted. The WebUI needs a login, so `ARR_QBITTORRENT_USERNAME` and `ARR_QBITTORRENT_PASSWORD` join the env-only secrets; leaving both unset is valid for an instance that whitelists arr's subnet. Verified against qBittorrent 5 (WebAPI 2.15.1) in a container: it rejects `setShareLimits` without `shareLimitAction`, which 4.x ignores, so it is always sent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
123 lines
4.3 KiB
Makefile
123 lines
4.3 KiB
Makefile
# Dev entry points. `just` with no args lists them.
|
|
|
|
# The local development database. Committed offline query data (.sqlx) means
|
|
# neither CI nor a plain `cargo build` needs this to exist.
|
|
database_url := env_var_or_default("DATABASE_URL", "sqlite://dev.db")
|
|
|
|
# The full per-push gate, same as CI. Run this before pushing.
|
|
ci: fmt-check lint deps test web-check
|
|
|
|
# Formatting, checked not applied.
|
|
fmt-check:
|
|
cargo fmt --all -- --check
|
|
|
|
# Format every Rust source.
|
|
fmt:
|
|
cargo fmt --all
|
|
|
|
# Clippy, warnings denied. `--all-features` matters: every subtitle
|
|
# translation backend sits behind a default-off cargo feature, so without it
|
|
# the gate never compiles a line of them (#191, #192).
|
|
lint:
|
|
cargo clippy --all-targets --all-features -- -D warnings
|
|
|
|
# Unused workspace dependencies. Stable toolchain, unlike cargo-udeps.
|
|
deps:
|
|
cargo machete
|
|
|
|
# Unit and integration tests, excluding the e2e crate. `--all-features` for
|
|
# the same reason `lint` needs it: the translation backends are feature-gated
|
|
# and would otherwise never run.
|
|
test:
|
|
cargo nextest run --workspace --exclude arr-e2e --all-features
|
|
|
|
# End-to-end tests. Needs a qBittorrent container (`just e2e-up`); not part
|
|
# of the push gate. Never point this at the production qBittorrent.
|
|
e2e:
|
|
cargo nextest run -p arr-e2e
|
|
|
|
# Throwaway local qBittorrent for `just e2e`, mirroring the CI container.
|
|
#
|
|
# The config is seeded before first start because qBittorrent prints a random
|
|
# WebUI password per boot and has no environment variable to set one; the
|
|
# subnet whitelist is what lets the tests talk to it without credentials.
|
|
e2e-up:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
mkdir -p /tmp/arr-e2e-qbt/config/qBittorrent
|
|
cat > /tmp/arr-e2e-qbt/config/qBittorrent/qBittorrent.conf <<'CONF'
|
|
[Preferences]
|
|
WebUI\Port=8080
|
|
WebUI\AuthSubnetWhitelistEnabled=true
|
|
WebUI\AuthSubnetWhitelist=0.0.0.0/0
|
|
WebUI\CSRFProtection=false
|
|
WebUI\HostHeaderValidation=false
|
|
Downloads\SavePath=/downloads
|
|
CONF
|
|
docker run --rm -d --name arr-e2e-qbittorrent \
|
|
-e PUID=1000 -e PGID=1000 -e WEBUI_PORT=8080 -p 8080:8080 \
|
|
-v /tmp/arr-e2e-qbt/config:/config \
|
|
linuxserver/qbittorrent:latest
|
|
until curl -sf http://127.0.0.1:8080/api/v2/app/version >/dev/null; do sleep 1; done
|
|
|
|
# Stop and discard the local qBittorrent container.
|
|
e2e-down:
|
|
docker stop arr-e2e-qbittorrent
|
|
|
|
# Frontend lint and typecheck. No-op until the SPA exists (issue #6). Probes
|
|
# for package.json, not the directory: `just gen-client` writes into web/ and
|
|
# must not turn the gate on before there is a project to check.
|
|
web-check:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
if [ ! -f web/package.json ]; then echo "web/ not present yet, skipping"; exit 0; fi
|
|
pnpm -C web install --frozen-lockfile
|
|
pnpm -C web exec biome ci .
|
|
pnpm -C web exec tsc -b --noEmit
|
|
pnpm -C web run check-tokens
|
|
|
|
# Build the SPA and the binary that embeds it.
|
|
build:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
if [ -f web/package.json ]; then
|
|
pnpm -C web install --frozen-lockfile
|
|
pnpm -C web build
|
|
fi
|
|
cargo build --release
|
|
|
|
# Regenerate the TypeScript client from the OpenAPI document (DESIGN.md §9.1).
|
|
# The document is dumped from the binary, so nothing has to be running. Both
|
|
# outputs are generated: never edit them, and never hand-write the spec.
|
|
gen-client:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
mkdir -p web/src/api
|
|
cargo run --quiet -p arr-daemon --bin arr -- --openapi > web/src/api/openapi.json
|
|
pnpm dlx openapi-typescript@7 web/src/api/openapi.json -o web/src/api/schema.d.ts
|
|
|
|
# Recreate the development database from an empty file.
|
|
db-reset:
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
export DATABASE_URL="{{database_url}}"
|
|
sqlx database drop -y
|
|
sqlx database create
|
|
sqlx migrate run --source crates/arr-db/migrations
|
|
|
|
# Refresh the committed offline query data. Run after changing any query.
|
|
db-prepare: db-reset
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
export DATABASE_URL="{{database_url}}"
|
|
cargo sqlx prepare --workspace -- --all-targets
|
|
|
|
# Advisory and licence scan. Scheduled, not part of the push gate.
|
|
audit:
|
|
cargo deny check
|
|
|
|
# Install the tools the gate needs.
|
|
setup:
|
|
cargo install cargo-nextest cargo-machete --locked
|
|
cargo install sqlx-cli --no-default-features --features sqlite,rustls --locked
|