Consolidated ASP Classic MVC framework from best components
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

325 řádky
9.7KB

  1. <#
  2. Deploy asp-territory to an existing IIS site, locally or over SSH.
  3. Remote mode:
  4. - Copies this script to the remote Windows host with scp
  5. - Executes it remotely via ssh in -RunRemoteCore mode
  6. - Preserves the remote site's current DB path unless -DbPath is passed
  7. - Can run standard migrations and an optional legacy migration script
  8. Local / remote core behavior:
  9. - Infers IIS site/app pool/work dir from the existing site when possible
  10. - Stops the site/app pool while deploying
  11. - Clones/pulls and hard-resets to origin/<Branch>
  12. - Points IIS at <WorkDir>\public
  13. - Reapplies the effective DB path in public\web.config
  14. - Grants IIS AppPool rights to the DB folder
  15. - Runs migrations
  16. - Restarts the site/app pool and smoke tests key routes
  17. #>
  18. param(
  19. [string]$Repo = 'git@onefortheroadgit.sytes.net:dcovington/asp-classic-unified-framework.git',
  20. [string]$Branch = 'main',
  21. [string]$SiteName = 'ttasp',
  22. [string]$AppPool = '',
  23. [string]$WorkDir = '',
  24. [string]$PublicDir = '',
  25. [string]$BaseUrl = '',
  26. [string]$DbPath = '',
  27. [switch]$RunMigrations = $true,
  28. [switch]$SkipLegacyIsBusinessMigration,
  29. [string]$LegacyMigrationScript = 'scripts\migrate_isbusiness_to_households.vbs',
  30. [switch]$UseRemoteSsh,
  31. [string]$RemoteTarget = '',
  32. [int]$RemotePort = 22,
  33. [string]$SshExe = 'ssh',
  34. [string]$ScpExe = 'scp',
  35. [switch]$RunRemoteCore
  36. )
  37. $ErrorActionPreference = 'Stop'
  38. function Ensure-Dir {
  39. param([string]$Path)
  40. if([string]::IsNullOrWhiteSpace($Path)){ return }
  41. if(!(Test-Path $Path)){
  42. New-Item -ItemType Directory -Force -Path $Path | Out-Null
  43. }
  44. }
  45. function Ensure-Command {
  46. param([string]$Name)
  47. if(!(Get-Command $Name -ErrorAction SilentlyContinue)){
  48. throw "$Name not found on PATH"
  49. }
  50. }
  51. function Get-DefaultRemoteTargetFromInfo {
  52. $infoPath = Join-Path $PSScriptRoot 'depolyinfo.txt'
  53. if(!(Test-Path $infoPath)){ return '' }
  54. $sshLine = Get-Content $infoPath | Where-Object { $_ -match '^\s*ssh\s+' } | Select-Object -First 1
  55. if([string]::IsNullOrWhiteSpace($sshLine)){ return '' }
  56. return ($sshLine -replace '^\s*ssh\s+', '').Trim()
  57. }
  58. function ConvertTo-PowerShellLiteral {
  59. param([AllowNull()][string]$Value)
  60. if($null -eq $Value){ return "''" }
  61. return "'" + ($Value -replace "'", "''") + "'"
  62. }
  63. function ConvertTo-CmdDoubleQuoted {
  64. param([AllowNull()][string]$Value)
  65. if($null -eq $Value){ return '""' }
  66. return '"' + ($Value -replace '"', '""') + '"'
  67. }
  68. function Get-DataSourceFromConfig {
  69. param([string]$ConfigPath)
  70. if(!(Test-Path $ConfigPath)){ return '' }
  71. $raw = Get-Content $ConfigPath -Raw
  72. $match = [regex]::Match($raw, 'Data Source=([^;]+);', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
  73. if($match.Success){
  74. return $match.Groups[1].Value.Trim()
  75. }
  76. return ''
  77. }
  78. function Set-DataSourceInConfig {
  79. param(
  80. [string]$ConfigPath,
  81. [string]$EffectiveDbPath
  82. )
  83. if(!(Test-Path $ConfigPath)){ return }
  84. $raw = Get-Content $ConfigPath -Raw
  85. $updated = [regex]::Replace(
  86. $raw,
  87. 'Data Source=[^;]*;',
  88. ('Data Source=' + $EffectiveDbPath + ';'),
  89. [System.Text.RegularExpressions.RegexOptions]::IgnoreCase
  90. )
  91. if($updated -ne $raw){
  92. Set-Content -Path $ConfigPath -Value $updated -Encoding UTF8
  93. Write-Host "Updated ConnectionString Data Source to $EffectiveDbPath"
  94. }
  95. }
  96. function Get-BaseUrlFromSite {
  97. param($Site)
  98. $httpBind = $Site.Bindings.Collection | Where-Object { $_.protocol -eq 'http' } | Select-Object -First 1
  99. if($httpBind){
  100. $parts = $httpBind.bindingInformation.Split(':')
  101. $port = $parts[1]
  102. if([string]::IsNullOrWhiteSpace($port)){ $port = '80' }
  103. return ('http://127.0.0.1:' + $port)
  104. }
  105. return 'http://127.0.0.1'
  106. }
  107. function Invoke-DeployCore {
  108. Ensure-Command git
  109. Import-Module WebAdministration
  110. $site = Get-Website -Name $SiteName
  111. if(!$site){ throw "IIS site not found: $SiteName" }
  112. if([string]::IsNullOrWhiteSpace($AppPool)){
  113. $AppPool = $site.applicationPool
  114. }
  115. if([string]::IsNullOrWhiteSpace($PublicDir)){
  116. $PublicDir = $site.physicalPath
  117. }
  118. if([string]::IsNullOrWhiteSpace($WorkDir)){
  119. $pd = [Environment]::ExpandEnvironmentVariables($PublicDir)
  120. $pd = $pd.Trim().Trim('"')
  121. $pd = $pd.TrimEnd('\','/')
  122. if((Split-Path $pd -Leaf).ToLower() -eq 'public'){
  123. $WorkDir = Split-Path $pd -Parent
  124. } else {
  125. $WorkDir = $pd
  126. }
  127. }
  128. if([string]::IsNullOrWhiteSpace($BaseUrl)){
  129. $BaseUrl = Get-BaseUrlFromSite -Site $site
  130. }
  131. $currentPublicDir = $PublicDir
  132. $currentConfigPath = Join-Path $currentPublicDir 'web.config'
  133. $effectiveDbPath = $DbPath
  134. if([string]::IsNullOrWhiteSpace($effectiveDbPath)){
  135. $effectiveDbPath = Get-DataSourceFromConfig -ConfigPath $currentConfigPath
  136. }
  137. if([string]::IsNullOrWhiteSpace($effectiveDbPath)){
  138. throw 'No database path was provided and no existing Data Source could be read from the current web.config'
  139. }
  140. Write-Host "Stopping IIS site $SiteName and app pool $AppPool"
  141. try { Stop-Website -Name $SiteName } catch { }
  142. try { Stop-WebAppPool -Name $AppPool } catch { }
  143. Ensure-Dir (Split-Path $WorkDir -Parent)
  144. if((Test-Path $WorkDir) -and !(Test-Path (Join-Path $WorkDir '.git'))){
  145. $bak = ($WorkDir.TrimEnd('\') + '_pre_git_' + (Get-Date -Format 'yyyyMMdd_HHmmss'))
  146. Write-Host "Existing non-git folder detected. Moving to $bak"
  147. Move-Item -Force $WorkDir $bak
  148. }
  149. if(!(Test-Path $WorkDir)){
  150. Write-Host "Cloning $Repo -> $WorkDir"
  151. git clone $Repo $WorkDir
  152. }
  153. Push-Location $WorkDir
  154. try {
  155. Write-Host "Updating to origin/$Branch"
  156. git fetch origin
  157. git checkout $Branch
  158. & git reset --hard ("origin/" + $Branch)
  159. } finally {
  160. Pop-Location
  161. }
  162. if((Split-Path $WorkDir -Leaf).ToLower() -eq 'public'){
  163. $WorkDir = Split-Path $WorkDir -Parent
  164. }
  165. $PublicDir = Join-Path $WorkDir 'public'
  166. $cfg = Join-Path $PublicDir 'web.config'
  167. Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name physicalPath -Value $PublicDir
  168. Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name applicationPool -Value $AppPool
  169. Set-ItemProperty ('IIS:\AppPools\' + $AppPool) -Name processModel.identityType -Value NetworkService
  170. Set-DataSourceInConfig -ConfigPath $cfg -EffectiveDbPath $effectiveDbPath
  171. $dbFolder = Split-Path $effectiveDbPath -Parent
  172. if(!(Test-Path $dbFolder)){
  173. Ensure-Dir $dbFolder
  174. }
  175. icacls $dbFolder /grant ("IIS AppPool\" + $AppPool + ":(OI)(CI)(M)") /T | Out-Null
  176. Push-Location $WorkDir
  177. try {
  178. if($RunMigrations){
  179. Write-Host 'Running standard migrations'
  180. cscript //nologo scripts\runMigrations.vbs up
  181. }
  182. if(-not $SkipLegacyIsBusinessMigration){
  183. $legacyPath = Join-Path $WorkDir $LegacyMigrationScript
  184. if(!(Test-Path $legacyPath)){
  185. throw "Legacy migration script not found: $legacyPath"
  186. }
  187. Write-Host 'Running legacy IsBusiness migration'
  188. cscript //nologo $legacyPath $effectiveDbPath
  189. }
  190. } finally {
  191. Pop-Location
  192. }
  193. if((Get-WebAppPoolState -Name $AppPool).Value -eq 'Started'){
  194. Restart-WebAppPool -Name $AppPool
  195. } else {
  196. Start-WebAppPool -Name $AppPool
  197. }
  198. Start-Website $SiteName
  199. Start-Sleep -Seconds 1
  200. $paths = @('/','/territories','/households','/householder-names')
  201. foreach($path in $paths){
  202. $url = $BaseUrl + $path
  203. $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
  204. Write-Host ("OK " + $path + ' -> ' + $response.StatusCode)
  205. }
  206. Write-Host 'Deploy complete.'
  207. }
  208. if($UseRemoteSsh -and !$RunRemoteCore -and [string]::IsNullOrWhiteSpace($RemoteTarget)){
  209. $RemoteTarget = Get-DefaultRemoteTargetFromInfo
  210. }
  211. if($UseRemoteSsh -and !$RunRemoteCore -and -not [string]::IsNullOrWhiteSpace($RemoteTarget)){
  212. Ensure-Command $SshExe
  213. Ensure-Command $ScpExe
  214. $remoteScriptPath = 'C:\Windows\Temp\deploy-test-territory-git.ps1'
  215. $scpDestination = "${RemoteTarget}:C:/Windows/Temp/deploy-test-territory-git.ps1"
  216. Write-Host "Copying deploy script to $RemoteTarget"
  217. & $ScpExe -P $RemotePort $PSCommandPath $scpDestination
  218. if($LASTEXITCODE -ne 0){ throw 'scp failed' }
  219. $remoteCommand = New-Object System.Collections.Generic.List[string]
  220. @(
  221. 'powershell',
  222. '-NoProfile',
  223. '-ExecutionPolicy', 'Bypass',
  224. '-File', (ConvertTo-CmdDoubleQuoted $remoteScriptPath),
  225. '-RunRemoteCore',
  226. '-Repo', (ConvertTo-CmdDoubleQuoted $Repo),
  227. '-Branch', (ConvertTo-CmdDoubleQuoted $Branch),
  228. '-SiteName', (ConvertTo-CmdDoubleQuoted $SiteName)
  229. ) | ForEach-Object { [void]$remoteCommand.Add($_) }
  230. if(-not [string]::IsNullOrWhiteSpace($AppPool)){
  231. [void]$remoteCommand.Add('-AppPool')
  232. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $AppPool))
  233. }
  234. if(-not [string]::IsNullOrWhiteSpace($WorkDir)){
  235. [void]$remoteCommand.Add('-WorkDir')
  236. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $WorkDir))
  237. }
  238. if(-not [string]::IsNullOrWhiteSpace($PublicDir)){
  239. [void]$remoteCommand.Add('-PublicDir')
  240. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $PublicDir))
  241. }
  242. if(-not [string]::IsNullOrWhiteSpace($BaseUrl)){
  243. [void]$remoteCommand.Add('-BaseUrl')
  244. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $BaseUrl))
  245. }
  246. if(-not [string]::IsNullOrWhiteSpace($DbPath)){
  247. [void]$remoteCommand.Add('-DbPath')
  248. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $DbPath))
  249. }
  250. if(-not [string]::IsNullOrWhiteSpace($LegacyMigrationScript)){
  251. [void]$remoteCommand.Add('-LegacyMigrationScript')
  252. [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $LegacyMigrationScript))
  253. }
  254. if($RunMigrations){ $remoteCommand += '-RunMigrations' }
  255. if($SkipLegacyIsBusinessMigration){ $remoteCommand += '-SkipLegacyIsBusinessMigration' }
  256. Write-Host "Executing remote deploy on $RemoteTarget"
  257. & $SshExe -p $RemotePort $RemoteTarget ($remoteCommand -join ' ')
  258. if($LASTEXITCODE -ne 0){ throw 'remote deploy failed' }
  259. exit 0
  260. }
  261. Invoke-DeployCore

Powered by TurnKey Linux.