ci: add secret/vuln scanning + staging & production deploy pipelines
security.yml: gitleaks secret scan + NuGet/npm vulnerability gate on PRs and pushes to main/develop (detective backstop to the pre-commit hook). deploy-staging.yml: on merge to develop, re-verify then rebuild the isolated local staging stack (needs a self-hosted Windows runner). deploy-prod.yml: tag-gated production promotion (the tag is the approval), ready to activate once the Linux server + its runner exist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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; }
|
||||||
@@ -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"
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
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: |
|
||||||
|
docker run --rm -v "$PWD:/repo" zricethezav/gitleaks:latest \
|
||||||
|
detect --source=/repo --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)
|
||||||
|
run: |
|
||||||
|
dotnet list InboxIntel.sln package --vulnerable --include-transitive 2>&1 | tee vuln.txt
|
||||||
|
if grep -qiE 'Critical|High|Moderate|Low' 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 (fail on high/critical)
|
||||||
|
working-directory: frontend
|
||||||
|
run: npm audit --audit-level=high
|
||||||
@@ -6,6 +6,9 @@ All notable changes to InboxIntel are documented here. Format follows
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- CI/CD pipeline (`.gitea/workflows/`): `security` (gitleaks secret scan + NuGet/npm
|
||||||
|
vulnerability gate), `deploy-staging` (auto-redeploy local staging on `develop`),
|
||||||
|
`deploy-prod` (tag-gated production promotion, inactive until the server exists).
|
||||||
- Formal Git workflow & environment strategy (`docs/WORKFLOW.md`).
|
- Formal Git workflow & environment strategy (`docs/WORKFLOW.md`).
|
||||||
- Staging environment overlay (`docker-compose.staging.yml`) — production-shaped
|
- Staging environment overlay (`docker-compose.staging.yml`) — production-shaped
|
||||||
Linux containers on Windows, isolated ports/volumes.
|
Linux containers on Windows, isolated ports/volumes.
|
||||||
|
|||||||
Reference in New Issue
Block a user