#Requires -Version 5.1 Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' param( [switch]$Yes ) $RootDir = Split-Path -Parent $MyInvocation.MyCommand.Path $EnvFile = Join-Path $RootDir ".env" function Confirm-Run { param( [string]$Label, [string]$ScriptPath ) if ($Yes) { Write-Host "[x] $Label not done yet. Running $ScriptPath (-Yes enabled)..." & $ScriptPath return } $answer = Read-Host "[x] $Label not done yet. Run now? [y/N]" if ($answer -match '^[Yy]$') { & $ScriptPath } } function Get-EnvConfig { if (-not (Test-Path $EnvFile)) { throw "GCR/.env not found. Copy GCR/.env.example to GCR/.env first." } $cfg = @{} foreach ($line in Get-Content $EnvFile) { if ($line -match '^\s*$' -or $line -match '^\s*#') { continue } if ($line -match '^([^=]+)=(.*)$') { $cfg[$Matches[1].Trim()] = $Matches[2].Trim() } } foreach ($key in @('GCP_PROJECT_ID', 'GCP_REGION', 'GCP_REPOSITORY', 'SERVICE_NAME')) { if (-not $cfg[$key]) { throw "$key is not set in GCR/.env" } } return $cfg } function Test-GcloudInstalled { return [bool](Get-Command gcloud -ErrorAction SilentlyContinue) } function Test-Login { param([hashtable]$Cfg) $activeAccount = (gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>$null | Select-Object -First 1) $currentProject = (gcloud config get-value project 2>$null) $currentRegion = (gcloud config get-value run/region 2>$null) $dockerCfg = if ($env:DOCKER_CONFIG) { Join-Path $env:DOCKER_CONFIG "config.json" } else { Join-Path $HOME ".docker\config.json" } $dockerOk = $false if (Test-Path $dockerCfg) { $dockerOk = (Select-String -Path $dockerCfg -Pattern "\"$($Cfg['GCP_REGION'])-docker.pkg.dev\"" -SimpleMatch -Quiet) } return (-not [string]::IsNullOrWhiteSpace($activeAccount)) -and ($currentProject.Trim() -eq $Cfg['GCP_PROJECT_ID']) -and ($currentRegion.Trim() -eq $Cfg['GCP_REGION']) -and $dockerOk } function Test-ProjectSetup { param([hashtable]$Cfg) $billingEnabled = (gcloud billing projects describe $Cfg['GCP_PROJECT_ID'] --format="value(billingEnabled)" 2>$null) if ($billingEnabled -ne 'True') { return $false } try { gcloud artifacts repositories describe $Cfg['GCP_REPOSITORY'] --location=$Cfg['GCP_REGION'] --project=$Cfg['GCP_PROJECT_ID'] 2>$null | Out-Null } catch { return $false } foreach ($api in @('run.googleapis.com', 'artifactregistry.googleapis.com', 'secretmanager.googleapis.com', 'cloudresourcemanager.googleapis.com')) { $enabled = gcloud services list --enabled --project=$Cfg['GCP_PROJECT_ID'] --format="value(config.name)" 2>$null | Select-String -Pattern "^$([regex]::Escape($api))$" if (-not $enabled) { return $false } } return $true } function Test-SecretsSetup { param([hashtable]$Cfg) try { gcloud secrets describe mongodb-connection-string --project=$Cfg['GCP_PROJECT_ID'] 2>$null | Out-Null } catch { return $false } $serviceAccount = "serviceAccount:$($Cfg['GCP_PROJECT_ID'])@appspot.gserviceaccount.com" $binding = gcloud secrets get-iam-policy mongodb-connection-string ` --project=$Cfg['GCP_PROJECT_ID'] ` --flatten="bindings[].members" ` --filter="bindings.role=roles/secretmanager.secretAccessor AND bindings.members=$serviceAccount" ` --format="value(bindings.members)" 2>$null return ($binding -match [regex]::Escape($serviceAccount)) } function Test-DeployDone { param([hashtable]$Cfg) try { gcloud run services describe $Cfg['SERVICE_NAME'] --region=$Cfg['GCP_REGION'] --project=$Cfg['GCP_PROJECT_ID'] 2>$null | Out-Null return $true } catch { return $false } } function Write-Done { param([string]$Text) Write-Host "[v] $Text" } Write-Host "================================================================" Write-Host " Htmx deployment flow runner (Windows)" Write-Host "================================================================" if (-not (Test-Path $EnvFile)) { Write-Host "[x] Step 0: GCR/.env is missing" Write-Host " Copy GCR/.env.example to GCR/.env and fill required values." exit 1 } Write-Done "Step 0: .env exists" $cfg = Get-EnvConfig if (Test-GcloudInstalled) { Write-Done "Step 1: gcloud installed" } else { Confirm-Run "Step 1: gcloud install" (Join-Path $RootDir "scripts\00-install-gcloud.ps1") } if (Test-Login $cfg) { Write-Done "Step 2: login + docker auth configured" } else { Confirm-Run "Step 2: login" (Join-Path $RootDir "scripts\01-login.ps1") } if (Test-ProjectSetup $cfg) { Write-Done "Step 3: project setup complete" } else { Confirm-Run "Step 3: project setup" (Join-Path $RootDir "scripts\02-setup-project.ps1") } if (Test-SecretsSetup $cfg) { Write-Done "Step 4: secrets created and access granted" } else { Confirm-Run "Step 4: secrets setup" (Join-Path $RootDir "scripts\03-create-secrets.ps1") } if (Test-DeployDone $cfg) { Write-Done "Step 5: service is already deployed" } else { Confirm-Run "Step 5: deploy" (Join-Path $RootDir "scripts\04-deploy.ps1") } Write-Host "" Write-Host "================================================================" Write-Host " Final verification" Write-Host "================================================================" if (Test-GcloudInstalled) { Write-Done "Step 1" } else { Write-Host "[x] Step 1" } if (Test-Login $cfg) { Write-Done "Step 2" } else { Write-Host "[x] Step 2" } if (Test-ProjectSetup $cfg) { Write-Done "Step 3" } else { Write-Host "[x] Step 3" } if (Test-SecretsSetup $cfg) { Write-Done "Step 4" } else { Write-Host "[x] Step 4" } if (Test-DeployDone $cfg) { Write-Done "Step 5" } else { Write-Host "[x] Step 5" } Write-Host "" Write-Host "Tip: run .\GCR\run-all.ps1 -Yes to auto-run missing steps without prompts."