Visitar URL original
Surface package-upgrade stdout so failures are diagnosable by timabell · Pull Request #790 · dotnet-outdated/dotnet-outdated · GitHub
Skip to content

Surface package-upgrade stdout so failures are diagnosable - #790

Merged
slang25 merged 7 commits into
dotnet-outdated:masterfrom
timabell:surface-upgrade-errors
Nov 8, 2026
Merged

slang25 merged 7 commits into
dotnet-outdated:masterfrom
timabell:surface-upgrade-errors

Conversation

@timabell

Copy link
Copy Markdown
Contributor

I've had some trouble with some projects failing to upgrade, and when they do the output is a cryptic "An error occurred while upgrading [project name]" with no further information.

The PR (which I've tested on a real project that fails to upgrade due to the existence of a .props file) surfaces the underlying error to assist with troubleshooting.

This probably would have assisted with #131 which I see has a similar lack of error output.

The code change was generated by claude, but I have verified it works and the patch seems reasonable to me, though I don't know the project's code particularly well.

timabell and others added 5 commits September 15, 2026 12:20
dotnet add package reports NuGet restore/edit failures on stdout,
while the upgrade loop printed only stderr. A failed upgrade therefore
showed a bare "An error occurred while upgrading <project>" with no
cause. For example, when a package's version is centralised in an
imported Directory.Build.props, dotnet add package cannot edit the
imported item and fails with a message that was previously swallowed.

Also write RunStatus.Output on failure. UpgradePackages is widened from
private to internal (the class is already internal and exposed to the
test assembly via InternalsVisibleTo) so the behaviour can be covered
by a unit test.

Refs: dotnet-outdated#131

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When DotNetRunner kills a process that has produced no output for the
idle timeout, it returned only the partial output, so a failed upgrade
looked like a restore that simply stopped (dotnet-outdated#547). Add a message to the
errors that names the command, the timeout and the --idle-timeout option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add RunStatus.FailureDetails, which joins the trimmed stdout and stderr
and leaves out empty parts, and use it for the upgrade failure message.
The log that leads up to an error now comes before the error itself, and
an empty stderr or the stream's trailing newline no longer adds blank
lines. Cover the layout, the no-output case and the success path in tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The update_package tool reported only stderr, so a failure that dotnet
add package explains on stdout came back as an empty "Failed to update
package:" message. Use RunStatus.FailureDetails, as the CLI does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

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.

🟡 Changes recommended

The timeout test can flake because dotnet --info may exit successfully before the idle-timeout check, so it needs a controlled process or abstraction to exercise the timeout deterministically.

1 open issue
What changed in this PR

This PR makes package-upgrade failures easier to diagnose by exposing standard output alongside standard error in the CLI and MCP server, addressing the missing diagnostics described in issue #131.

Changes:

  • Add shared failure-detail formatting and use it in package-upgrade error responses.
  • Explain when a process is stopped by the idle timeout.
  • Add tests for failure formatting, upgrade diagnostics, and timeout reporting.
File Description
test/​DotNetOutdated.Tests/​UpgradePackagesTests.cs Tests CLI failure details and quiet successful upgrades.
test/​DotNetOutdated.Tests/​RunStatusTests.cs Tests trimming and combining process output streams.
test/​DotNetOutdated.Tests/​McpServerTests.cs Tests stdout and stderr in failed update responses.
test/​DotNetOutdated.Tests/​DotNetRunnerTests.cs Adds a timeout-diagnostic regression test.
src/​DotNetOutdated/​Program.cs Displays combined upgrade failure details and enables direct testing.
src/​DotNetOutdated/​McpServer.cs Returns combined details for failed package updates.
src/​DotNetOutdated.Core/​Services/​RunStatus.cs Adds reusable FailureDetails formatting.
src/​DotNetOutdated.Core/​Services/​DotNetRunner.cs Adds an explanatory idle-timeout message.

Review effort: Balanced

slang25 and others added 2 commits November 8, 2026 12:08
Copilot review: with a zero timeout, 'dotnet --info' could exit before
the runner's first idle check, making the test flaky. Add an internal
DotNetRunnerOptions.FileName so the test can run a process that stays
silent for 60 seconds (sleep, or Start-Sleep on Windows) against a
1 second timeout. The timeout message now names the executable that ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@slang25 slang25 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed and tested; changes applied on top. Thanks!

@slang25
slang25 merged commit c3fbf21 into dotnet-outdated:master Nov 8, 2026
9 checks passed
slang25 added a commit to GREsau/dotnet-outdated that referenced this pull request Nov 8, 2026
Brings in dotnet-outdated#790 (surface package-upgrade stdout). Keeps both sides' MCP
tests, and points UpgradePackagesTests' AddPackage stub at the VersionRange
overload that UpgradePackages now calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@slang25 slang25 mentioned this pull request Nov 8, 2026
ptr727-codegen Bot pushed a commit to ptr727/LanguageTags that referenced this pull request Nov 10, 2026
Updated
[dotnet-outdated-tool](https://github.com/dotnet-outdated/dotnet-outdated)
from 4.8.1 to 4.9.0.

<details>
<summary>Release notes</summary>

_Sourced from [dotnet-outdated-tool's
releases](https://github.com/dotnet-outdated/dotnet-outdated/releases)._

## 4.9.0

## What's Changed
* Adopt MCP 2026-07-28 spec with legacy version negotiation by @​slang25
in dotnet-outdated/dotnet-outdated#772
* Fix concurrent cache corruption during recursive file-based app
upgrades by @​DamianEdwards in
dotnet-outdated/dotnet-outdated#788
* Fix: Parameter --maximum-version ignores exact version references by
@​chefarbeiter in
dotnet-outdated/dotnet-outdated#671
* Show a friendly message instead of crashing when a solution has no
projects by @​DanielC000 in
dotnet-outdated/dotnet-outdated#771
* Surface package-upgrade stdout so failures are diagnosable by
@​timabell in
dotnet-outdated/dotnet-outdated#790
* Fix multiline central package version updates by @​onuracar-dev in
dotnet-outdated/dotnet-outdated#783
* Support versioned MSBuild project SDKs in project files by @​thomhurst
in dotnet-outdated/dotnet-outdated#793
* Preserve whitespace in Directory.Packages.props during upgrades by
@​symbiogenesis in
dotnet-outdated/dotnet-outdated#803
* Treat package references injected by custom SDKs as auto-referenced by
@​thomhurst in
dotnet-outdated/dotnet-outdated#795
* Preserve version ranges when upgrading packages by @​GREsau in
dotnet-outdated/dotnet-outdated#702
* Preserve MSBuild version variables across project and props files by
@​symbiogenesis in
dotnet-outdated/dotnet-outdated#805
* Replace McMaster command-line parsing with System.CommandLine by
@​slang25 in dotnet-outdated/dotnet-outdated#808
* Bump version to 4.9.0 by @​slang25 in
dotnet-outdated/dotnet-outdated#806

## New Contributors
* @​chefarbeiter made their first contribution in
dotnet-outdated/dotnet-outdated#671
* @​DanielC000 made their first contribution in
dotnet-outdated/dotnet-outdated#771
* @​timabell made their first contribution in
dotnet-outdated/dotnet-outdated#790
* @​onuracar-dev made their first contribution in
dotnet-outdated/dotnet-outdated#783
* @​thomhurst made their first contribution in
dotnet-outdated/dotnet-outdated#793
* @​GREsau made their first contribution in
dotnet-outdated/dotnet-outdated#702

**Full Changelog**:
dotnet-outdated/dotnet-outdated@v4.8.1...v4.9.0


Commits viewable in [compare
view](dotnet-outdated/dotnet-outdated@v4.8.1...v4.9.0).
</details>

Updated [ptr727.Utilities](https://github.com/ptr727/Utilities) from
4.1.105 to 4.1.109.

<details>
<summary>Release notes</summary>

_Sourced from [ptr727.Utilities's
releases](https://github.com/ptr727/Utilities/releases)._

## 4.1.109

## What's Changed
* Bump the actions-deps group with 4 updates by @​dependabot[bot] in
ptr727/Utilities#574
* Bump the nuget-deps group with 2 updates by @​dependabot[bot] in
ptr727/Utilities#576
* Resync the Instruction Set With the Hub by @​ptr727 in
ptr727/Utilities#581
* Resync the Skills Tree and Markdownlint Config With the Hub by
@​ptr727 in ptr727/Utilities#578
* Bring the Publisher Concurrency and Aggregator Name to the Workflow
Contract by @​ptr727 in ptr727/Utilities#579
* Reconcile the Intent-Fidelity Carried Files With the Hub by @​ptr727
in ptr727/Utilities#580
* Promote the Hub Resync to Main by @​ptr727 in
ptr727/Utilities#582


**Full Changelog**:
ptr727/Utilities@4.1.108...4.1.109

## 4.1.108

## What's Changed
* Bump the actions-deps group with 4 updates by @​dependabot[bot] in
ptr727/Utilities#575
* Bump the nuget-deps group with 2 updates by @​dependabot[bot] in
ptr727/Utilities#577


**Full Changelog**:
ptr727/Utilities@4.1.105...4.1.108

Commits viewable in [compare
view](ptr727/Utilities@4.1.105...4.1.109).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ptr727 added a commit to ptr727/NxWitness that referenced this pull request Nov 11, 2026
Updated [csharpier](https://github.com/belav/csharpier) from 1.3.0 to
1.4.0.

<details>
<summary>Release notes</summary>

_Sourced from [csharpier's
releases](https://github.com/belav/csharpier/releases)._

## 1.4.0

## Breaking Changes
### Drop support for net8.0 and net9.0
[#​1888](belav/csharpier#1888)
CSharpier and CSharpier.MsBuild now target `net10.0` and `net11.0`.
CSharpier.Core targets `net10.0`, `net11.0` and `netstandard2.0`.

## What's Changed
### Feature: Support for .NET 11 and C# 15
[#​1888](belav/csharpier#1888)
CSharpier now supports the new syntax in C# 15.
```c#
public union Pet(Cat, Dog, Bird);

public closed record GateState;

HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];

outer:
for (int row = 0; row < grid.Height; row++)
{
    for (int column = 0; column < grid.Width; column++)
    {
        if (grid[row, column].IsBlocked)
        {
            continue outer;
        }

        if (grid[row, column].IsGoal)
        {
            break outer;
        }
    }
}
```

### Feature: Avoid chopping single trailing lambda
[#​1865](belav/csharpier#1865)
When the last argument is a lambda with a block body, and the arguments
up to and including the `=>` fit on one line, CSharpier now keeps them
on the same line as the method name.
```c#
// input & expected output
logging.AddFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "server{0}.log"), o =>
{
    o.MaxRollingFiles = 1;
});

// 1.3.0
logging.AddFile(
    Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "server{0}.log"),
    o =>
    {
        o.MaxRollingFiles = 1;
    }
);
```
 ... (truncated)

## 1.3.11-beta1



## 1.3.6

# 1.3.6
## What's Changed
#### Fix bug where 2nd instance of VSCode was not able to format code
[#​784](belav/csharpier#784)

Thanks to @​BenasB for reporting the issue

Commits viewable in [compare
view](belav/csharpier@1.3.0...1.4.0).
</details>

Updated
[dotnet-outdated-tool](https://github.com/dotnet-outdated/dotnet-outdated)
from 4.8.1 to 4.9.0.

<details>
<summary>Release notes</summary>

_Sourced from [dotnet-outdated-tool's
releases](https://github.com/dotnet-outdated/dotnet-outdated/releases)._

## 4.9.0

## What's Changed
* Adopt MCP 2026-07-28 spec with legacy version negotiation by @​slang25
in dotnet-outdated/dotnet-outdated#772
* Fix concurrent cache corruption during recursive file-based app
upgrades by @​DamianEdwards in
dotnet-outdated/dotnet-outdated#788
* Fix: Parameter --maximum-version ignores exact version references by
@​chefarbeiter in
dotnet-outdated/dotnet-outdated#671
* Show a friendly message instead of crashing when a solution has no
projects by @​DanielC000 in
dotnet-outdated/dotnet-outdated#771
* Surface package-upgrade stdout so failures are diagnosable by
@​timabell in
dotnet-outdated/dotnet-outdated#790
* Fix multiline central package version updates by @​onuracar-dev in
dotnet-outdated/dotnet-outdated#783
* Support versioned MSBuild project SDKs in project files by @​thomhurst
in dotnet-outdated/dotnet-outdated#793
* Preserve whitespace in Directory.Packages.props during upgrades by
@​symbiogenesis in
dotnet-outdated/dotnet-outdated#803
* Treat package references injected by custom SDKs as auto-referenced by
@​thomhurst in
dotnet-outdated/dotnet-outdated#795
* Preserve version ranges when upgrading packages by @​GREsau in
dotnet-outdated/dotnet-outdated#702
* Preserve MSBuild version variables across project and props files by
@​symbiogenesis in
dotnet-outdated/dotnet-outdated#805
* Replace McMaster command-line parsing with System.CommandLine by
@​slang25 in dotnet-outdated/dotnet-outdated#808
* Bump version to 4.9.0 by @​slang25 in
dotnet-outdated/dotnet-outdated#806

## New Contributors
* @​chefarbeiter made their first contribution in
dotnet-outdated/dotnet-outdated#671
* @​DanielC000 made their first contribution in
dotnet-outdated/dotnet-outdated#771
* @​timabell made their first contribution in
dotnet-outdated/dotnet-outdated#790
* @​onuracar-dev made their first contribution in
dotnet-outdated/dotnet-outdated#783
* @​thomhurst made their first contribution in
dotnet-outdated/dotnet-outdated#793
* @​GREsau made their first contribution in
dotnet-outdated/dotnet-outdated#702

**Full Changelog**:
dotnet-outdated/dotnet-outdated@v4.8.1...v4.9.0


Commits viewable in [compare
view](dotnet-outdated/dotnet-outdated@v4.8.1...v4.9.0).
</details>

Updated
[Microsoft.Extensions.Http.Resilience](https://github.com/dotnet/extensions)
from 10.11.0 to 11.0.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Http.Resilience's
releases](https://github.com/dotnet/extensions/releases)._

## 11.0.0

<!-- DRAFT: not published. Public baseline
f8ee27151b21e33793164b39de5bcebfdced81a4 (includes #​7923). Official
build source/tag target c60d72e7973a90cd4e4300d9153e97b93315e4b7 =
baseline + 9 internal release-prep commits (infrastructure, .NET
8/9/10/11 dependency updates, test pins, feeds, SDK 11.0.100 pin); no
additional PRs. Official build 3132207, BAR 340823, final published
manifest SHA256
A694CB054BBD836E73E2EF9EFA26FAD025EC6F709273D4CF217E08F5E1DE5CE6.
c60d72e is publicly reachable. -->
<!-- AVAILABILITY STATUS (2026-11-10): Public SDK 11.0.100 GA/download
and servicing 8.0.34/9.0.23/10.0.15 are confirmed available. Three
successful dotnet-public reads at 16:50:57Z, 17:05:36Z, and 17:20:30Z
found neither Microsoft.WindowsDesktop.App.Runtime.win-x64 8.0.34 nor
9.0.23 listed. The index samples do not establish restore, official CI,
or dependency closure. -->
<!-- VALIDATION STATUS (NOT PASSED): All 55 published package listings,
provenance/content and signatures were verified, as were 900
library/analyzer consumer graphs. The user's BAR readback confirms build
340823 in .NET 11 channel 8297. Published-source commit
c60d72e7973a90cd4e4300d9153e97b93315e4b7 is publicly reachable. All 272
selected library PDBs on msdl matched the original identities and bytes.
Website follow-up is user-deferred. -->

Version 11.0.0 ships alongside .NET 11 and with net11 targets added to
packages. Highlights of this release include new experimental Documents
and Document Extraction abstractions, metric instrument descriptions,
and a sliding-window retry budget for HTTP resilience.

## Experimental API Changes

### Now Stable
* HTTP server latency telemetry (`AddHttpServerLatencyTelemetry`) APIs
are now stable (previously `EXTEXP0013`) #​7898

### New Experimental APIs
* New experimental API: Documents abstractions in the new
`Microsoft.Extensions.Documents.Abstractions` package (`EXTEXP0019`)
#​7835
* New experimental API: Document Extraction in the new
`Microsoft.Extensions.DocumentExtraction` and
`Microsoft.Extensions.DocumentExtraction.Abstractions` packages
(`MEDE0001`) #​7588
* New experimental API: HTTP resilience retry budget
(`HttpRetryBudgetOptions`,
`HttpRetryResiliencePipelineBuilderExtensions`,
`OnRetryBudgetExhaustedArguments`) (`EXTEXP0001`) #​7867
* New experimental API: `Description` on metric attributes
(`EXTEXP0003`) #​7837

### Obsoleted APIs
* `HybridCacheOptions.DisableCompression` is now obsolete (`EXTOBS0003`)
#​7885

## What's Changed

### Telemetry and Logging

* Add Description to metric attributes and describe all instruments
#​7837 by @​evgenyfedorov2 (co-authored by @​Copilot)
* Capture trace context, thread ID and message template in buffered logs
#​7818 by @​evgenyfedorov2 (co-authored by @​Copilot)
* Allow bool and numeric types for strongly-typed metric tags #​7877 by
@​evgenyfedorov2 (co-authored by @​Copilot)
* Fix route parameter redaction when a parameter value contains a
delimiter used later in the same path segment #​7828 by @​evgenyfedorov2
(co-authored by @​Copilot)
* Fix lost log records when logging reentrantly on the same thread
#​7847 by @​evgenyfedorov2 (co-authored by @​Copilot)
* Fix logging generator stack overflow on type cycles #​7861 by
@​evgenyfedorov2 (co-authored by @​Copilot)
* Honor `[LogPropertyIgnore]` during transitive logging and
`[LogProperties(Transitive = true)]` on properties #​7868 by
@​evgenyfedorov2 (co-authored by @​Copilot)
* Make DataClassificationSet static to decrease allocations #​7874 by
@​evgenyfedorov2 (co-authored by @​Copilot)

### Caching

* Add HybridCache.GetOrCreateAsync overloads for dynamic setting of
options #​7568 by @​svick (co-authored by @​Copilot)
* Mark HybridCacheOptions.DisableCompression as obsolete #​7885 by
@​slang25
* Fix HybridCache stampede state removal race #​7814 by @​svick
(co-authored by @​Copilot)
* Fix HybridCache reservation over-release on DisableUnderlyingData miss
#​7879 by @​Eng3bbas

### AI

* AI: Restore neutral tool approval rejection message #​7913 by @​jozkee
(co-authored by @​Copilot)
* Fix ImageGenerationOptions.Clone dropping StreamingCount #​7829 by
@​Empiree
* Fix evaluation context serialization without reflection fallback
#​7869 by @​valkiara
* Pin MessagePack 2.5.307 in AI template AppHost project #​7853 by
@​evgenyfedorov2 (co-authored by @​Copilot)

### Packaging and Target Frameworks

* Condition framework-provided package references for .NET 11 #​7915 by
@​jeffhandley (co-authored by @​Copilot)
 ... (truncated)

Commits viewable in [compare
view](dotnet/extensions@v10.11.0...v11.0.0).
</details>

Updated [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest)
from 18.11.0 to 18.12.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.NET.Test.Sdk's
releases](https://github.com/microsoft/vstest/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/microsoft/vstest/commits).
</details>

Updated [System.CommandLine](https://github.com/dotnet/dotnet) from
2.0.14 to 3.0.0.

<details>
<summary>Release notes</summary>

_Sourced from [System.CommandLine's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated [xunit.analyzers](https://github.com/xunit/xunit.analyzers) from
2.2.0 to 2.3.0.

<details>
<summary>Release notes</summary>

_Sourced from [xunit.analyzers's
releases](https://github.com/xunit/xunit.analyzers/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/xunit/xunit.analyzers/commits).
</details>

Updated [xunit.v3](https://github.com/xunit/xunit) from 4.0.2 to 4.0.3.

<details>
<summary>Release notes</summary>

_Sourced from [xunit.v3's
releases](https://github.com/xunit/xunit/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/xunit/xunit/commits).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pieter Viljoen <ptr727@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@timabell
timabell deleted the surface-upgrade-errors branch November 12, 2026 16:25
timabell added a commit to timabell/dotnet-outdated that referenced this pull request Nov 12, 2026
…ops (Issue dotnet-outdated#301)

A Directory.Build.props can set package versions once for every .csproj
in the folders below it, for example to pin a vulnerable transitive
dependency. dotnet-outdated already upgrades a version set in one
through a `$(Property)`, but not a literal version on a
`PackageReference`, which is what dotnet-outdated#301 asks for: "if a
Directory.Build.props file existed and provided a package version".

For those, `dotnet outdated -u` runs `dotnet add package`, which refuses
with "Cannot edit items in imported files", and the upgrade fails. The
same happens when a csproj overrides an imported version with its own
`<PackageReference Update="..." Version="...">`. Since c3fbf21 (dotnet-outdated#790)
that error is shown rather than swallowed. It comes from NuGet's
`dotnet add package` command:
https://github.com/NuGet/NuGet.Client/blob/7e935f9c72b287adc13b9f42fd7b3bdcaa1500e7/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs#L495-L502
https://github.com/NuGet/NuGet.Client/blob/7e935f9c72b287adc13b9f42fd7b3bdcaa1500e7/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx#L334-L340

In theory `dotnet add` should be able to make these edits itself; these
NuGet issues cover the area:
NuGet/Home#14317
NuGet/Home#13218
NuGet/Home#12156

dotnet-outdated#301 notes that NuKeeper handled this. NuKeeper is now archived in
favour of Dependabot and dotnet-outdated, so dotnet-outdated needs to:
NuKeeperDotNet/NuKeeper#1155

Where `dotnet add` would refuse, write the new version directly into the
file that declares it, then restore:

- The MSBuild evaluator already works out which file sets each package's
  version. It now also reports when that is a literal `PackageReference`
  version that `dotnet add` can't edit: one in an imported file, or an
  `Update` item.
- `VersionLocator` turns that into a new location type,
  `PackageReferenceVersionInFile`.
- `DotNetPackageService` writes the version there with the code split
  out of `GlobalPackageReference` updates two commits back: the file's
  encoding is kept, and a props file shared by several projects restores
  each project once.

Deciding this up front, rather than running `dotnet add` and reacting to
its failure, avoids parsing dotnet's error text, which is localised and
changes between SDKs.

The end-to-end test from the previous commit, which showed the upgrade
failing, now expects it to succeed and the project to restore.

This commit decides *which file* gets the new version. How the version
text is found inside a file is the next commit's concern.

Fixes: dotnet-outdated#301

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
timabell added a commit to timabell/dotnet-outdated that referenced this pull request Nov 13, 2026
…ops (Issue dotnet-outdated#301)

A Directory.Build.props can set package versions once for every .csproj
in the folders below it, for example to pin a vulnerable transitive
dependency. dotnet-outdated already upgrades a version set in one
through a `$(Property)`, but not a literal version on a
`PackageReference`, which is what dotnet-outdated#301 asks for: "if a
Directory.Build.props file existed and provided a package version".

For those, `dotnet outdated -u` runs `dotnet add package`, which refuses
with "Cannot edit items in imported files", and the upgrade fails. The
same happens when a csproj overrides an imported version with its own
`<PackageReference Update="..." Version="...">`. Since c3fbf21 (dotnet-outdated#790)
that error is shown rather than swallowed. It comes from NuGet's
`dotnet add package` command:
https://github.com/NuGet/NuGet.Client/blob/7e935f9c72b287adc13b9f42fd7b3bdcaa1500e7/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs#L495-L502
https://github.com/NuGet/NuGet.Client/blob/7e935f9c72b287adc13b9f42fd7b3bdcaa1500e7/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx#L334-L340

In theory `dotnet add` should be able to make these edits itself; these
NuGet issues cover the area:
NuGet/Home#14317
NuGet/Home#13218
NuGet/Home#12156

dotnet-outdated#301 notes that NuKeeper handled this. NuKeeper is now archived in
favour of Dependabot and dotnet-outdated, so dotnet-outdated needs to:
NuKeeperDotNet/NuKeeper#1155

Where `dotnet add` would refuse, write the new version directly into the
file that declares it, then restore:

- The MSBuild evaluator already works out which file sets each package's
  version. It now also reports when that is a literal `PackageReference`
  version that `dotnet add` can't edit: one in an imported file, or an
  `Update` item.
- `VersionLocator` turns that into a new location type,
  `PackageReferenceVersionInFile`.
- `DotNetPackageService` writes the version there with the code split
  out of `GlobalPackageReference` updates two commits back: the file's
  encoding is kept, and a props file shared by several projects restores
  each project once.

Deciding this up front, rather than running `dotnet add` and reacting to
its failure, avoids parsing dotnet's error text, which is localised and
changes between SDKs.

The end-to-end test from the previous commit, which showed the upgrade
failing, now expects it to succeed and the project to restore.

This commit decides *which file* gets the new version. How the version
text is found inside a file is the next commit's concern.

Fixes: dotnet-outdated#301

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants