Skip to content

[dotnet] [build] Use paket as package manager in sdk projects#17577

Open
nvborisenko wants to merge 6 commits into
SeleniumHQ:trunkfrom
nvborisenko:dotnet-paket-sdk
Open

[dotnet] [build] Use paket as package manager in sdk projects#17577
nvborisenko wants to merge 6 commits into
SeleniumHQ:trunkfrom
nvborisenko:dotnet-paket-sdk

Conversation

@nvborisenko
Copy link
Copy Markdown
Member

💥 What does this PR do?

This pull request migrates the .NET projects to use Paket for dependency management instead of direct NuGet PackageReference entries in the .csproj files. The changes remove package references from project files, add Paket references, and update build configuration to ensure compatibility with .NET 10+. This streamlines dependency management and makes transitive dependencies explicit.

Dependency management migration:

  • Removed all <PackageReference> entries for libraries such as Microsoft.Bcl.AsyncInterfaces, System.Collections.Immutable, System.Text.Json, System.Threading.Channels, NUnit, Moq, and others from .csproj files, moving their declarations to paket.references files instead. [1] [2] [3] [4]
  • Added new paket.references files for each project, explicitly listing the required dependencies per project and target framework. [1] [2] [3] [4]

Build and compatibility updates:

  • Updated paket.dependencies to add support for net10.0 and included missing dependencies.
  • Added imports for Paket.Restore.targets to all relevant .csproj files to ensure Paket restores dependencies during build. [1] [2] [3] [4] [5]
  • Added a root-level Directory.Build.props to suppress .NET 10+ package pruning warnings (NU1510), ensuring clean builds with the new framework.

Project configuration cleanup:

  • Added empty or group headers to paket.references files where appropriate to conform to Paket's expected format.

These changes collectively improve dependency management, future-proof the build for .NET 10+, and simplify project maintenance by consolidating dependency declarations.

🔧 Implementation Notes

paket.dependencies is single source of truth.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s):
    • What was generated:
    • I reviewed all AI output and can explain the change

🔄 Types of changes

  • Cleanup (formatting, renaming)

@nvborisenko nvborisenko requested a review from titusfortner May 27, 2026 09:27
@selenium-ci selenium-ci added C-dotnet .NET Bindings B-support Issue or PR related to support classes labels May 27, 2026
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Migrate .NET SDK projects to Paket package manager

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Migrate .NET SDK projects to use Paket package manager
• Remove PackageReference entries from .csproj files
• Add paket.references files for dependency declarations
• Add Paket.Restore.targets imports to all projects
• Support .NET 10.0 framework and suppress package pruning warnings
Diagram
flowchart LR
  A["PackageReference in .csproj"] -->|"Remove"| B["paket.references files"]
  C["paket.dependencies"] -->|"Add net10.0 support"| D["Updated framework list"]
  B -->|"Import"| E["Paket.Restore.targets"]
  F["Directory.Build.props"] -->|"Suppress NU1510"| G["Clean .NET 10+ builds"]

Loading

Grey Divider

File Changes

1. dotnet/Directory.Build.props ⚙️ Configuration changes +6/-0

Add root build properties for Paket

dotnet/Directory.Build.props


2. dotnet/paket.dependencies Dependencies +2/-1

Add net10.0 framework and dependencies

dotnet/paket.dependencies


3. dotnet/src/webdriver/Selenium.WebDriver.csproj ✨ Enhancement +2/-7

Remove PackageReference, add Paket import

dotnet/src/webdriver/Selenium.WebDriver.csproj


View more (9)
4. dotnet/src/webdriver/paket.references Dependencies +5/-0

Add framework-specific package references

dotnet/src/webdriver/paket.references


5. dotnet/src/support/Selenium.Support.csproj ✨ Enhancement +2/-0

Add Paket.Restore.targets import

dotnet/src/support/Selenium.Support.csproj


6. dotnet/src/support/paket.references Dependencies +2/-0

Create empty paket references file

dotnet/src/support/paket.references


7. dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj ✨ Enhancement +2/-9

Remove PackageReference, add Paket import

dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj


8. dotnet/test/webdriver/paket.references Dependencies +7/-0

Add test project package dependencies

dotnet/test/webdriver/paket.references


9. dotnet/test/support/Selenium.Support.Tests.csproj ✨ Enhancement +2/-8

Remove PackageReference, add Paket import

dotnet/test/support/Selenium.Support.Tests.csproj


10. dotnet/test/support/paket.references Dependencies +6/-0

Add support test package dependencies

dotnet/test/support/paket.references


11. dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj ✨ Enhancement +2/-6

Remove PackageReference, add Paket import

dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj


12. dotnet/test/remote/paket.references Dependencies +4/-0

Add remote test package dependencies

dotnet/test/remote/paket.references


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (4)

Grey Divider


Action required

1. Missing newline in dotnet-tools.json 📘 Rule violation ⚙ Maintainability
Description
dotnet/.config/dotnet-tools.json is missing a final newline, which violates the repository’s
EditorConfig formatting requirement and can cause formatting checks to fail. This conflicts with the
requirement to run/commit formatting so it leaves no remaining diff.
Code

dotnet/.config/dotnet-tools.json[13]

Evidence
The repo’s .editorconfig requires insert_final_newline = true, but the modified
dotnet/.config/dotnet-tools.json ends without a newline (as indicated by the diff). This indicates
formatting is not fully applied/committed as required by the formatting compliance rule.

AGENTS.md: Run ./go format after code changes and commit any formatter modifications
.editorconfig[5-12]
dotnet/.config/dotnet-tools.json[1-13]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`dotnet/.config/dotnet-tools.json` is missing a final newline (`insert_final_newline = true`), which can cause formatting/CI checks to fail.

## Issue Context
Repo-wide formatting rules are defined in `.editorconfig`.

## Fix Focus Areas
- dotnet/.config/dotnet-tools.json[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Dotnet tool restore missing 🐞 Bug ☼ Reliability
Description
SetPaketCommand can select dotnet paket based on the local tool manifest, but PaketRestore
invokes $(PaketCommand) restore without ever running dotnet tool restore. On a clean checkout
(or any environment where the local tool manifest hasn’t been restored yet), MSBuild can fail before
compilation because the paket tool command isn’t available.
Code

dotnet/.paket/Paket.Restore.targets[R68-83]

Evidence
The targets file explicitly switches to using dotnet paket when it detects the tool manifest, but
there is no corresponding dotnet tool restore step anywhere in the targets before running `paket
restore. Repo docs/scripts show dotnet tool restore` is the required step to make the manifest
tool available.

dotnet/.paket/Paket.Restore.targets[66-83]
dotnet/.paket/Paket.Restore.targets[166-172]
dotnet/.config/dotnet-tools.json[1-12]
dotnet/README.md[13-20]
dotnet/update-deps.sh[19-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Paket.Restore.targets` may choose `PaketCommand = "dotnet paket"` (local tool), but it never ensures the tool manifest has been restored (`dotnet tool restore`) before invoking `$(PaketCommand) restore`. This can fail builds on clean machines / first VS load.

### Issue Context
The repo uses a local tool manifest (`dotnet/.config/dotnet-tools.json`) and documentation/scripts already call `dotnet tool restore`. The MSBuild integration should do the same (ideally only when `dotnet paket` is selected) to avoid requiring an undocumented manual pre-step.

### Fix approach (one good option)
1. Add a new target, e.g. `PaketDotnetToolRestore`, that runs `dotnet tool restore` with `WorkingDirectory="$(PaketRootPath)"`.
2. Make `PaketRestore` depend on it (after `SetPaketCommand`).
3. Condition it to only run when the chosen `PaketCommand` is `dotnet paket` (or when `.config/dotnet-tools.json` exists and `_ConfigContainsPaket` is true).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]
- dotnet/.paket/Paket.Restore.targets[117-172]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Multi-target resolved check flawed 🐞 Bug ≡ Correctness
Description
DoAllResolvedFilesExist is set to true if any *.paket.resolved file exists, not if all of them
exist; for multi-target builds this can incorrectly skip restore when one framework’s resolved file
is missing, leading to missing/incorrect PackageReference forwarding or subsequent file-read
failures.
Code

dotnet/.paket/Paket.Restore.targets[R174-213]

Evidence
The file creates multiple PaketResolvedFilePaths (one per target framework) but sets a single
boolean property based on Exists(%(...)), which will flip true as soon as any item exists; the
subsequent restore decision relies on that property and then reads each resolved file.

dotnet/.paket/Paket.Restore.targets[173-213]
dotnet/.paket/Paket.Restore.targets[190-213]
dotnet/.paket/Paket.Restore.targets[228-231]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DoAllResolvedFilesExist` is computed using a scalar property assignment batched over an item list, which makes it effectively mean “any exists” rather than “all exist”. This breaks restore decisioning for multi-target projects.

### Issue Context
`PaketResolvedFilePaths` is built from `@(MyTargetFrameworks)`, so it commonly contains multiple items. The restore decision (and later error checking) relies on `DoAllResolvedFilesExist`.

### Fix
Replace the property batching with an item-based missing-file check, e.g.:
- Create `MissingResolvedFiles` item group with `Condition="!Exists('%(Identity)')"`
- Set `DoAllResolvedFilesExist` based on whether `@(MissingResolvedFiles)` is empty
- Reuse the same logic in both the pre-restore and post-restore checks.

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[173-227]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
4. dotnet paket probe can hang 📘 Rule violation ☼ Reliability
Description
The build target runs dotnet paket --version as an availability probe while explicitly warning it
“can lead to processes hanging forever”, and it ignores the exit code. This can cause
non-deterministic CI/build hangs instead of failing fast with actionable diagnostics.
Code

dotnet/.paket/Paket.Restore.targets[R74-78]

Evidence
PR Compliance ID 12 requires automation/build steps to validate external command outputs and fail
safely. The added Exec runs an external command that the file itself warns may hang indefinitely,
and it suppresses output and ignores exit codes, making failures non-deterministic and hard to
diagnose.

dotnet/.paket/Paket.Restore.targets[74-78]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SetPaketCommand` probes for Paket by executing `dotnet paket --version` with `IgnoreExitCode="true"` and no timeout, despite an in-file warning that the command can hang forever.

## Issue Context
This targets file runs during MSBuild evaluation/restore and impacts developer builds and CI reliability. Compliance requires validating external command outcomes and failing safely with deterministic behavior.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]

## Suggested remediation
- Avoid executing `dotnet paket --version` as a probe where possible (prefer deterministic checks such as `dotnet tool list --local` when `.config/dotnet-tools.json` exists).
- If an Exec probe remains necessary, add an explicit timeout (MSBuild `Exec` supports `Timeout` in newer MSBuilds; otherwise wrap the command in a platform-appropriate timeout helper) and surface a clear error when it times out.
- Don’t silently ignore failures: either remove `IgnoreExitCode="true"` or gate subsequent decisions on validated output/exit code and emit an actionable message when Paket cannot be located.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Missing Exists() before ReadLines 📘 Rule violation ☼ Reliability
Description
ReadLinesFromFile unconditionally reads $(PaketCliToolFilePath) (i.e.,
$(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools) without checking that the
file exists, which can hard-fail builds when Paket doesn’t generate it or restore was skipped. This
violates the requirement to validate required artifacts/preconditions and fail safely with clear
diagnostics.
Code

dotnet/.paket/Paket.Restore.targets[R261-267]

Evidence
PR Compliance ID 12 requires automation to validate required artifacts before consuming them, but
the added MSBuild logic reads the derived .paket.clitools path via ReadLinesFromFile without an
Exists(...) condition, making clean builds brittle when that generated file is absent. This is
underscored by the fact that the .paket.resolved read earlier in the same targets file is guarded
by a condition while the .paket.clitools read is not, so the build behavior depends on whether
that file happens to exist rather than handling the missing-artifact case deterministically.

dotnet/.paket/Paket.Restore.targets[261-267]
dotnet/.paket/Paket.Restore.targets[228-232]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The targets file reads `$(PaketCliToolFilePath)` (the generated `.paket.clitools` file under `$(PaketIntermediateOutputPath)`) unconditionally via `ReadLinesFromFile`, which will fail with a file-not-found error when the file is not generated for a project or restore was skipped.

## Issue Context
Compliance requires validating preconditions/artifacts in automation and failing safely with clear diagnostics. In the same area of the targets file, the `.paket.resolved` read above is conditionally executed, but the `.paket.clitools` read is not, making builds sensitive to whether that file exists.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[261-278]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Restore runs every build 🐞 Bug ➹ Performance
Description
PaketRestoreRequired is forced to true whenever $(PaketPropsVersion) != '6.0.0'; since
PaketPropsVersion is not set by the imported projects, this evaluates true and triggers `paket
restore` on every build, defeating the lock/cache fast-path and making builds slower and more
network/flakiness-prone.
Code

dotnet/.paket/Paket.Restore.targets[R162-171]

Evidence
The targets file forces PaketRestoreRequired when PaketPropsVersion differs, and then runs
$(PaketCommand) restore when required; at least one repo project imports only this targets file
and does not define PaketPropsVersion, so the version condition will evaluate true when the
property is empty.

dotnet/.paket/Paket.Restore.targets[158-172]
dotnet/src/webdriver/Selenium.WebDriver.csproj[85-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`PaketRestoreRequired` is unconditionally flipped back to `true` via the `PaketPropsVersion` check, which (when `PaketPropsVersion` is unset) forces a restore every build.

### Issue Context
Projects in this repo import `Paket.Restore.targets` directly and do not import any generated Paket props that would define `PaketPropsVersion`, so `$(PaketPropsVersion)` is typically empty.

### Fix
Update the condition so it only forces restore when `PaketPropsVersion` is actually present and mismatched (or set a sane default in a single place if that’s the intended contract).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[158-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing Paket restore targets ✓ Resolved 🐞 Bug ☼ Reliability
Description
Multiple SDK-style .csproj files now unconditionally import ..\..\.paket\Paket.Restore.targets,
but the repository does not contain that file (and dotnet/.paket/ is gitignored), so MSBuild will
fail to load/build these projects with an Import error on a clean checkout. This blocks Visual
Studio/MSBuild-based workflows for all affected projects unless an undocumented bootstrap step
creates that targets file before any project evaluation happens.
Code

dotnet/src/webdriver/Selenium.WebDriver.csproj[90]

Evidence
The projects now import a relative .paket/Paket.Restore.targets file, but the repo ignores
dotnet/.paket/ and the documented dependency workflow uses dotnet tool run paket install without
any guarantee that .paket/Paket.Restore.targets exists before MSBuild evaluates the project
imports.

.gitignore[17-20]
dotnet/src/webdriver/Selenium.WebDriver.csproj[88-92]
dotnet/src/support/Selenium.Support.csproj[50-54]
dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj[21-25]
dotnet/test/support/Selenium.Support.Tests.csproj[18-22]
dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj[27-31]
dotnet/update-deps.sh[19-23]
dotnet/README.md[11-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several projects import `..\\..\\.paket\\Paket.Restore.targets`, but `dotnet/.paket/` is not present in the repo and is ignored by git. This causes MSBuild/VS project load failures (missing import) on a clean checkout.

## Issue Context
- The repo currently uses `dotnet tool run paket ...` (tool-manifest based) and does not document/guarantee creation of `dotnet/.paket/Paket.Restore.targets` prior to project evaluation.
- `dotnet/.paket/` is explicitly gitignored, so the imported file is not available by default.

## Fix Focus Areas
Choose one approach and apply consistently across all affected projects:
1) **Ensure the imported file exists by default**: commit the necessary `.paket` restore targets (and any required bootstrap artifacts) and adjust `.gitignore` accordingly.
2) **Or avoid hard dependency on a non-existent file**: change each `<Import ...>` to be conditional (`Condition="Exists('...')"`) and add a clear, early build error/target (or documented bootstrap step) that instructs how to generate/restore Paket integration artifacts before building.

- .gitignore[17-20]
- dotnet/src/webdriver/Selenium.WebDriver.csproj[90-90]
- dotnet/src/support/Selenium.Support.csproj[52-52]
- dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj[23-23]
- dotnet/test/support/Selenium.Support.Tests.csproj[20-20]
- dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj[29-29]
- dotnet/update-deps.sh[19-23]
- dotnet/README.md[11-26]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

8. Brittle paket.restore.cached parsing 📘 Rule violation ☼ Reliability ⭐ New
Description
Paket.Restore.targets parses paket.restore.cached using regex/string splitting that assumes
specific JSON formatting (quotes/spacing/order), which can silently miscompute hashes and lead to
incorrect restore decisions. This violates the requirement to validate/parse external command
outputs defensively and fail safely with clear diagnostics on unexpected formats.
Code

dotnet/.paket/Paket.Restore.targets[R132-143]

Evidence
PR Compliance ID 9 requires defensive parsing/validation of external command outputs (including
JSON). The added MSBuild logic reads paket.restore.cached and extracts fields by regex-splitting
and assuming a specific ": " tokenization, which is not a robust JSON parse and can break or
misread values if formatting changes.

dotnet/.paket/Paket.Restore.targets[132-143]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`paket.restore.cached` is treated as JSON but parsed via regex splitting and hard-coded `": "` separators, making the restore decision logic fragile to formatting changes and risking silent incorrect behavior.

## Issue Context
Compliance requires defensive validation/parsing of external outputs (including JSON) and safe failure modes with actionable errors when assumptions are violated.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[132-149]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Bash command substitution risk 🐞 Bug ⛨ Security
Description
The generated Unix and Windows scripts interpolate ctx.attr.solution into SOLUTION using an
unescaped .format(...), allowing shell metacharacters to be interpreted at runtime (e.g., bash
$(...)/backticks or cmd.exe &|><). This can execute unintended commands and/or corrupt the
computed solution path before the scripts’ existence checks run.
Code

dotnet/private/dotnet_format.bzl[R52-56]

Evidence
In the Unix script template, {solution} appears inside a double-quoted assignment
(SOLUTION="$DOTNET_DIR/{solution}") and is populated directly from ctx.attr.solution without
escaping or validation, so bash parsing rules apply and command substitution can still occur inside
double quotes; in the Windows batch template, {solution} is similarly inserted into a raw `set
SOLUTION=%DOTNET_DIR%\{solution}` command from the same plain string attribute, so cmd.exe will
parse any provided metacharacters as separators/redirections, affecting the value and potentially
executing additional commands before the -f "$SOLUTION" / if not exist "%SOLUTION%" checks.

dotnet/private/dotnet_format.bzl[34-77]
dotnet/private/dotnet_format.bzl[141-147]
dotnet/private/dotnet_format.bzl[91-131]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ctx.attr.solution` is templated directly into generated Unix (`SOLUTION="$DOTNET_DIR/{solution}"`) and Windows (`set SOLUTION=%DOTNET_DIR%\\{solution}`) scripts via unescaped `.format(solution = ctx.attr.solution)`. Because bash expands command substitution even inside double quotes and cmd.exe interprets metacharacters in `set` lines, a crafted `solution` value can lead to unintended command execution and/or an incorrect `SOLUTION` value before the scripts’ file-existence checks.

## Issue Context
This behavior was introduced when the solution path was made configurable via a new `solution` attribute and then interpolated into the generated `.sh` and `.bat` script content.

## Fix Focus Areas
- dotnet/private/dotnet_format.bzl[34-77]
- dotnet/private/dotnet_format.bzl[91-131]
- dotnet/private/dotnet_format.bzl[141-147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 1572713

Results up to commit 400cf9f


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)


Action required
1. Missing Paket restore targets ✓ Resolved 🐞 Bug ☼ Reliability
Description
Multiple SDK-style .csproj files now unconditionally import ..\..\.paket\Paket.Restore.targets,
but the repository does not contain that file (and dotnet/.paket/ is gitignored), so MSBuild will
fail to load/build these projects with an Import error on a clean checkout. This blocks Visual
Studio/MSBuild-based workflows for all affected projects unless an undocumented bootstrap step
creates that targets file before any project evaluation happens.
Code

dotnet/src/webdriver/Selenium.WebDriver.csproj[90]

Evidence
The projects now import a relative .paket/Paket.Restore.targets file, but the repo ignores
dotnet/.paket/ and the documented dependency workflow uses dotnet tool run paket install without
any guarantee that .paket/Paket.Restore.targets exists before MSBuild evaluates the project
imports.

.gitignore[17-20]
dotnet/src/webdriver/Selenium.WebDriver.csproj[88-92]
dotnet/src/support/Selenium.Support.csproj[50-54]
dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj[21-25]
dotnet/test/support/Selenium.Support.Tests.csproj[18-22]
dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj[27-31]
dotnet/update-deps.sh[19-23]
dotnet/README.md[11-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several projects import `..\\..\\.paket\\Paket.Restore.targets`, but `dotnet/.paket/` is not present in the repo and is ignored by git. This causes MSBuild/VS project load failures (missing import) on a clean checkout.

## Issue Context
- The repo currently uses `dotnet tool run paket ...` (tool-manifest based) and does not document/guarantee creation of `dotnet/.paket/Paket.Restore.targets` prior to project evaluation.
- `dotnet/.paket/` is explicitly gitignored, so the imported file is not available by default.

## Fix Focus Areas
Choose one approach and apply consistently across all affected projects:
1) **Ensure the imported file exists by default**: commit the necessary `.paket` restore targets (and any required bootstrap artifacts) and adjust `.gitignore` accordingly.
2) **Or avoid hard dependency on a non-existent file**: change each `<Import ...>` to be conditional (`Condition="Exists('...')"`) and add a clear, early build error/target (or documented bootstrap step) that instructs how to generate/restore Paket integration artifacts before building.

- .gitignore[17-20]
- dotnet/src/webdriver/Selenium.WebDriver.csproj[90-90]
- dotnet/src/support/Selenium.Support.csproj[52-52]
- dotnet/test/remote/Selenium.WebDriver.Remote.Tests.csproj[23-23]
- dotnet/test/support/Selenium.Support.Tests.csproj[20-20]
- dotnet/test/webdriver/Selenium.WebDriver.Tests.csproj[29-29]
- dotnet/update-deps.sh[19-23]
- dotnet/README.md[11-26]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 781c035


🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)


Action required
1. Missing Exists() before ReadLines 📘 Rule violation ☼ Reliability
Description
ReadLinesFromFile unconditionally reads $(PaketCliToolFilePath) (i.e.,
$(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools) without checking that the
file exists, which can hard-fail builds when Paket doesn’t generate it or restore was skipped. This
violates the requirement to validate required artifacts/preconditions and fail safely with clear
diagnostics.
Code

dotnet/.paket/Paket.Restore.targets[R261-267]

Evidence
PR Compliance ID 12 requires automation to validate required artifacts before consuming them, but
the added MSBuild logic reads the derived .paket.clitools path via ReadLinesFromFile without an
Exists(...) condition, making clean builds brittle when that generated file is absent. This is
underscored by the fact that the .paket.resolved read earlier in the same targets file is guarded
by a condition while the .paket.clitools read is not, so the build behavior depends on whether
that file happens to exist rather than handling the missing-artifact case deterministically.

dotnet/.paket/Paket.Restore.targets[261-267]
dotnet/.paket/Paket.Restore.targets[228-232]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The targets file reads `$(PaketCliToolFilePath)` (the generated `.paket.clitools` file under `$(PaketIntermediateOutputPath)`) unconditionally via `ReadLinesFromFile`, which will fail with a file-not-found error when the file is not generated for a project or restore was skipped.

## Issue Context
Compliance requires validating preconditions/artifacts in automation and failing safely with clear diagnostics. In the same area of the targets file, the `.paket.resolved` read above is conditionally executed, but the `.paket.clitools` read is not, making builds sensitive to whether that file exists.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[261-278]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Restore runs every build 🐞 Bug ➹ Performance
Description
PaketRestoreRequired is forced to true whenever $(PaketPropsVersion) != '6.0.0'; since
PaketPropsVersion is not set by the imported projects, this evaluates true and triggers `paket
restore` on every build, defeating the lock/cache fast-path and making builds slower and more
network/flakiness-prone.
Code

dotnet/.paket/Paket.Restore.targets[R162-171]

Evidence
The targets file forces PaketRestoreRequired when PaketPropsVersion differs, and then runs
$(PaketCommand) restore when required; at least one repo project imports only this targets file
and does not define PaketPropsVersion, so the version condition will evaluate true when the
property is empty.

dotnet/.paket/Paket.Restore.targets[158-172]
dotnet/src/webdriver/Selenium.WebDriver.csproj[85-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`PaketRestoreRequired` is unconditionally flipped back to `true` via the `PaketPropsVersion` check, which (when `PaketPropsVersion` is unset) forces a restore every build.

### Issue Context
Projects in this repo import `Paket.Restore.targets` directly and do not import any generated Paket props that would define `PaketPropsVersion`, so `$(PaketPropsVersion)` is typically empty.

### Fix
Update the condition so it only forces restore when `PaketPropsVersion` is actually present and mismatched (or set a sane default in a single place if that’s the intended contract).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[158-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Multi-target resolved check flawed 🐞 Bug ≡ Correctness
Description
DoAllResolvedFilesExist is set to true if any *.paket.resolved file exists, not if all of them
exist; for multi-target builds this can incorrectly skip restore when one framework’s resolved file
is missing, leading to missing/incorrect PackageReference forwarding or subsequent file-read
failures.
Code

dotnet/.paket/Paket.Restore.targets[R174-213]

Evidence
The file creates multiple PaketResolvedFilePaths (one per target framework) but sets a single
boolean property based on Exists(%(...)), which will flip true as soon as any item exists; the
subsequent restore decision relies on that property and then reads each resolved file.

dotnet/.paket/Paket.Restore.targets[173-213]
dotnet/.paket/Paket.Restore.targets[190-213]
dotnet/.paket/Paket.Restore.targets[228-231]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DoAllResolvedFilesExist` is computed using a scalar property assignment batched over an item list, which makes it effectively mean “any exists” rather than “all exist”. This breaks restore decisioning for multi-target projects.

### Issue Context
`PaketResolvedFilePaths` is built from `@(MyTargetFrameworks)`, so it commonly contains multiple items. The restore decision (and later error checking) relies on `DoAllResolvedFilesExist`.

### Fix
Replace the property batching with an item-based missing-file check, e.g.:
- Create `MissingResolvedFiles` item group with `Condition="!Exists('%(Identity)')"`
- Set `DoAllResolvedFilesExist` based on whether `@(MissingResolvedFiles)` is empty
- Reuse the same logic in both the pre-restore and post-restore checks.

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[173-227]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. dotnet paket probe can hang 📘 Rule violation ☼ Reliability
Description
The build target runs dotnet paket --version as an availability probe while explicitly warning it
“can lead to processes hanging forever”, and it ignores the exit code. This can cause
non-deterministic CI/build hangs instead of failing fast with actionable diagnostics.
Code

dotnet/.paket/Paket.Restore.targets[R74-78]

Evidence
PR Compliance ID 12 requires automation/build steps to validate external command outputs and fail
safely. The added Exec runs an external command that the file itself warns may hang indefinitely,
and it suppresses output and ignores exit codes, making failures non-deterministic and hard to
diagnose.

dotnet/.paket/Paket.Restore.targets[74-78]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SetPaketCommand` probes for Paket by executing `dotnet paket --version` with `IgnoreExitCode="true"` and no timeout, despite an in-file warning that the command can hang forever.

## Issue Context
This targets file runs during MSBuild evaluation/restore and impacts developer builds and CI reliability. Compliance requires validating external command outcomes and failing safely with deterministic behavior.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]

## Suggested remediation
- Avoid executing `dotnet paket --version` as a probe where possible (prefer deterministic checks such as `dotnet tool list --local` when `.config/dotnet-tools.json` exists).
- If an Exec probe remains necessary, add an explicit timeout (MSBuild `Exec` supports `Timeout` in newer MSBuilds; otherwise wrap the command in a platform-appropriate timeout helper) and surface a clear error when it times out.
- Don’t silently ignore failures: either remove `IgnoreExitCode="true"` or gate subsequent decisions on validated output/exit code and emit an actionable message when Paket cannot be located.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit ff29936


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)


Action required
1. Dotnet tool restore missing 🐞 Bug ☼ Reliability
Description
SetPaketCommand can select dotnet paket based on the local tool manifest, but PaketRestore
invokes $(PaketCommand) restore without ever running dotnet tool restore. On a clean checkout
(or any environment where the local tool manifest hasn’t been restored yet), MSBuild can fail before
compilation because the paket tool command isn’t available.
Code

dotnet/.paket/Paket.Restore.targets[R68-83]

Evidence
The targets file explicitly switches to using dotnet paket when it detects the tool manifest, but
there is no corresponding dotnet tool restore step anywhere in the targets before running `paket
restore. Repo docs/scripts show dotnet tool restore` is the required step to make the manifest
tool available.

dotnet/.paket/Paket.Restore.targets[66-83]
dotnet/.paket/Paket.Restore.targets[166-172]
dotnet/.config/dotnet-tools.json[1-12]
dotnet/README.md[13-20]
dotnet/update-deps.sh[19-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Paket.Restore.targets` may choose `PaketCommand = "dotnet paket"` (local tool), but it never ensures the tool manifest has been restored (`dotnet tool restore`) before invoking `$(PaketCommand) restore`. This can fail builds on clean machines / first VS load.

### Issue Context
The repo uses a local tool manifest (`dotnet/.config/dotnet-tools.json`) and documentation/scripts already call `dotnet tool restore`. The MSBuild integration should do the same (ideally only when `dotnet paket` is selected) to avoid requiring an undocumented manual pre-step.

### Fix approach (one good option)
1. Add a new target, e.g. `PaketDotnetToolRestore`, that runs `dotnet tool restore` with `WorkingDirectory="$(PaketRootPath)"`.
2. Make `PaketRestore` depend on it (after `SetPaketCommand`).
3. Condition it to only run when the chosen `PaketCommand` is `dotnet paket` (or when `.config/dotnet-tools.json` exists and `_ConfigContainsPaket` is true).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]
- dotnet/.paket/Paket.Restore.targets[117-172]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 1257a45


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)


Remediation recommended
1. Bash command substitution risk 🐞 Bug ⛨ Security
Description
The generated Unix and Windows scripts interpolate ctx.attr.solution into SOLUTION using an
unescaped .format(...), allowing shell metacharacters to be interpreted at runtime (e.g., bash
$(...)/backticks or cmd.exe &|><). This can execute unintended commands and/or corrupt the
computed solution path before the scripts’ existence checks run.
Code

dotnet/private/dotnet_format.bzl[R52-56]

Evidence
In the Unix script template, {solution} appears inside a double-quoted assignment
(SOLUTION="$DOTNET_DIR/{solution}") and is populated directly from ctx.attr.solution without
escaping or validation, so bash parsing rules apply and command substitution can still occur inside
double quotes; in the Windows batch template, {solution} is similarly inserted into a raw `set
SOLUTION=%DOTNET_DIR%\{solution}` command from the same plain string attribute, so cmd.exe will
parse any provided metacharacters as separators/redirections, affecting the value and potentially
executing additional commands before the -f "$SOLUTION" / if not exist "%SOLUTION%" checks.

dotnet/private/dotnet_format.bzl[34-77]
dotnet/private/dotnet_format.bzl[141-147]
dotnet/private/dotnet_format.bzl[91-131]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ctx.attr.solution` is templated directly into generated Unix (`SOLUTION="$DOTNET_DIR/{solution}"`) and Windows (`set SOLUTION=%DOTNET_DIR%\\{solution}`) scripts via unescaped `.format(solution = ctx.attr.solution)`. Because bash expands command substitution even inside double quotes and cmd.exe interprets metacharacters in `set` lines, a crafted `solution` value can lead to unintended command execution and/or an incorrect `SOLUTION` value before the scripts’ file-existence checks.

## Issue Context
This behavior was introduced when the solution path was made configurable via a new `solution` attribute and then interpolated into the generated `.sh` and `.bat` script content.

## Fix Focus Areas
- dotnet/private/dotnet_format.bzl[34-77]
- dotnet/private/dotnet_format.bzl[91-131]
- dotnet/private/dotnet_format.bzl[141-147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 77556b6


🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0)


Action required
1. Missing newline in dotnet-tools.json 📘 Rule violation ⚙ Maintainability
Description
dotnet/.config/dotnet-tools.json is missing a final newline, which violates the repository’s
EditorConfig formatting requirement and can cause formatting checks to fail. This conflicts with the
requirement to run/commit formatting so it leaves no remaining diff.
Code

dotnet/.config/dotnet-tools.json[13]

Evidence
The repo’s .editorconfig requires insert_final_newline = true, but the modified
dotnet/.config/dotnet-tools.json ends without a newline (as indicated by the diff). This indicates
formatting is not fully applied/committed as required by the formatting compliance rule.

AGENTS.md: Run ./go format after code changes and commit any formatter modifications
.editorconfig[5-12]
dotnet/.config/dotnet-tools.json[1-13]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`dotnet/.config/dotnet-tools.json` is missing a final newline (`insert_final_newline = true`), which can cause formatting/CI checks to fail.

## Issue Context
Repo-wide formatting rules are defined in `.editorconfig`.

## Fix Focus Areas
- dotnet/.config/dotnet-tools.json[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

@selenium-ci
Copy link
Copy Markdown
Member

Thank you, @nvborisenko for this code suggestion.

The support packages contain example code that many users find helpful, but they do not necessarily represent
the best practices for using Selenium, and the Selenium team is not currently merging changes to them.

After reviewing the change, unless it is a critical fix or a feature that is needed for Selenium
to work, we will likely close the PR.

We actively encourage people to add the wrapper and helper code that makes sense for them to their own frameworks.
If you have any questions, please contact us

Comment thread dotnet/src/webdriver/Selenium.WebDriver.csproj
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code review by qodo was updated up to the latest commit 781c035

Comment on lines +74 to +78
<!-- Second test: Call 'dotnet paket' and see if it returns without an error. Mute all the output. Only run if previous test failed and the test has not been disabled. -->
<!-- WARNING: This method can lead to processes hanging forever, and should be used as little as possible. See https://github.com/fsprojects/Paket/issues/3705 for details. -->
<Exec Condition=" '$(PaketExePath)' == '' AND !$(PaketDisableCliTest) AND !$(_ConfigContainsPaket)" Command="dotnet paket --version" IgnoreExitCode="true" StandardOutputImportance="low" StandardErrorImportance="low" >
<Output TaskParameter="ExitCode" PropertyName="LocalPaketToolExitCode" />
</Exec>
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.

Action required

1. dotnet paket probe can hang 📘 Rule violation ☼ Reliability

The build target runs dotnet paket --version as an availability probe while explicitly warning it
“can lead to processes hanging forever”, and it ignores the exit code. This can cause
non-deterministic CI/build hangs instead of failing fast with actionable diagnostics.
Agent Prompt
## Issue description
`SetPaketCommand` probes for Paket by executing `dotnet paket --version` with `IgnoreExitCode="true"` and no timeout, despite an in-file warning that the command can hang forever.

## Issue Context
This targets file runs during MSBuild evaluation/restore and impacts developer builds and CI reliability. Compliance requires validating external command outcomes and failing safely with deterministic behavior.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]

## Suggested remediation
- Avoid executing `dotnet paket --version` as a probe where possible (prefer deterministic checks such as `dotnet tool list --local` when `.config/dotnet-tools.json` exists).
- If an Exec probe remains necessary, add an explicit timeout (MSBuild `Exec` supports `Timeout` in newer MSBuilds; otherwise wrap the command in a platform-appropriate timeout helper) and surface a clear error when it times out.
- Don’t silently ignore failures: either remove `IgnoreExitCode="true"` or gate subsequent decisions on validated output/exit code and emit an actionable message when Paket cannot be located.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +261 to +267
<PropertyGroup>
<PaketCliToolFilePath>$(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools</PaketCliToolFilePath>
</PropertyGroup>

<ReadLinesFromFile File="$(PaketCliToolFilePath)" >
<Output TaskParameter="Lines" ItemName="PaketCliToolFileLines"/>
</ReadLinesFromFile>
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.

Action required

2. Missing exists() before readlines 📘 Rule violation ☼ Reliability

ReadLinesFromFile unconditionally reads $(PaketCliToolFilePath) (i.e.,
$(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools) without checking that the
file exists, which can hard-fail builds when Paket doesn’t generate it or restore was skipped. This
violates the requirement to validate required artifacts/preconditions and fail safely with clear
diagnostics.
Agent Prompt
## Issue description
The targets file reads `$(PaketCliToolFilePath)` (the generated `.paket.clitools` file under `$(PaketIntermediateOutputPath)`) unconditionally via `ReadLinesFromFile`, which will fail with a file-not-found error when the file is not generated for a project or restore was skipped.

## Issue Context
Compliance requires validating preconditions/artifacts in automation and failing safely with clear diagnostics. In the same area of the targets file, the `.paket.resolved` read above is conditionally executed, but the `.paket.clitools` read is not, making builds sensitive to whether that file exists.

## Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[261-278]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +162 to +171
<PropertyGroup Condition="'$(PaketPropsVersion)' != '6.0.0' ">
<PaketRestoreRequired>true</PaketRestoreRequired>
</PropertyGroup>

<!-- Do a global restore if required -->
<Warning Text="This version of MSBuild (we assume '$(DetectedMSBuildVersion)' or older) doesn't support GetFileHash, so paket fast restore is disabled." Condition=" '$(MSBuildSupportsHashing)' != 'true' " />
<Error Text="Stop build because of PAKET_ERROR_ON_MSBUILD_EXEC and we always call the bootstrapper" Condition=" '$(PAKET_ERROR_ON_MSBUILD_EXEC)' == 'true' AND '$(PaketBootstrapperStyle)' == 'classic' AND Exists('$(PaketBootStrapperExePath)') AND !(Exists('$(PaketExePath)'))" />
<Exec Command='$(PaketBootStrapperCommand)' Condition=" '$(PaketBootstrapperStyle)' == 'classic' AND Exists('$(PaketBootStrapperExePath)') AND !(Exists('$(PaketExePath)'))" ContinueOnError="false" />
<Error Text="Stop build because of PAKET_ERROR_ON_MSBUILD_EXEC and we need a full restore (hashes don't match)" Condition=" '$(PAKET_ERROR_ON_MSBUILD_EXEC)' == 'true' AND '$(PaketRestoreRequired)' == 'true' AND '$(PaketDisableGlobalRestore)' != 'true'" />
<Exec Command='$(PaketCommand) restore' Condition=" '$(PaketRestoreRequired)' == 'true' AND '$(PaketDisableGlobalRestore)' != 'true' " ContinueOnError="false" />
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.

Action required

3. Restore runs every build 🐞 Bug ➹ Performance

PaketRestoreRequired is forced to true whenever $(PaketPropsVersion) != '6.0.0'; since
PaketPropsVersion is not set by the imported projects, this evaluates true and triggers `paket
restore` on every build, defeating the lock/cache fast-path and making builds slower and more
network/flakiness-prone.
Agent Prompt
### Issue description
`PaketRestoreRequired` is unconditionally flipped back to `true` via the `PaketPropsVersion` check, which (when `PaketPropsVersion` is unset) forces a restore every build.

### Issue Context
Projects in this repo import `Paket.Restore.targets` directly and do not import any generated Paket props that would define `PaketPropsVersion`, so `$(PaketPropsVersion)` is typically empty.

### Fix
Update the condition so it only forces restore when `PaketPropsVersion` is actually present and mismatched (or set a sane default in a single place if that’s the intended contract).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[158-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +174 to +213
<ItemGroup>
<MyTargetFrameworks Condition="'$(TargetFramework)' != '' " Include="$(TargetFramework)"></MyTargetFrameworks>
<!-- Don't include all frameworks when msbuild explicitly asks for a single one -->
<MyTargetFrameworks Condition="'$(TargetFrameworks)' != '' AND '$(TargetFramework)' == '' " Include="$(TargetFrameworks)"></MyTargetFrameworks>
<PaketResolvedFilePaths Include="@(MyTargetFrameworks -> '$(PaketIntermediateOutputPath)\$(MSBuildProjectFile).%(Identity).paket.resolved')"></PaketResolvedFilePaths>
</ItemGroup>

<PropertyGroup>
<PaketReferencesCachedFilePath>$(PaketIntermediateOutputPath)\$(MSBuildProjectFile).paket.references.cached</PaketReferencesCachedFilePath>
<!-- MyProject.fsproj.paket.references has the highest precedence -->
<PaketOriginalReferencesFilePath>$(MSBuildProjectFullPath).paket.references</PaketOriginalReferencesFilePath>
<!-- MyProject.paket.references -->
<PaketOriginalReferencesFilePath Condition=" !Exists('$(PaketOriginalReferencesFilePath)')">$(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references</PaketOriginalReferencesFilePath>
<!-- paket.references -->
<PaketOriginalReferencesFilePath Condition=" !Exists('$(PaketOriginalReferencesFilePath)')">$(MSBuildProjectDirectory)\paket.references</PaketOriginalReferencesFilePath>

<DoAllResolvedFilesExist>false</DoAllResolvedFilesExist>
<DoAllResolvedFilesExist Condition="Exists(%(PaketResolvedFilePaths.Identity))">true</DoAllResolvedFilesExist>
<PaketRestoreRequired>true</PaketRestoreRequired>
<PaketRestoreRequiredReason>references-file-or-cache-not-found</PaketRestoreRequiredReason>
</PropertyGroup>

<!-- Step 2 a Detect changes in references file -->
<PropertyGroup Condition="Exists('$(PaketOriginalReferencesFilePath)') AND Exists('$(PaketReferencesCachedFilePath)') ">
<PaketRestoreCachedHash>$([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)'))</PaketRestoreCachedHash>
<PaketRestoreReferencesFileHash>$([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)'))</PaketRestoreReferencesFileHash>
<PaketRestoreRequiredReason>references-file</PaketRestoreRequiredReason>
<PaketRestoreRequired Condition=" '$(PaketRestoreReferencesFileHash)' == '$(PaketRestoreCachedHash)' ">false</PaketRestoreRequired>
</PropertyGroup>

<PropertyGroup Condition="!Exists('$(PaketOriginalReferencesFilePath)') AND !Exists('$(PaketReferencesCachedFilePath)') ">
<!-- If both don't exist there is nothing to do. -->
<PaketRestoreRequired>false</PaketRestoreRequired>
</PropertyGroup>

<!-- Step 2 b detect relevant changes in project file (new targetframework) -->
<PropertyGroup Condition=" '$(DoAllResolvedFilesExist)' != 'true' ">
<PaketRestoreRequired>true</PaketRestoreRequired>
<PaketRestoreRequiredReason>target-framework '$(TargetFramework)' or '$(TargetFrameworks)' files @(PaketResolvedFilePaths)</PaketRestoreRequiredReason>
</PropertyGroup>
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.

Action required

4. Multi-target resolved check flawed 🐞 Bug ≡ Correctness

DoAllResolvedFilesExist is set to true if any *.paket.resolved file exists, not if all of them
exist; for multi-target builds this can incorrectly skip restore when one framework’s resolved file
is missing, leading to missing/incorrect PackageReference forwarding or subsequent file-read
failures.
Agent Prompt
### Issue description
`DoAllResolvedFilesExist` is computed using a scalar property assignment batched over an item list, which makes it effectively mean “any exists” rather than “all exist”. This breaks restore decisioning for multi-target projects.

### Issue Context
`PaketResolvedFilePaths` is built from `@(MyTargetFrameworks)`, so it commonly contains multiple items. The restore decision (and later error checking) relies on `DoAllResolvedFilesExist`.

### Fix
Replace the property batching with an item-based missing-file check, e.g.:
- Create `MissingResolvedFiles` item group with `Condition="!Exists('%(Identity)')"`
- Set `DoAllResolvedFilesExist` based on whether `@(MissingResolvedFiles)` is empty
- Reuse the same logic in both the pre-restore and post-restore checks.

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[173-227]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code review by qodo was updated up to the latest commit ff29936

Comment on lines +68 to +83
<PropertyGroup Condition=" '$(PaketExePath)' == '' ">
<_DotnetToolsJson Condition="Exists('$(PaketRootPath)/.config/dotnet-tools.json')">$([System.IO.File]::ReadAllText("$(PaketRootPath)/.config/dotnet-tools.json"))</_DotnetToolsJson>
<_ConfigContainsPaket Condition=" '$(_DotnetToolsJson)' != ''">$(_DotnetToolsJson.Contains('"paket"'))</_ConfigContainsPaket>
<_ConfigContainsPaket Condition=" '$(_ConfigContainsPaket)' == ''">false</_ConfigContainsPaket>
</PropertyGroup>

<!-- Second test: Call 'dotnet paket' and see if it returns without an error. Mute all the output. Only run if previous test failed and the test has not been disabled. -->
<!-- WARNING: This method can lead to processes hanging forever, and should be used as little as possible. See https://github.com/fsprojects/Paket/issues/3705 for details. -->
<Exec Condition=" '$(PaketExePath)' == '' AND !$(PaketDisableCliTest) AND !$(_ConfigContainsPaket)" Command="dotnet paket --version" IgnoreExitCode="true" StandardOutputImportance="low" StandardErrorImportance="low" >
<Output TaskParameter="ExitCode" PropertyName="LocalPaketToolExitCode" />
</Exec>

<!-- If paket is installed as CLI use that. Again, only if paket haven't already been found in standard locations. -->
<PropertyGroup Condition=" '$(PaketExePath)' == '' AND ($(_ConfigContainsPaket) OR '$(LocalPaketToolExitCode)' == '0') ">
<_PaketCommand>dotnet paket</_PaketCommand>
</PropertyGroup>
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.

Action required

1. Dotnet tool restore missing 🐞 Bug ☼ Reliability

SetPaketCommand can select dotnet paket based on the local tool manifest, but PaketRestore
invokes $(PaketCommand) restore without ever running dotnet tool restore. On a clean checkout
(or any environment where the local tool manifest hasn’t been restored yet), MSBuild can fail before
compilation because the paket tool command isn’t available.
Agent Prompt
### Issue description
`Paket.Restore.targets` may choose `PaketCommand = "dotnet paket"` (local tool), but it never ensures the tool manifest has been restored (`dotnet tool restore`) before invoking `$(PaketCommand) restore`. This can fail builds on clean machines / first VS load.

### Issue Context
The repo uses a local tool manifest (`dotnet/.config/dotnet-tools.json`) and documentation/scripts already call `dotnet tool restore`. The MSBuild integration should do the same (ideally only when `dotnet paket` is selected) to avoid requiring an undocumented manual pre-step.

### Fix approach (one good option)
1. Add a new target, e.g. `PaketDotnetToolRestore`, that runs `dotnet tool restore` with `WorkingDirectory="$(PaketRootPath)"`.
2. Make `PaketRestore` depend on it (after `SetPaketCommand`).
3. Condition it to only run when the chosen `PaketCommand` is `dotnet paket` (or when `.config/dotnet-tools.json` exists and `_ConfigContainsPaket` is true).

### Fix Focus Areas
- dotnet/.paket/Paket.Restore.targets[66-83]
- dotnet/.paket/Paket.Restore.targets[117-172]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code review by qodo was updated up to the latest commit 1257a45

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code review by qodo was updated up to the latest commit 77556b6

Comment thread dotnet/.config/dotnet-tools.json Outdated
"rollForward": false
}
}
} No newline at end of file
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.

Action required

1. Missing newline in dotnet-tools.json 📘 Rule violation ⚙ Maintainability

dotnet/.config/dotnet-tools.json is missing a final newline, which violates the repository’s
EditorConfig formatting requirement and can cause formatting checks to fail. This conflicts with the
requirement to run/commit formatting so it leaves no remaining diff.
Agent Prompt
## Issue description
`dotnet/.config/dotnet-tools.json` is missing a final newline (`insert_final_newline = true`), which can cause formatting/CI checks to fail.

## Issue Context
Repo-wide formatting rules are defined in `.editorconfig`.

## Fix Focus Areas
- dotnet/.config/dotnet-tools.json[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 27, 2026

Code review by qodo was updated up to the latest commit 1572713

@titusfortner
Copy link
Copy Markdown
Member

The reason I wanted to do #17563 was to make it easier for Renovate to update versions for us. Renovate doesn't work with Paket for some reason, and moving everything over still has us manually updating versions or discovering issues ourselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-support Issue or PR related to support classes C-dotnet .NET Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants