Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
2cc17f9
rtbit-fluent: Files tab as a folder Tree with include/skip priority (…
thedancingdeveloper Nov 2, 2026
664e4ba
rtbit-ui-core: drop last egui reference from error doc comment (WI-2770)
thedancingdeveloper Nov 2, 2026
ef9bbe2
Revert "rtbit-fluent: Files tab as a folder Tree with include/skip pr…
thedancingdeveloper Nov 2, 2026
6727fba
rtbit-fluent: Files tab as a folder Tree with include/skip priority (…
thedancingdeveloper Nov 2, 2026
b57dd86
test(swarmforge): router-level tests for POST /torrents/{id}/files/{i…
thedancingdeveloper Nov 2, 2026
320c84c
qbit: real app/preferences share limits and dht/pex/lsd; apply on set…
thedancingdeveloper Nov 2, 2026
06ea46f
ci: report nightly release dry-run failures (WI-2898)
thedancingdeveloper Nov 2, 2026
2b27b01
rtbit-fluent: main-screen keyboard shortcuts (WI-2869)
thedancingdeveloper Nov 2, 2026
14adefa
ci: enforce cargo-deny dependency policy
thedancingdeveloper Nov 2, 2026
e196909
Add torrent table keyboard navigation
thedancingdeveloper Nov 2, 2026
9631d56
rtbit-fluent: add detail pane empty states
thedancingdeveloper Nov 2, 2026
8a0a837
Improve toolbar accessibility and focus visibility
thedancingdeveloper Nov 2, 2026
0fedb12
rtbit-fluent: dialogs close on Escape and confirm on Enter (WI-2871)
thedancingdeveloper Nov 2, 2026
4750f4a
rtbit-fluent: daemon-lost banner with 1s-30s backoff reconnect (WI-2874)
thedancingdeveloper Nov 2, 2026
a1ff3e5
rtbit-fluent: tests for detail pane tabs (WI-2875)
thedancingdeveloper Nov 2, 2026
c87bb06
rtbit-fluent: tests for status bar, shell navigation and bridge error…
thedancingdeveloper Nov 2, 2026
a45e82f
qbit API: app/getFreeSpaceAtPath and real free_space_on_disk in sync/…
thedancingdeveloper Nov 2, 2026
81d7736
rtbit-fluent: right-click context menu on torrent rows (WI-2878)
thedancingdeveloper Nov 2, 2026
9cbc80f
qbit API: add torrents/pieceAvailability (WI-2886)
thedancingdeveloper Nov 2, 2026
310be8e
rtbit-fluent: persist table sort and column widths; add Up column (WI…
thedancingdeveloper Nov 2, 2026
f1ff9b3
docs: audit Fluent client parity (WI-2877)
thedancingdeveloper Nov 2, 2026
2ad2672
feat(rtbit-fluent): open add dialog from torrent drops
thedancingdeveloper Nov 2, 2026
99de228
rtbit-fluent: add RSS rule priority selector (#287)
thedancingdeveloper Nov 2, 2026
8d9cef1
rtbit-fluent: add trackers from torrent detail (#286)
thedancingdeveloper Nov 2, 2026
10b3f21
Add per-category share limits (WI-2889)
thedancingdeveloper Nov 2, 2026
570e3f3
Report real qbit API build information
thedancingdeveloper Nov 2, 2026
3d1ede6
Resolve metadata test merge
thedancingdeveloper Nov 2, 2026
2e83871
CI: check generated parity summary drift
thedancingdeveloper Nov 2, 2026
c7f7aef
ci: test fluent app for client dependency changes
thedancingdeveloper Nov 2, 2026
fd88576
Add Radarr and Lidarr replay fixtures (WI-2896)
thedancingdeveloper Nov 2, 2026
80a4ad7
Add missing payload diagnosis
thedancingdeveloper Nov 2, 2026
54bf844
Add bulk category and tag editing
thedancingdeveloper Nov 2, 2026
1056041
Add real per-file priority levels
thedancingdeveloper Nov 2, 2026
04a7261
Release: smoke-test fluent artifacts before publishing
thedancingdeveloper Nov 2, 2026
7ca2236
CI: smoke test daemon container in merge queue
thedancingdeveloper Nov 2, 2026
06fb7db
Add fluent per-file priority controls
thedancingdeveloper Nov 2, 2026
53a0a5c
librtbit: crash-safe atomic state writes; quarantine corrupt state files
thedancingdeveloper Nov 2, 2026
8f03ab6
Low-disk guard: default 1 GiB floor and 'low disk space' status error
thedancingdeveloper Nov 2, 2026
e5ce937
Fixes #290: bound graceful shutdown save
thedancingdeveloper Nov 2, 2026
643bfaa
fix: add tracker and DHT retry backoff (Fixes #299)
thedancingdeveloper Nov 2, 2026
99d83b5
rtbit-fluent: add filter sidebar (Fixes #297)
thedancingdeveloper Nov 2, 2026
865a324
rtbit-fluent: add status bar speed controls (Fixes #296)
thedancingdeveloper Nov 2, 2026
c2457ce
fix(fluent-1): restore build after concurrent lands
thedancingdeveloper Nov 2, 2026
7f11b6e
Fixes #291: add torrent metadata and management controls
thedancingdeveloper Nov 2, 2026
39c5a26
Add torrent location and rename dialogs; Fixes #298
thedancingdeveloper Nov 2, 2026
b964675
Implement qbit app network and shutdown endpoints (Fixes #293)
thedancingdeveloper Nov 2, 2026
6ebfb32
Fix release dry-run runner policy
thedancingdeveloper Nov 2, 2026
786e695
chore: fold Dependabot updates and group scheduling
thedancingdeveloper Nov 2, 2026
4a368c7
Harden session recovery and torrent locations
thedancingdeveloper Nov 2, 2026
633fe66
feat(qbit): report in-flight piece states and refresh parity notes (F…
thedancingdeveloper Nov 2, 2026
b0702e2
style: cargo fmt
thedancingdeveloper Nov 2, 2026
5425cab
Fix clippy needless_borrows_for_generic_args in qbit tags handler
thedancingdeveloper Nov 2, 2026
7da2cae
feat: show Files missing status with Set location / Force recheck (WI…
thedancingdeveloper Nov 2, 2026
b295956
Recheck throughput: concurrent-recheck cap (default 2), live resize, …
thedancingdeveloper Nov 2, 2026
eaa1b49
WI-2895: shared SSRF-safe fetcher for URL add, RSS and blocklists
thedancingdeveloper Nov 2, 2026
e880166
fix(webui): replace self-referential missingFiles.ts shim with a .d.ts
thedancingdeveloper Nov 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 9 additions & 63 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,77 +1,23 @@
# Dependabot version-update configuration.
#
# Security updates are enabled separately at the repo level and run regardless
# of this file. This config adds scheduled *version* updates to keep manifests
# and lockfiles fresh proactively. Updates are grouped to minimise PR noise.
version: 2
updates:
# ---- Rust: main workspace (covers all crates/* and desktop/src-tauri) ----
- package-ecosystem: cargo
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
interval: monthly
groups:
cargo-minor-patch:
update-types:
- minor
- patch

# ---- Rust: benchv2 (excluded from the workspace) ----
- package-ecosystem: cargo
directory: "/benchv2"
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
benchv2-minor-patch:
update-types:
- minor
- patch

# ---- Rust: fuzz (excluded from the workspace) ----
- package-ecosystem: cargo
directory: "/fuzz"
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
fuzz-minor-patch:
update-types:
- minor
- patch

# ---- npm: web UI (crates/librtbit/webui) ----
- package-ecosystem: npm
directory: "/crates/librtbit/webui"
schedule:
interval: weekly
open-pull-requests-limit: 10
groups:
webui-minor-patch:
update-types:
- minor
- patch

# ---- npm: root workspace (includes the desktop package) ----
cargo:
patterns: ["*"]
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
interval: monthly
groups:
root-npm-minor-patch:
update-types:
- minor
- patch

# ---- GitHub Actions workflows ----
npm:
patterns: ["*"]
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
interval: monthly
groups:
actions-all:
patterns:
- "*"
actions:
patterns: ["*"]
24 changes: 18 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
rust: ${{ steps.classify.outputs.rust }}
rust_full: ${{ steps.classify.outputs.rust_full }}
rust_packages: ${{ steps.classify.outputs.rust_packages }}
rust_fluent: ${{ steps.classify.outputs.rust_fluent }}
desktop: ${{ steps.classify.outputs.desktop }}
webui: ${{ steps.classify.outputs.webui }}
website: ${{ steps.classify.outputs.website }}
Expand All @@ -46,11 +47,11 @@ jobs:
run: |
set -euo pipefail
if [[ "$EVENT" != pull_request ]]; then
printf 'rust=true\nrust_full=true\nrust_packages=\ndesktop=true\nwebui=true\nwebsite=true\n' >>"$GITHUB_OUTPUT"
printf 'rust=true\nrust_full=true\nrust_packages=\nrust_fluent=true\ndesktop=true\nwebui=true\nwebsite=true\n' >>"$GITHUB_OUTPUT"
exit 0
fi
files="$(git diff --name-only "$(git merge-base "$BASE_SHA" HEAD)" HEAD)"
rust=false full=false desktop=false webui=false website=false
rust=false full=false fluent=false desktop=false webui=false website=false
packages=()
add_package() {
local pkg
Expand Down Expand Up @@ -80,8 +81,15 @@ jobs:
# The menu description has no window toolkit, so CI can test it.
# The Tauri binary stays excluded: the runner has no webkit.
desktop/menu/*) rust=true desktop=true; add_package rtbit-desktop-menu ;;
# The native app consumes the daemon's API types directly.
crates/librtbit/src/http_api_types.rs)
rust=true; fluent=true ;;
crates/librtbit/*) rust=true desktop=true; add_package swarmforge ;;
crates/rtbit/*) rust=true; add_package rtbit ;;
# The native client and UI crates are reverse dependencies of the
# fluent app, so changes to either must test the app as well.
crates/rtbit-client/*|crates/rtbit-ui-core/*)
rust=true; fluent=true ;;
website/*) website=true ;;
# An unrecognised crate path (a new member, or a path we have not
# mapped yet) is not safe to narrow, so it takes everything.
Expand All @@ -90,11 +98,11 @@ jobs:
done <<<"$files"
# A Rust change we could not attribute to a package still needs the
# full workspace rather than no check at all.
if [[ "$rust" == true && "$full" == false && ${#packages[@]} -eq 0 ]]; then
if [[ "$rust" == true && "$full" == false && "$fluent" == false && ${#packages[@]} -eq 0 ]]; then
full=true
fi
printf 'rust=%s\nrust_full=%s\nrust_packages=%s\ndesktop=%s\nwebui=%s\nwebsite=%s\n' \
"$rust" "$full" "${packages[*]-}" "$desktop" "$webui" "$website" | tee -a "$GITHUB_OUTPUT"
printf 'rust=%s\nrust_full=%s\nrust_packages=%s\nrust_fluent=%s\ndesktop=%s\nwebui=%s\nwebsite=%s\n' \
"$rust" "$full" "${packages[*]-}" "$fluent" "$desktop" "$webui" "$website" | tee -a "$GITHUB_OUTPUT"

# Shell checks for the release scripts and repository layout. They take
# seconds and touch no network, so they run on every event.
Expand Down Expand Up @@ -154,7 +162,7 @@ jobs:
with:
shared-key: ci
save-if: ${{ env.RUST_CACHE_SAVE_IF }}
- uses: taiki-e/install-action@8cca0918f8471c33515202b73f56ab27f2bed283 # v2.87.29
- uses: taiki-e/install-action@5488ac5a951645ce95062214902ac4b6bc1a1037 # v2.87.32
with:
tool: cargo-nextest
- uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
Expand All @@ -181,10 +189,14 @@ jobs:
- name: Test
env:
SCOPE_ARGS: ${{ steps.scope.outputs.args }}
RUST_FLUENT: ${{ needs.changes.outputs.rust_fluent }}
CARGO_PROFILE_TEST_DEBUG: 0
run: |
read -r -a args <<<"$SCOPE_ARGS"
cargo nextest run "${args[@]}" --all-targets --locked --no-default-features --features default-tls
if [[ "$RUST_FLUENT" == true ]]; then
cargo nextest run -p rtbit-fluent --all-targets --locked
fi
# The shipped binary is worth a release-profile compile on a PR that
# touches it. The queue builds it for every entry anyway.
- name: Build the release binary
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ jobs:
esac
fi
printf 'ref=%s\n' "$ref" >> "$GITHUB_OUTPUT"
- uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
- uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
languages: ${{ matrix.language }}
- uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
- uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
category: /language:${{ matrix.language }}
ref: ${{ steps.target.outputs.ref }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/compat-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
- uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
- uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
63 changes: 62 additions & 1 deletion .github/workflows/container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [main, dev]
tags: ['v*']
workflow_dispatch:
merge_group:
types: [checks_requested]

permissions:
contents: read
Expand All @@ -16,6 +18,7 @@ concurrency:

jobs:
publish:
if: github.event_name != 'merge_group'
# Privileged publication capacity is selected only for this repository and
# this workflow deliberately has no pull_request trigger.
runs-on: [self-hosted, linux, docker, publish]
Expand All @@ -25,7 +28,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
- uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down Expand Up @@ -80,3 +83,61 @@ jobs:
-H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json' \
"https://ghcr.io/v2/thedancingdeveloper-org/rusttorrent/manifests/$tag" > manifest.json
jq -e '.schemaVersion == 2' manifest.json >/dev/null

smoke:
if: github.event_name == 'merge_group'
runs-on: [self-hosted, linux, docker, publish]
permissions:
contents: read
env:
IMAGE: rusttorrent-container-smoke:${{ github.sha }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Build the daemon image
run: docker build --tag "$IMAGE" --file Dockerfile .
- name: Start and probe the daemon
shell: bash
run: |
set -euo pipefail
volume="rusttorrent-smoke-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
container="rusttorrent-smoke-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
cleanup() {
docker rm --force "$container" >/dev/null 2>&1 || true
docker volume rm "$volume" >/dev/null 2>&1 || true
docker image rm "$IMAGE" >/dev/null 2>&1 || true
}
trap cleanup EXIT

docker volume create "$volume" >/dev/null
docker run --detach --name "$container" --volume "$volume:/home/rtbit/db" \
--publish 3030:3030 "$IMAGE" >/dev/null

deadline=$((SECONDS + 30))
setup_token=''
while (( SECONDS < deadline )); do
if ! setup_token=$(docker exec "$container" cat /home/rtbit/db/session/setup-token 2>/dev/null); then
setup_token=''
fi
if [[ -n "$setup_token" ]] && curl --fail --silent --show-error \
--output /dev/null http://127.0.0.1:3030/healthz; then
break
fi
sleep 1
done
[[ -n "$setup_token" ]]
(( SECONDS < deadline ))

curl --fail --silent --show-error \
--header 'Content-Type: application/json' \
--data "{\"username\":\"smoke\",\"password\":\"smoke-password\",\"setup_token\":\"$setup_token\"}" \
http://127.0.0.1:3030/api/v2/auth/setup >/dev/null
version=$(curl --fail --silent --show-error \
--user smoke:smoke-password http://127.0.0.1:3030/api/v2/app/webapiVersion)
[[ "$version" == '2.11.3' ]]
curl --fail --silent --show-error \
http://127.0.0.1:3030/healthz >/dev/null

docker stop --time 30 "$container" >/dev/null
[[ "$(docker inspect --format '{{.State.ExitCode}}' "$container")" == 0 ]]
docker run --rm --volume "$volume:/data" --entrypoint sh "$IMAGE" \
-c 'test -s /data/session/session.json'
19 changes: 19 additions & 0 deletions .github/workflows/policy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,22 @@ jobs:
- name: Verify migration policies
shell: bash
run: bash ci/migration-policy.sh
- name: Detect dependency policy changes
id: dependency-policy
if: github.event_name == 'pull_request'
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
if git diff --quiet "$BASE_SHA" HEAD -- Cargo.lock ':(glob)**/Cargo.toml' deny.toml; then
echo "run=false" >> "$GITHUB_OUTPUT"
else
echo "run=true" >> "$GITHUB_OUTPUT"
fi
- uses: taiki-e/install-action@8cca0918f8471c33515202b73f56ab27f2bed283 # v2.87.29
if: github.event_name != 'pull_request' || steps.dependency-policy.outputs.run == 'true'
with:
tool: cargo-deny@0.20.2
- name: Verify Rust dependency policy
if: github.event_name != 'pull_request' || steps.dependency-policy.outputs.run == 'true'
run: cargo deny check advisories licenses bans sources
62 changes: 62 additions & 0 deletions .github/workflows/release-dry-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ jobs:
cargo build -p rtbit-fluent --release --target x86_64-pc-windows-msvc
rm -rf target/desktop-stage
bash packaging/scripts/build-desktop.sh windows "$tag" target/desktop-stage
- name: Smoke-test Windows fluent artifact
shell: bash
run: |
set -euo pipefail
version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/rtbit/Cargo.toml | head -1)"
tag="v$version"
expected="rtbit-fluent $version"
actual="$(target/desktop-stage/rtbit-fluent-"$tag"-windows-x86_64.exe --version)"
test "$actual" = "$expected"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-dry-run-windows-fluent-${{ github.sha }}
Expand Down Expand Up @@ -157,6 +166,14 @@ jobs:
rm -rf target/desktop-stage
bash packaging/scripts/build-desktop.sh linux "$tag" target/desktop-stage
bash packaging/scripts/build-desktop.sh sbom "$tag" target/desktop-stage
- name: Smoke-test Linux fluent artifact
shell: bash
run: |
set -euo pipefail
tag="${{ steps.tag.outputs.tag }}"
expected="rtbit-fluent ${tag#v}"
actual="$(target/desktop-stage/rtbit-fluent-"$tag"-linux-x86_64 --version)"
test "$actual" = "$expected"
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
continue-on-error: true
with:
Expand Down Expand Up @@ -278,3 +295,48 @@ jobs:
done
test "$count" -eq 2 || { echo "::error::expected 2 signed executables (daemon and fluent), verified $count"; exit 1; }
(cd release-artifacts && sha256sum -c "SHA256SUMS-$tag.txt")

report-failure:
name: Report release dry-run failure
if: >-
${{ always() && github.event_name != 'pull_request' &&
(needs.build-windows-fluent.result == 'failure' || needs.dry-run.result == 'failure') }}
needs: [build-windows-fluent, dry-run]
runs-on: [self-hosted, linux]
permissions:
contents: read
issues: write
steps:
- name: Open or update the tracking issue
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const title = '[CI] Release dry run is failing';
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const report = [
`The ${context.eventName} release dry run failed.`,
'',
`Run: ${runUrl}`,
`Commit: ${context.sha}`,
'',
'This workflow does not publish release artifacts.',
].join('\n');
const {data: issues} = await github.rest.issues.listForRepo({
...context.repo,
state: 'open',
per_page: 100,
});
const existing = issues.find(issue => !issue.pull_request && issue.title === title);
if (existing) {
await github.rest.issues.createComment({
...context.repo,
issue_number: existing.number,
body: report,
});
} else {
await github.rest.issues.create({
...context.repo,
title,
body: report,
});
}
Loading
Loading