Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8758a0d
Extract checks and security scan from `run-ci-cd.yaml` (#4623)
ahmedxgouda May 9, 2026
6e94d96
Update pnpm
ahmedxgouda May 12, 2026
0debe2b
Extract backend tests workflow and remove docker layer (#4635)
ahmedxgouda May 12, 2026
facf234
Extract codecov upload into a separate workflow (#4668)
ahmedxgouda May 13, 2026
f48f852
Remove docker layer from fuzz tests workflow (#4663)
ahmedxgouda May 14, 2026
825cad6
Remove update_nest_test_images.yaml (#4691)
ahmedxgouda May 17, 2026
35e0a89
Extract infrastructure tests into a separate workflow (#4679)
ahmedxgouda May 17, 2026
3f7a3b6
Extract frontend tests into a separate workflow (#4681)
ahmedxgouda May 17, 2026
35c4100
Extract e2e tests into a separate workflow (#4687)
ahmedxgouda May 18, 2026
7196069
Extract set-release-version into a separate workflow (#4693)
ahmedxgouda May 19, 2026
e661190
Extract build-images into a separate reusable worflow (#4709)
ahmedxgouda May 23, 2026
2adc30f
Fix e2e and fuzz errors
ahmedxgouda May 31, 2026
8363e55
Extract run-lighthouse-ci into a separate reusable workflow (#4807)
ahmedxgouda Jun 1, 2026
73d625a
Extract run-zap-baseline-scan into a reusable workflow (#4808)
ahmedxgouda Jun 2, 2026
597ffdc
Extract bootstrap-nest-infrastructure into a separate workflow (#4711)
ahmedxgouda Jun 2, 2026
3ae7762
Extract scan-images into a separate workflow (#4710)
ahmedxgouda Jun 3, 2026
ff10838
Extract deploy-nest into a separate reusable workflow (#4818)
ahmedxgouda Jun 3, 2026
822b8ca
Extract staging (#4701)
ahmedxgouda Jun 4, 2026
fb51cf7
Extract production jobs into a separate workflow (#4826)
ahmedxgouda Jun 4, 2026
51e56c3
Generalize production and staging workflows (#4835)
ahmedxgouda Jun 6, 2026
cc11eb0
Finalize feature/ci-cd-optimization merge
arkid15r Jun 6, 2026
74bd672
Address review comments
arkid15r Jun 6, 2026
e9ac5d9
Update secrets handling
arkid15r Jun 6, 2026
6081960
Fix coverage uploading, add docker image tag validation
arkid15r Jun 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/actions/apply-infrastructure-changes/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Apply infrastructure changes
description: Run IaC plan and apply steps

inputs:
plugin-cache-dir:
description: Directory to store Terraform plugin cache
required: false
default: /home/runner/.terraform.d/plugin-cache
summary-title:
description: Title used for the plan output summary
required: false
default: Terraform Plan Output
tfbackend-path:
description: Path to write Terraform backend config
required: true
tfbackend-content:
description: Content for Terraform backend config
required: true
tfvars-path:
description: Path to write Terraform variables
required: true
tfvars-content:
description: Content for Terraform variables
required: true
working-directory:
description: Terraform working directory
required: true

runs:
using: composite
steps:
- name: Install Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: 1.14

- name: Create plugin cache directory
env:
TF_PLUGIN_CACHE_DIR: ${{ inputs.plugin-cache-dir }}
run: mkdir -p "${TF_PLUGIN_CACHE_DIR}"
shell: bash

- name: Cache Terraform plugins
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ runner.os }}-infrastructure-${{ hashFiles('infrastructure/**/.terraform.lock.hcl') }}
path: ${{ inputs.plugin-cache-dir }}

- name: Prepare Terraform backend
env:
TF_BACKEND_CONTENT: ${{ inputs.tfbackend-content }}
TF_BACKEND_PATH: ${{ inputs.tfbackend-path }}
run: |
umask 377
printf '%s\n' "$TF_BACKEND_CONTENT" > "$TF_BACKEND_PATH"
shell: bash

- name: Prepare Terraform variables
env:
TF_VARS_CONTENT: ${{ inputs.tfvars-content }}
TF_VARS_PATH: ${{ inputs.tfvars-path }}
run: |
umask 377
printf '%s\n' "$TF_VARS_CONTENT" > "$TF_VARS_PATH"
shell: bash

- name: Initialize Terraform
env:
TF_PLUGIN_CACHE_DIR: ${{ inputs.plugin-cache-dir }}
run: terraform init -backend-config=terraform.tfbackend
shell: bash
working-directory: ${{ inputs.working-directory }}

- name: Validate Terraform configuration
run: terraform validate
shell: bash
working-directory: ${{ inputs.working-directory }}

- name: Plan Terraform changes
run: terraform plan -out=tfplan
shell: bash
working-directory: ${{ inputs.working-directory }}

- name: Show plan summary
env:
SUMMARY_TITLE: ${{ inputs.summary-title }}
run: |
echo "## $SUMMARY_TITLE" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
terraform show -no-color tfplan >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
shell: bash
working-directory: ${{ inputs.working-directory }}

- name: Apply Terraform changes
run: terraform apply -auto-approve tfplan
shell: bash
working-directory: ${{ inputs.working-directory }}
28 changes: 28 additions & 0 deletions .github/actions/install-backend-dependencies/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Install backend dependencies

description: >-
Installs Python and Poetry, and installs backend dependencies.

runs:
using: composite
steps:
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.13'

- name: Install Poetry
uses: ./.github/actions/install-poetry

- name: Cache backend dependencies
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: poetry-venv-${{ runner.os }}-${{ hashFiles('backend/poetry.lock') }}
path: backend/.venv
restore-keys: |
poetry-venv-${{ runner.os }}-

- name: Install backend dependencies
run: poetry install --no-interaction --no-root
shell: bash
working-directory: backend
24 changes: 24 additions & 0 deletions .github/actions/install-frontend-dependencies/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Install frontend dependencies

description: >-
Installs pnpm and Node, and installs frontend dependencies.

runs:
using: composite
steps:
- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with:
version: 11.5.1

- name: Set up Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
node-version: 24

- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
shell: bash
working-directory: frontend
32 changes: 32 additions & 0 deletions .github/actions/install-poetry/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Install Poetry

description: >-
Installs Poetry from backend/requirements.build.txt with pip cache. Run after actions/setup-python.

runs:
using: composite
steps:
- name: Cache pip for Poetry install
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: pip-poetry-${{ runner.os }}-${{ hashFiles('backend/requirements.build.txt') }}
path: ${{ runner.temp }}/poetry-ci-home/.cache/pip
restore-keys: |
pip-poetry-${{ runner.os }}-

- name: Prepare pip cache directory
env:
POETRY_CI_HOME: ${{ runner.temp }}/poetry-ci-home
run: |
set -euo pipefail
mkdir -p "$POETRY_CI_HOME/.cache/pip"
chown -R "$(id -u):$(id -g)" "$POETRY_CI_HOME"
chmod -R u+rwX "$POETRY_CI_HOME"
shell: bash

- name: Install Poetry
env:
HOME: ${{ runner.temp }}/poetry-ci-home
PIP_ROOT_USER_ACTION: ignore
run: python -m pip install --requirement backend/requirements.build.txt
shell: bash
30 changes: 30 additions & 0 deletions .github/actions/run-trivy-scan/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Run Trivy scan
description: Configure Trivy cache and run Trivy scan

inputs:
command:
description: Command to run the Trivy scan
required: true

runs:
using: composite
steps:
- name: Set Trivy cache key
env:
DOCKERFILE_HASH: ${{ hashFiles('docker/trivy/Dockerfile') }}
RUNNER_OS: ${{ runner.os }}
run: echo "TRIVY_CACHE_KEY=trivy-${RUNNER_OS}-${DOCKERFILE_HASH}" >> "$GITHUB_ENV"
shell: bash

- name: Cache Trivy
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ env.TRIVY_CACHE_KEY }}
path: .trivy-cache
restore-keys: trivy-${{ runner.os }}-

- name: Run Trivy scan
env:
TRIVY_RUN_COMMAND: ${{ inputs.command }}
run: bash -c "$TRIVY_RUN_COMMAND"
shell: bash
104 changes: 104 additions & 0 deletions .github/actions/setup-backend-environment/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Set up Backend environment

description: >-
Fetches nest.dump from S3 using the same Poetry environment as local Make targets, waits for
the Postgres service container, runs migrations, restores the database dump, starts the backend,
and waits for it to be ready.

inputs:
backend_port:
description: Backend port to bind
required: true
db_name:
description: Database name
required: true
db_password:
description: Database password
required: true
db_username:
description: Database username
required: true
env_file:
description: Backend environment file
required: true

runs:
using: composite
steps:
- name: Install backend dependencies
uses: ./.github/actions/install-backend-dependencies

- name: Fetch nest.dump from S3
run: poetry run python -m scripts.fetch_nest_dump
shell: bash
working-directory: backend

- name: Wait for database to be ready
env:
DB_NAME: ${{ inputs.db_name }}
DB_USERNAME: ${{ inputs.db_username }}
run: |
timeout 5m bash -c '
until docker exec ${{ job.services.db.id }} pg_isready -U "$DB_USERNAME" -d "$DB_NAME"; do
echo "Waiting for database..."
sleep 5
done
'
shell: bash

- name: Migrate Database
env:
ENV_FILE: ${{ inputs.env_file }}
run: |
set -euo pipefail && set -a && source $ENV_FILE && set +a
export DJANGO_DB_HOST=localhost
export DJANGO_REDIS_AUTH_ENABLED=False
export DJANGO_REDIS_HOST=localhost
poetry run python manage.py migrate
shell: bash
working-directory: backend

- name: Load Postgres data
env:
DB_NAME: ${{ inputs.db_name }}
DB_USERNAME: ${{ inputs.db_username }}
PGPASSWORD: ${{ inputs.db_password }} # The env name required by PostgreSQL clients.
run: |
set -euo pipefail
if ! docker exec -i -e PGPASSWORD="$PGPASSWORD" ${{ job.services.db.id }} \
pg_restore -U "$DB_USERNAME" -d "$DB_NAME" < backend/data/nest.dump; then
echo "Data loading failed"
exit 1
fi
echo "Data loading completed."
shell: bash

- name: Start Backend in the background
env:
BACKEND_PORT: ${{ inputs.backend_port }}
ENV_FILE: ${{ inputs.env_file }}
run: |
set -euo pipefail && set -a && source $ENV_FILE && set +a
export DJANGO_DB_HOST=localhost
export DJANGO_REDIS_AUTH_ENABLED=False
export DJANGO_REDIS_HOST=localhost
nohup poetry run gunicorn wsgi:application \
--bind 0.0.0.0:$BACKEND_PORT \
--workers 2 \
> /dev/null 2>&1 &
disown
shell: bash
working-directory: backend

- name: Waiting for the backend to be ready
env:
BACKEND_PORT: ${{ inputs.backend_port }}
run: |
timeout 5m bash -c '
until wget --spider http://localhost:$BACKEND_PORT/a; do
echo "Waiting for backend..."
sleep 5
done
'
echo "Backend is up!"
shell: bash
File renamed without changes.
59 changes: 59 additions & 0 deletions .github/workflows/ci-cd-production.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: CI/CD Production

on:
release:
types:
- published

concurrency:
cancel-in-progress: false
group: ci-cd-production

permissions: {}

env:
FORCE_COLOR: 1

jobs:
run-ci-cd:
name: Run production CI/CD
permissions:
actions: write
contents: write
id-token: write
secrets:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
AWS_ROLE_EXTERNAL_ID: ${{ secrets.AWS_ROLE_EXTERNAL_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
BOOTSTRAP_AWS_ACCESS_KEY_ID: ${{ secrets.BOOTSTRAP_AWS_ACCESS_KEY_ID }}
BOOTSTRAP_AWS_SECRET_ACCESS_KEY: ${{ secrets.BOOTSTRAP_AWS_SECRET_ACCESS_KEY }}
BOOTSTRAP_TF_STATE_BUCKET_NAME: ${{ secrets.BOOTSTRAP_TF_STATE_BUCKET_NAME }}
NEXT_PUBLIC_GTM_ID: ${{ secrets.NEXT_PUBLIC_GTM_ID }}
NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED: ${{ secrets.NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED }}
NEXT_PUBLIC_POSTHOG_HOST: ${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}
NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
TF_STATE_BUCKET_NAME: ${{ secrets.TF_STATE_BUCKET_NAME }}
VITE_API_URL: ${{ secrets.VITE_API_URL }}
VITE_CSRF_URL: ${{ secrets.VITE_CSRF_URL }}
VITE_ENVIRONMENT: ${{ secrets.VITE_ENVIRONMENT }}
VITE_GRAPHQL_URL: ${{ secrets.VITE_GRAPHQL_URL }}
VITE_IDX_URL: ${{ secrets.VITE_IDX_URL }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
uses: ./.github/workflows/run-ci-cd.yaml
with:
backend_gid: '1002'
backend_uid: '1002'
backend_use_fargate_spot: false
base_url: https://nest.owasp.org
django_configuration: Production
django_settings_module: settings.production
enable_additional_parameters: true
enable_cron_tasks: true
enable_nat_gateway: true
enable_rds_proxy: true
environment: production
frontend_use_fargate_spot: false
release_tag: ${{ github.event.release.tag_name }}
tasks_use_fargate_spot: false
Loading
Loading