Git workflow, environments & CI/CD pipeline (#1)
CI / backend (push) Successful in 1m14s
CI / frontend (push) Successful in 28s
Security / secrets (push) Successful in 6s
Security / dependencies (push) Successful in 1m14s
Deploy Staging / deploy (push) Failing after 43s

This commit was merged in pull request #1.
This commit is contained in:
2026-07-01 11:44:34 +02:00
parent 8c2e52ad60
commit ae8e6b672e
16 changed files with 566 additions and 16 deletions
+45
View File
@@ -0,0 +1,45 @@
name: Deploy Production
# Production promotion. The APPROVAL GATE is the git tag: production only ever
# deploys a tagged release cut on main (see docs/WORKFLOW.md §5). Cutting the tag
# is the deliberate, auditable "approve to go live" action — and the tag doubles
# as the rollback target. workflow_dispatch adds a manual "Run workflow" button
# for re-deploys/rollbacks.
#
# STATUS: inactive until (a) the Linux production server exists and (b) a
# self-hosted Gitea runner is registered on it with labels [self-hosted, production].
# Tag pushes before then will queue harmlessly. This file is the wiring, ready to
# switch on — review the deploy step for your server before first use.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
ref:
description: 'Tag or commit to deploy (e.g. v1.2.0)'
required: true
jobs:
deploy:
runs-on: [self-hosted, production]
steps:
- uses: actions/checkout@v4
with:
# Deploy the exact tag that triggered the run (immutable), or the
# ref given to a manual dispatch.
ref: ${{ github.event.inputs.ref || github.ref_name }}
fetch-depth: 0
- name: Deploy release to production
run: |
echo "Deploying ${{ github.event.inputs.ref || github.ref_name }} to production"
# deploy/.env lives on the server (never in git). up.sh validates it,
# builds the Linux images, applies EF migrations on boot, and starts
# the stack behind the nginx reverse proxy.
./deploy/up.sh --proxy
- name: Smoke check
run: |
sleep 5
curl -fsS http://localhost/ >/dev/null && echo "Prod responding on :80" || \
{ echo "Smoke check failed"; exit 1; }
+42
View File
@@ -0,0 +1,42 @@
name: Deploy Staging
# Continuous deployment to the LOCAL staging stack. Fires when develop advances
# (i.e. after a PR is merged into develop). It re-verifies the code, then rebuilds
# and restarts the isolated staging stack on this machine.
#
# CRITICAL: staging lives on your Windows box (ports 18080/18081). A cloud or
# container runner CANNOT reach it, so this job MUST run on a self-hosted Gitea
# Actions runner registered ON that Windows machine with Docker access
# (labels: self-hosted, windows). Until that runner exists this job just waits
# in the queue (harmless, cancelable) — deploy staging manually meanwhile with:
# ./deploy/up.ps1 -Staging
on:
push:
branches: [develop]
workflow_dispatch: {} # also allow a manual "Run workflow" from the Gitea UI
jobs:
deploy:
runs-on: [self-hosted, windows]
steps:
- uses: actions/checkout@v4
# Re-run the gate on the exact merged code before it touches staging.
- name: Build + test (Release)
shell: powershell
run: |
dotnet build InboxIntel.sln -c Release --nologo
dotnet test InboxIntel.sln -c Release --no-build --nologo
- name: Redeploy staging stack
shell: powershell
run: |
docker compose -p inboxintel-staging `
--env-file deploy/.env.staging `
-f docker-compose.yml -f docker-compose.staging.yml `
up -d --build
docker compose -p inboxintel-staging ps
- name: Staging endpoints
shell: powershell
run: Write-Host "Staging up — Frontend http://localhost:18081 API http://localhost:18080/swagger"
+54
View File
@@ -0,0 +1,54 @@
name: Security
# Scans run alongside CI on every PR and on pushes to the long-lived branches.
# This is the DETECTIVE layer (backstop). The PREVENTIVE layer is the local
# pre-commit hook — this catches anything that slipped past it (e.g. --no-verify)
# and scans the full history, not just the staged diff.
on:
push:
branches: [main, develop]
pull_request:
jobs:
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so gitleaks scans every commit
- name: Secret scan (gitleaks)
# Run the binary directly — the container-mode runner has no Docker socket,
# so `docker run` isn't available inside a job.
run: |
GITLEAKS_VERSION=8.18.4
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tar.gz
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
/tmp/gitleaks detect --source=. --redact --verbose --exit-code=1
dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore InboxIntel.sln
- name: .NET vulnerable packages (fail on any)
# Match dotnet's own "has the following vulnerable packages" line rather
# than raw severity words, so package/project names can't false-positive.
run: |
dotnet list InboxIntel.sln package --vulnerable --include-transitive 2>&1 | tee vuln.txt
if grep -q "has the following vulnerable" vuln.txt; then
echo "::error::Vulnerable NuGet packages detected — see the table above."
exit 1
fi
echo "No vulnerable NuGet packages."
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: npm audit (production deps, fail on high/critical)
# Only production dependencies ship to users; dev-only toolchain advisories
# (Vite/PostCSS/etc.) shouldn't block a merge.
working-directory: frontend
run: npm audit --omit=dev --audit-level=high