In the journey of. NET development, efficient version management and convenient release process are key links to improve development efficiency and project quality. Today, let's discuss in depth how to use scripts to automatically update program versions and publish them in one click for the. NET Avalonia UI project (of course, these methods can also be used in other. NET projects) to make your development process smoother and more efficient.
PowerShell script execution permissions check
Before running PowerShell scripts, it is crucial to understand and set execution policies correctly. Execution policies determine whether PowerShell allows scripts to be run and what types of scripts to run. You can use the following simple command to view the current execution strategy:
Get-ExecutionPolicy
Different execution policy values have different meanings and impose different degrees of restrictions on the execution of scripts:
- Restricted: This is the default policy. It is like a strict gatekeeper. No scripts are allowed to run. You can only type commands line by line in the PowerShell console. This ensures the security of the system to a certain extent, but it is undoubtedly a constraint for developers who need to execute scripts in batches.
- AllSigned: Only scripts signed by trusted publishers are allowed to run. This is like allowing only people with a specific pass to enter, ensuring the reliability of the source of the script, but also increasing the threshold for script use, because not all scripts are signed.
- RemoteSigned: Locally created scripts can be run directly, while scripts downloaded from the Internet must be signed by a trusted publisher to run. This is a setting that balances security and convenience, allowing flexibility for local development while also providing security for scripts downloaded online.
- Unrestricted: All scripts are allowed to run, but scripts downloaded from the Internet will be prompted for confirmation before running. This gives developers a lot of freedom, but it also carries certain security risks, because scripts that are not strictly checked can carry malicious code.
- Bypass: No scripts are blocked from running and there will be no security prompts. This is a very open setting and is generally only used in specific, safe and controllable environments, otherwise the system may be exposed to security threats.
By default, the execution policy is Restricted, which may cause writeback to the file to fail, prompting that writing to the stream is not allowed. For example, when you try to run a script that needs to modify the contents of a file, you encounter permissions issues.
Modify execution strategy
In order to run scripts smoothly, we need to choose the appropriate execution strategy based on our needs. If you want to easily run local scripts and network download scripts that have passed certain security checks while ensuring certain security, setting the execution policy to RemoteSigned is a good choice.
However, it should be noted that modifying the execution policy may require administrator privileges, so be sure to run PowerShell as an administrator and use the following command to modify it:
Set-ExecutionPolicy RemoteSigned
In this way, scripts created locally can be run directly, while scripts downloaded from the Internet need to be signed and verified, effectively reducing security risks.
Verify modification results
After modifying the execution policy, in order to ensure that the settings take effect, you can use the Get-ExecutionPolicy command again to verify that the modification was successful. When you see that the output is RemoteSigned, it means that you have successfully modified the execution policy, and you should be able to run the.ps1 script normally after that.
VS Release
In Visual Studio, we can automatically update project versions by adding scripts to pre-built events. The details are as follows: Find the "Build Events" tab in the project properties, and add the following command to the "Pre-Build Events Command Line":
powershell -ExecutionPolicy Bypass -File "UpdateAssemblyVersion.ps1" -AssemblyInfoFile "GlobalAssembly.cs" -Configuration "$(ConfigurationName)" -Platform "$(PlatformName)"
The parameters here have clear meanings:
- ConfigurationName: Represents the active solution configuration, common ones such as Debug (debug mode) and Release (release mode). Under different configurations, we may want to generate programs with different version numbers to distinguish between development and official release versions.
- PlatformName: Represents the active solution platform, such as AnyCPU (for any CPU architecture), x86 (32-bit architecture), x64 (64-bit architecture), etc. Different platforms may require different version identities to meet compatibility and performance requirements.
Batch document release
Sometimes, we may want to publish programs without opening Visual Studio and be able to easily expand to multiple platforms such as Windows, Linux, and macOS. At this time, batch files come in handy. The following is an example batch file:
@echo off
setlocal enabledelayedexpansion
rem 遍历 GlobalAssemblies 目录下的文件执行 PowerShell 脚本
for %%f in (GlobalAssemblies\*) do (
powershell -ExecutionPolicy Bypass -File "UpdateAssemblyVersion.ps1" -AssemblyInfoFile "%%f" -Configuration "Release" -Platform "x64"
rem 检查 PowerShell 脚本执行是否成功
if !errorlevel! neq 0 (
echo Failed to update assembly version for file %%f!
endlocal
exit /b !errorlevel!
)
)
rem 设置发布路径
set PUBLISH_PATH=publish\win-x64
rem 定义项目信息数组,格式为 项目名称,项目路径(只写项目目录),相对发布目录名
set "projects=码坊工具箱,src\CodeWF.Toolbox.Desktop,codewf Avalonia发布测试,tests\AvaloniaAotDemo,AvaloniaAotDemo"
rem 遍历项目数组
for %%a in ("%projects: =","%") do (
set "current_project=%%~a"
for /f "tokens=1,2,3 delims=," %%b in ("!current_project!") do (
set "projectName=%%b"
set "projectPath=%%c"
set "relativePublishDir=%%d"
rem 拼接 .pubxml 文件的默认路径
set "pubxmlPath=!projectPath!\Properties\PublishProfiles\FolderProfile-win_x64.pubxml"
rem 拼接当前项目的发布路径
set "CURRENT_PUBLISH_DIR=!PUBLISH_PATH!\!relativePublishDir!"
echo "projectName after assignment: !projectName!"
echo "projectPath after assignment: !projectPath!"
echo "relativePublishDir after assignment: !relativePublishDir!"
echo "pubxmlPath after assignment: !pubxmlPath!"
echo "current publish dir after assignment: !CURRENT_PUBLISH_DIR!"
echo Publishing !projectName! for win-64...
rem 清空发布目录
if exist "!CURRENT_PUBLISH_DIR!" (
rd /s /q "!CURRENT_PUBLISH_DIR!"
)
rem 创建发布目录
mkdir /p "!CURRENT_PUBLISH_DIR!" 2>nul
rem 执行 dotnet publish 命令进行 AOT 发布,并指定目标框架
dotnet publish "!projectPath!" /p:PublishProfile="!pubxmlPath!" -f net9.0-windows -o "!CURRENT_PUBLISH_DIR!"
rem 检查 dotnet publish 命令的退出代码
if !errorlevel! neq 0 (
echo Publish of !projectName! failed!
endlocal
exit /b !errorlevel!
)
rem 检查发布目录是否存在
if exist "!CURRENT_PUBLISH_DIR!" (
rem 删除调试符号文件
del "!CURRENT_PUBLISH_DIR!\*.pdb"
)
echo Publish of !projectName! succeeded!
echo Published files of !projectName! are located at: "!CURRENT_PUBLISH_DIR!"
)
)
endlocal
This batch file first traverses the files in the GlobalAssemblies directory and executes a PowerShell script to update the program version. It then defines an array of project information, including the project name, project path, and relative publishing directory name. Then, you loop through the array of items, publishing each item in turn. During the publishing process, first clear the publishing directory, then create a new publishing directory, and then execute the dotnet publish command for AOT publishing, and specify the target framework as net9.0-windows. Finally, check whether the release was successful. If it was successful, delete the debugging symbol file and output the success information and release path.
PowerShell scripts implement automatic version updates
The following PowerShell script is used to automatically update program versions based on Git tags:
param(
[Parameter(Mandatory=$true)]
[string]$AssemblyInfoFile,
[Parameter(Mandatory=$true)]
[string]$Configuration,
[Parameter(Mandatory=$true)]
[string]$Platform
)
# 检查文件是否存在
if (-not (Test-Path $AssemblyInfoFile)) {
throw "错误:文件 $AssemblyInfoFile 不存在"
}
Write-Output "AssemblyInfoFile: $AssemblyInfoFile"
Write-Output "Configuration: $Configuration"
Write-Output "Platform: $Platform"
#获取当前日期时间,并生成格式化的时间戳
$currentDateTime =Get-Date
#获取当前分支,签出标签创建时间[基准时间]
$strBranchCreateTime =git log -1 --format=%ai $latest_tag
$dateBranchCreateTime=[DateTime]::Parse($strBranchCreateTime)
#时间差的总分数
$timeDifMinutes =[math]::Round(($currentDateTime-$dateBranchcreateTime).TotalMinutes)
#版本号格式处理成x.x.65535.65535
$yy=[math]::Floor($timeDifMinutes/65535)
$thirdInfo=$timeDifMinutes-$yy*65535
#读取文件内容
$content = Get-Content -Path $AssemblyInfoFile -Encoding Unicode
#使用正则表达式获职 AssemblyProduct 的值
$productPattern='^\[assembly: AssemblyProduct\("([^"]+)"\)\]'
$product = ""
#查找包含 AssemblyProduct 的行,并匹配正则表达式
foreach($line in $content){
if($line -match $productPattern){
$product= $matches[1]
break #找到匹配后退出循环
}
}
$items=$product -split '_'
$product=$items[0]
#当前最近标签、checkout hash值
$currentCommitHash=git describe --always --tag --long
$items=$currentCommitHash.Split('-')
$hashInfo=$items[$items.Length-1]
$firstFileVersion="0.0"
$secondFileVersion="100"
if($items.Length -eq 3)
{
$firstFileVersion=$items[0].Replace("v","");
$firstFileItems=$firstFileVersion.split('.');
$firstFileversion=$firstFileItems[0]+"."+$firstFileItems[1]
$secondFileVersion=$items[1]+"$yy".PadLeft(2,'0')
}
$platformInfo = ""
if ($Configuration -eq "Debug") {
$platformInfo = "D"
} elseif ($Configuration -eq "Release") {
$platformInfo = "R"
} else {
$platformInfo = "A"
}
switch ($Platform) {
"x86" {
$platformInfo += "-86"
}
"x64" {
$platformInfo += "-64"
}
"ARM" {
$platformInfo += "-ARM"
}
"AnyCPU" {
$platformInfo += "-AnyCPU"
}
default {
$platformInfo += "-Unknow"
}
}
#定义新的 AssemblyProduct和 AssemblyFileversion
$strDayInfo= $currentDateTime.Tostring("yyyyMMddHHmm")
$newAssemblyProduct = "$product"+"_"+"$platformInfo"+"_"+"$hashInfo"+"_"+"$strDayInfo"
if($secondFileVersion -eq "000")
{
$secondFileVersion="0"
}
$newAssemblyFileVersion = "$firstFileVersion"+"."+"$secondFileVersion"+"."+"$thirdInfo"
# 更新 AssemblyProduct
$content = $content -replace '^\[assembly: AssemblyProduct\(".*"\)\]', "[assembly: AssemblyProduct(`"$newAssemblyProduct`")]"
# 更新AssemblyVersion
$content = $content -replace '^\[assembly: AssemblyVersion\(".*"\)\]', "[assembly: AssemblyVersion(`"$newAssemblyFileVersion`")]"
# 更新AssemblyFileVersion
$content = $content -replace '^\[assembly: AssemblyFileVersion\(".*"\)\]', "[assembly: AssemblyFileVersion(`"$newAssemblyFileVersion`")]"
# 将更新后的内容写入 AssemblyInfo.cs 文件
Set-Content -Path $AssemblyInfoFile -Value $content -Encoding Unicode
This script first receives three parameters: AssemblyInfoFile (path to the assembly information file), Configuration (configuration name), and Platform (platform name). It then checks whether the file exists and throws an error if it does not. Then, each part of the version number is calculated by obtaining information such as the current date and time, the Git tag creation time, etc. Generate a new AssemblyProduct and AssemblyFileVersion based on configuration and platform information. Finally, regular expressions are used to replace the old version information in the file, and the updated content is written to the AssemblyInfo.cs file, realizing automatic update of the program version.
Through these scripts and methods, we can greatly improve the development efficiency of. NET projects and automate version management and release processes. I hope these contents will be helpful to your development work and make you more comfortable on the road to. NET development.
以上示例脚本可在开源项目找到:dotnet9/CodeWF.Toolbox: CodeWF Toolbox 使用 Avalonia 开发的跨平台工具箱 Cross platform toolbox developed using Avalonia