From 61eac6c6f8c36c0fa3eb71289f158e6bb41035c5 Mon Sep 17 00:00:00 2001 From: rowell_m_soriano Date: Thu, 30 Jul 2026 11:56:12 +0800 Subject: [PATCH] docker composer --- .gitea/workflows/deploy.yml | 156 ++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..1af687d --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,156 @@ +name: Build and Deploy LSFE + +on: + push: + branches: [ main ] + +jobs: + build-and-deploy: + runs-on: windows + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Clean previous publish output + shell: pwsh + run: | + Remove-Item -Recurse -Force "C:\ci-output\webapi" -ErrorAction SilentlyContinue + + - name: Publish WebApi + shell: pwsh + run: dotnet publish .\LSFE.API\LSFE.API.csproj -c Release -o C:\ci-output\webapi + + # ---- Generate production config from Gitea Actions secrets (never committed to git) ---- + - name: Write production appsettings - WebApi + shell: pwsh + env: + GROQ_KEY: ${{ secrets.LLI_SFE_PROD_GROQ_API_KEY }} + JWT_SECRET: ${{ secrets.LLI_SFE_PROD_JWT_SECRET }} + DB_CONN: ${{ secrets.LLI_SFE_PROD_DB_CONNECTION }} + run: | + $config = @{ + Groq = @{ + ApiKey = $env:GROQ_KEY + ApiUrl = "https://api.groq.com/openai/v1/chat/completions" + Model = "llama-3.1-8b-instant" + } + JWT = @{ + ValidAudience = "https://lloydwebapi.lloydlab.com:2021" + ValidIssuer = "https://lloydwebapi.lloydlab.com:2021" + Secret = $env:JWT_SECRET + } + ConnectionStrings = @{ + DefaultConnection = $env:DB_CONN + } + } + + $json = $config | ConvertTo-Json -Depth 5 + $json | Out-File -FilePath "C:\ci-output\webapi\appsettings.Production.json" -Encoding utf8 + Write-Host "Wrote appsettings.Production.json to webapi output (secret values not echoed)" + exit 0 + + # ---- Backup current live deployment before touching anything ---- + # NOTE: this backs up Content too (harmless - it's just a point-in-time copy + # and is never used to restore Content, see rollback step below). + - name: Backup current live files + shell: pwsh + run: | + $stamp = Get-Date -Format "yyyyMMdd-HHmmss" + New-Item -ItemType Directory -Force -Path "C:\backups\$stamp" | Out-Null + + if (Test-Path "C:\inetpub\LSFE-api") { + robocopy "C:\inetpub\LSFE-api" "C:\backups\$stamp\webapi" /MIR /R:2 /W:3 /XD Content | Out-Null + } + + $stamp | Out-File -FilePath "C:\backups\latest.txt" -Encoding ascii -NoNewline + + # Keep only the last 5 backups to avoid filling the disk + $all = Get-ChildItem "C:\backups" -Directory | Sort-Object Name -Descending + if ($all.Count -gt 5) { + $all | Select-Object -Skip 5 | Remove-Item -Recurse -Force + } + + Write-Host "Backed up current deployment to C:\backups\$stamp (Content excluded - never backed up or restored)" + exit 0 + + - name: Stop app pools + shell: pwsh + run: | + Import-Module WebAdministration + Stop-WebAppPool -Name "LSFE-Api" -ErrorAction SilentlyContinue + Start-Sleep -Seconds 3 + + - name: Deploy WebApi files + id: deploy_api + shell: pwsh + run: | + # Deploy everything EXCEPT the Content folder (and its subfolders, Images/Uploads). + # /XD Content excludes the directory by NAME, which robocopy matches reliably + # against the source tree it walks, at any depth - so wwwroot\Content and + # everything under it (Images, Uploads) is never copied over or deleted. + # This prevents /MIR from purging/replacing the live C:\inetpub\LSFE-api\wwwroot\Content folder. + robocopy "C:\ci-output\webapi" "C:\inetpub\LSFE-api" /MIR /R:3 /W:5 /XD Content + $rc = $LASTEXITCODE + Write-Host "ROBOCOPY EXIT CODE: $rc" + if ($rc -ge 8) { + throw "robocopy failed for WebApi with exit code $rc" + } + exit 0 + + - name: Start app pools + shell: pwsh + run: | + Import-Module WebAdministration + Start-WebAppPool -Name "LSFE-Api" + + - name: Verify app pools are running + shell: pwsh + run: | + Start-Sleep -Seconds 3 + Import-Module WebAdministration + $api = Get-WebAppPoolState -Name "LSFE-Api" + Write-Host "LSFE-Api: $($api.Value)" + if ($api.Value -ne "Started") { + throw "One or more app pools failed to start" + } + + # ---- Rollback path: only runs if any prior step in this job failed ---- + - name: ROLLBACK - restore previous backup + if: failure() + shell: pwsh + run: | + $stamp = (Get-Content "C:\backups\latest.txt" -Raw).Trim() + $backupPath = "C:\backups\$stamp" + Write-Host "Deployment failed - rolling back to backup: $backupPath" + + Import-Module WebAdministration + Stop-WebAppPool -Name "LSFE-Api" -ErrorAction SilentlyContinue + Start-Sleep -Seconds 3 + + if (Test-Path "$backupPath\webapi") { + # /XD Content here is critical: the backup was taken BEFORE this deploy + # attempt, so it holds a stale snapshot of Content. Without this exclusion, + # rolling back would blow away any files/uploads written to Content since + # the backup was made. Content must never be touched by CI/CD, including rollback. + robocopy "$backupPath\webapi" "C:\inetpub\LSFE-api" /MIR /R:3 /W:5 /XD Content | Out-Null + } + + Start-WebAppPool -Name "LSFE-Api" + + Write-Host "Rollback complete. Restored from $backupPath (Content left untouched)" + exit 0 + + - name: ROLLBACK - verify pools after restore + if: failure() + shell: pwsh + run: | + Start-Sleep -Seconds 3 + Import-Module WebAdministration + $api = Get-WebAppPoolState -Name "LSFE-Api" + + Write-Host "After rollback - LSFE-Api: $($api.Value)" + + if ($api.Value -ne "Started") { + Write-Host "WARNING: app pool still not running after rollback. Manual intervention needed." + } \ No newline at end of file