Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

156 рядки
6.6KB

  1. [CmdletBinding()]
  2. param(
  3. [Parameter(Mandatory = $true)]
  4. [string]$RemoteHost,
  5. [Parameter(Mandatory = $true)]
  6. [string]$RemoteProjectPath,
  7. [string]$ProductionSiteName = 'WscMvc',
  8. [string]$ProductionPoolName = 'WscMvc',
  9. [int]$ProductionPort = 8090,
  10. [string]$ProductionBaseUrl = 'http://localhost:8090',
  11. [string]$TestSiteName = 'WscMvcTests',
  12. [string]$TestPoolName = 'WscMvcTests',
  13. [int]$TestPort = 8091,
  14. [string]$TestBaseUrl = 'http://localhost:8091',
  15. [string]$RemoteTempRoot = 'C:\Windows\Temp',
  16. [switch]$PullOriginMaster,
  17. [switch]$RunTests
  18. )
  19. $ErrorActionPreference = 'Stop'
  20. Set-StrictMode -Version 2.0
  21. function Invoke-Native {
  22. param(
  23. [Parameter(Mandatory = $true)][string]$FilePath,
  24. [Parameter(Mandatory = $true)][string[]]$ArgumentList
  25. )
  26. & $FilePath @ArgumentList
  27. if ($LASTEXITCODE -ne 0) {
  28. throw "$FilePath failed with exit code $LASTEXITCODE"
  29. }
  30. }
  31. function Quote-PowerShellLiteral {
  32. param([Parameter(Mandatory = $true)][string]$Value)
  33. return "'" + $Value.Replace("'", "''") + "'"
  34. }
  35. $projectRoot = Split-Path -Parent $PSScriptRoot
  36. $gitRoot = (& git -C $projectRoot rev-parse --show-toplevel 2>$null)
  37. if ($LASTEXITCODE -ne 0 -or -not $gitRoot) {
  38. throw "Project root is not a Git checkout: $projectRoot"
  39. }
  40. $gitRoot = [System.IO.Path]::GetFullPath($gitRoot.Trim())
  41. if ($gitRoot.TrimEnd('\') -ne ([System.IO.Path]::GetFullPath($projectRoot)).TrimEnd('\')) {
  42. throw "Deploy-Remote.ps1 must run from this repository checkout (expected $projectRoot, Git reported $gitRoot)."
  43. }
  44. if ($ProductionSiteName -eq $TestSiteName) { throw 'ProductionSiteName and TestSiteName must differ.' }
  45. if ($ProductionPoolName -eq $TestPoolName) { throw 'ProductionPoolName and TestPoolName must differ.' }
  46. if ($ProductionPort -lt 1 -or $ProductionPort -gt 65535) { throw 'ProductionPort must be between 1 and 65535.' }
  47. if ($TestPort -lt 1 -or $TestPort -gt 65535) { throw 'TestPort must be between 1 and 65535.' }
  48. if ($ProductionPort -eq $TestPort) { throw 'ProductionPort and TestPort must differ.' }
  49. if ($PullOriginMaster) {
  50. $dirty = (& git -C $gitRoot status --porcelain)
  51. if ($LASTEXITCODE -ne 0) { throw 'Unable to inspect Git worktree state.' }
  52. if ($dirty) {
  53. throw 'Refusing to pull with local changes. Commit, stash, or omit -PullOriginMaster.'
  54. }
  55. Invoke-Native -FilePath 'git' -ArgumentList @('-C', $gitRoot, 'pull', '--ff-only', 'origin', 'master')
  56. }
  57. $trackedFiles = @(& git -C $gitRoot ls-files)
  58. if ($LASTEXITCODE -ne 0 -or $trackedFiles.Count -eq 0) {
  59. throw 'git ls-files did not return the tracked deployment set.'
  60. }
  61. $required = @(
  62. 'tools/Register-Components.ps1',
  63. 'tools/Setup-Site.ps1',
  64. 'tools/Invoke-RemoteInstall.ps1',
  65. 'tests/Invoke-SelfTest.ps1',
  66. 'tests/Test-Http.ps1',
  67. 'Framework/ViewRenderer.wsc'
  68. )
  69. foreach ($requiredPath in $required) {
  70. if ($trackedFiles -notcontains $requiredPath) {
  71. throw "Required deployment file is not tracked by Git: $requiredPath"
  72. }
  73. if (-not (Test-Path -LiteralPath (Join-Path $gitRoot $requiredPath))) {
  74. throw "Required deployment file is missing: $requiredPath"
  75. }
  76. }
  77. $invocationId = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ') + '-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)
  78. $localWork = Join-Path ([System.IO.Path]::GetTempPath()) "wsc-mvc-deploy-$invocationId"
  79. $packageRoot = Join-Path $localWork 'package'
  80. $archivePath = Join-Path $localWork 'wsc-mvc.zip'
  81. $remoteArchive = Join-Path $RemoteTempRoot "wsc-mvc-$invocationId.zip"
  82. $remoteInstaller = Join-Path $RemoteTempRoot "wsc-mvc-install-$invocationId.ps1"
  83. try {
  84. New-Item -ItemType Directory -Path $packageRoot -Force | Out-Null
  85. foreach ($relativePath in $trackedFiles) {
  86. if ([string]::IsNullOrWhiteSpace($relativePath)) { continue }
  87. if ([System.IO.Path]::IsPathRooted($relativePath) -or $relativePath -match '(^|[\\/])\.\.([\\/]|$)') {
  88. throw "Unsafe tracked path: $relativePath"
  89. }
  90. $source = Join-Path $gitRoot $relativePath
  91. $item = Get-Item -LiteralPath $source -Force
  92. if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
  93. throw "Refusing to package a reparse point or symbolic link: $relativePath"
  94. }
  95. $destination = Join-Path $packageRoot $relativePath
  96. $destinationParent = Split-Path -Parent $destination
  97. if (-not (Test-Path -LiteralPath $destinationParent)) {
  98. New-Item -ItemType Directory -Path $destinationParent -Force | Out-Null
  99. }
  100. Copy-Item -LiteralPath $source -Destination $destination -Force
  101. }
  102. Add-Type -AssemblyName System.IO.Compression.FileSystem
  103. [System.IO.Compression.ZipFile]::CreateFromDirectory($packageRoot, $archivePath, [System.IO.Compression.CompressionLevel]::Optimal, $false)
  104. $packageSha256 = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
  105. Write-Output "Package contains $($trackedFiles.Count) Git-tracked paths."
  106. Write-Output "Package SHA-256: $packageSha256"
  107. Write-Output "Inspecting and deploying to $RemoteHost without supplying credentials..."
  108. Invoke-Native -FilePath 'scp' -ArgumentList @($archivePath, "$RemoteHost`:$remoteArchive")
  109. Invoke-Native -FilePath 'scp' -ArgumentList @((Join-Path $PSScriptRoot 'Invoke-RemoteInstall.ps1'), "$RemoteHost`:$remoteInstaller")
  110. $arguments = @(
  111. '-ArchivePath', (Quote-PowerShellLiteral $remoteArchive),
  112. '-ProjectPath', (Quote-PowerShellLiteral $RemoteProjectPath),
  113. '-InvocationId', (Quote-PowerShellLiteral $invocationId),
  114. '-ExpectedArchiveSha256', (Quote-PowerShellLiteral $packageSha256),
  115. '-ProductionSiteName', (Quote-PowerShellLiteral $ProductionSiteName),
  116. '-ProductionPoolName', (Quote-PowerShellLiteral $ProductionPoolName),
  117. '-ProductionPort', $ProductionPort,
  118. '-ProductionBaseUrl', (Quote-PowerShellLiteral $ProductionBaseUrl),
  119. '-TestSiteName', (Quote-PowerShellLiteral $TestSiteName),
  120. '-TestPoolName', (Quote-PowerShellLiteral $TestPoolName),
  121. '-TestPort', $TestPort,
  122. '-TestBaseUrl', (Quote-PowerShellLiteral $TestBaseUrl)
  123. )
  124. if ($RunTests) { $arguments += '-RunTests' }
  125. $remoteCommand = "& $(Quote-PowerShellLiteral $remoteInstaller) " + ($arguments -join ' ')
  126. $encodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($remoteCommand))
  127. Invoke-Native -FilePath 'ssh' -ArgumentList @($RemoteHost, "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encodedCommand")
  128. } finally {
  129. if (Test-Path -LiteralPath $localWork) {
  130. Remove-Item -LiteralPath $localWork -Recurse -Force
  131. }
  132. }

Powered by TurnKey Linux.