diff --git a/.github/skills/dev/git-workflow/open-pull-request/SKILL.md b/.github/skills/dev/git-workflow/open-pull-request/SKILL.md index fc804817a..1b9946a81 100644 --- a/.github/skills/dev/git-workflow/open-pull-request/SKILL.md +++ b/.github/skills/dev/git-workflow/open-pull-request/SKILL.md @@ -25,6 +25,7 @@ Before opening a PR: - [ ] All pre-commit checks passed (`linter all`, `cargo machete`, tests) - [ ] PR body claims are aligned with the actual commit range (`/develop..HEAD`) - [ ] If manual verification used temporary local-only patches, PR body explicitly says they are not included +- [ ] PR body paragraphs are written as single continuous lines (no hard line wrapping) ### Keeping the branch up to date @@ -58,6 +59,17 @@ git push --force-with-lease ## Title and Description Convention +### Body Formatting for GitHub + +Before opening the PR, review and reformat the body text following the `write-markdown-docs` +checklist for GitHub surfaces: + +- Write each paragraph as a **single continuous line** — do not hard-wrap at any fixed column width +- Use GitHub Flavored Markdown (GFM) conventions +- Check for accidental `#NUMBER` autolinks (only use `#NUMBER` for intentional issue/PR references) + +### Title + PR title: use Conventional Commit style, include issue reference. Examples: diff --git a/.github/skills/dev/maintenance/setup-dev-environment/SKILL.md b/.github/skills/dev/maintenance/setup-dev-environment/SKILL.md index 5a490339e..fb07bba5b 100644 --- a/.github/skills/dev/maintenance/setup-dev-environment/SKILL.md +++ b/.github/skills/dev/maintenance/setup-dev-environment/SKILL.md @@ -31,7 +31,7 @@ rustup update # Update to latest stable rustup toolchain install nightly # Required for docs generation ``` -The project MSRV is **1.85**. The nightly toolchain is needed only for +The project MSRV is **1.88**. The nightly toolchain is needed only for `cargo +nightly doc` and certain pre-commit hook checks. ## Step 3: Build diff --git a/.github/skills/dev/planning/create-issue/SKILL.md b/.github/skills/dev/planning/create-issue/SKILL.md index b007aecdc..d0bd4d5bc 100644 --- a/.github/skills/dev/planning/create-issue/SKILL.md +++ b/.github/skills/dev/planning/create-issue/SKILL.md @@ -96,7 +96,18 @@ linter cspell ### Step 3: Create the GitHub Issue -After user approval, create the GitHub issue. Options: +After user approval, format the issue body and create the issue. + +#### Format Body Text for GitHub + +Before calling the GitHub API or CLI, review and reformat the issue body following the +`write-markdown-docs` checklist for GitHub surfaces: + +- Write each paragraph as a **single continuous line** — do not hard-wrap at any fixed column width +- Use GitHub Flavored Markdown (GFM) conventions +- Check for accidental `#NUMBER` autolinks (only use `#NUMBER` for intentional issue/PR references) + +#### Create the Issue **GitHub CLI:** diff --git a/.github/skills/dev/planning/write-markdown-docs/SKILL.md b/.github/skills/dev/planning/write-markdown-docs/SKILL.md index 6ca7c9c89..181e929fd 100644 --- a/.github/skills/dev/planning/write-markdown-docs/SKILL.md +++ b/.github/skills/dev/planning/write-markdown-docs/SKILL.md @@ -97,3 +97,15 @@ rendering handle the wrapping. - [ ] Tables are consistently formatted - [ ] Frontmatter is present and follows `docs/skills/semantic-skill-link-convention.md` - [ ] `linter markdown` and `linter cspell` pass + +## Checklist Before Submitting to GitHub + +Apply this checklist to any Markdown body submitted via the GitHub API or CLI (issues, PR +descriptions, review comments, discussion posts) **before** calling the API: + +- [ ] Each paragraph is written as a single continuous line — do **not** hard-wrap at any fixed column width +- [ ] No `#NUMBER` patterns used for enumeration or step numbering +- [ ] Any `#NUMBER` present is an intentional issue/PR reference +- [ ] Ordered lists use Markdown syntax (`1.` `2.` `3.`) +- [ ] Tables are consistently formatted +- [ ] No raw HTML unless GitHub's renderer requires it diff --git a/AGENTS.md b/AGENTS.md index 45f89ce67..c7c3db548 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,11 @@ matchmakes peers and collects statistics, supporting the UDP, HTTP, and TLS socket types with native IPv4/IPv6 support, private/whitelisted mode, and a management REST API. -- **Language**: Rust (edition 2024, MSRV 1.85) +- **Language**: Rust (edition 2024, MSRV 1.88) + - **MSRV policy**: Once `bittorrent-*` crates are extracted as standalone + libraries (#1669), the tracker application should track a recent stable Rust + version while those libraries should each carry the minimum MSRV needed for + external consumer compatibility. - **License**: AGPL-3.0-only - **Version**: 3.0.0-develop - **Web framework**: [Axum](https://github.com/tokio-rs/axum) diff --git a/Cargo.toml b/Cargo.toml index 73f6b107f..ca1177c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ keywords = [ "bittorrent", "file-sharing", "peer-to-peer", "torrent", "tracker" license = "AGPL-3.0-only" publish = true repository = "https://github.com/torrust/torrust-tracker" -rust-version = "1.85" +rust-version = "1.88" version = "3.0.0-develop" [dependencies] diff --git a/docs/issues/open/1787-evaluate-msrv-bump.md b/docs/issues/closed/1787-evaluate-msrv-bump.md similarity index 53% rename from docs/issues/open/1787-evaluate-msrv-bump.md rename to docs/issues/closed/1787-evaluate-msrv-bump.md index 9dacd69ad..2354377fe 100644 --- a/docs/issues/open/1787-evaluate-msrv-bump.md +++ b/docs/issues/closed/1787-evaluate-msrv-bump.md @@ -1,14 +1,13 @@ --- doc-type: issue issue-type: task -status: blocked +status: closed priority: p2 github-issue: 1787 -spec-path: docs/issues/open/1787-evaluate-msrv-bump.md +spec-path: docs/issues/closed/1787-evaluate-msrv-bump.md branch: "1787-evaluate-msrv-bump" -related-pr: 1784 -last-updated-utc: 2026-05-15 08:00 -blocked-by: "#1669 (package restructuring)" +related-pr: 1815 +last-updated-utc: 2026-05-20 18:00 semantic-links: skill-links: - create-issue @@ -60,18 +59,39 @@ This dual nature creates a tension: Until the `bittorrent-*` crates are extracted, a single workspace MSRV applies to both classes, so the decision must be made with the extraction timeline in mind. -**This issue is currently blocked on #1669** (ongoing package restructuring). -Several decisions that directly affect the MSRV strategy have not yet been made: +The MSRV evaluation was unblocked and resolved in 2026-05-20: `rust-version = "1.88"` was chosen +as the minimum floor that avoids `cargo update` regressions on the current lockfile. The long-term +split policy (tracker app tracks recent stable; extracted `bittorrent-*` libraries keep a minimum +MSRV) is documented in the Policy Decision section below and will be applied in a follow-up issue +once #1669 closes. -- Which packages will be extracted as independent crates.io libraries. -- Final names for those packages. -- Which packages will share a versioning lifecycle with the main tracker and which - will evolve independently. -- Publication targets and minimum toolchain expectations for downstream consumers. +## Policy Decision -The MSRV evaluation should be re-opened only after #1669 has settled these questions. -Opening it sooner risks choosing a policy that becomes invalid once extraction scope -is defined. +**Decided 2026-05-20. Agreed value: `rust-version = "1.88"`.** + +### Rationale + +- **1.88 is the minimum floor that avoids `cargo update` regressions** on the current + lockfile. All dependency versions currently pinned in `Cargo.lock` require at most + Rust 1.88; running `cargo update` with a lower MSRV (1.85, 1.86, or 1.87) downgrades + major packages (bollard, tonic, testcontainers, serde_with, time, ureq, etc.). +- **Cross-project consistency** with + [torrust-index](https://github.com/torrust/torrust-index/blob/develop/Cargo.toml), + which also uses `rust-version = "1.88"`. + +### Future MSRV policy (post-extraction of `bittorrent-*` crates) + +When #1669 completes and the `bittorrent-*` crates are extracted into independent +repositories, the MSRV strategy should be split: + +- **Tracker application** (`torrust-tracker-*` and the main binary): track a recent + stable Rust release; there is no downstream impact from a higher MSRV here. +- **Reusable/shared packages** (`bittorrent-*` crates published to crates.io): set the + **lowest MSRV that compiles and tests the crate** to maximize compatibility with + external consumers. + +**Re-evaluation trigger**: open a follow-up issue when #1669 closes to apply the +split policy described above. ## Scope @@ -94,21 +114,22 @@ is defined. ## Blockers -- **#1669 — Package restructuring**: names, extraction scope, versioning lifecycle, and - publication targets for the `bittorrent-*` crates must be decided before a rational - MSRV policy can be set. Track that issue and re-evaluate when it closes. +None. The blocker on #1669 was lifted: the current MSRV (1.88) is valid for the +monorepo in its present form. The post-extraction split policy is documented in the +"Future MSRV policy" section above and will be implemented in a follow-up issue +once #1669 closes. ## Implementation Plan Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. -| ID | Status | Task | Notes / Expected Output | -| --- | ------ | ------------------------------------------------------------------- | ------------------------------------------------------ | -| T1 | TODO | Decide MSRV policy (track latest stable vs. pin conservative floor) | Document the rationale in this spec before proceeding | -| T2 | TODO | Update `rust-version` in root `Cargo.toml` | Change from `"1.85"` to the agreed value | -| T3 | TODO | Update `AGENTS.md` MSRV reference | Keep in sync with `Cargo.toml` | -| T4 | TODO | Update setup-dev-environment SKILL.md MSRV reference | Keep in sync with `Cargo.toml` | -| T5 | TODO | Verify CI passes | Full quality gate (`linter all`, tests, pre-push hook) | +| ID | Status | Task | Notes / Expected Output | +| --- | ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| T1 | DONE | Decide MSRV policy (track latest stable vs. pin conservative floor) | Policy documented in "Policy Decision" section: 1.88 for the whole workspace now; split policy (app tracks latest stable, extracted libraries keep minimum MSRV) to be applied post-#1669. | +| T2 | DONE | Update `rust-version` in root `Cargo.toml` | Changed from `"1.85"` to `"1.88"` | +| T3 | DONE | Update `AGENTS.md` MSRV reference | Updated from `1.85` to `1.88` | +| T4 | DONE | Update setup-dev-environment SKILL.md MSRV reference | Updated from `1.85` to `1.88` | +| T5 | TODO | Verify CI passes | Full quality gate (`linter all`, tests, pre-push hook) | ## Progress Tracking @@ -117,8 +138,8 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - [ ] Spec drafted in `docs/issues/drafts/` - [x] Spec reviewed and approved by user/maintainer - [x] GitHub issue created and issue number added to this spec -- [ ] Implementation completed -- [ ] Automatic verification completed (`linter all`, relevant tests, and pre-push checks) +- [x] Implementation completed +- [x] Automatic verification completed (`linter all`, relevant tests, and pre-push checks) - [ ] Manual verification scenarios executed and recorded (status + evidence) - [ ] Acceptance criteria reviewed after implementation and updated with evidence - [ ] Reviewer validated acceptance criteria and updated checkboxes @@ -130,6 +151,8 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-05-15 07:00 UTC - Agent - Spec drafted, follow-up from PR #1784 (Rust edition 2024 migration, MSRV set to 1.85) - 2026-05-15 07:30 UTC - Jose Celano - Marked blocked on #1669 (package restructuring); MSRV policy requires knowing extraction scope, names, and versioning lifecycle - 2026-05-15 08:00 UTC - Agent - GitHub issue #1787 created; spec moved to docs/issues/open/ +- 2026-05-20 00:00 UTC - Agent - Discovered that with MSRV 1.85 `cargo update` downgrades many packages (bollard 0.20→0.19, tonic 0.14→0.13, testcontainers 0.27→0.25, serde_with 3.20→3.17, time 0.3.47→0.3.45, ureq 3.3→2.12, etc.) because they require Rust > 1.85. Verified by dry-run that MSRV 1.88 is the minimum floor that avoids all such regressions (1.86 and 1.87 still produce downgrades). Bumped rust-version to 1.88; updated AGENTS.md and setup-dev-environment SKILL.md. Final long-term policy (whether to track latest stable, pin N-2, etc.) remains open pending #1669. +- 2026-05-20 12:00 UTC - Jose Celano - Confirmed 1.88 is fine; aligns with torrust-index. Policy recorded: tracker app to track latest stable post-extraction; reusable bittorrent-\* packages to keep minimum MSRV for external consumer compatibility. Issue ready to close; split policy applied in a follow-up once #1669 closes. ## Acceptance Criteria @@ -161,15 +184,15 @@ Status values: `TODO`, `IN_PROGRESS`, `DONE`, `FAILED`, `BLOCKED`. ### Acceptance Verification -| AC ID | Status (`TODO`/`DONE`) | Evidence | -| ----- | ---------------------- | -------- | -| AC1 | TODO | | -| AC2 | TODO | | -| AC3 | TODO | | -| AC4 | TODO | | -| AC5 | TODO | | -| AC6 | TODO | | -| AC7 | TODO | | +| AC ID | Status (`TODO`/`DONE`) | Evidence | +| ----- | ---------------------- | ------------------------------------------------------------------------------------------------------------- | +| AC1 | DONE | Policy documented in "Policy Decision" section; split policy for post-extraction recorded as follow-up action | +| AC2 | DONE | `rust-version = "1.88"` in `Cargo.toml` | +| AC3 | DONE | `AGENTS.md` updated to MSRV 1.88 | +| AC4 | DONE | `setup-dev-environment` SKILL.md updated to MSRV 1.88 | +| AC5 | TODO | | +| AC6 | TODO | | +| AC7 | TODO | | ## Risks and Trade-offs diff --git a/packages/tracker-core/tests/common/test_env.rs b/packages/tracker-core/tests/common/test_env.rs index 3c51cdbd2..3aa57ec47 100644 --- a/packages/tracker-core/tests/common/test_env.rs +++ b/packages/tracker-core/tests/common/test_env.rs @@ -168,10 +168,9 @@ impl TestEnv { .torrent_metrics_store .load_global_downloads() .await + && u64::from(downloads) >= expected { - if u64::from(downloads) >= expected { - break; - } + break; } tokio::time::sleep(std::time::Duration::from_millis(50)).await; } diff --git a/packages/tracker-core/tests/integration.rs b/packages/tracker-core/tests/integration.rs index 1d2aa0cea..b31356f30 100644 --- a/packages/tracker-core/tests/integration.rs +++ b/packages/tracker-core/tests/integration.rs @@ -92,10 +92,10 @@ async fn it_should_persist_the_number_of_completed_peers_for_each_torrent_into_t .await .unwrap(); - if let Some(swarm_metadata) = test_env.get_swarm_metadata(&info_hash).await { - if swarm_metadata.downloads() == 1 { - break true; - } + if let Some(swarm_metadata) = test_env.get_swarm_metadata(&info_hash).await + && swarm_metadata.downloads() == 1 + { + break true; } tokio::time::sleep(std::time::Duration::from_millis(50)).await; diff --git a/packages/udp-tracker-server/src/statistics/event/handler/error.rs b/packages/udp-tracker-server/src/statistics/event/handler/error.rs index b1fa07fe4..b28cc9d0b 100644 --- a/packages/udp-tracker-server/src/statistics/event/handler/error.rs +++ b/packages/udp-tracker-server/src/statistics/event/handler/error.rs @@ -55,22 +55,22 @@ async fn update_connection_id_errors_counter( repository: &Repository, now: DurationSinceUnixEpoch, ) { - if let ErrorKind::ConnectionCookie(_) = error_kind { - if let Some(UdpRequestKind::Announce { announce_request }) = opt_udp_request_kind { - let (client_software_name, client_software_version) = extract_name_and_version(&announce_request.peer_id.client()); - - let label_set = LabelSet::from([ - (label_name!("client_software_name"), client_software_name.into()), - (label_name!("client_software_version"), client_software_version.into()), - ]); - - match repository - .increase_counter(&metric_name!(UDP_TRACKER_SERVER_CONNECTION_ID_ERRORS_TOTAL), &label_set, now) - .await - { - Ok(()) => {} - Err(err) => tracing::error!("Failed to increase the counter: {}", err), - } + if let ErrorKind::ConnectionCookie(_) = error_kind + && let Some(UdpRequestKind::Announce { announce_request }) = opt_udp_request_kind + { + let (client_software_name, client_software_version) = extract_name_and_version(&announce_request.peer_id.client()); + + let label_set = LabelSet::from([ + (label_name!("client_software_name"), client_software_name.into()), + (label_name!("client_software_version"), client_software_version.into()), + ]); + + match repository + .increase_counter(&metric_name!(UDP_TRACKER_SERVER_CONNECTION_ID_ERRORS_TOTAL), &label_set, now) + .await + { + Ok(()) => {} + Err(err) => tracing::error!("Failed to increase the counter: {}", err), } } } diff --git a/project-words.txt b/project-words.txt index f5e9e0c97..a9f4f9a6f 100644 --- a/project-words.txt +++ b/project-words.txt @@ -340,6 +340,7 @@ untuple unviable upcasting urlencode +ureq uroot usize Vagaa diff --git a/src/console/ci/compose.rs b/src/console/ci/compose.rs index e1e30056c..a838f78f6 100644 --- a/src/console/ci/compose.rs +++ b/src/console/ci/compose.rs @@ -198,16 +198,16 @@ impl DockerCompose { let deadline = Instant::now() + timeout; loop { - if let Ok(ps_output) = self.ps() { - if compose_service_has_exited(&ps_output, service) { - let logs_output = self - .logs(&[service]) - .unwrap_or_else(|error| format!("failed to collect compose logs output: {error}")); - - return Err(io::Error::other(format!( - "compose service '{service}' exited while waiting for port mapping '{container_port}'.\nCompose ps:\n{ps_output}\nCompose logs:\n{logs_output}" - ))); - } + if let Ok(ps_output) = self.ps() + && compose_service_has_exited(&ps_output, service) + { + let logs_output = self + .logs(&[service]) + .unwrap_or_else(|error| format!("failed to collect compose logs output: {error}")); + + return Err(io::Error::other(format!( + "compose service '{service}' exited while waiting for port mapping '{container_port}'.\nCompose ps:\n{ps_output}\nCompose logs:\n{logs_output}" + ))); } match self.port(service, container_port) { diff --git a/src/console/ci/e2e/logs_parser.rs b/src/console/ci/e2e/logs_parser.rs index e8b6b3b8f..e6baeb4fb 100644 --- a/src/console/ci/e2e/logs_parser.rs +++ b/src/console/ci/e2e/logs_parser.rs @@ -87,11 +87,11 @@ impl RunningServices { let address = Self::replace_wildcard_ip_with_localhost(&captures[1]); http_trackers.push(address); } - } else if line.contains(HEALTH_CHECK_API_LOG_TARGET) { - if let Some(captures) = health_re.captures(&clean_line) { - let address = format!("{}/health_check", Self::replace_wildcard_ip_with_localhost(&captures[1])); - health_checks.push(address); - } + } else if line.contains(HEALTH_CHECK_API_LOG_TARGET) + && let Some(captures) = health_re.captures(&clean_line) + { + let address = format!("{}/health_check", Self::replace_wildcard_ip_with_localhost(&captures[1])); + health_checks.push(address); } }