Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@
# ── Dev tooling (not needed in any build stage) ───────────────────────────────
# su-exec is compiled in the gcc stage: COPY ./contrib/dev-tools/su-exec/
# workspace-coupling/Cargo.toml is copied in the recipe stage for cargo chef prepare
# clippy-allow-reasons/Cargo.toml is copied in the recipe stage for cargo chef prepare
/contrib/dev-tools/
!/contrib/dev-tools/su-exec/
!/contrib/dev-tools/analysis/workspace-coupling/Cargo.toml
!/contrib/dev-tools/checks/clippy-allow-reasons/Cargo.toml

# ── Build artifacts and runtime state ─────────────────────────────────────────
/bin/
Expand Down
6 changes: 4 additions & 2 deletions .github/agents/clippy-fixer.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ You are the repository's Clippy warning fixer agent. Your job is to analyze clip

- Follow `AGENTS.md` for repository-wide behavior
- Always prefer applying clippy suggestions over adding `#[allow(...)]` attributes
- When allowances are needed, **always document the reason** in a clear comment
- For new or modified `#[allow(clippy::...)]` attributes, use a specific native
`reason = "..."` parameter. Temporary reasons also need a stable issue reference or explicit
non-empty removal condition.
- Create **atomic commits** for each clippy type warning (e.g., one commit per `explicit_iter_loop` issue)
- Link to the specific clippy warning in commit messages for traceability
- Use the `Committer` agent for final commits
Expand All @@ -23,7 +25,7 @@ You are the repository's Clippy warning fixer agent. Your job is to analyze clip
1. **Analyze clippy output**: Receive clippy warnings from user or `linter clippy`
2. **Identify fixable warnings**: Determine which warnings can be fixed with clippy suggestions
3. **Apply fixes**: Modify source code to apply clippy suggestions properly
4. **Document exceptions**: Add clear comments for any `#[allow(...)]` attributes
4. **Document exceptions**: Use the native `reason = "..."` parameter for changed Clippy allows
5. **Commit fixes**: Use `Committer` agent to create properly formatted commits
6. **Verify**: Ensure `linter all` passes after fixes

Expand Down
10 changes: 6 additions & 4 deletions .github/skills/dev/git-workflow/run-pre-commit-checks/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ The script runs these steps in order:

1. `./contrib/dev-tools/git/format-project-words.sh` - formats `project-words.txt` with
`LC_ALL=C sort -u`
2. `cargo machete --with-metadata` - unused dependency check
3. `cargo deny check bans` - workspace layer-boundary dependency check
4. `linter all` - all linters (markdown, lychee local links, YAML, TOML, clippy, rustfmt, shellcheck, cspell)
5. `cargo test --doc --workspace` - documentation tests
2. `cargo run --quiet --package clippy-allow-reasons -- --base-ref torrust/develop` - prospective
native-reason check for changed Clippy allow attributes
3. `cargo machete --with-metadata` - unused dependency check
4. `cargo deny check bans` - workspace layer-boundary dependency check
5. `linter all` - all linters (markdown, lychee local links, YAML, TOML, clippy, rustfmt, shellcheck, cspell)
6. `cargo test --doc --workspace` - documentation tests

If the formatter changes the dictionary, the hook exits non-zero before the verification steps.
Stage `project-words.txt` and retry the commit. Run the formatter independently with:
Expand Down
51 changes: 51 additions & 0 deletions .github/skills/dev/maintenance/add-workspace-member/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
name: add-workspace-member
description: Add or remove an explicit Cargo workspace member in Torrust Tracker. Use when editing the root workspace members list, adding a developer-tool crate, or registering a new standalone workspace package.
metadata:
author: torrust
version: "1.0"
semantic-links:
related-artifacts:
- Cargo.toml
- Containerfile
- .dockerignore
- .github/skills/dev/maintenance/add-rust-dependency/SKILL.md
---

# Add a Cargo Workspace Member

Use this workflow when changing the root `Cargo.toml` `[workspace].members` list. It applies to
explicit members only; path dependencies can be auto-discovered separately by Cargo.

## Required Review

1. Add or remove the member in root `Cargo.toml`.
2. Read the semantic link above that list and review `Containerfile` at the cargo-chef recipe
stage. Add or remove its manifest copy and all target stubs required by `cargo metadata`.
3. Decide whether the member has value in container test archives.
- Production-relevant members remain included.
- Developer-only analysis, checks, benchmarks, clients, and host-only E2E tools are normally
excluded from every `cargo nextest archive` invocation.
- Keep the explanation and all four archive exclusion lists synchronized.
4. Review `.dockerignore`; a manifest copied in the recipe stage must not be excluded from the
build context.

## Verification

Run the narrow validation appropriate to the change before the normal repository gate:

```bash
docker build --target recipe --file Containerfile .
```

For a changed archive inclusion/exclusion, also run:

```bash
docker build --target test_debug --file Containerfile .
```

Then run `linter all`, `cargo test --doc --workspace`, and the mandatory pre-commit workflow.

## Related Skills

- [`add-rust-dependency`](../add-rust-dependency/SKILL.md) — add an external dependency.
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ For Cargo dependency updates, use
- Prefer a scoped, stable pattern over a moving `owner/action@v2` tag when Dependabot updates exact versions.
- Confirm that the configured pattern matches the full `uses:` reference, including its version.
5. Add one semantic `skill-link: update-github-workflow-actions` comment near the workflow's top-level metadata and review the related skills when updating the workflow policy.
6. Run `linter yaml`, `git diff --check`, and the relevant repository checks before committing.
7. Commit with a signed Conventional Commit, push the branch to the fork remote, and open a PR targeting `develop`.
8. Confirm affected workflow runs are queued and pass. If a run is blocked by the allowlist, correct the organization policy and rerun the failed jobs; do not weaken the workflow pin.
6. When a workflow command compares Git revisions, configure `actions/checkout` with
`fetch-depth: 0` so its merge base is available in CI.
7. Run `linter yaml`, `git diff --check`, and the relevant repository checks before committing.
8. Commit with a signed Conventional Commit, push the branch to the fork remote, and open a PR targeting `develop`.
9. Confirm affected workflow runs are queued and pass. If a run is blocked by the allowlist, correct the organization policy and rerun the failed jobs; do not weaken the workflow pin.

## Allowlist Failure Diagnosis

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,34 @@ Only add `#[allow(...)]` when:

## How to Document Exceptions

When adding `#[allow(...)]` attributes, always include a clear comment explaining why:
When adding or modifying `#[allow(clippy::...)]` attributes, use Rust's native `reason` parameter.
The prospective Rust validator checks changed attributes against the branch merge base, so existing
allows remain the separate remediation scope of #2158.

```rust
// This is a temporary workaround during refactoring of the announce response parser
// TODO: Remove this allowance when the parser is fully refactored
#[allow(clippy::unnecessary_wraps)]
#[allow(
clippy::unnecessary_wraps,
reason = "Temporary parser compatibility shim; remove when #2158 is complete."
)]
```

The reason must be specific. A temporary reason must also contain either a stable issue reference
such as `#2158` or a non-empty `remove when`, `remove after`, `remove by`, or `until` condition.

Do not enable `clippy::allow_attributes_without_reason` workspace-wide until #2158 has remediated
the historical attributes that lack native reasons. It is the correct eventual compiler-aware
enforcement mechanism, but enabling it now would violate this issue's prospective-baseline scope.

For a temporary item-level suppression, prefer `#[expect(..., reason = "...")]` when it is useful
Comment thread
josecelano marked this conversation as resolved.
to learn that the underlying lint no longer fires. Do not force `expect` for crate-level policy.

For example:

```rust
#[expect(
clippy::unnecessary_wraps,
reason = "Temporary parser compatibility shim; remove when #2158 is complete."
)]
fn parse_announce_response(data: &[u8]) -> Result<Response, ParseError> {
// implementation
}
Expand Down Expand Up @@ -93,7 +115,7 @@ for item in &items {
1. **Identify the warning**: Run `linter clippy` to see specific clippy errors
2. **Apply suggestion**: Try the suggested fix first
3. **Verify functionality**: Ensure the change doesn't break existing behavior
4. **Document exceptions**: Add clear comments for any allowances
4. **Document exceptions**: Use the native `reason = "..."` parameter for changed Clippy allows
5. **Run full linters**: Confirm `linter all` passes

## Related Skills
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:
- id: checkout
name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0

- id: setup
name: Setup Toolchain
Expand Down Expand Up @@ -90,6 +92,10 @@ jobs:
name: Run All Linters
run: linter all

- id: documented-clippy-allows
name: Check Documented Clippy Allows
run: cargo run --quiet --package clippy-allow-reasons -- --base-ref "origin/${{ github.base_ref || 'develop' }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion (S-f; round-1 S6/S7 unaddressed). A check that needs only git and a small binary still sits inside the unit matrix job behind toolchain install, Node, sccache, cargo fetch, cargo install --locked torrust-linting, cargo fmt and linter all, so it runs twice (nightly and stable) and only after the slowest steps have already passed; and the fetch-depth: 0 added at line 46 puts a full-history clone on the heaviest job in the workflow. A standalone job modelled on layer-bans would fail fast, run once, and confine the deep clone to a cheap job. Separately, ${{ github.base_ref || 'develop' }} is interpolated straight into the run: string; binding it through env: and reading "$BASE_REF" is the standard hygiene here and survives odd branch names.


- id: test-docs
name: Run Documentation Tests
run: cargo test --doc --workspace
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,13 @@ url = { version = "2", features = [ "serde" ] }
nix = { version = "0.31.3", default-features = false, features = [ "signal" ] }

[workspace]
# skill-link: add-workspace-member
# related-artifact: Containerfile — when adding or removing an explicit member,
# review its cargo-chef manifest/stub lists and nextest archive exclusions.
members = [
"console/tracker-client",
"contrib/dev-tools/analysis/workspace-coupling",
"contrib/dev-tools/checks/clippy-allow-reasons",
"packages/e2e-tools",
"packages/persistence-benchmark",
"packages/rest-api-application",
Expand Down
14 changes: 13 additions & 1 deletion Containerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# syntax=docker/dockerfile:latest
#
# semantic-links:
# skill-links:
# - add-workspace-member
# related-artifacts:
# - Cargo.toml # explicit workspace members; review this recipe when they change
# - .hadolint.yaml # hadolint global linting rules and ignore policies with rationale

# Torrust Tracker
Expand Down Expand Up @@ -72,6 +75,7 @@ COPY console/tracker-client/Cargo.toml console/tracker-client/
# Build stages below) because they are not part of the production tracker service
# and do not need to be tested inside the container image:
# - workspace-coupling (analysis/coupling tool, no production value)
# - clippy-allow-reasons (prospective source-quality check, no production value)
Comment thread
josecelano marked this conversation as resolved.
# - torrust-tracker-torrent-repository-benchmarking (benchmarking only)
# - torrust-tracker-client (CLI dev tools: tracker_client, tracker_checker, etc.)
# - torrust-tracker-e2e-tools (E2E runners + profiling tool, GHA host-only)
Expand All @@ -82,6 +86,7 @@ COPY console/tracker-client/Cargo.toml console/tracker-client/
# or declared target file is missing. `cargo chef prepare` has no `--exclude`
# flag (only `--bin`), so these stubs cannot be omitted from the recipe stage.
COPY contrib/dev-tools/analysis/workspace-coupling/Cargo.toml contrib/dev-tools/analysis/workspace-coupling/
COPY contrib/dev-tools/checks/clippy-allow-reasons/Cargo.toml contrib/dev-tools/checks/clippy-allow-reasons/
COPY packages/e2e-tools/Cargo.toml packages/e2e-tools/
COPY packages/persistence-benchmark/Cargo.toml packages/persistence-benchmark/
COPY packages/axum-health-check-api-server/Cargo.toml packages/axum-health-check-api-server/
Expand Down Expand Up @@ -127,6 +132,7 @@ RUN mkdir -p \
packages/e2e-tools/src/bin \
packages/persistence-benchmark/src/bin \
contrib/dev-tools/analysis/workspace-coupling/src \
contrib/dev-tools/checks/clippy-allow-reasons/src \
console/tracker-client/src/bin \
packages/axum-health-check-api-server/src \
packages/axum-http-server/src \
Expand Down Expand Up @@ -163,6 +169,8 @@ RUN mkdir -p \
packages/e2e-tools/src/bin/qbittorrent_e2e_runner.rs \
packages/persistence-benchmark/src/bin/persistence_benchmark_runner.rs \
contrib/dev-tools/analysis/workspace-coupling/src/main.rs \
contrib/dev-tools/checks/clippy-allow-reasons/src/lib.rs \
contrib/dev-tools/checks/clippy-allow-reasons/src/main.rs \
console/tracker-client/src/lib.rs \
console/tracker-client/src/bin/http_tracker_client.rs \
console/tracker-client/src/bin/tracker_checker.rs \
Expand Down Expand Up @@ -222,7 +230,7 @@ COPY --from=recipe /build/recipe.json /build/recipe.json
# Note: `cargo chef cook` does not support `--exclude` (the cargo-chef CLI only
# exposes `--workspace` and `--package`, not `--exclude`). The excluded workspace
# members (workspace-coupling, torrust-tracker-torrent-repository-benchmarking,
# torrust-tracker-client, torrust-tracker-contrib-bencode,
# clippy-allow-reasons, torrust-tracker-client, torrust-tracker-contrib-bencode,
# torrust-tracker-e2e-tools, torrust-tracker-persistence-benchmark) are therefore
# still compiled as part of the cook skeleton (their Cargo.toml manifests are in
# the recipe, so cargo-chef cooks them). The build-time savings come from the
Expand All @@ -235,6 +243,7 @@ RUN cargo chef cook --tests --workspace --all-features --recipe-path /build/reci
# by pre-faulting the linker phases, avoiding redundant linking work in later stages.
RUN cargo nextest archive --tests --workspace --all-features \
--exclude workspace-coupling \
--exclude clippy-allow-reasons \
--exclude torrust-tracker-torrent-repository-benchmarking \
--exclude torrust-tracker-client \
--exclude torrust-tracker-contrib-bencode \
Expand All @@ -261,6 +270,7 @@ RUN cargo chef cook --tests --workspace --all-features --recipe-path /build/reci
# by pre-faulting the linker phases, avoiding redundant linking work in later stages.
RUN cargo nextest archive --tests --workspace --all-features \
--exclude workspace-coupling \
--exclude clippy-allow-reasons \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (N-b, new). This continuation line is indented with two spaces; every other --exclude line in all four archive invocations uses four, including the one this commit added at line 243. Same at lines 285 and 299. hadolint does not check continuation indentation (its step passes at this head) and the shell does not care, so this is purely cosmetic — but the four invocations are otherwise character-identical blocks, which is exactly what makes them cheap to keep in sync by eye, and three of them now break that.

The substance of C1 is fixed here and I confirmed it independently: grep -c '^RUN cargo nextest archive' gives 4, grep -n 'exclude clippy-allow-reasons' gives 243/270/285/299, and the cook-stage note at line 230 was updated too — so the manifest-stage comment at line 75 is now accurate. Choosing to keep a Git-dependent, dev-only crate out of the archive was the right repair rather than installing git into the tester image.

--exclude torrust-tracker-torrent-repository-benchmarking \
--exclude torrust-tracker-client \
--exclude torrust-tracker-contrib-bencode \
Expand All @@ -275,6 +285,7 @@ WORKDIR /build/src
COPY . /build/src
RUN cargo nextest archive --tests --workspace --all-features \
--exclude workspace-coupling \
--exclude clippy-allow-reasons \
--exclude torrust-tracker-torrent-repository-benchmarking \
--exclude torrust-tracker-client \
--exclude torrust-tracker-contrib-bencode \
Expand All @@ -288,6 +299,7 @@ WORKDIR /build/src
COPY . /build/src
RUN cargo nextest archive --tests --workspace --all-features \
--exclude workspace-coupling \
--exclude clippy-allow-reasons \
--exclude torrust-tracker-torrent-repository-benchmarking \
--exclude torrust-tracker-client \
--exclude torrust-tracker-contrib-bencode \
Expand Down
18 changes: 18 additions & 0 deletions contrib/dev-tools/checks/clippy-allow-reasons/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
description = "Prospective validation for documented Clippy allow attributes."
name = "clippy-allow-reasons"
publish = false

authors.workspace = true
edition.workspace = true
rust-version.workspace = true
version = "0.1.0"

[lints]
workspace = true

[dependencies]
proc-macro2 = { version = "1", features = [ "span-locations" ] }
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
syn = { version = "2", features = [ "full", "visit" ] }
Loading
Loading