Repository navigation
fix: SentryCreateRelease uses the assembly's informational version and no longer requires UseSentryCLI - #5679
Conversation
…seSentryCLI SentryGetApplicationVersion read AssemblyInformationalVersionAttribute via reflection, which throws when any assembly attribute's type can't be loaded by MSBuild (e.g. UserSecretsIdAttribute), silently falling back to AssemblyVersion. Read FileVersionInfo.ProductVersion instead, which doesn't load the assembly. _GetSentryRelease ran before CheckSentryCLI had defaulted UseSentryCLI and SentryCreateRelease, so neither SentryCreateRelease nor SentrySetCommits did anything unless UseSentryCLI was set explicitly. Run it after PrepareSentryCLI. Fixes #3536 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5679 +/- ##
==========================================
- Coverage 75.21% 75.12% -0.09%
==========================================
Files 515 515
Lines 18989 18989
Branches 3693 3693
==========================================
- Hits 14282 14265 -17
- Misses 3852 3868 +16
- Partials 855 856 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…l version On Windows, FileVersionInfo.ProductVersion falls back to the file version when there's no AssemblyInformationalVersionAttribute, whereas the runtime falls back to the assembly version. When ProductVersion equals the file version but not the assembly version, check whether the assembly references the attribute type before using it. Also skip _GetSentryRelease when the Sentry CLI isn't usable, since nothing would create the release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Windows The type-name scan also matches assemblies that only mention AssemblyInformationalVersionAttribute without applying it, e.g. to read their own version. Skip the check when the SDK generated the attribute, only apply it on Windows, default to the assembly version like the runtime, and warn when the type name suggests the attribute might be there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4dce6e8. Configure here.
|
@ric-oliv the version handling on Windows has become hard to follow across the review threads, so here's a write-up of the whole picture, with a suggestion at the end. The problemWith The three version attributes
What the build task reads, and whyBefore this PR, the task loaded the built dll into MSBuild and read the informational version attribute through reflection. Reflection has to resolve the type of every assembly-level attribute. If any of them comes from an assembly MSBuild can't load, it throws, and the task silently fell back to the assembly version. A metadata reader ( With this PR, the task reads none of the three attributes directly. It reads
So the product version isn't a fourth attribute. It's a value derived from the attributes and stored in a separate, Windows-format part of the dll. Why the product version differs by platformThe compiler writes the Windows version resource whatever OS it runs on, so every dll has one.
So on Linux and macOS the task always gets the same version as the runtime. On Windows it gets a different one in exactly one situation: the assembly has no informational version, and its file version differs from its assembly version. The product version then comes back as the file version, but the runtime uses the assembly version. Why that's ambiguous on WindowsThe task can see the product version, file version and assembly version, but not whether an informational version attribute exists. Suppose the product version equals the file version but differs from the assembly version. Two quite different projects produce exactly that:
The task has two clues for telling them apart:
Project setupsThese examples use assembly version
The designs so far
This is how the current and proposed designs decide. They only differ at the last step: flowchart TD
A[Read product version] --> B{"Windows, attribute not generated by the SDK,<br/>and product = file ≠ assembly version?"}
B -- no --> C["Use product version<br/>(not ambiguous)"]
B -- yes --> D{"AssemblyInformationalVersionAttribute<br/>name in the dll?"}
D -- no --> E["Use assembly version<br/>(attribute is absent)"]
D -- yes --> F["Warn, then guess<br/>(applied, or only referenced)"]
F --> G["Current: assembly version<br/>wrong if hand-written"]
F --> H["Proposed: product version<br/>wrong if only mentioned"]
Results on Windows"Right" means the build-time release matches the runtime one.
* Only if nothing in the dll triggers the #3536 failure. If something does (for example On Linux and macOS, every design in this PR gets every setup right with no warnings. Only the pre-PR code fails there, in the #3536 setup. FYI: we've switched to the proposed designThe second commit fixed "no informational version, file ≠ assembly" (your first comment). The current design fixed "…and the app's code reads the attribute" (your second comment), but at the cost of "hand-written informational = file version". Neither option gets both right, and both warn in both setups, so I went with the less bad one: 63f0ceb switches to the proposed design above. Justification: keeping the assembly version fixed while bumping the file and informational versions together is a common pattern in older projects, whereas code that reads an attribute it never sets just gets 🤖 Written with Claude Code |
…iguous on Windows When the assembly references AssemblyInformationalVersionAttribute but the product version equals the file version, the attribute is more likely hand-written with that value than merely mentioned in code. Use the product version and keep the warning, rather than falling back to the assembly version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Fixes two bugs in the MSBuild targets that create a Sentry release at build time (
SentryCreateRelease/SentrySetCommits). Both were diagnosed in #3536 (comment).The release was named after
AssemblyVersioninstead of the informational versionSentryGetApplicationVersionloaded the built assembly withAssembly.LoadFileand readAssemblyInformationalVersionAttributethrough reflection. To do that, reflection has to resolve the type of every assembly-level attribute. If any of them comes from an assembly MSBuild can't load, it throws, and the task quietly fell back toAssemblyVersion(App@1.0.0.0). Two common triggers:UserSecretsIdAttribute, generated for any project with a<UserSecretsId>ApplicationPartAttribute, which the Web SDK generates for referenced packages such as Microsoft.Identity.Web (the original repro inSentryCreateReleasenot usingAssemblyInformationalVersionfromNerdbank.GitVersioning#3536)The task now reads the name with
AssemblyName.GetAssemblyNameand the version fromFileVersionInfo.ProductVersion. Neither loads the assembly. Two alternatives didn't work:GetCustomAttributesData()throws the sameFileNotFoundException, andSystem.Reflection.Metadatacan't be referenced from an inline task on the .NET SDK.Releases weren't created unless
UseSentryCLIwas set explicitly_GetSentryReleaseran afterAfterBuild, butCheckSentryCLIonly sets the defaults forUseSentryCLIandSentryCreateReleaseafterBuild. As a result:SentryCreateRelease=trueon its own did nothingSentrySetCommits=true, which is meant to implySentryCreateRelease, did nothing even withUseSentryCLI=true_GetSentryReleasenow runs afterPrepareSentryCLI, and is skipped when the CLI isn't usable, since nothing would create the release. TheDispatchToInnerBuildshook is gone: NuGet doesn't importbuildTransitivetargets into the outer build of a multi-targeted project, so it never ran. And since the CLI has been resolved by that point, thereleases propose-versionfallback now uses the bundled CLI rather than whateversentry-cliis onPATH.Notes
cli.Tests.ps1pointSentryCLIat a stub that records its arguments. The dummy Sentry server from getsentry/github-workflows has no route forPOST /api/0/organizations/{org}/releases/.ProductVersioncomes from the Win32 version resource the compiler generates. Without an informational version, that resource falls back to the file version, while the runtime falls back toAssemblyVersion. So on Windows, whenProductVersionequals the file version but not the assembly version and the SDK didn't generate the attribute, the task checks the dll for theAssemblyInformationalVersionAttributetype name. If the name isn't there, there's no informational version, and the task uses the assembly version, like the runtime. If it is there, the attribute is most likely hand-written with the file version's value, so the task keepsProductVersion. It also warns and points atSENTRY_RELEASE, because the type might only be referenced from code. The trade-offs are written up in fix: SentryCreateRelease uses the assembly's informational version and no longer requires UseSentryCLI #5679 (comment).SentryCreateReleasenot usingAssemblyInformationalVersionfromNerdbank.GitVersioning#3536 also asked for a publicSentryReleaseproperty. That's tracked in Add a publicSentryReleaseMSBuild property for release creation #5678.Closes #3536
🤖 Generated with Claude Code