Git workflow, environments & CI/CD pipeline #1

Merged
cesnimda merged 6 commits from feature/git-workflow into develop 2026-07-01 11:44:35 +02:00
5 changed files with 102 additions and 14 deletions
Showing only changes of commit 80fc813b61 - Show all commits
+17
View File
@@ -0,0 +1,17 @@
# Copy to deploy/.env.staging and fill in. Used by:
# docker compose -p inboxintel-staging --env-file deploy/.env.staging \
# -f docker-compose.yml -f docker-compose.staging.yml up
# (or ./deploy/up.ps1 -Staging). Kept separate from deploy/.env (production) so
# staging can never touch prod credentials, DB, or Google project.
POSTGRES_PASSWORD=change-me-staging
# Use a SEPARATE Google OAuth client for staging with redirect URI:
# http://localhost:18081/signin-google
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
AI_MODE=Disabled
FRONTEND_ORIGIN=http://localhost:18081
# Staging caps the initial mailbox sync so rehearsals are fast.
MAX_MESSAGES=2000
+1
View File
@@ -54,6 +54,7 @@ lpt[1-9].*
*.code-workspace
.env.*
!.env.example
!.env.staging.example
.next/
dist/
build/
+12 -3
View File
@@ -5,13 +5,22 @@
./deploy/down.ps1
./deploy/down.ps1 -Volumes
#>
param([switch]$Volumes)
param([switch]$Volumes, [switch]$Staging)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$envFile = Join-Path $PSScriptRoot '.env'
$composeArgs = @('compose', '--env-file', $envFile, 'down')
if ($Staging) {
$envFile = Join-Path $PSScriptRoot '.env.staging'
$composeFiles = @('-f', 'docker-compose.yml', '-f', 'docker-compose.staging.yml')
$project = @('-p', 'inboxintel-staging')
} else {
$envFile = Join-Path $PSScriptRoot '.env'
$composeFiles = @()
$project = @()
}
$composeArgs = @('compose') + $project + @('--env-file', $envFile) + $composeFiles + @('down')
if ($Volumes) { $composeArgs += '--volumes' }
Push-Location $root
+23 -6
View File
@@ -8,16 +8,29 @@
#>
param(
[switch]$Proxy,
[switch]$Foreground
[switch]$Foreground,
[switch]$Staging # production-shaped staging stack: separate env, ports, volumes
)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot # repo root (deploy/ is one level down)
$envFile = Join-Path $PSScriptRoot '.env'
# Staging vs production: pick the env file + compose overlay + isolated project name.
if ($Staging) {
$envFile = Join-Path $PSScriptRoot '.env.staging'
$composeFiles = @('-f', 'docker-compose.yml', '-f', 'docker-compose.staging.yml')
$project = @('-p', 'inboxintel-staging')
if (-not (Test-Path $envFile)) {
throw "Missing $envFile. Create it from .env.staging.example."
}
} else {
$envFile = Join-Path $PSScriptRoot '.env'
$composeFiles = @()
$project = @()
if (-not (Test-Path $envFile)) {
throw "Missing $envFile. Create it from .env.example with your real secrets."
}
}
# Fail fast if a required key is absent or blank.
$required = 'POSTGRES_PASSWORD','GOOGLE_CLIENT_ID','GOOGLE_CLIENT_SECRET'
@@ -28,19 +41,23 @@ Get-Content $envFile | ForEach-Object {
$missing = $required | Where-Object { [string]::IsNullOrWhiteSpace($envMap[$_]) }
if ($missing) { throw "deploy/.env is missing values for: $($missing -join ', ')" }
$composeArgs = @('compose', '--env-file', $envFile)
$composeArgs = @('compose') + $project + @('--env-file', $envFile) + $composeFiles
if ($Proxy) { $composeArgs += @('--profile', 'proxy') }
$composeArgs += @('up', '--build')
if (-not $Foreground) { $composeArgs += '-d' }
Push-Location $root
try {
Write-Host "Starting InboxIntel via docker compose (env: deploy/.env)..." -ForegroundColor Cyan
Write-Host "Starting InboxIntel via docker compose (env: $envFile)..." -ForegroundColor Cyan
& docker @composeArgs
if (-not $Foreground) {
& docker compose --env-file $envFile ps
& docker compose @project --env-file $envFile @composeFiles ps
if ($Staging) {
Write-Host "`n[STAGING] Frontend: http://localhost:18081 API/Swagger: http://localhost:18080/swagger" -ForegroundColor Green
} else {
Write-Host "`nFrontend: http://localhost:8081 API/Swagger: http://localhost:8080/swagger" -ForegroundColor Green
Write-Host "Logs: ./deploy/logs.ps1 Stop: ./deploy/down.ps1" -ForegroundColor DarkGray
}
Write-Host "Logs: ./deploy/logs.ps1 Stop: ./deploy/down.ps1$(if($Staging){' -Staging'})" -ForegroundColor DarkGray
}
}
finally { Pop-Location }
+44
View File
@@ -0,0 +1,44 @@
# Staging overlay for InboxIntel.
#
# The base docker-compose.yml IS the production definition (Linux containers,
# ASPNETCORE_ENVIRONMENT=Production). This overlay layers a *staging* variant on
# top of it so you can run a production-shaped stack locally on Windows WITHOUT
# clobbering a real production deployment's data, ports, or volumes.
#
# It differs from prod only in the ways staging is meant to differ:
# - the dev/test banner is on (App__DevMode=true)
# - the initial Gmail sync is capped so a big mailbox doesn't take forever
# - ports are shifted into the 18xxx range so staging can run alongside prod
# - a distinct project name gives it its own isolated pgdata + keys volumes
#
# Run it with the -p (project name) flag so volumes/networks are namespaced:
#
# docker compose -p inboxintel-staging \
# --env-file deploy/.env.staging \
# -f docker-compose.yml -f docker-compose.staging.yml up --build -d
#
# (deploy/up.ps1 -Staging / up.sh --staging wrap this for you.)
services:
postgres:
ports:
- "127.0.0.1:15432:5432"
api:
environment:
# Same Production runtime as prod (real config binding, real build), but
# flagged as a non-production instance so the UI shows the staging banner
# and the first sync is bounded.
App__DevMode: "true"
GmailSync__MaxMessages: ${MAX_MESSAGES:-2000}
Cors__Origins__0: ${FRONTEND_ORIGIN:-http://localhost:18081}
ports:
- "127.0.0.1:18080:8080"
frontend:
ports:
- "18081:80"
nginx:
ports:
- "18000:80"