Update .csproj and package.json version
During my job I’ve to maintain several projects generally written in .NET Core or Angular project.
Each time that me or someone of the team I belong have to create a new software version, looses a lot of time to manually update the various projects versions.
So this is a little powershell script that allows you to update .csproj and package.json version.
If you just want to jump to the solution look here, otherwise there are a few thing I’d like to tell you!
How version is composed?
Typically a version number is composed by 3 or 4 digits separated by a dot (.), like this: 1.20.2.3589
.
looking to this example:
1
= major version
Typically you have to change this if your project has very significative changes that prevent all backward compatibility.
A new project that improve an existing one built with different technologies/languages (for example the “version 1” was written in VB6, the “version 2” is written in .NET Core)20
= minor version
This version indicates that there is some new changes in your software or you have developed a new functionality.2
= patch version
With the same version, some problems have been corrected-
3589
= build version
This number could be not exists. If exist typically is a reference to the build process that has make the package. Generally is useful if you have some automatic build procedure (such as Azure DevOps build pipeline or Bitbicket build pipeline), which provide you an identifier to the agent that ha generate you package.
If you have it, my suggestion is to use it: in this way you can identify wich process has generated you package and easily detect if there is some problem with a specific build. Or, simply, you can differentiate two packages with the same version.
What is .csproj and package.json file?
csproj
“.csproj” is a Visual Studio .NET C# Project file extension. This file will have information about the files included in that project, assemblies used in that project, project GUID and project version etc. This file is related to your project. It will be automatically generated when we create a new project
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e8e9a55e-6e43-4c4d-93f6-5a89157b12f1/what-is-csproj-file?forum=csharpgeneral
A csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <Authors>Davide Zoccarato</Authors> <Version>0.0.1.7</Version> <VersionPrefix>0.0.1.7</VersionPrefix> <AssemblyVersion>0.0.1.7</AssemblyVersion> <FileVersion>0.0.1.7</FileVersion> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <DocumentationFile>bin\Debug\net461\Industry40.Monitoring.PLC.xml</DocumentationFile> <NoWarn>1701;1702;1591</NoWarn> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <DocumentationFile>bin\Release\net461\Industry40.Monitoring.PLC.xml</DocumentationFile> <NoWarn>1701;1702;1591</NoWarn> </PropertyGroup> <ItemGroup> <PackageReference Include="Dapper" Version="1.60.6" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.6" /> <PackageReference Include="S7netplus" Version="0.3.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Industry40.Monitoring.Common\Industry40.Monitoring.Common.csproj" /> </ItemGroup> <ItemGroup> <None Update="Dependencies\x64\SQLite.Interop.dll"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> <None Update="Dependencies\x86\SQLite.Interop.dll"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> <ItemGroup> <Folder Include="Migrations\" /> </ItemGroup> </Project>
There are several tag in here, but the tags below is the only interested in this article.
- Version
TheVersion
property is the value most commonly set when building .NET Core applications. It controls the default values of all the version numbers embedded in the build output, such asPackageVersion
andAssemblyVersion
so it’s often used as the single source of the app/library version. - VersionPrefix
You can useVersionPrefix
to set the “base” version number for your library/app. It indirectly controls all of the other version numbers generated by your app (though you can override it for other specific versions). - AssemblyVersion
Every assembly you produce as part of your build process has a version number embedded in it, which forms an important part of the assembly’s identity. It’s stored in the assembly manifest and is used by the run time to ensure correct versions are loaded etc. - FileVersion
The file version is literally the version number exposed by the DLL to the file system. It’s the number displayed in Windows explorer, which often matches theAssemblyVersion
, but it doesn’t have to. TheFileVersion
number isn’t part of the assembly identity as far as the .NET Framework or run time are concerned.
https://andrewlock.net/version-vs-versionsuffix-vs-packageversion-what-do-they-all-mean/
packages.json
“package.json” is similar to csproj file, but is used for npm packages, angular project, etc.. is contains the dependencies to other npm packages, info about project name, version etc..
http://npm.github.io/using-pkgs-docs/package-json/using-a-packagejson.html
A package.json file looks like this:
{ "name": "gestionale.axel", "version": "2.0.1", "license": "GNU AGPLv3", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build --prod", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", "electron": "ng build --base-href ./ && electron ." }, "private": true, "dependencies": { "@agm/core": "^1.0.0-beta.5", "@angular/animations": "^7.0.2", "@angular/cdk": "^7.0.2", "@angular/common": "^7.0.2", "@angular/compiler": "^7.0.2", "@angular/core": "^7.0.2", "@angular/flex-layout": "^7.0.0-beta.19", "@angular/forms": "^7.0.2", "@angular/http": "^7.0.2", "@angular/material": "^7.0.2", "@angular/material-moment-adapter": "^7.0.2", "@angular/platform-browser": "^7.0.2", "@angular/platform-browser-dynamic": "^7.0.2", "@angular/router": "^7.0.2", "@types/jspdf": "^1.1.31", "@types/lodash": "^4.14.117", "core-js": "^2.5.7", "crypto-js": "^3.1.9-1", "file-saver": "^2.0.0-rc.4", "hammerjs": "^2.0.8", "jspdf": "^1.4.1", "jspdf-autotable": "^2.3.5", "moment": "^2.22.2", "node-sass": "^4.9.4", "rxjs": "^6.3.3", "rxjs-compat": "^6.3.3", "stream": "0.0.2", "timers": "^0.1.1", "xlsx": "^0.14.0", "zone.js": "^0.8.26" }, "devDependencies": { "@angular-devkit/build-angular": "~0.10.4", "@angular/cli": "7.0.4", "@angular/compiler-cli": "^7.0.2", "@angular/language-service": "^7.0.2", "@types/jasmine": "~2.8.9", "@types/jasminewd2": "~2.0.5", "@types/node": "~10.12.2", "codelyzer": "^4.5.0", "electron": "^4.1.4", "jasmine-core": "~3.3.0", "jasmine-spec-reporter": "~4.2.1", "karma": "~3.1.1", "karma-chrome-launcher": "~2.2.0", "karma-cli": "~1.0.1", "karma-coverage-istanbul-reporter": "^2.0.4", "karma-jasmine": "~1.1.2", "karma-jasmine-html-reporter": "^1.4.0", "protractor": "~5.4.1", "ts-node": "~7.0.1", "tslint": "~5.11.0", "typescript": "3.1.6" } }
The script
The idea is to have a tool able to read this files, extract the version information contents into them, change them it and re-write the file with the new version.
You can copy and paste this code into your “updateVersion.ps1
” and use it!
For example:updateVersion.ps1
: +1 on patch versionupdateVersion.ps1-MINOR
: + 1 on minor versionupdateVersion.ps1 -BUILD {BUILD_NUMBER}
: set the build number
For csproj file
param ( [Parameter(Mandatory=$false)][switch]$HELP, [Parameter(Mandatory=$false)][switch]$MAJOR, [Parameter(Mandatory=$false)][switch]$MINOR, [Parameter(Mandatory=$false)][switch]$PATCH, [Parameter(Mandatory=$false)][string]$BUILD, [Parameter(Mandatory=$false)][switch]$TAG_AND_PUSH, [Parameter(Mandatory=$false)][switch]$SHOW ) if($HELP) { echo "# version updater for csproj files"; echo "bump version +1 of all *.csproj file in subfolder"; echo "# params:"; echo " -HELP (not mandatory) => call this help"; echo " -SHOW (not mandatory) => shows versions. DOES NOT MODIFY THEM"; echo " -MAJOR (not mandatory) => +1 on major version. reset minor and patch to 0"; echo " -MINOR (not mandatory) => +1 on minor version. reset patch to 0"; echo " -PATCH (not mandatory) => +1 on patch version."; echo " none of above => +1 on patch version"; echo " -BUILD (not mandatory) => exact version. If not specified, default is 0"; echo " -TAG_AND_PUSH (not mandatory) => automatically tag with new version and push the *.csproj to repo"; echo "# example:"; echo ".\updateVersion.ps1"; echo ".\updateVersion.ps1 -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -MINOR -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -MAJOR -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -BUILD 1.2.3.4 -TAG_AND_PUSH"; return; } $projectInfos = ( Get-ChildItem -Recurse -Filter *.csproj ).FullName # tags $VersionPrefixTag = "VersionPrefix"; $VersionTag = "Version"; $AssemblyVersionTag = "AssemblyVersion"; $FileVersionTag = "FileVersion"; $newVersion = ""; foreach ($info in $projectInfos) { # version info [int]$_major = 0; [int]$_minor = 0; [int]$_patch = 0; [int]$_build = 0; $version = ""; # look for version foreach($line in Get-Content $info) { if($line -match "(?<=<$FileVersionTag>)(.*)(?=<\/$FileVersionTag>)"){ $version = $matches[1].Split("{.}"); $_major = [int]$version[0]; $_minor = [int]$version[1]; $_patch = [int]$version[2]; $_build = [int]$version[3]; if ($SHOW) { $file = Split-Path $info -leaf; Write-Host "${file}: " -NoNewline -ForegroundColor DarkGray Write-Host "${version}" -ForegroundColor Gray continue; } if($MAJOR) { $_major = $_major + 1; $_minor = 0; $_patch = 0; $_build = 0; } if($MINOR) { $_minor = $_minor + 1; $_patch = 0; $_build = 0; } if($PATCH) { $_patch = $_patch + 1; $_build = 0; } if(-not $MAJOR -and -not $MINOR -and -not $PATCH -and -not [string]::IsNullOrWhiteSpace($BUILD)) { $_build = $BUILD; } else { $_build = 0; } $version = "$_major.$_minor.$_patch.$_build"; } } # replace version where needed if (-not $SHOW) { (Get-Content $info) -replace "(?<=<$VersionPrefixTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$VersionPrefixTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$VersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$VersionTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$AssemblyVersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$AssemblyVersionTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$FileVersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$FileVersionTag>)", "$version" | Set-Content $info #display version inforation to output echo "BUMP VERSION TO: $version ($info)"; $newVersion = $version; } } if($TAG_AND_PUSH -and -not $SHOW) { git pull git add *.csproj git commit -m "bump version to $newVersion" git push git pull git tag -a "$newVersion" -m "$newVersion" git push origin --tags }
For package.json file
param ( [Parameter(Mandatory=$false)][switch]$HELP, [Parameter(Mandatory=$false)][switch]$MAJOR, [Parameter(Mandatory=$false)][switch]$MINOR, [Parameter(Mandatory=$false)][switch]$PATCH, [Parameter(Mandatory=$false)][string]$BUILD, [Parameter(Mandatory=$false)][switch]$TAG_AND_PUSH, [Parameter(Mandatory=$false)][switch]$SHOW ) if($HELP) { echo "# version updater for package.json files"; echo "bump version +1 in package.json"; echo "# params:"; echo " -HELP (not mandatory) =&amp;gt; call this help"; echo " -SHOW (not mandatory) =&amp;gt; shows versions. DOES NOT MODIFY THEM"; echo " -MAJOR (not mandatory) =&amp;gt; +1 on major version. reset minor and patch to 0"; echo " -MINOR (not mandatory) =&amp;gt; +1 on minor version. reset patch to 0"; echo " -PATCH (not mandatory) =&amp;gt; +1 on patch version."; echo " none of above =&amp;gt; +1 on patch version"; echo " -BUILD (not mandatory) =&amp;gt; exact version. If not specified, default is 0"; echo " -TAG_AND_PUSH (not mandatory) =&amp;gt; automatically tag with new version and push the *.csproj to repo"; echo "# example:"; echo ".\updateVersion.ps1"; echo ".\updateVersion.ps1 -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -MINOR -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -MAJOR -TAG_AND_PUSH"; echo ".\updateVersion.ps1 -BUILD 1.2.3.4 -TAG_AND_PUSH"; return; } $projectInfos = ( Get-ChildItem -Filter package.json ).FullName $newVersion = ""; foreach ($info in $projectInfos) { # version info [int]$_major = 0; [int]$_minor = 0; [int]$_patch = 0; [int]$_build = 0; $version = ""; # look for version foreach($line in Get-Content $info) { if($line -match """Version""\:\s*""(.*)"","){ $version = $matches[1].Split("{.}"); $_major = [int]$version[0]; $_minor = [int]$version[1]; $_patch = [int]$version[2]; $_build = [int]$version[3]; if ($SHOW) { $file = Split-Path $info -leaf; Write-Host "${file}: " -NoNewline -ForegroundColor DarkGray Write-Host "${version}" -ForegroundColor Gray continue; } if($MAJOR) { $_major = $_major + 1; $_minor = 0; $_patch = 0; $_build = 0; } if($MINOR) { $_minor = $_minor + 1; $_patch = 0; $_build = 0; } if($PATCH) { $_patch = $_patch + 1; $_build = 0; } if(-not $MAJOR -and -not $MINOR -and -not $PATCH -and -not [string]::IsNullOrWhiteSpace($BUILD)) { $_build = $BUILD; } else { $_build = 0; } $version = "$_major.$_minor.$_patch.$_build"; } } # replace version where needed (Get-Content $info) -replace "(?<=""Version""\:\s*"")([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?="",)", "$version" | Set-Content $info #display version inforation to output echo "BUMP VERSION TO: $version ($info)"; $newVersion = $version; } if($TAG_AND_PUSH) { git pull git add package.json git commit -m "bump version to $newVersion" git push git pull git tag -a "v$newVersion" -m "v$newVersion" git push origin --tags }
Looking at the script
$projectInfos = ( Get-ChildItem -Recurse -Filter *.csproj ).FullName
Get all csproj
file in folder and subfolder
foreach ($info in $projectInfos) { # version info [int]$_major = 0; [int]$_minor = 0; [int]$_patch = 0; [int]$_build = 0; $version = ""; # look for version foreach($line in Get-Content $info) { if($line -match "(?<=<$FileVersionTag>)(.*)(?=<\/$FileVersionTag>)"){
For each csproj
file look for <FileVersionTag>
tag, via regular expression and read the version contains into it
$version = $matches[1].Split("{.}"); $_major = [int]$version[0]; $_minor = [int]$version[1]; $_patch = [int]$version[2]; $_build = [int]$version[3];
Split the version into major, minor, patch and build number, then manipulate according the given option
if (-not $SHOW) { (Get-Content $info) -replace "(?<=<$VersionPrefixTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$VersionPrefixTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$VersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$VersionTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$AssemblyVersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$AssemblyVersionTag>)", "$version" | Set-Content $info (Get-Content $info) -replace "(?<=<$FileVersionTag>)([\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)([\.]{1}[\d]+)*(?=<\/$FileVersionTag>)", "$version" | Set-Content $info #display version inforation to output echo "BUMP VERSION TO: $version ($info)"; }
Replace the new version via regular expression and write to original csproj file
if($TAG_AND_PUSH -and -not $SHOW) { git pull git add *.csproj git commit -m "bump version to $newVersion" git push git pull git tag -a "$newVersion" -m "$newVersion" git push origin --tags }
Last, but not the least, this allows you to push the version update to your repo and add a “version tag” in format “major.minor.patch.build”