Skip to content

fix(ci): prevent secret exposure to fork PRs in neon-integration-pg-m… #2

fix(ci): prevent secret exposure to fork PRs in neon-integration-pg-m…

fix(ci): prevent secret exposure to fork PRs in neon-integration-pg-m… #2

# NixOS Runner — Local Test Workflow

Check failure on line 1 in .github/workflows/nixos-local-test.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/nixos-local-test.yml

Invalid workflow file

(Line: 57, Col: 10): Unrecognized named-value: 'matrix'. Located at position 1 within expression: matrix.postgres_version
#
# Runs all four test suites (health checks, managed DB validation, migration
# validation, PR audit) on the self-hosted NixOS runner against the PG
# instances your secrets point at (Neon / DigitalOcean / etc.).
#
# Triggers:
# - pull_request [opened, synchronize] — runs the full matrix on PG15-18
# in parallel against the secrets for each version. Fork PRs do NOT
# receive secrets (by design — secrets stay out of untrusted PR code).
# - workflow_dispatch — pick a single PG version + cloud_provider label
# for an ad-hoc run.
#
# The NixOS runner already has psql, uv, and network access to the test PG
# instances. No Docker or local PG install needed.
#
# The cloud auth steps (AWS/GCP/Azure CLI) in managed-db-validate.yml are
# NOT replicated here — they require real cloud credentials and live cloud
# DBs, and remain manual-dispatch only in CI.
name: NixOS Local Test Workflow
on:
workflow_dispatch:
inputs:
postgres_version:
description: 'PostgreSQL major version to test against'
required: true
type: choice
options:
- '15'
- '16'
- '17'
- '18'
default: '16'
cloud_provider:
description: 'Cloud provider label (for managed DB test output)'
required: false
type: choice
options:
- direct
- aws
- gcp
- azure
default: 'direct'
pull_request:
types: [opened, synchronize]
paths:
- pgFirstAid.sql
- view_pgFirstAid.sql
- view_pgFirstAid_managed.sql
- .github/workflows/**
- testing/local-workflows/**
- .github/workflows/nixos-local-test.yml
concurrency:
group: nixos-local-test-${{ github.ref }}-${{ matrix.postgres_version }}
cancel-in-progress: true
jobs:
local-test:
runs-on: [self-hosted, nix, nixos, x86_64-linux]
permissions:
contents: read
strategy:
fail-fast: false
# ponytail: explicit per-version secret mapping — any future PG major
# added to the matrix without a corresponding leg here will fail closed
# (missing secret) instead of silently falling through to PG18.
matrix:
include:
- postgres_version: "15"
pg_host_secret: PG15_HOST
pg_port_secret: PG15_PORT
pg_user_secret: PG15_USER
pg_password_secret: PG15_PASSWORD
pg_database_secret: PG15_DATABASE
pg_database_url_secret: PG15_DATABASE_URL
- postgres_version: "16"
pg_host_secret: PG16_HOST
pg_port_secret: PG16_PORT
pg_user_secret: PG16_USER
pg_password_secret: PG16_PASSWORD
pg_database_secret: PG16_DATABASE
pg_database_url_secret: PG16_DATABASE_URL
- postgres_version: "17"
pg_host_secret: PG17_HOST
pg_port_secret: PG17_PORT
pg_user_secret: PG17_USER
pg_password_secret: PG17_PASSWORD
pg_database_secret: PG17_DATABASE
pg_database_url_secret: PG17_DATABASE_URL
- postgres_version: "18"
pg_host_secret: PG18_HOST
pg_port_secret: PG18_PORT
pg_user_secret: PG18_USER
pg_password_secret: PG18_PASSWORD
pg_database_secret: PG18_DATABASE
pg_database_url_secret: PG18_DATABASE_URL
defaults:
run:
shell: bash -l {0}
env:
PG_VERSION: ${{ matrix.postgres_version }}
PGHOST: ${{ secrets[matrix.pg_host_secret] }}
PGPORT: ${{ secrets[matrix.pg_port_secret] }}
PGUSER: ${{ secrets[matrix.pg_user_secret] }}
PGPASSWORD: ${{ secrets[matrix.pg_password_secret] }}
PGDATABASE: ${{ secrets[matrix.pg_database_secret] }}
PGSSLMODE: require
DATABASE_URL: ${{ secrets[matrix.pg_database_url_secret] }}
# cloud_provider input is undefined on pull_request — default to 'direct'
CLOUD_PROVIDER: ${{ inputs.cloud_provider || 'direct' }}
PGFIRSTAID_FAIL_SEVERITY: HIGH
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Add Nix profile paths
run: |
echo "/run/current-system/sw/bin" >> "$GITHUB_PATH"
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH"
echo "/etc/profiles/per-user/$USER/bin" >> "$GITHUB_PATH"
- name: Validate PG connection vars
run: |
missing=0
for var in PGHOST PGPORT PGUSER PGPASSWORD PGDATABASE; do
if [ -z "${!var}" ]; then
echo "::error::Missing required secret/env: ${var} for PG${{ matrix.postgres_version }}"
missing=1
fi
done
if [ "$missing" -ne 0 ]; then exit 1; fi
echo "Target: PG${{ matrix.postgres_version }} @ $PGHOST:$PGPORT"
- name: Verify psql + uv
run: |
command -v psql >/dev/null 2>&1 || { echo "::error::psql not found"; exit 1; }
psql --version
command -v uv >/dev/null 2>&1 || { echo "::error::uv not found"; exit 1; }
uv --version
- name: Test — Health Check Suite
working-directory: testing/local-workflows
run: |
mkdir -p reports
./test_db_health_checks.sh
- name: Test — Managed DB Validation
working-directory: testing/local-workflows
run: |
./test_managed_db_validate.sh
- name: Test — Pre/Post Migration Validation
working-directory: testing/local-workflows
run: |
./test_pre_post_migration.sh
continue-on-error: true
- name: Test — PR Audit Script
working-directory: testing/local-workflows
run: |
uv pip install --quiet psycopg2-binary 2>/dev/null || uv pip install psycopg2-binary
./test_pr_audit.sh
continue-on-error: true
- name: Upload reports
uses: actions/upload-artifact@v4
if: always()
with:
name: test-reports-pg${{ matrix.postgres_version }}
path: testing/local-workflows/reports/
retention-days: 14
- name: Post summary
if: always()
run: |
{
echo "## NixOS Local Test Results — PG${{ matrix.postgres_version }}"
echo ""
echo "| Suite | Status |"
echo "|-------|--------|"
echo "| Health Check | ${{ job.status == 'success' && '✅' || '❌' }} |"
echo "| Managed DB Validate | ${{ job.status == 'success' && '✅' || '❌' }} |"
echo "| Migration Validate | ${{ job.status == 'success' && '✅' || '❌' }} |"
echo "| PR Audit | ${{ job.status == 'success' && '✅' || '❌' }} |"
echo ""
echo "Cloud provider label: ${{ inputs.cloud_provider || 'direct' }}"
} >> "$GITHUB_STEP_SUMMARY"