No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

210 líneas
7.7KB

  1. <#
  2. Deploys the Purple Envelope Orders Site to the production IIS server.
  3. Flow:
  4. 1. Build a release locally (scripts\build-release.ps1) - a clean file tree with the
  5. production web.config swapped in, and dev-only files excluded via .gitattributes.
  6. 2. Zip it and scp the zip to the remote host.
  7. 3. scp scripts\deploy-iis-remote-apply.ps1 to the remote host and run it over ssh - it
  8. wipes the deploy directory, extracts the new release, points IIS at it, runs
  9. migrations, and restarts the site/app pool. The database and error log live outside
  10. the deploy directory, so the wipe never touches them.
  11. 4. Smoke-test a few routes over HTTPS from this machine.
  12. Usage:
  13. powershell -File scripts\deploy-iis.ps1
  14. powershell -File scripts\deploy-iis.ps1 -Ref master
  15. Remote target defaults to the "ssh user@host" line in scripts\deployinfo.txt (gitignored,
  16. not committed - create it locally, e.g.: ssh daniel_admin@kci-uluto-web).
  17. #>
  18. param(
  19. [string]$Ref = 'HEAD',
  20. [string]$RemoteTarget = '',
  21. [int]$RemotePort = 22,
  22. [string]$RemoteDir = 'C:\inetpub\wwwroot\Purple_Envelop_Order_Site',
  23. [string]$SiteName = 'PurpleEnvelopes',
  24. [string]$AppPool = 'PurpleEnvelopes',
  25. [string]$AdminSiteName = '',
  26. [string]$AdminAppPool = '',
  27. [string]$DbPath = 'C:\inetpub\data\webdata.accdb',
  28. [string]$BaseUrl = 'https://pe.kentcommunications.com/',
  29. [string]$AdminBaseUrl = '',
  30. [switch]$RunMigrations = $true,
  31. [string]$SshExe = 'ssh',
  32. [string]$ScpExe = 'scp'
  33. )
  34. $ErrorActionPreference = 'Stop'
  35. $repoRoot = Split-Path $PSScriptRoot -Parent
  36. function Ensure-Command {
  37. param([string]$Name)
  38. if(!(Get-Command $Name -ErrorAction SilentlyContinue)){
  39. throw "$Name not found on PATH"
  40. }
  41. }
  42. # deployinfo.txt (gitignored, local-only) holds one "key value" pair per line, e.g.:
  43. # ssh daniel_admin@kci-uluro-web
  44. # AdminSiteName PurpleEnvelopes-Admin
  45. # AdminAppPool PurpleEnvelopes-Admin
  46. function Get-DeployInfoValue {
  47. param([string]$Key)
  48. $infoPath = Join-Path $PSScriptRoot 'deployinfo.txt'
  49. if(!(Test-Path $infoPath)){ return '' }
  50. $pattern = '^\s*' + [regex]::Escape($Key) + '\s+'
  51. $line = Get-Content $infoPath | Where-Object { $_ -match $pattern } | Select-Object -First 1
  52. if([string]::IsNullOrWhiteSpace($line)){ return '' }
  53. return ($line -replace $pattern, '').Trim()
  54. }
  55. if([string]::IsNullOrWhiteSpace($RemoteTarget)){
  56. $RemoteTarget = Get-DeployInfoValue 'ssh'
  57. }
  58. if([string]::IsNullOrWhiteSpace($RemoteTarget)){
  59. throw 'No -RemoteTarget given and scripts\deployinfo.txt not found. Create scripts\deployinfo.txt with a line like: ssh user@host'
  60. }
  61. # The admin site (public-admin) is built and extracted into RemoteDir on every deploy
  62. # regardless of whether it's stopped first - if its IIS site/app pool name isn't known here,
  63. # deploy-iis-remote-apply.ps1 can't stop it before wiping RemoteDir, and it'll keep files
  64. # open under the same directory the wipe is trying to clear. Default from deployinfo.txt so
  65. # every deploy stops it without needing to pass -AdminSiteName/-AdminAppPool by hand.
  66. if([string]::IsNullOrWhiteSpace($AdminSiteName)){
  67. $AdminSiteName = Get-DeployInfoValue 'AdminSiteName'
  68. }
  69. if([string]::IsNullOrWhiteSpace($AdminAppPool)){
  70. $AdminAppPool = Get-DeployInfoValue 'AdminAppPool'
  71. }
  72. Ensure-Command $SshExe
  73. Ensure-Command $ScpExe
  74. Ensure-Command git
  75. # --- 1. Build ---
  76. $outDir = Join-Path $repoRoot ('dist\release_' + (Get-Date -Format 'yyyyMMdd_HHmmss'))
  77. & (Join-Path $PSScriptRoot 'build-release.ps1') -Ref $Ref -OutDir $outDir
  78. if($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne $null){ throw 'build-release.ps1 failed' }
  79. # --- 2. Zip and ship it ---
  80. $zipName = 'release_' + (Get-Date -Format 'yyyyMMdd_HHmmss') + '.zip'
  81. $localZip = Join-Path (Split-Path $outDir -Parent) $zipName
  82. Compress-Archive -Path (Join-Path $outDir '*') -DestinationPath $localZip -Force
  83. $remoteZip = 'C:\Windows\Temp\' + $zipName
  84. $remoteApplyScript = Join-Path $PSScriptRoot 'deploy-iis-remote-apply.ps1'
  85. $remoteApplyDest = 'C:\Windows\Temp\deploy-iis-remote-apply.ps1'
  86. # Force a fall-through to password auth: if pubkey auth isn't set up (or an agent offers a
  87. # key the server doesn't accept), plain ssh/scp can otherwise fail outright with
  88. # "Permission denied (publickey)" instead of ever prompting for a password.
  89. $AuthOpts = @(
  90. '-o', 'PreferredAuthentications=publickey,keyboard-interactive,password',
  91. '-o', 'NumberOfPasswordPrompts=3'
  92. )
  93. Write-Host "Copying release to $RemoteTarget"
  94. & $ScpExe -P $RemotePort @AuthOpts $localZip "${RemoteTarget}:$remoteZip"
  95. if($LASTEXITCODE -ne 0){ throw 'scp of release zip failed - see scp output above for the actual reason' }
  96. & $ScpExe -P $RemotePort @AuthOpts $remoteApplyScript "${RemoteTarget}:$remoteApplyDest"
  97. if($LASTEXITCODE -ne 0){ throw 'scp of remote-apply script failed - see scp output above for the actual reason' }
  98. # --- 3. Apply on the remote host ---
  99. $remoteCommandParts = @(
  100. 'powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass',
  101. '-File', ('"' + $remoteApplyDest + '"'),
  102. '-ZipPath', ('"' + $remoteZip + '"'),
  103. '-RemoteDir', ('"' + $RemoteDir + '"'),
  104. '-SiteName', ('"' + $SiteName + '"'),
  105. '-AppPool', ('"' + $AppPool + '"'),
  106. '-DbPath', ('"' + $DbPath + '"')
  107. )
  108. if(![string]::IsNullOrWhiteSpace($AdminSiteName)){
  109. $remoteCommandParts += @('-AdminSiteName', ('"' + $AdminSiteName + '"'))
  110. }
  111. if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
  112. $remoteCommandParts += @('-AdminAppPool', ('"' + $AdminAppPool + '"'))
  113. }
  114. if($RunMigrations){ $remoteCommandParts += '-RunMigrations' }
  115. Write-Host "Applying release on $RemoteTarget"
  116. & $SshExe -p $RemotePort @AuthOpts $RemoteTarget ($remoteCommandParts -join ' ')
  117. if($LASTEXITCODE -ne 0){ throw 'remote apply failed - see ssh output above for the actual reason' }
  118. # --- 4. Local cleanup ---
  119. Remove-Item $localZip -ErrorAction SilentlyContinue
  120. Remove-Item -Recurse -Force $outDir -ErrorAction SilentlyContinue
  121. # --- 5. Smoke test ---
  122. Write-Host 'Smoke testing...'
  123. # /404 is the app's own not-found route - a 404 there is correct, not a failure.
  124. $checks = @(
  125. @{ Path = '/'; Expect = 200 },
  126. @{ Path = '/request-order'; Expect = 200 },
  127. @{ Path = '/404'; Expect = 404 }
  128. )
  129. $failed = $false
  130. foreach($check in $checks){
  131. $url = $BaseUrl.TrimEnd('/') + $check.Path
  132. try {
  133. $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
  134. $status = [int]$response.StatusCode
  135. } catch {
  136. if($_.Exception.Response){
  137. $status = [int]$_.Exception.Response.StatusCode
  138. } else {
  139. Write-Host ("FAIL " + $check.Path + ' -> request failed: ' + $_.Exception.Message)
  140. $failed = $true
  141. continue
  142. }
  143. }
  144. if($status -eq $check.Expect){
  145. Write-Host ("OK " + $check.Path + ' -> ' + $status)
  146. } else {
  147. Write-Host ("FAIL " + $check.Path + ' -> ' + $status + ' (expected ' + $check.Expect + ')')
  148. $failed = $true
  149. }
  150. }
  151. if($failed){
  152. throw 'Smoke test failed - see above'
  153. }
  154. # --- 6. Admin site smoke test ---
  155. if(![string]::IsNullOrWhiteSpace($AdminBaseUrl)){
  156. Write-Host 'Smoke testing admin site...'
  157. $adminChecks = @(
  158. @{ Path = '/'; Expect = 200 }
  159. )
  160. foreach($check in $adminChecks){
  161. $url = $AdminBaseUrl.TrimEnd('/') + $check.Path
  162. try {
  163. $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
  164. $status = [int]$response.StatusCode
  165. } catch {
  166. if($_.Exception.Response){
  167. $status = [int]$_.Exception.Response.StatusCode
  168. } else {
  169. Write-Host ("FAIL (admin) " + $check.Path + ' -> request failed: ' + $_.Exception.Message)
  170. $failed = $true
  171. continue
  172. }
  173. }
  174. if($status -eq $check.Expect){
  175. Write-Host ("OK (admin) " + $check.Path + ' -> ' + $status)
  176. } else {
  177. Write-Host ("FAIL (admin) " + $check.Path + ' -> ' + $status + ' (expected ' + $check.Expect + ')')
  178. $failed = $true
  179. }
  180. }
  181. if($failed){
  182. throw 'Admin smoke test failed - see above'
  183. }
  184. }
  185. Write-Host 'Deploy complete.'

Powered by TurnKey Linux.