-
Notifications
You must be signed in to change notification settings - Fork 53
feat(qbittorrent-e2e): add qBittorrent E2E runner with Docker Compose orchestration #1707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
josecelano
merged 93 commits into
torrust:develop
from
josecelano:1525-02-qbittorrent-e2e
Apr 28, 2026
Merged
Changes from all commits
Commits
Show all changes
93 commits
Select commit
Hold shift + click to select a range
1c34026
docs(issues): rename 1525-02 spec for issue 1706
josecelano 55ef63a
feat(qbittorrent-e2e): add --keep-containers flag and fix race condit…
josecelano 2885f0b
test(qbittorrent-e2e): verify leecher completion and update troublesh…
josecelano d154153
fix(qbittorrent-e2e): address PR review feedback
josecelano 24061f5
docs(skills): add PR review thread workflows
josecelano 6f8959d
docs(agents): clarify linter command usage
josecelano a5f7a23
refactor(qbittorrent-e2e): extract bencode helpers
josecelano cc6ce0b
refactor(qbittorrent-e2e): extract single-client initialization
josecelano 231b1ee
refactor(qbittorrent-e2e): extract single-client torrent upload
josecelano abdfc29
refactor(qbittorrent-e2e): extract single-client torrent counting
josecelano 293d591
refactor(qbittorrent-e2e): extract workspace module
josecelano 8e4341d
refactor(qbittorrent-e2e): move upload label context into qbittorrent…
josecelano 55076cd
refactor(qbittorrent-e2e): extract torrent upload value type
josecelano 086aeec
refactor(qbittorrent-e2e): inline torrent upload helper
josecelano ddd39e0
refactor(qbittorrent-e2e): move torrent count logic into client
josecelano 757009f
refactor(qbittorrent-e2e): replace path and polling literals with con…
josecelano a84b53a
refactor(qbittorrent-e2e): add aliases for client pair signatures
josecelano d0ae4a8
refactor(qbittorrent-e2e): normalize runner role and service naming
josecelano e1a0bfa
refactor(qbittorrent-e2e): extract transfer flow phase from runner
josecelano 0c6f35a
refactor(qbittorrent-e2e): add reusable poller helper
josecelano b6c2cfb
refactor(qbittorrent-e2e): extract login candidate helper state
josecelano c061063
refactor(qbittorrent-e2e): pass initial passwords to login candidates
josecelano ae1e4c0
refactor(qbittorrent-e2e): use named payload and torrent result
josecelano 50a583b
refactor(qbittorrent-e2e): extract torrent artifact builders
josecelano 20936b8
refactor(qbittorrent-e2e): introduce client role enum
josecelano fba3fb7
refactor(qbittorrent-e2e): group flow helpers in scenario runner
josecelano 873755b
refactor(qbittorrent-e2e): tidy polling docs and hash formatting
josecelano 11f1929
refactor(qbittorrent-e2e): extract client role module
josecelano 689268c
refactor(qbittorrent-e2e): extract poller module
josecelano 33060e0
refactor(ci): move compose port wait into DockerCompose
josecelano 65f66fb
refactor(qbittorrent-e2e): split run() into ARRANGE and ACT phases
josecelano 95e9fde
refactor(qbittorrent-e2e): extract fixture builders into scenario_ste…
josecelano d35c80d
refactor(qbittorrent-e2e): extract generic torrent submission and pre…
josecelano 940ffa6
refactor(qbittorrent-e2e): extract login readiness step
josecelano 8c6046a
refactor(qbittorrent-e2e): split login step, extract leecher steps, a…
josecelano 65d9a87
refactor(qbittorrent-e2e): remove redundant add_torrent_file_to_leech…
josecelano 008edb4
refactor(qbittorrent-e2e): group scenario steps into fixtures/ and qb…
josecelano a9923ba
fix(qbittorrent-e2e): replace log-polling password with injected cred…
josecelano eaa9202
refactor(qbittorrent-e2e): dissolve ScenarioRunner into free functions
josecelano d60c6a6
refactor(qbittorrent-e2e): extract service-oriented arrange helpers
josecelano 48c200a
refactor(qbittorrent-e2e): move verify_payload_integrity to scenario_…
josecelano 6477efd
refactor(qbittorrent-e2e): fix verify_payload_integrity signature to …
josecelano 6b597da
refactor(qbittorrent-e2e): remove sha1 from verify_payload_integrity
josecelano 6aefb94
refactor(qbittorrent-e2e): extract QbittorrentConfigBuilder from runner
josecelano 9f996e2
refactor(qbittorrent-e2e): unify qBittorrent upload API
josecelano bd6e466
refactor(qbittorrent-e2e): delegate tracker image build to docker com…
josecelano 6b09d1a
refactor(qbittorrent-e2e): split initialize_client into three focused…
josecelano 9f13354
refactor(qbittorrent-e2e): extract build_api_clients from run_scenario
josecelano 7cba481
docs(qbittorrent-e2e): add module-level doc comment explaining BDD sc…
josecelano 0808f40
refactor(qbittorrent-e2e): extract scenario into dedicated module
josecelano 83e04d5
refactor(qbittorrent-e2e): reorder run fn body into ARRANGE/ACT/ASSERT
josecelano 6e3b9ef
refactor(qbittorrent-e2e): extract compose stack provisioning into co…
josecelano b5fe409
refactor(qbittorrent-e2e): rename and extract client builder function…
josecelano 847b452
refactor(qbittorrent-e2e): extract workspace setup into workspace_set…
josecelano d70a251
refactor(qbittorrent-e2e): rename compose_stack and workspace_setup m…
josecelano 4924f0c
docs(qbittorrent-e2e): add workspace layout tree to filesystem_setup …
josecelano 3769e19
refactor(qbittorrent-e2e): introduce TimingConfig to group polling Du…
josecelano 051047a
refactor(qbittorrent-e2e): introduce TrackerFilesystem to group track…
josecelano 23a41cb
refactor(qbittorrent-e2e): introduce SharedFixtures to group shared f…
josecelano 531f496
refactor(qbittorrent-e2e): introduce PeerConfig to group per-peer fields
josecelano 404c316
refactor(qbittorrent-e2e): extract QbittorrentCredentials from PeerCo…
josecelano 3d596b6
refactor(qbittorrent-e2e): extract TorrentFixture from SharedFixtures
josecelano 6acc115
refactor(qbittorrent-e2e): introduce FileName newtype and types module
josecelano ae8f49a
refactor(qbittorrent-e2e): introduce ContainerPath newtype for contai…
josecelano a194860
refactor(qbittorrent-e2e): introduce Deadline and PollInterval newtypes
josecelano 5ed2e78
refactor(qbittorrent-e2e): introduce TorrentState enum for TorrentInfo
josecelano b4b201f
refactor(qbittorrent-e2e): introduce TorrentProgress newtype
josecelano dc99841
refactor(qbittorrent-e2e): introduce WebUiBaseUrl for validated base URL
josecelano a799810
refactor(qbittorrent-e2e): introduce ComposeProjectName newtype
josecelano cf2faf4
refactor(qbittorrent-e2e): introduce TrackerImage and QbittorrentImag…
josecelano f643b44
refactor(qbittorrent-e2e): introduce TorrentHash newtype for TorrentI…
josecelano 53e4c2c
refactor(qbittorrent-e2e): introduce PayloadSize and PieceLength newt…
josecelano 7f584d4
refactor(qbittorrent): move torrent state into client module
josecelano d1dd8b0
refactor(qbittorrent): move torrent progress into client module
josecelano 11fb987
refactor(qbittorrent): split types module and add unit tests
josecelano 09c5c33
refactor(qbittorrent-e2e): rename module and split qbittorrent featur…
josecelano aaa59b0
refactor(qbittorrent-e2e): pass QbittorrentCredentials to login inste…
josecelano 11c2f2c
ci(testing): add qBittorrent E2E job to testing workflow
josecelano fd26ad5
refactor(qbittorrent-e2e): replace tracker config template arg with T…
josecelano d636151
refactor(qbittorrent-e2e): introduce TrackerConfig DTO with typed Soc…
josecelano c641ef9
chore(qbittorrent-e2e): suppress DevSkim DS137138 warning for test an…
josecelano 841453f
test(qbittorrent-e2e): add unit tests for bencode encoder and torrent…
josecelano 48db166
refactor(qbittorrent-e2e): use InfoHash-based torrent presence checks
josecelano ad5c076
fix(qbittorrent-e2e): use InfoHash to identify torrent in wait_until_…
josecelano fcff35f
refactor(qbittorrent-e2e): return domain types directly from setup fu…
josecelano 9c11c91
fix(qbittorrent-e2e): disable DHT, LSD, and PeX in qBittorrent config…
josecelano 4f79bc8
feat(qbittorrent-e2e): verify tracker swarm participation via REST AP…
josecelano a3ccbc5
refactor(ci): use structured tracing fields in qbittorrent e2e runner
josecelano 19e09b7
test(qbittorrent-e2e): cover transfer over HTTP and UDP
josecelano 18073cf
refactor(qbittorrent-e2e): polish docs and staged test/readability im…
josecelano a823fa0
ci(testing): merge E2E jobs and rename step IDs
josecelano 6de9fbd
fix(qbittorrent-e2e): pre-seed scenario fixtures before compose startup
josecelano d9fa45c
fix(qbittorrent-e2e): harden FileName validation and fix WebUI/announ…
josecelano File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
.github/skills/dev/pr-reviews/fetch-review-threads/SKILL.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| --- | ||
| name: fetch-review-threads | ||
| description: Fetch unresolved GitHub pull request review thread IDs for the torrust-tracker project. Use when asked to find open PR review threads, list unresolved review comments, collect thread IDs before resolving suggestions, or inspect Copilot review feedback. Triggers on "fetch review threads", "list unresolved PR comments", "get review thread IDs", or "find open review suggestions". | ||
| metadata: | ||
| author: torrust | ||
| version: "1.0" | ||
| --- | ||
|
|
||
| # Fetching PR Review Threads | ||
|
|
||
| Use this skill before resolving review feedback. Its purpose is to collect the unresolved | ||
| review thread IDs and enough context to decide whether each thread should stay open or be closed. | ||
|
|
||
| ## Preferred Sources | ||
|
|
||
| Use one of these approaches: | ||
|
|
||
| 1. Active pull request tools when they are available in the environment. | ||
| 2. GitHub CLI GraphQL when you need a terminal-based fallback. | ||
|
|
||
| Prefer the active PR tools first because they provide thread metadata together with file paths, | ||
| resolution state, and comments. | ||
|
|
||
| ## What to Collect | ||
|
|
||
| For each unresolved thread, capture: | ||
|
|
||
| - thread ID | ||
| - file path | ||
| - `isResolved` | ||
| - `canResolve` | ||
| - comment author | ||
| - comment body | ||
|
|
||
| Only unresolved threads should be considered for follow-up work. | ||
|
|
||
| ## Active PR Tool Workflow | ||
|
|
||
| 1. Read the active PR. | ||
| 2. Inspect the `reviewThreads` array. | ||
| 3. Filter to threads where `isResolved == false`. | ||
| 4. Group them by file if you plan to address them in code. | ||
|
|
||
| ## GitHub CLI GraphQL Fallback | ||
|
|
||
| Use GitHub CLI if you need to retrieve threads directly from the terminal. | ||
|
|
||
| ```bash | ||
| gh api graphql \ | ||
| -F owner=torrust \ | ||
| -F repo=torrust-tracker \ | ||
| -F pullNumber=1707 \ | ||
| -f query='query($owner: String!, $repo: String!, $pullNumber: Int!) { | ||
| repository(owner: $owner, name: $repo) { | ||
| pullRequest(number: $pullNumber) { | ||
| reviewThreads(first: 100) { | ||
| nodes { | ||
| id | ||
| isResolved | ||
| isOutdated | ||
| comments(first: 20) { | ||
| nodes { | ||
| author { | ||
| login | ||
| } | ||
| body | ||
| path | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }' | ||
| ``` | ||
|
|
||
| Then filter for unresolved threads. | ||
|
|
||
| ## Practical Guidance | ||
|
|
||
| - Do not guess thread IDs. | ||
| - Do not resolve a thread immediately after fetching it. First confirm the fix exists. | ||
| - If a thread is outdated but unresolved, still read it before deciding what to do. | ||
| - If there are more than 100 threads, paginate instead of assuming the first page is complete. | ||
|
|
||
| ## Completion Checklist | ||
|
|
||
| - [ ] Unresolved thread IDs were collected from the current PR state | ||
| - [ ] Each thread has enough context for triage | ||
| - [ ] Already resolved threads were excluded from action items | ||
| - [ ] The result is ready to hand off to a fix or resolution workflow |
77 changes: 77 additions & 0 deletions
77
.github/skills/dev/pr-reviews/resolve-review-threads/SKILL.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --- | ||
| name: resolve-review-threads | ||
| description: Resolve addressed GitHub pull request review threads for the torrust-tracker project. Use when asked to mark PR suggestions as resolved, resolve review comments, close addressed review threads, or clear Copilot review feedback after fixes are pushed. Triggers on "resolve PR threads", "mark suggestions as resolved", "resolve review comments", or "close addressed review threads". | ||
| metadata: | ||
| author: torrust | ||
| version: "1.0" | ||
| --- | ||
|
|
||
| # Resolving PR Review Threads | ||
|
|
||
| Use this skill after the requested code or documentation changes are already implemented, | ||
| validated, committed, and pushed. | ||
|
|
||
| ## Preconditions | ||
|
|
||
| - The feedback has actually been addressed in the branch. | ||
| - Validation has been run for the touched scope (`linter all`, tests, or a targeted executable check). | ||
| - You have the target PR number and unresolved review thread IDs. | ||
|
|
||
| Do not resolve a thread just because a suggestion exists. Resolve it only when the underlying | ||
| concern is fixed or intentionally declined with a clear reason. | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. Read the active PR and collect unresolved review threads. | ||
| 2. Group threads by file and confirm each one is truly addressed. | ||
| 3. Implement and validate any missing fixes before resolving anything. | ||
| 4. Resolve the addressed threads. | ||
| 5. Re-check the PR state if needed. | ||
|
|
||
| ## Preferred Resolution Path | ||
|
|
||
| If PR tools are available, first gather thread IDs from the active pull request metadata. | ||
|
|
||
| - Use the active PR tools to identify unresolved `reviewThreads`. | ||
| - Resolve only threads where `isResolved == false` and the fix is already on the branch. | ||
|
|
||
| ## GitHub CLI GraphQL Command | ||
|
|
||
| Use GitHub CLI GraphQL when you need to resolve a thread directly from the terminal: | ||
|
|
||
| ```bash | ||
| gh api graphql \ | ||
| -F threadId=THREAD_ID \ | ||
| -f query='mutation($threadId: ID!) { resolveReviewThread(input: { threadId: $threadId }) { thread { isResolved } } }' | ||
| ``` | ||
|
|
||
| Successful output should report `isResolved: true`. | ||
|
|
||
| ## Batch Pattern | ||
|
|
||
| For multiple threads, resolve them one by one and check each result: | ||
|
|
||
| ```bash | ||
| for thread_id in \ | ||
| THREAD_ID_1 \ | ||
| THREAD_ID_2 | ||
| do | ||
| gh api graphql \ | ||
| -F threadId="$thread_id" \ | ||
| -f query='mutation($threadId: ID!) { resolveReviewThread(input: { threadId: $threadId }) { thread { isResolved } } }' | ||
| done | ||
| ``` | ||
|
|
||
| ## Notes | ||
|
|
||
| - Thread IDs are GraphQL node IDs, not PR numbers or comment IDs. | ||
| - This resolves the review thread, not the entire review. | ||
| - If a thread should remain open, leave it open and explain why. | ||
| - If you do not know the thread IDs yet, query the active PR first instead of guessing. | ||
|
|
||
| ## Completion Checklist | ||
|
|
||
| - [ ] All targeted threads were verified against the current branch state | ||
| - [ ] Validation passed before resolution | ||
| - [ ] Each resolved mutation returned `isResolved: true` | ||
| - [ ] Any intentionally unresolved feedback is documented with reasoning |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.