Adventures with MVC 6 - Episode IV : Synchronizing Package Versions in a Solution
One of the more repetitive tasks I've run into is synchronizing the package versions of NuGet packages I'm building in a client-server solution. I like to keep my Acme.Disgronifier.Client and Acme.Disgronifier.Common packages versions in sync not just because the client package references the common package, but also because it just makes it easier to track when it's out in production. Whenever I rev one package version number, I have to go to the other package version number to rev it as well, and I frequently run into visual studio hanging while it tries to communicate with our NuGet server to verify what versions are available. I also forget to update the package references in the test and api projects, and this usually results in a warning that looks something like:
Dependency specified was Acme.Disgronifier.Common >= 1.0.0-* but ended up with Acme.Disgronifier.Common 1.0.1
I eventually broke down and wrote a script to sync all these versions for me.
Install PowerShell tools for Visual Studio 2015
- In VS2015, click Tools->Extensions and Updates…
- Under Online, search for PowerShell Tools for Visual Studio 2015 by Adam Driscoll
- Install it.
Add a PowerShell script to the solution
- Right click that part of the solution where you want to add a PowerShell script project, click Add->New Project... I chose to put the new project under the '1.0' solution folder that I set up in my previous post.
- Select PowerShell Script Project, verify the location and name of the PowerShell script project you want to add, and click OK.
- Rename Script.ps1 to SyncClientVersions.ps1
- Add the following to the script
#
# Define file locations
#
$Acme_Disgronifier_Api_Project_FilePath = "$PSScriptRoot\..\src\Acme.Disgronifier.Api\project.json"
$Acme_Disgronifier_Common_Project_FilePath = "$PSScriptRoot\..\src\Acme.Disgronifier.Common\project.json"
$Acme_Disgronifier_Client_Project_FilePath = "$PSScriptRoot\..\src\Acme.Disgronifier.Client\project.json"
$Acme_Disgronifier_Client_Tests_Project_FilePath = "$PSScriptRoot\..\test\Acme.Disgronifier.Client.Tests\project.json"
#
# Load project files
#
Write-Output "Loading project files..."
$Acme_Disgronifier_Api_Project = Get-Content -Path $Acme_Disgronifier_Api_Project_FilePath -Raw -ErrorAction Ignore
$Acme_Disgronifier_Common_Project = Get-Content -Path $Acme_Disgronifier_Common_Project_FilePath -Raw -ErrorAction Ignore
$Acme_Disgronifier_Client_Project = Get-Content -Path $Acme_Disgronifier_Client_Project_FilePath -Raw -ErrorAction Ignore
$Acme_Disgronifier_Client_Tests_Project = Get-Content -Path $Acme_Disgronifier_Client_Tests_Project_FilePath -Raw -ErrorAction Ignore
#
# Get highest client/common version number
#
Write-Output "Determining highest version number..."
$Acme_Disgronifier_Common_Project_Version = [version]($Acme_Disgronifier_Common_Project | ConvertFrom-Json -ErrorAction Ignore).version.Replace("-*", "")
$Acme_Disgronifier_Client_Project_Version = [version]($Acme_Disgronifier_Client_Project | ConvertFrom-Json -ErrorAction Ignore).version.Replace("-*", "")
if ($Acme_Disgronifier_Common_Project_Version.CompareTo($Acme_Disgronifier_Client_Project_Version) -gt 0)
{
$highestVersion = $Acme_Disgronifier_Common_Project_Version
}
else
{
$highestVersion = $Acme_Disgronifier_Client_Project_Version
}
$highestVersionString = $highestVersion.ToString()
#
# Assign the highest version number everywhere it needs to be
#
Write-Output "Replacing version numbers..."
$dependenciesRegexFindPattern = "(""Acme.Disgronifier.(Common|Client)""\s*:\s*"").*"""
$versionRegexFindPattern = "(""version""\s*:\s*"").*"""
$regexReplacePattern = '${1}' + $highestVersionString + '"'
$Acme_Disgronifier_Api_Project = $Acme_Disgronifier_Api_Project -replace $dependenciesRegexFindPattern, $regexReplacePattern
$Acme_Disgronifier_Common_Project = $Acme_Disgronifier_Common_Project -replace $versionRegexFindPattern, $regexReplacePattern
$Acme_Disgronifier_Client_Project = $Acme_Disgronifier_Client_Project -replace $versionRegexFindPattern, $regexReplacePattern
$Acme_Disgronifier_Client_Project = $Acme_Disgronifier_Client_Project -replace $dependenciesRegexFindPattern, $regexReplacePattern
$Acme_Disgronifier_Client_Tests_Project = $Acme_Disgronifier_Client_Tests_Project -replace $dependenciesRegexFindPattern, $regexReplacePattern
#
# Save back to disk
#
Write-Output "Saving changes..."
# Use WriteAllText because other methods like Set-Content append an unnecessary line break at the end of the
# file
[io.file]::WriteAllText($Acme_Disgronifier_Api_Project_FilePath, $Acme_Disgronifier_Api_Project)
[io.file]::WriteAllText($Acme_Disgronifier_Common_Project_FilePath, $Acme_Disgronifier_Common_Project)
[io.file]::WriteAllText($Acme_Disgronifier_Client_Project_FilePath, $Acme_Disgronifier_Client_Project)
[io.file]::WriteAllText($Acme_Disgronifier_Client_Tests_Project_FilePath, $Acme_Disgronifier_Client_Tests_Project)
Write-Output "Running dnu restore"
dnu restore "$PSScriptRoot\.."
Write-Output "Sync completed"
Now when I increment the version number in either the Acme.Disgronifier.Client or Acme.Disgronifier.Common projects, I only have to right click the SyncClientVersions.ps1 file and click Execute as Script to get all project.json references properly synchronized as well as keep the Acme.Disgronifier.Client and Acme.Disgronifier.Common package version numbers in sync.