LuTong 1 месяц назад
Родитель
Сommit
5754ae354d
1 измененных файлов с 357 добавлено и 0 удалено
  1. 357 0
      deploy-copy.ps1

+ 357 - 0
deploy-copy.ps1

@@ -0,0 +1,357 @@
+#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()