-
Notifications
You must be signed in to change notification settings - Fork 11
Targets and PowerShell script to auto update project versions #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
78e80dc
2870f92
3add54d
dfefc99
5abc5e8
c986c07
2b548ec
dc97716
3dc2aaa
7ea4be4
562b082
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Licensed to the .NET Foundation under one or more agreements. | ||
| # The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| #### Functions #### | ||
|
|
||
| Function Write-Color { | ||
| Param ( | ||
| [ValidateNotNullOrWhiteSpace()] | ||
| [string] $newColor | ||
| ) | ||
|
|
||
| $oldColor = $host.UI.RawUI.ForegroundColor | ||
| $host.UI.RawUI.ForegroundColor = $newColor | ||
|
|
||
| If ($args) { | ||
| Write-Output $args | ||
| } | ||
| Else { | ||
| $input | Write-Output | ||
| } | ||
|
|
||
| $host.UI.RawUI.ForegroundColor = $oldColor | ||
| } | ||
|
|
||
| Function VerifyPathOrExit { | ||
| Param ( | ||
| [Parameter(Mandatory = $true)] | ||
| [ValidateNotNullOrEmpty()] | ||
| [string] | ||
| $path | ||
| ) | ||
|
|
||
| If (-Not (Test-Path -Path $path)) { | ||
| Write-Error "The path does not exist: $path" | ||
| Exit | ||
| } | ||
| ElseIf (-Not ($path -match "^[a-zA-Z0-9\.\-_:/ ]+$")) { | ||
| Write-Error "The path will not work with Git. Avoid backslashes: $path" | ||
| Exit | ||
| } | ||
|
|
||
| Write-Color green "The path is valid: $path" | ||
| } | ||
|
|
||
| Function Execute-Command { | ||
| Param ( | ||
| [Parameter(Mandatory = $true)] | ||
| [ValidateNotNullOrWhiteSpace()] | ||
| [string] | ||
| $command | ||
| ) | ||
|
|
||
| Write-Color cyan "Executing: $command" | ||
| Invoke-Expression $command | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,160 @@ | ||||||
| # Licensed to the .NET Foundation under one or more agreements. | ||||||
| # The .NET Foundation licenses this file to you under the MIT license. | ||||||
|
|
||||||
| ########### | ||||||
| # Imports # | ||||||
| ########### | ||||||
|
|
||||||
| . $PSScriptRoot/Shared.ps1 | ||||||
|
|
||||||
| ############# | ||||||
| # Functions # | ||||||
| ############# | ||||||
|
|
||||||
| Function Get-NewVersion | ||||||
| { | ||||||
| Param( | ||||||
| [Parameter(Mandatory = $true)] | ||||||
| [ValidateNotNullOrWhiteSpace()] | ||||||
| [string]$ElementValue, | ||||||
| [Parameter(Mandatory = $true)] | ||||||
| [ValidateNotNullOrWhiteSpace()] | ||||||
| [int]$NegativePosition | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why negative position and not just index?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sometimes I'm in need of some spice in my code. I guess I can change it to index, yeah. I can still do the out of bounds check. |
||||||
| ) | ||||||
|
|
||||||
| If ($NegativePosition -ge 0) | ||||||
| { | ||||||
| Write-Error "The position number '$NegativePosition' should have been negative." -ErrorAction Stop | ||||||
| } | ||||||
|
|
||||||
| $components = $ElementValue -Split '\.' | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason not to use Version class?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not familiar. Are you talking about this? https://learn.microsoft.com/en-us/dotnet/api/system.version?view=net-9.0
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that's the one. It's meant to deal with the versions as numbers, it handles 2-4 parts with no suffix. I think that'd work here. Might save even having to pass any sort of extra info if we're always modifying the same offset. |
||||||
|
|
||||||
| If (($NegativePosition * -1) -gt $components.Length) | ||||||
| { | ||||||
| Write-Error "Version number '$ElementValue' is out of range." -ErrorAction Stop | ||||||
| } | ||||||
|
|
||||||
| $components[$NegativePosition] = [int]$components[$NegativePosition] + 1 | ||||||
|
|
||||||
| Return [string]::Join('.', $components) | ||||||
| } | ||||||
|
|
||||||
| Function Get-AssemblyNameLowerCase | ||||||
| { | ||||||
| Param( | ||||||
| [Parameter(Mandatory = $true)] | ||||||
| [ValidateNotNullOrWhiteSpace()] | ||||||
| [string]$MSBuildVersionFilePath | ||||||
| ) | ||||||
|
|
||||||
| $assemblyName = [System.IO.Path]::GetFileNameWithoutExtension($MSBuildVersionFilePath) | ||||||
|
|
||||||
| If ($assemblyName -eq "Versioning") | ||||||
| { | ||||||
| $srcFolderPath = Join-Path -Path ([System.IO.Path]::GetDirectoryName($MSBuildVersionFilePath)) -ChildPath "src" | ||||||
| $actualCsproj = Get-ChildItem -Path $srcFolderPath -Filter "*.*proj" -Recurse | Select-Object -First 1 | ||||||
| $assemblyName = [System.IO.Path]::GetFileNameWithoutExtension($actualCsproj.FullName) | ||||||
| } | ||||||
|
|
||||||
| Return $assemblyName.ToLowerInvariant() | ||||||
| } | ||||||
|
|
||||||
| Function Save-Project | ||||||
| { | ||||||
| Param( | ||||||
| [Parameter(Mandatory = $true)] | ||||||
| [ValidateNotNullOrEmpty()] | ||||||
| [System.Xml.Linq.XDocument] | ||||||
| $XDoc, | ||||||
| [Parameter(Mandatory = $true)] | ||||||
| [ValidateNotNullOrWhiteSpace()] | ||||||
| [string]$File | ||||||
| ) | ||||||
|
|
||||||
| # Ideally, we would've simply used: | ||||||
| # $XDoc.Save($file, [System.Xml.Linq.SaveOptions]::DisableFormatting) | ||||||
| # Unforutnately, it does not work as expected in PowerShell. Unlike in C#, the xml declaration | ||||||
| # is still getting added at the top even though we specified to disable formatting. | ||||||
| # Workaround: Use an XmlWriter to serialize into a string the XML without the xml declaration. | ||||||
| # Also, we need to remove the trailing new line that is added by the XmlWriter. | ||||||
|
|
||||||
| $stringWriter = New-Object System.IO.StringWriter | ||||||
| $xmlWriterSettings = New-Object System.Xml.XmlWriterSettings | ||||||
| $xmlWriterSettings.Indent = $false | ||||||
|
||||||
| $xmlWriterSettings.Indent = $false |
ericstj marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe [System.Xml.NewLineHandling]::None will fix this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I just realized that it's VS Code that adds the newline whenever I open the file, not the xml API. So I don't need the NewLineHandling nor these if/else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm they're still getting added even if I open the file in notepad, and even if I add that NewLineHandline.None setting. Oh well, I'll have to keep the if/else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This worked for me:
[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq")
$xdoc = [System.Xml.Linq.XDocument]::Load("in.csproj", [System.Xml.Linq.LoadOptions]::PreserveWhitespace)\
$xmlWriterSettings = New-Object System.Xml.XmlWriterSettings
$xmlWriterSettings.OmitXmlDeclaration = $true
$xmlWriterSettings.NewLineHandling = [System.Xml.NewLineHandling]::None
$xmlWriter = [System.Xml.XmlWriter]::Create("out.csproj", $xmlWriterSettings)
$xdoc.WriteTo($xmlWriter)
$xmlWriter.Close()It was able to round trip the project file produced by .NET new without any changes.
I also found that the built-in XML support in PS worked well as shown in those other scripts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not hard-code special cases like this. It's something we need to remember to do. Just evaluate props/targets as well. I think your logic for IsPackable will do the right thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean. Okay sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you wanted to try it there is an MSBuild construction API https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.construction. Not sure it adds much value here since we're just modifying existing elements, but it might help with the save issues you were facing.
There's also native support for XML in powershell which might also be an option. I see some folks using it in dotnet repos including to modify projects: https://github.com/search?q=org%3Adotnet+%2F%5C%5Bxml%5C%5D%2F+path%3A*.ps1&type=code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is to handle a case where we run this script and didn't publish all the previously shipped packages? Or someone versioned them separately? Walk me though the scenario where we run this and don't want to reset packages. (consider adding as comment). Silently skipping work like this makes me nervous because it creates a way for us to miss things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take the current case of System.Runtime.CompilerServices.Unsafe, it's set to IsPackable=true at the moment.
We want this script to only update the packages after they're pushed to nuget.org.
Let's say we already pushed the packages, the script runs, and it will find that S.R.CS.Unsafe is enabled. The script will analyze this project.
So now we are going to check if the version of the freshly pushed package to nuget.org matches the version that we have in the VersionPrefix property that has the IsPackable condition. At that moment, the script would confirm this is one of those packages that need to be updated, and will bump all the version numbers and set IsPackable to false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I understand that. Let's suppose we haven't yet pushed the packages to NuGet.org - in that case why are we even running the script?
Or let's suppose someone manually incremented the version of the package - is that something we want folks to do?
I'm just trying to understand why we'd ever want to skip versioning a package that's enabled. If you really wish to keep this then introduce logging that says skipping versioning package ID because it's version is higher than that on nuget.org - and make sure the condition you check matches that (equals seems wrong).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For all of the below - do we want to fail if any are not found, or do any comparisons against latestNuGetVersion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you handle the properties that are meant not to change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am only looking for the <PropertyGroup> that contains the definition of <IsPackable>.
Any property that isn't meant to change is supposed to not be part of that same property group. That was something suggested by @ViktorHofer when we were initially working on all of this. Examples:
| <AssemblyVersion>4.0.2.0</AssemblyVersion> <!-- NO-INCREMENT: This version is frozen for netstandard2.0. --> |
| <AssemblyVersion>4.6.1.6</AssemblyVersion> <!-- NO-INCREMENT: This version is frozen for .NET Standard and .NETCoreApp because the assembly ships inbox. --> |
Those aren't getting modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very unusual requirement and not enforced. Folks "have to know". At least add comments about why that's the case here.
If you wanted to also safeguard and honor that comment you could do something like (assemblyVersionElement.NextNode() as XComment)?.Value.?StartsWith("NO-INCREMENT") to check for a sibling comment with the token word. You could do that coinsistently.
Uh oh!
There was an error while loading. Please reload this page.