首页 > 系统相关 >Gitlab Runner在Windows如何自动发布基于.NET Framework 4.8的传统ASP.NET Web应用程序

Gitlab Runner在Windows如何自动发布基于.NET Framework 4.8的传统ASP.NET Web应用程序

时间:2024-06-05 09:55:01浏览次数:32  
标签:Web ASP remote name web app project NET

摘要

软件流水线能把程序员从繁琐的发布工作中解脱出来,但是跑在Windows IIS里的传统Web应用程序,用Docker的方式不是最方便的。本文详细描述如何用Windows的OpenSSH Server来上传网站后,用PowerShell创建和修改IIS的虚拟目录应用程序。

一、自动打包传统ASP.NET Web应用程序

1、Gitlab仓库中.gitlab-ci.yml中的配置

打包会员订单站点:
  stage: publish-web
  tags:
    - runner-windows
  before_script:
    - . $BUILD_WEB_SCRIPT
  script:
    - echo "开始部署Passport……"
    - cd $MSBUILD_PATH
    - Build-Web $MSBUILD_PATH $CI_PROJECT_DIR "/****/*******.Orders.Web/********.Orders.Web.csproj" $YEE_CUSTOMER_ORDERS_WEB_OUTPUT_DIR

说明:

  • before_script下的命令,是注册PowerShell的函数。因为服务器上的脚本是function。
  • scripts的“Build-Web”就是PowerShell的函数名称,接受4个参数。

2、Windows Gitlab Runner中的PowerShell脚本

function Build-Web {
	param (
		[string]$msbuild_dir,
		[string]$ci_project_dir,
		[string]$project_path,
		[string]$web_output_path
	)

$project_file = $ci_project_dir + "\\" + $project_path
if(( $project_path -like "/*") -or ($project_path -like "\\*")){
	$project_file = $ci_project_dir + $project_path
}
echo "项目文件"$project_file
echo "版本号"$version
echo "网站输出路径"$web_output_path

cd $ci_project_dir
COPY-ITEM -PATH 'x:/gitlab-runner/scripts/download.bat' -Destination .
.\download.bat

if ($LASTEXITCODE -ne 0)  {
	throw "下载Directory.Build.props及其相关文件时出错。"
}

dotnet nuget locals http-cache -c

$exePath = $msbuild_dir + "\\msbuild.exe"
if(Test-Path -Path $exePath){
}
else
{
	throw "MSBuild.exe directory not found:" + $msbuild_dir
}
cd $msbuild_dir

.\msbuild -restore $project_file /v:m
 if ($LASTEXITCODE -ne 0) {
	throw "还原项目时出错:" + $project_file
}

cd X:\gitlab-runner\tools\binding-redirect-helper
.\BindingRedirect check --project:$project_file --save:true
if ($LASTEXITCODE -ne 0) {
	throw "自动更新web.config中的绑定重定向时时出错:" + $project_file
}
cd $msbuild_dir

.\msbuild $project_file /t:ResolveReferences /t:Compile /p:configuration="Release" /t:_CopyWebApplication /p:WebProjectOutputDir=$web_output_path /p:OutputPath=$web_output_path"\bin" /v:m

Exit $LASTEXITCODE
}

说明:

  • download.bat是配合Directory.Build.props技术,下载一堆相关文件。下载后清空NuGet的Http缓存;
  • msbuild.exe先还原,再生成,分成了两步;
  • /v:m是msbuild命令执行时给出最少的日志。需要调试的时候,用/v:diag;
  • /t:_CopyWebApplication,需要在Web项目的.csproj文件中Import这个目标,在前文有详细说明;
  • WebProjectOutputDir和OutputPath两个输出目录都要配置,一个管.aspx、.ascx和.css等资源文件,一个管dll。

4个参数的说明:

  • $msbuild_dir msbuild.exe程序所在目录的路径;
  • $ci_project_dir 这个地址是Gitlab内置的函数给的,就是Gitlab Runner从Gitlab服务器签出代码后的工作目录;
  • $project_path,项目的.csproj文件,相对于仓库根目录的路径;
  • $web_output_path的路径,一定要在解决方案之外。因为下一个负责上传的stage是拿不到这个stage执行时候目录下的文件的。

二、自动上传打包的网站到Web服务器

这里很复杂,需要几个准备工作

  • Web服务器安装Open SSH Server;
  • Web服务器配置好管理员账户的SSH公钥,并允许使用秘钥而不是密码来登入;

1、Gitlab仓库根目录.gitlab-ci.yml中的配置

上传会员订单网站到开发站:
  stage: upload-web
  tags:
    - runner-windows
  script:
    - cd ${GITLAB_RUNNER_POWERSHELL_DIR}
    - |
      powershell.exe .\upload-web.ps1 `
        -ssh_rsa_file $SSH_RSA_FILE `
        -remote_username $DEV_TEST_SERVER_LOGIN_NAME `
        -remote_server $DEV_TEST_SERVER `
        -remote_root_dir_name loda `
        -remote_site_name $REMOTE_WINDOWS_SITE_NAME `
        -remote_windows_web_app_pool $REMOTE_WINDOWS_WEB_APP_POOL `
        -web_app_name orders `
        -web_output_path $YEE_CUSTOMER_ORDERS_WEB_OUTPUT_DIR `
        -version_id $YEE_ORDERCENTER_VERSION_ID

说明:

  • 上述命令是多行,换了行的。参数只能是美元符号开头直接跟参数名称,如果$YEE_ORDERCENTER_VERSION_ID换成${YEE_ORDERCENTER_VERSION_ID}这种大括号包裹参数名称,就会出错;
  • 每一行里不能有双引号。比如倒数第3行,orders不能写成“orders”

参数说明:

  • $SSH_RSA_FILE 用于SSH登入Web服务器的私钥的完整路径,这个私钥是放在Gitlab Runner服务器的。私钥所在的文件夹的权限要特殊配置:只有运行Gitlab Runner这个Windows服务器的账户有只读权限。否则SSH会报错;
  • $DEV_TEST_SERVER_LOGIN_NAME 用于SSH登入Web服务器的用户名,比如Administrator;
  • $DEV_TEST_SERVER Web服务器的DNS名称或IP地址;
  • remote_root_dir_name 这个参数,是为了Web服务器安全。Web服务器上,我们的网站如果放在“D:\apps",这里传入loda,就是说网站根目录是“D:\apps\loda”;
  • remote_site_name 这个参数,就是IIS中网站的名称,比如:Default Web Site。但是空格在命令从Gitlab Runner服务器传递到Web服务器会造成别的歧义,所以在Web服务器上的网站名字,实际上不能有空格;
  • remote_windows_web_app_pool 这个就是Web服务器上,IIS应用程序池的名字,因为Gitlab CI传参数给Gitlab Runner的PowerShell,又从这个PowerShell执行SSH到Web服务器,参数传递中空格有特别含义,所以这个应用程序池的名字和网站名字一样:不能有空格;
  • web_app_name 这个名字,就是IIS网站根目录下的名字,会出现在URL里,所以不能有小数点不能有空格,只能是英文字母、数字和连接符;
  • web_output_path 网站打包后,放在Gitlab Runner的路径,这个路径是绝对路径,不能实在stage工作目录内部;
  • version_id 我们NuGet的包的版本号都是小数点分隔的,但是作为Web路径的时候,小数点有特别的含义,所以我们的函数收到带小数点的版本号后,会把小数点替换成下划线。

2、Windows Gitlab Runner服务器上被调用的PowerShell脚本

下边的脚本,最值得说的就是sshCmd。
因为这个脚本在Gitlab Runner服务器上,被Gitlab Runner调用以后,要SSH到Web服务器去执行命令。

# 设置环境变量以支持UTF-8编码
param (
	[Parameter(Mandatory=$true)]
	[string]$ssh_rsa_file,
	[Parameter(Mandatory=$true)]
	[string]$web_output_path,
	[Parameter(Mandatory=$true)]
	[string]$remote_username,
	[Parameter(Mandatory=$true)]
	[string]$remote_server,
	[Parameter(Mandatory=$true)]
	[string]$remote_root_dir_name,
	[Parameter(Mandatory=$true)]
	[string]$remote_site_name,
	[Parameter(Mandatory=$true)]
	[string]$remote_windows_web_app_pool,
	[Parameter(Mandatory=$true)]
	[string]$web_app_name,
	[Parameter(Mandatory=$true)]
	[string]$version_id
)
chcp 65001
 
$version_id = $version_id.Replace('.','_') 

# 确保所有参数值中的特殊字符被适当转义,特别是对于包含在路径或字符串中的双引号
$escapedSiteName = $remote_site_name -replace "'", "''"
$escapedPool = $remote_windows_web_app_pool -replace "'", "''"
$escapedAppName = $web_app_name -replace "'", "''"
$escapedVersion = $version_id -replace "'", "''"

$remote_script_path = "c:\for-gitlab-runner\Setup-LodaWebApp.ps1"

# 构建远程脚本调用的参数字符串,保持简单并确保参数间以空格分隔
$remoteParams = "-remote_site_name '$escapedSiteName' -remote_windows_web_app_pool '$escapedPool' -web_app_name '$escapedAppName' -version_id '$escapedVersion'"

# 构建完整的SSH命令,注意引号的正确使用和转义
$sshCmd = "ssh -i `"$ssh_rsa_file`" -o ""StrictHostKeyChecking=no"" -v `"$remote_username@$remote_server`" 'powershell.exe -ExecutionPolicy Bypass -File `"$remote_script_path`" $remoteParams'"
# 注意:$remote_script_path 应替换为实际的远程脚本路径,如"c:\for-gitlab-runner\Setup-LodaWebApp.ps1"

# 打印命令以供调试
Write-Host "Executing Command: $sshCmd"

# 执行SSH命令
Invoke-Expression $sshCmd


if ($LASTEXITCODE -ne 0)  {
	if ($Errors.Count -gt 0) {
    Write-Host "清空远程目录时出错:" $Errors[0].Exception.Message
    Write-Host "错误信息:" $Errors[0].Exception.Message
    Write-Host "堆栈跟踪:" $Errors[0].Exception.StackTrace
}
else {
    Write-Host "没有错误信息记录。"
}
}
$remote_dir = "C:\" + $remote_root_dir_name + "\" + $web_app_name + "\" + $version_id
scp -i $ssh_rsa_file -o StrictHostKeyChecking=no -rv ${web_output_path}"\*" ${remote_username}@${remote_server}:$remote_dir

Exit $LASTEXITCODE

3、Web服务器上,被Gitlab Runner的PowerShell调用的PowerShell脚本

看这个标题就拗口,理解后就很简单,这个脚本的作用是:

  • 为Web程序创建新目录,这个目录的名字,是带了version_id的。因为web程序在IIS里运行的时候,dll文件无法被覆盖,所以要准备新目录;
  • 在网站根目录下,以这个新目录创建应用程序。如果应用程序已经存在,则NEW-WebApplication的最后一个参数-Force,就强制把应用程序物理路径给修改到新目录

下边是脚本内容:

 param(
    [Parameter(Mandatory=$true)]
    [string]$remote_site_name,
    [Parameter(Mandatory=$true)]
    [string]$remote_windows_web_app_pool,
    [Parameter(Mandatory=$true)]
    [string]$web_app_name,
    [Parameter(Mandatory=$true)]
    [string]$version_id
)

chcp 65001

Write-Host "已°?绑㨮定¡§的Ì?参?数ºy及¡ã其?值¦Ì:"
$PSBoundParameters.GetEnumerator() | ForEach-Object {
    Write-Host "$($_.Key): $($_.Value)"
}
$version_id = $version_id.Replace('.','_')

$AppPhysicalPath = "C:\loda\" + ${web_app_name} + "\" + $version_id
New-Item -Path $AppPhysicalPath -ItemType Directory -Force

# 定¡§义°?IIS PowerShell模¡ê块¨¦路¡¤径?,ê?根¨´据Y实º¦Ì际¨º情¨¦况?调Ì¡Â整?
$IISModulePath = "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\WebAdministration\WebAdministration.psd1"
if (!(Test-Path $IISModulePath)) {
    Write-Error "IIS Administration PowerShell module not found at $IISModulePath"
    exit 1
}

Import-Module $IISModulePath

# 确¨¡¤保À¡ê应®|用®?池?存ä?在¨²
if (!(Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -eq $remote_windows_web_app_pool })) {
    Write-Error "Application Pool '$remote_windows_web_app_pool' does not exist."
    exit 1
}

# 确¨¡¤保À¡ê站?点Ì?存ä?在¨²
$sitePath = "IIS:\Sites\$remote_site_name"
if (!(Test-Path $sitePath)) {
    Write-Error "Site '$remote_site_name' does not exist."
    exit 1
}

# 创ä¡ä建¡§应®|用®?程¨¬序¨°
Write-Host "Creating application '$web_app_name' under site '$remote_site_name' with physical path '$AppPhysicalPath'"
NEW-WebApplication -Name $web_app_name -Site $remote_site_name -ApplicationPool $remote_windows_web_app_pool -PhysicalPath $AppPhysicalPath -Force

if($LASTEXITCODE -ne 0)
{
    Write-Error "Failed to create the application."
    exit $LASTEXITCODE
}

# 确¨¡¤认¨?操¨´作Á¡Â成¨¦功|
$appPath = Join-Path $sitePath $web_app_name
if (Test-Path $appPath) {
    Write-Host "Application created successfully at '$appPath'."
} else {
    Write-Error "Failed to create the application."
}

exit 0

参数说明

前两个参数的remote_其实要删除才合理。因为这里已经是目的地了,是别人的remote。

标签:Web,ASP,remote,name,web,app,project,NET
From: https://www.cnblogs.com/amisoft/p/18232246/auto-build-aspnet-web-and-publish

相关文章

  • ASP.NET Web应用程序升级最新的MSBuild格式后,Visual Studio 2022中如何调试?
    摘要把ASP.NET的Web应用程序,Project文件从<ProjectToolsVersion="12.0"DefaultTargets="Build"xmlns="http://schemas.microsoft.com/developer/msbuild/2003">改为<ProjectSdk="Microsoft.NET.Sdk.Web">之后,升级成了最新的格式之后,如......
  • .NET C# 程序自动更新组件
    引言本来博主想偷懒使用AutoUpdater.NET组件,但由于博主项目有些特殊性和它的功能过于多,于是博主自己实现一个轻量级独立自动更新组件,可稍作修改集成到大家自己项目中,比如:WPF/Winform/Windows服务。大致思路:发现更新后,从网络上下载更新包并进行解压,同时在WinForms应用程序中显示......
  • ASP.NET Web应用程序升级最新的MSBuild格式后,Visual Studio 2022中如何调试?
    摘要把ASP.NET的Web应用程序,Project文件从<ProjectToolsVersion="12.0"DefaultTargets="Build"xmlns="http://schemas.microsoft.com/developer/msbuild/2003">改为<ProjectSdk="Microsoft.NET.Sdk.Web">之后,升级成了最新的格式之后,如......
  • 分析webpack编译结果, 实现__webpack_require__函数
    分析webpack编译结果,实现__webpack_require__函数本篇文章我们通过手写来分析一下Webpack打包后的代码,并研究一下Webpack是如何将多个模块合并在一起的首先控制台输入npminit-y初始化一个项目,再输入npmiwebpackwebpack-cli-D安装Webpack在src目录想创建入......
  • Kubernetes - 安装方法
    Minikube:对于想要在系统上安装Kubernetes但系统资源有限的用户来说,它是理想的选择。因此,minikube的关键点在于它没有单独的Kubernetes主节点和Kubernetes工作节点架构。在这里,我们将所有Kubernetes组件打包在一起作为一体化设置。单个系统同时充当主节点和工作节点。访......
  • 【Kubernetes】k8s的调度约束(亲和与反亲和)
    一、调度约束list-watch组件Kubernetes是通过List-Watch的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。用户是通过kubectl根据配置文件,向APIServer发送命令,在Node节点上面建立Pod和Container。APIServer经过API调用,权限控制,调......
  • Kubernetes – 架构
    Kubernetes集群主要由称为节点的工作机器和控制平面组成。集群中至少有一个工作节点。KubectlCLI与控制平面通信,控制平面管理工作节点。Kubernetes–集群架构如下图所示,Kubernetes采用客户端-服务器架构,有主节点和工作节点,主节点安装在单个Linux系统上,而节点安装在多个......
  • 前端引入,Web服务器的本质,HTTP协议,HTML基础,常用标签介绍
    Ⅰ前端引入【一】前端/后端【1】什么是前端任何与用户直接打交道的操作界面都可以称之为前端前端可以是浏览器的界面、也可以是客户端的界面、还可以是手机的界面...比如电脑界面、手机界面、平板界面【2】什么是后端不直接与用户打交道的用于执行真正业务逻辑的代......
  • webapi 接收前端上传的文件
    usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Mvc;usingSystem.IO;usingSystem.IO.Compression;namespaceWebApplication1.Controllers{[Route("api/[controller]/[action]")][ApiController]publicclassValuesControlle......
  • 基于web学校校园教室预约管理系统设计与实现
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......