| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- #Requires -Version 5.1
- param(
- [switch] $NonInteractive,
- [ValidateSet('Sit', 'Uat', 'Prod')]
- [string] $Environment = 'Sit',
- [string] $GitUrl = 'http://8.152.195.41:3000/dujian/h5.git',
- [string] $Branch = 'master',
- [string] $SshUser = 'root',
- # 留空则使用当前环境在 $Environments 中的默认远程目录
- [string] $RemotePath = '',
- # 非空时覆盖环境默认 IP(便于 Jenkins / 命令行临时指定)
- [string] $TargetHost = ''
- )
- <#
- .SYNOPSIS
- 从 Git 将 master(或指定分支)部署到目标服务器:先清空远程目录再覆盖上传。
- .DESCRIPTION
- 无参数启动:图形界面。加 -NonInteractive 时供 Jenkins 等无人值守调用。
- 默认仓库:http://8.152.195.41:3000/dujian/h5.git
- 默认:Sit → 120.26.186.130:/docker/middleware/nginx/html/h5/;
- Uat → 39.106.135.88:/alien_uat/nginx/html/h5/;
- Prod → 39.106.135.88:/alien_produ/nginx/html/h5/
- #>
- $ErrorActionPreference = 'Stop'
- $Environments = @(
- @{
- Key = 'Sit'
- Label = 'Sit(测试环境)'
- Server = '120.26.186.130'
- RemotePath = '/docker/middleware/nginx/html/h5/'
- },
- @{
- Key = 'Uat'
- Label = 'Uat(预生产环境)'
- Server = '39.106.135.88'
- RemotePath = '/alien_uat/nginx/html/h5/'
- },
- @{
- Key = 'Prod'
- Label = 'Prod(生产环境)'
- Server = '39.106.135.88'
- RemotePath = '/alien_produ/nginx/html/h5/'
- }
- )
- function Test-GitAvailable {
- $g = Get-Command git -ErrorAction SilentlyContinue
- if (-not $g) {
- throw "未找到 git。请先安装 Git for Windows 并确保 git 在 PATH 中。"
- }
- }
- function Test-SshScpAvailable {
- $ssh = Get-Command ssh -ErrorAction SilentlyContinue
- $scp = Get-Command scp -ErrorAction SilentlyContinue
- if (-not $ssh -or -not $scp) {
- throw "未找到 ssh 或 scp。请在 Windows「可选功能」中安装「OpenSSH 客户端」。"
- }
- }
- function Invoke-GitCloneMaster {
- param(
- [string] $RepoUrl,
- [string] $Branch,
- [string] $CloneInto
- )
- if (Test-Path -LiteralPath $CloneInto) {
- Remove-Item -LiteralPath $CloneInto -Recurse -Force
- }
- New-Item -ItemType Directory -Path (Split-Path -LiteralPath $CloneInto -Parent) -Force | Out-Null
- $cloneArgs = @('clone', '--depth', '1', '--branch', $Branch, $RepoUrl, $CloneInto)
- & git @cloneArgs
- if ($LASTEXITCODE -ne 0) {
- throw "git clone 失败,退出码:$LASTEXITCODE"
- }
- }
- function Remove-LocalGitMetadata {
- param([string] $RepoRoot)
- $gitDir = Join-Path $RepoRoot '.git'
- if (Test-Path -LiteralPath $gitDir) {
- Remove-Item -LiteralPath $gitDir -Recurse -Force
- }
- }
- function Invoke-RemoteClearDirectory {
- param(
- [string] $User,
- [string] $TargetHost,
- [string] $RemotePath
- )
- $r = $RemotePath.Trim().TrimEnd('/')
- if ($r.IndexOf([char]39) -ge 0 -or $r.Contains('$') -or $r.Contains('`')) {
- throw '远程目录路径暂不支持包含单引号、反引号或美元符号。'
- }
- $remoteShell = "mkdir -p '$r' && find '$r' -mindepth 1 -delete"
- & ssh @('-o', 'StrictHostKeyChecking=accept-new', "${User}@${TargetHost}", $remoteShell)
- if ($LASTEXITCODE -ne 0) {
- throw "远程清空目录失败(ssh),退出码:$LASTEXITCODE"
- }
- }
- function Invoke-DeployScp {
- param(
- [string] $StagePath,
- [string] $User,
- [string] $TargetHost,
- [string] $RemotePath
- )
- $dest = ('{0}@{1}:{2}' -f $User, $TargetHost, ($RemotePath.TrimEnd('/') + '/'))
- $items = @(Get-ChildItem -LiteralPath $StagePath -Force)
- if ($items.Count -eq 0) {
- throw '克隆后的目录为空,无法上传。'
- }
- $scpArgs = @('-r', '-o', 'StrictHostKeyChecking=accept-new') + ($items | ForEach-Object { $_.FullName }) + @($dest)
- & scp @scpArgs
- if ($LASTEXITCODE -ne 0) {
- throw "scp 失败,退出码:$LASTEXITCODE"
- }
- }
- function Invoke-H5DeployFromGit {
- param(
- [Parameter(Mandatory)]
- [ValidateSet('Sit', 'Uat', 'Prod')]
- [string] $Environment,
- [string] $RepoUrl,
- [string] $Branch,
- [string] $User,
- [string] $RemotePath,
- [string] $TargetHostOverride
- )
- $envItem = @($Environments | Where-Object { $_.Key -eq $Environment })[0]
- if (-not $envItem) { throw "未知环境:$Environment" }
- $hostAddr = if ($TargetHostOverride) { $TargetHostOverride.Trim() } else { $envItem.Server }
- if (-not $hostAddr) { throw '目标主机不能为空。' }
- $resolvedRemote = if ($RemotePath -and $RemotePath.Trim()) { $RemotePath.Trim() } else { $envItem.RemotePath }
- if (-not $resolvedRemote) { throw '远程目录不能为空。' }
- Test-GitAvailable
- Test-SshScpAvailable
- $base = Join-Path ([System.IO.Path]::GetTempPath()) ('h5-deploy-' + [Guid]::NewGuid().ToString('N'))
- $cloneInto = Join-Path $base 'h5'
- try {
- Write-Host "[$($envItem.Key)] 克隆 $Branch …"
- Invoke-GitCloneMaster -RepoUrl $RepoUrl -Branch $Branch -CloneInto $cloneInto
- Write-Host '移除 .git(仅上传站点文件)…'
- Remove-LocalGitMetadata -RepoRoot $cloneInto
- Write-Host "清空远程目录 $hostAddr`:$resolvedRemote …"
- Invoke-RemoteClearDirectory -User $User -TargetHost $hostAddr -RemotePath $resolvedRemote
- Write-Host "上传到 $hostAddr …"
- Invoke-DeployScp -StagePath $cloneInto -User $User -TargetHost $hostAddr -RemotePath $resolvedRemote
- Write-Host "完成:$($envItem.Label) → ${User}@${hostAddr}:${resolvedRemote}"
- }
- finally {
- if (Test-Path -LiteralPath $base) {
- Remove-Item -LiteralPath $base -Recurse -Force -ErrorAction SilentlyContinue
- }
- }
- }
- if ($NonInteractive) {
- try {
- Invoke-H5DeployFromGit -Environment $Environment -RepoUrl $GitUrl -Branch $Branch `
- -User $SshUser -RemotePath $RemotePath -TargetHostOverride $TargetHost
- exit 0
- }
- catch {
- Write-Error $_
- exit 1
- }
- }
- # ---------- 以下为图形界面 ----------
- Add-Type -AssemblyName System.Windows.Forms
- Add-Type -AssemblyName System.Drawing
- $DefaultGitUrl = $GitUrl
- $DefaultBranch = $Branch
- $form = New-Object System.Windows.Forms.Form
- $form.Text = '从 Git 部署 H5 到服务器'
- $form.Size = New-Object System.Drawing.Size(520, 400)
- $form.StartPosition = 'CenterScreen'
- $form.FormBorderStyle = 'FixedDialog'
- $form.MaximizeBox = $false
- $y = 18
- $dy = 36
- $lblEnv = New-Object System.Windows.Forms.Label
- $lblEnv.Location = New-Object System.Drawing.Point(20, $y)
- $lblEnv.Size = New-Object System.Drawing.Size(120, 22)
- $lblEnv.Text = '环境:'
- $combo = New-Object System.Windows.Forms.ComboBox
- $combo.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $combo.Size = New-Object System.Drawing.Size(340, 24)
- $combo.DropDownStyle = 'DropDownList'
- foreach ($envDef in $Environments) {
- [void] $combo.Items.Add($envDef.Label)
- }
- $idxGui = 0
- for ($i = 0; $i -lt $Environments.Count; $i++) {
- if ($Environments[$i].Key -eq $Environment) { $idxGui = $i; break }
- }
- $combo.SelectedIndex = $idxGui
- $y += $dy
- $lblGit = New-Object System.Windows.Forms.Label
- $lblGit.Location = New-Object System.Drawing.Point(20, $y)
- $lblGit.Size = New-Object System.Drawing.Size(120, 22)
- $lblGit.Text = '仓库 URL:'
- $txtGit = New-Object System.Windows.Forms.TextBox
- $txtGit.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $txtGit.Size = New-Object System.Drawing.Size(340, 22)
- $txtGit.Text = $DefaultGitUrl
- $y += $dy
- $lblBranch = New-Object System.Windows.Forms.Label
- $lblBranch.Location = New-Object System.Drawing.Point(20, $y)
- $lblBranch.Size = New-Object System.Drawing.Size(120, 22)
- $lblBranch.Text = '分支:'
- $txtBranch = New-Object System.Windows.Forms.TextBox
- $txtBranch.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $txtBranch.Size = New-Object System.Drawing.Size(120, 22)
- $txtBranch.Text = $DefaultBranch
- $y += $dy
- $lblHost = New-Object System.Windows.Forms.Label
- $lblHost.Location = New-Object System.Drawing.Point(20, $y)
- $lblHost.Size = New-Object System.Drawing.Size(120, 22)
- $lblHost.Text = '目标主机:'
- $txtHost = New-Object System.Windows.Forms.TextBox
- $txtHost.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $txtHost.Size = New-Object System.Drawing.Size(340, 22)
- $txtHost.ReadOnly = $true
- $y += $dy
- $lblUser = New-Object System.Windows.Forms.Label
- $lblUser.Location = New-Object System.Drawing.Point(20, $y)
- $lblUser.Size = New-Object System.Drawing.Size(120, 22)
- $lblUser.Text = 'SSH 用户:'
- $txtUser = New-Object System.Windows.Forms.TextBox
- $txtUser.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $txtUser.Size = New-Object System.Drawing.Size(340, 22)
- $txtUser.Text = $SshUser
- $y += $dy
- $lblPath = New-Object System.Windows.Forms.Label
- $lblPath.Location = New-Object System.Drawing.Point(20, $y)
- $lblPath.Size = New-Object System.Drawing.Size(120, 22)
- $lblPath.Text = '远程目录:'
- $txtPath = New-Object System.Windows.Forms.TextBox
- $txtPath.Location = New-Object System.Drawing.Point(140, ($y - 2))
- $txtPath.Size = New-Object System.Drawing.Size(340, 22)
- $sel0 = $Environments[$idxGui]
- $txtHost.Text = $sel0.Server
- $txtPath.Text = if ($RemotePath.Trim()) { $RemotePath.Trim() } else { $sel0.RemotePath }
- $y += 44
- $btnOk = New-Object System.Windows.Forms.Button
- $btnOk.Location = New-Object System.Drawing.Point(160, $y)
- $btnOk.Size = New-Object System.Drawing.Size(120, 32)
- $btnOk.Text = '开始部署'
- $btnCancel = New-Object System.Windows.Forms.Button
- $btnCancel.Location = New-Object System.Drawing.Point(300, $y)
- $btnCancel.Size = New-Object System.Drawing.Size(120, 32)
- $btnCancel.Text = '取消'
- $y += 48
- $status = New-Object System.Windows.Forms.Label
- $status.Location = New-Object System.Drawing.Point(20, $y)
- $status.Size = New-Object System.Drawing.Size(470, 72)
- $status.Text = "将克隆:$DefaultGitUrl ($DefaultBranch)`n上传前会删除远程目录内已有文件后再覆盖。"
- function Update-FromEnvironmentSelection {
- $idx = $combo.SelectedIndex
- if ($idx -lt 0) { return }
- $sel = $Environments[$idx]
- $txtHost.Text = $sel.Server
- $txtPath.Text = $sel.RemotePath
- }
- $combo.Add_SelectedIndexChanged({ Update-FromEnvironmentSelection })
- $form.Controls.AddRange(@(
- $lblEnv, $combo,
- $lblGit, $txtGit,
- $lblBranch, $txtBranch,
- $lblHost, $txtHost,
- $lblUser, $txtUser,
- $lblPath, $txtPath,
- $btnOk, $btnCancel,
- $status
- ))
- $btnCancel.Add_Click({ $form.DialogResult = 'Cancel'; $form.Close() })
- $btnOk.Add_Click({
- try {
- $idx = $combo.SelectedIndex
- if ($idx -lt 0) { throw '请选择环境。' }
- $envItem = $Environments[$idx]
- $hostAddr = $txtHost.Text.Trim()
- $user = $txtUser.Text.Trim()
- $remotePath = $txtPath.Text.Trim()
- $repoUrl = $txtGit.Text.Trim()
- $branch = $txtBranch.Text.Trim()
- if (-not $repoUrl) { throw '仓库 URL 不能为空。' }
- if (-not $branch) { throw '分支不能为空。' }
- $btnOk.Enabled = $false
- $btnCancel.Enabled = $false
- Invoke-H5DeployFromGit -Environment $envItem.Key -RepoUrl $repoUrl -Branch $branch `
- -User $user -RemotePath $remotePath -TargetHostOverride ''
- [System.Windows.Forms.MessageBox]::Show(
- ("部署完成。`n环境:{0}`n仓库:{1} ({2})`n目标:{3}@{4}:{5}" -f `
- $envItem.Label, $repoUrl, $branch, $user, $hostAddr, $remotePath),
- '成功',
- 'OK',
- 'Information'
- ) | Out-Null
- $form.DialogResult = 'OK'
- $form.Close()
- }
- catch {
- [System.Windows.Forms.MessageBox]::Show(
- $_.Exception.Message,
- '错误',
- 'OK',
- 'Error'
- ) | Out-Null
- $btnOk.Enabled = $true
- $btnCancel.Enabled = $true
- $status.Text = "将克隆:$DefaultGitUrl ($DefaultBranch)`n上传前会删除远程目录内已有文件后再覆盖。"
- }
- })
- [void] $form.ShowDialog()
|