Migrate to Node 24 (#2740) #8492
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Run CI/CD | |
| on: | |
| merge_group: | |
| pull_request: | |
| branches: | |
| - feature/** | |
| - main | |
| paths-ignore: | |
| - backend/data/nest.json.gz | |
| push: | |
| branches: | |
| - feature/** | |
| - main | |
| paths-ignore: | |
| - backend/data/nest.json.gz | |
| release: | |
| types: | |
| - published | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| cancel-in-progress: true | |
| group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} | |
| env: | |
| FORCE_COLOR: 1 | |
| jobs: | |
| pre-commit: | |
| name: Run pre-commit checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Install Poetry | |
| run: pipx install poetry | |
| - name: Set up Python | |
| uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 | |
| with: | |
| cache: 'poetry' | |
| cache-dependency-path: backend/poetry.lock | |
| python-version: '3.13' | |
| - name: Set up pre-commit cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pre-commit | |
| key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} | |
| restore-keys: | | |
| pre-commit-${{ runner.os }}- | |
| - name: Run pre-commit | |
| uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd | |
| - name: Check for uncommitted changes | |
| run: | | |
| git diff --exit-code || (echo 'Unstaged changes detected. \ | |
| Run `make check` and use `git add` to address it.' && exit 1) | |
| check-frontend: | |
| name: Run frontend checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 | |
| with: | |
| version: 10 | |
| run_install: true | |
| - name: Set up Node | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 | |
| with: | |
| node-version: 24 | |
| cache: 'pnpm' | |
| cache-dependency-path: frontend/pnpm-lock.yaml | |
| - name: Run pnpm format | |
| working-directory: frontend | |
| run: pnpm run format | |
| - name: Run pnpm lint check | |
| working-directory: frontend | |
| run: pnpm run lint:check | |
| - name: Check for uncommitted changes | |
| run: | | |
| git diff --exit-code || (echo 'Unstaged changes detected. \ | |
| Run `make check` and use `git add` to address it.' && exit 1) | |
| spellcheck: | |
| name: Run spell check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Run cspell | |
| run: | | |
| make check-spelling | |
| scan-code: | |
| name: Run Code Scan | |
| needs: | |
| - check-frontend | |
| - pre-commit | |
| - spellcheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Run Trivy Repository Scan | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| scan-type: repo | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| version: latest | |
| scan-ci-dependencies: | |
| name: Run CI Denendencies Scan | |
| needs: | |
| - check-frontend | |
| - pre-commit | |
| - spellcheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Run Trivy Filesystem Scan | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| scan-type: fs | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| version: latest | |
| run-backend-tests: | |
| name: Run backend tests | |
| needs: | |
| - scan-code | |
| - scan-ci-dependencies | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Set up Docker buildx | |
| uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 | |
| - name: Build backend test image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:test-backend-cache | |
| cache-to: | | |
| type=gha,compression=zstd | |
| context: backend | |
| file: backend/docker/Dockerfile.test | |
| load: true | |
| platforms: linux/amd64 | |
| tags: owasp/nest:test-backend-latest | |
| - name: Run backend tests | |
| run: | | |
| docker run -e DJANGO_SETTINGS_MODULE=settings.test --env-file backend/.env.example owasp/nest:test-backend-latest pytest | |
| run-frontend-unit-tests: | |
| name: Run frontend unit tests | |
| needs: | |
| - scan-code | |
| - scan-ci-dependencies | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Set up Docker buildx | |
| uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 | |
| - name: Build frontend unit-testing image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:test-frontend-unit-cache | |
| cache-to: | | |
| type=gha,compression=zstd | |
| context: frontend | |
| file: frontend/docker/Dockerfile.unit.test | |
| load: true | |
| platforms: linux/amd64 | |
| tags: owasp/nest:test-frontend-unit-latest | |
| - name: Run frontend unit tests | |
| run: | | |
| docker run --env-file frontend/.env.example owasp/nest:test-frontend-unit-latest pnpm run test:unit | |
| run-frontend-e2e-tests: | |
| name: Run frontend e2e tests | |
| needs: | |
| - scan-code | |
| - scan-ci-dependencies | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Set up Docker buildx | |
| uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 | |
| - name: Build frontend end-to-end testing image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:test-frontend-e2e-cache | |
| context: frontend | |
| file: frontend/docker/Dockerfile.e2e.test | |
| load: true | |
| platforms: linux/amd64 | |
| tags: owasp/nest:test-frontend-e2e-latest | |
| - name: Run frontend end-to-end tests | |
| run: | | |
| docker run --env-file frontend/.env.example owasp/nest:test-frontend-e2e-latest pnpm run test:e2e | |
| set-release-version: | |
| name: Set release version | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_version: ${{ steps.set.outputs.release_version }} | |
| steps: | |
| - name: Set release version | |
| id: set | |
| run: | | |
| if [ -n "${{ github.event.release.tag_name }}" ]; then | |
| echo "release_version=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "release_version=$(date '+%y.%-m.%-d')-${GITHUB_SHA::7}" >> $GITHUB_OUTPUT | |
| fi | |
| build-staging-images: | |
| name: Build Staging Images | |
| env: | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.release_version }} | |
| environment: staging | |
| if: | | |
| github.repository == 'OWASP/Nest' && | |
| github.ref == 'refs/heads/main' | |
| needs: | |
| - run-backend-tests | |
| - run-frontend-e2e-tests | |
| - run-frontend-unit-tests | |
| - set-release-version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 | |
| - name: Set up Docker buildx | |
| uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build backend image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| build-args: | | |
| OWASP_GID=1001 | |
| OWASP_UID=1001 | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:backend-staging-cache | |
| cache-to: | | |
| type=registry,ref=owasp/nest:backend-staging-cache | |
| context: backend | |
| file: backend/docker/Dockerfile | |
| load: true | |
| platforms: linux/amd64 | |
| push: true | |
| tags: owasp/nest:backend-staging | |
| - name: Prepare frontend public environment | |
| env: | |
| NEXT_PUBLIC_API_URL: ${{ secrets.VITE_API_URL }} | |
| NEXT_PUBLIC_CSRF_URL: ${{ secrets.VITE_CSRF_URL }} | |
| NEXT_PUBLIC_ENVIRONMENT: ${{ secrets.VITE_ENVIRONMENT }} | |
| NEXT_PUBLIC_GRAPHQL_URL: ${{ secrets.VITE_GRAPHQL_URL }} | |
| NEXT_PUBLIC_GTM_ID: ${{ secrets.NEXT_PUBLIC_GTM_ID }} | |
| NEXT_PUBLIC_IDX_URL: ${{ secrets.VITE_IDX_URL }} | |
| NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED: ${{ secrets.NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED }} | |
| NEXT_PUBLIC_RELEASE_VERSION: ${{ env.RELEASE_VERSION }} | |
| NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| run: | | |
| umask 377 | |
| cat > frontend/.env <<EOF | |
| NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL | |
| NEXT_PUBLIC_CSRF_URL=$NEXT_PUBLIC_CSRF_URL | |
| NEXT_PUBLIC_ENVIRONMENT=$NEXT_PUBLIC_ENVIRONMENT | |
| NEXT_PUBLIC_GRAPHQL_URL=$NEXT_PUBLIC_GRAPHQL_URL | |
| NEXT_PUBLIC_GTM_ID=$NEXT_PUBLIC_GTM_ID | |
| NEXT_PUBLIC_IDX_URL=$NEXT_PUBLIC_IDX_URL | |
| NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=$NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED | |
| NEXT_PUBLIC_RELEASE_VERSION=$NEXT_PUBLIC_RELEASE_VERSION | |
| NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN | |
| EOF | |
| - name: Get backend image size | |
| id: backend-size | |
| run: | | |
| IMAGE_NAME="owasp/nest:backend-staging" | |
| RAW_SIZE=$(docker image inspect "$IMAGE_NAME" --format='{{.Size}}') | |
| DISPLAY_SIZE=$(numfmt --to=iec --suffix=B "$RAW_SIZE") | |
| echo "human_readable=$DISPLAY_SIZE" >> $GITHUB_OUTPUT | |
| - name: Build frontend image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:frontend-staging-cache | |
| cache-to: | | |
| type=registry,ref=owasp/nest:frontend-staging-cache | |
| context: frontend | |
| file: frontend/docker/Dockerfile | |
| load: true | |
| platforms: linux/amd64 | |
| push: true | |
| secrets: | | |
| RELEASE_VERSION=${{ needs.set-release-version.outputs.release_version }} | |
| SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} | |
| tags: owasp/nest:frontend-staging | |
| - name: Get frontend image size | |
| id: frontend-size | |
| run: | | |
| IMAGE_NAME="owasp/nest:frontend-staging" | |
| RAW_SIZE=$(docker image inspect "$IMAGE_NAME" --format='{{.Size}}') | |
| DISPLAY_SIZE=$(numfmt --to=iec --suffix=B "$RAW_SIZE") | |
| echo "human_readable=$DISPLAY_SIZE" >> $GITHUB_OUTPUT | |
| - name: Create Docker image size report | |
| run: | | |
| { | |
| echo "## Docker Image Size Report" | |
| echo "" | |
| echo "**Backend:** ${{ steps.backend-size.outputs.human_readable }}" | |
| echo "**Frontend:** ${{ steps.frontend-size.outputs.human_readable }}" | |
| } >> $GITHUB_STEP_SUMMARY | |
| scan-staging-images: | |
| name: Scan Staging Images | |
| needs: | |
| - build-staging-images | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Scan backend image | |
| continue-on-error: true | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| exit-code: 1 | |
| image-ref: owasp/nest:backend-staging | |
| scan-type: image | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| version: latest | |
| - name: Scan frontend image | |
| continue-on-error: true | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| exit-code: 1 | |
| image-ref: owasp/nest:frontend-staging | |
| scan-type: image | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| version: latest | |
| deploy-staging-nest: | |
| name: Deploy Nest Staging | |
| env: | |
| ANSIBLE_HOST_KEY_CHECKING: false | |
| NEST_HOST_IP_ADDRESS: ${{ secrets.NEST_HOST_IP_ADDRESS }} | |
| NEST_SSH_PRIVATE_KEY_PATH: ${{ vars.NEST_SSH_PRIVATE_KEY_PATH }} | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.release_version }} | |
| environment: staging | |
| if: | | |
| github.repository == 'OWASP/Nest' && | |
| github.ref == 'refs/heads/main' | |
| needs: | |
| - scan-staging-images | |
| - set-release-version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Prepare SSH key | |
| env: | |
| NEST_SSH_PRIVATE_KEY: ${{ secrets.NEST_SSH_PRIVATE_KEY }} | |
| NEST_SSH_PRIVATE_KEY_PATH: ${{ env.NEST_SSH_PRIVATE_KEY_PATH }} | |
| run: | | |
| SSH_KEY_PATH="${NEST_SSH_PRIVATE_KEY_PATH/#\~/$HOME}" | |
| mkdir -p -m 700 "$(dirname "$SSH_KEY_PATH")" | |
| umask 377 | |
| cat > "$SSH_KEY_PATH" <<EOF | |
| $NEST_SSH_PRIVATE_KEY | |
| EOF | |
| - name: Prepare secrets | |
| env: | |
| DJANGO_ALGOLIA_APPLICATION_ID: ${{ secrets.DJANGO_ALGOLIA_APPLICATION_ID }} | |
| DJANGO_ALGOLIA_WRITE_API_KEY: ${{ secrets.DJANGO_ALGOLIA_WRITE_API_KEY }} | |
| DJANGO_ALLOWED_HOSTS: ${{ secrets.DJANGO_ALLOWED_HOSTS }} | |
| DJANGO_AWS_ACCESS_KEY_ID: ${{ secrets.DJANGO_AWS_ACCESS_KEY_ID }} | |
| DJANGO_AWS_SECRET_ACCESS_KEY: ${{ secrets.DJANGO_AWS_SECRET_ACCESS_KEY }} | |
| DJANGO_CONFIGURATION: ${{ secrets.DJANGO_CONFIGURATION }} | |
| DJANGO_DB_HOST: ${{ secrets.DJANGO_DB_HOST }} | |
| DJANGO_DB_NAME: ${{ secrets.DJANGO_DB_NAME }} | |
| DJANGO_DB_PASSWORD: ${{ secrets.DJANGO_DB_PASSWORD }} | |
| DJANGO_DB_PORT: ${{ secrets.DJANGO_DB_PORT }} | |
| DJANGO_DB_USER: ${{ secrets.DJANGO_DB_USER }} | |
| DJANGO_OPEN_AI_SECRET_KEY: ${{ secrets.DJANGO_OPEN_AI_SECRET_KEY }} | |
| DJANGO_REDIS_HOST: ${{ secrets.DJANGO_REDIS_HOST }} | |
| DJANGO_REDIS_PASSWORD: ${{ secrets.DJANGO_REDIS_PASSWORD }} | |
| DJANGO_RELEASE_VERSION: ${{ secrets.DJANGO_RELEASE_VERSION }} | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| DJANGO_SENTRY_DSN: ${{ secrets.DJANGO_SENTRY_DSN }} | |
| DJANGO_SETTINGS_MODULE: ${{ secrets.DJANGO_SETTINGS_MODULE }} | |
| DJANGO_SLACK_BOT_TOKEN: ${{ secrets.DJANGO_SLACK_BOT_TOKEN }} | |
| DJANGO_SLACK_SIGNING_SECRET: ${{ secrets.DJANGO_SLACK_SIGNING_SECRET }} | |
| NEXT_SERVER_CSRF_URL: ${{ secrets.NEXT_SERVER_CSRF_URL }} | |
| NEXT_SERVER_GITHUB_CLIENT_ID: ${{ secrets.NEST_GITHUB_CLIENT_ID }} | |
| NEXT_SERVER_GITHUB_CLIENT_SECRET: ${{ secrets.NEST_GITHUB_CLIENT_SECRET }} | |
| NEXT_SERVER_GRAPHQL_URL: ${{ secrets.NEXT_SERVER_GRAPHQL_URL }} | |
| NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} | |
| NEXTAUTH_URL: ${{ secrets.VITE_API_URL }} | |
| SLACK_BOT_TOKEN_T04T40NHX: ${{ secrets.SLACK_BOT_TOKEN_T04T40NHX }} | |
| run: | | |
| # Backend | |
| umask 377 | |
| cat > .env.backend <<EOF | |
| DJANGO_ALGOLIA_APPLICATION_ID=$DJANGO_ALGOLIA_APPLICATION_ID | |
| DJANGO_ALGOLIA_WRITE_API_KEY=$DJANGO_ALGOLIA_WRITE_API_KEY | |
| DJANGO_ALLOWED_HOSTS=$DJANGO_ALLOWED_HOSTS | |
| DJANGO_AWS_ACCESS_KEY_ID=$DJANGO_AWS_ACCESS_KEY_ID | |
| DJANGO_AWS_SECRET_ACCESS_KEY=$DJANGO_AWS_SECRET_ACCESS_KEY | |
| DJANGO_CONFIGURATION=$DJANGO_CONFIGURATION | |
| DJANGO_DB_HOST=$DJANGO_DB_HOST | |
| DJANGO_DB_NAME=$DJANGO_DB_NAME | |
| DJANGO_DB_PASSWORD=$DJANGO_DB_PASSWORD | |
| DJANGO_DB_PORT=$DJANGO_DB_PORT | |
| DJANGO_DB_USER=$DJANGO_DB_USER | |
| DJANGO_OPEN_AI_SECRET_KEY=$DJANGO_OPEN_AI_SECRET_KEY | |
| DJANGO_REDIS_HOST=$DJANGO_REDIS_HOST | |
| DJANGO_REDIS_PASSWORD=$DJANGO_REDIS_PASSWORD | |
| DJANGO_RELEASE_VERSION=$DJANGO_RELEASE_VERSION | |
| DJANGO_SECRET_KEY=$DJANGO_SECRET_KEY | |
| DJANGO_SENTRY_DSN=$DJANGO_SENTRY_DSN | |
| DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE | |
| DJANGO_SLACK_BOT_TOKEN=$DJANGO_SLACK_BOT_TOKEN | |
| DJANGO_SLACK_SIGNING_SECRET=$DJANGO_SLACK_SIGNING_SECRET | |
| SLACK_BOT_TOKEN_T04T40NHX=$SLACK_BOT_TOKEN_T04T40NHX | |
| EOF | |
| # Cache | |
| umask 377 | |
| cat > .env.cache <<EOF | |
| REDIS_PASSWORD=$DJANGO_REDIS_PASSWORD | |
| EOF | |
| # Database | |
| umask 377 | |
| cat > .env.db <<EOF | |
| POSTGRES_DB=$DJANGO_DB_NAME | |
| POSTGRES_PASSWORD=$DJANGO_DB_PASSWORD | |
| POSTGRES_USER=$DJANGO_DB_USER | |
| EOF | |
| # Frontend | |
| umask 377 | |
| cat > .env.frontend <<EOF | |
| NEXT_SERVER_CSRF_URL=$NEXT_SERVER_CSRF_URL | |
| NEXT_SERVER_GITHUB_CLIENT_ID=$NEXT_SERVER_GITHUB_CLIENT_ID | |
| NEXT_SERVER_GITHUB_CLIENT_SECRET=$NEXT_SERVER_GITHUB_CLIENT_SECRET | |
| NEXT_SERVER_GRAPHQL_URL=$NEXT_SERVER_GRAPHQL_URL | |
| NEXTAUTH_SECRET=$NEXTAUTH_SECRET | |
| NEXTAUTH_URL=$NEXTAUTH_URL | |
| EOF | |
| - name: Run Nest deploy | |
| working-directory: .github/ansible | |
| run: ansible-playbook -i inventory.yaml staging/nest.yaml -e "github_workspace=$GITHUB_WORKSPACE" | |
| deploy-staging-nest-proxy: | |
| name: Deploy Staging Nest Proxy | |
| env: | |
| ANSIBLE_HOST_KEY_CHECKING: false | |
| PROXY_HOST_IP_ADDRESS: ${{ secrets.PROXY_HOST_IP_ADDRESS }} | |
| PROXY_SSH_PRIVATE_KEY_PATH: ${{ vars.PROXY_SSH_PRIVATE_KEY_PATH }} | |
| environment: staging | |
| if: | | |
| github.repository == 'OWASP/Nest' && | |
| github.ref == 'refs/heads/main' | |
| needs: | |
| - deploy-staging-nest | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Prepare SSH key | |
| env: | |
| PROXY_SSH_PRIVATE_KEY: ${{ secrets.PROXY_SSH_PRIVATE_KEY }} | |
| PROXY_SSH_PRIVATE_KEY_PATH: ${{ env.PROXY_SSH_PRIVATE_KEY_PATH }} | |
| run: | | |
| SSH_KEY_PATH="${PROXY_SSH_PRIVATE_KEY_PATH/#\~/$HOME}" | |
| mkdir -p -m 700 "$(dirname "$SSH_KEY_PATH")" | |
| umask 377 | |
| cat > "$SSH_KEY_PATH" <<EOF | |
| $PROXY_SSH_PRIVATE_KEY | |
| EOF | |
| - name: Run proxy deploy | |
| working-directory: .github/ansible | |
| run: ansible-playbook -i inventory.yaml staging/proxy.yaml -e "github_workspace=$GITHUB_WORKSPACE" | |
| run-lighthouse-ci: | |
| name: Run Lighthouse CI | |
| needs: | |
| - deploy-staging-nest-proxy | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 | |
| with: | |
| run_install: true | |
| version: 10 | |
| - name: Set up Node | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 | |
| with: | |
| cache-dependency-path: frontend/pnpm-lock.yaml | |
| cache: 'pnpm' | |
| node-version: 24 | |
| - name: Run lighthouse-ci | |
| env: | |
| LHCI_BASE_URL: 'https://nest.owasp.dev' | |
| run: | | |
| pnpm run lighthouse-ci | |
| timeout-minutes: 15 | |
| working-directory: frontend | |
| build-production-images: | |
| name: Build Production Images | |
| env: | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.release_version }} | |
| environment: production | |
| if: | | |
| github.event_name == 'release' && | |
| github.event.action == 'published' | |
| needs: | |
| - run-backend-tests | |
| - run-frontend-e2e-tests | |
| - run-frontend-unit-tests | |
| - set-release-version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 | |
| - name: Set up Docker buildx | |
| uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build backend image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| build-args: | | |
| OWASP_GID=1002 | |
| OWASP_UID=1002 | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:backend-staging-cache | |
| context: backend | |
| file: backend/docker/Dockerfile | |
| load: true | |
| platforms: linux/amd64 | |
| push: true | |
| tags: owasp/nest:backend-production | |
| - name: Get backend image size | |
| id: backend-size | |
| run: | | |
| IMAGE_NAME="owasp/nest:backend-production" | |
| RAW_SIZE=$(docker image inspect "$IMAGE_NAME" --format='{{.Size}}') | |
| DISPLAY_SIZE=$(numfmt --to=iec --suffix=B "$RAW_SIZE") | |
| echo "human_readable=$DISPLAY_SIZE" >> $GITHUB_OUTPUT | |
| - name: Prepare frontend public environment | |
| env: | |
| NEXT_PUBLIC_API_URL: ${{ secrets.VITE_API_URL }} | |
| NEXT_PUBLIC_CSRF_URL: ${{ secrets.VITE_CSRF_URL }} | |
| NEXT_PUBLIC_ENVIRONMENT: ${{ secrets.VITE_ENVIRONMENT }} | |
| NEXT_PUBLIC_GRAPHQL_URL: ${{ secrets.VITE_GRAPHQL_URL }} | |
| NEXT_PUBLIC_GTM_ID: ${{ secrets.NEXT_PUBLIC_GTM_ID }} | |
| NEXT_PUBLIC_IDX_URL: ${{ secrets.VITE_IDX_URL }} | |
| NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED: ${{ secrets.NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED }} | |
| NEXT_PUBLIC_RELEASE_VERSION: ${{ env.RELEASE_VERSION }} | |
| NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| run: | | |
| umask 377 | |
| cat > frontend/.env <<EOF | |
| NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL | |
| NEXT_PUBLIC_CSRF_URL=$NEXT_PUBLIC_CSRF_URL | |
| NEXT_PUBLIC_ENVIRONMENT=$NEXT_PUBLIC_ENVIRONMENT | |
| NEXT_PUBLIC_GRAPHQL_URL=$NEXT_PUBLIC_GRAPHQL_URL | |
| NEXT_PUBLIC_GTM_ID=$NEXT_PUBLIC_GTM_ID | |
| NEXT_PUBLIC_IDX_URL=$NEXT_PUBLIC_IDX_URL | |
| NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=$NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED | |
| NEXT_PUBLIC_RELEASE_VERSION=$NEXT_PUBLIC_RELEASE_VERSION | |
| NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN | |
| EOF | |
| - name: Build frontend image | |
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | |
| with: | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=owasp/nest:frontend-staging-cache | |
| context: frontend | |
| file: frontend/docker/Dockerfile | |
| load: true | |
| platforms: linux/amd64 | |
| push: true | |
| secrets: | | |
| RELEASE_VERSION=${{ needs.set-release-version.outputs.release_version }} | |
| SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} | |
| tags: owasp/nest:frontend-production | |
| - name: Get frontend image size | |
| id: frontend-size | |
| run: | | |
| IMAGE_NAME="owasp/nest:frontend-production" | |
| RAW_SIZE=$(docker image inspect "$IMAGE_NAME" --format='{{.Size}}') | |
| DISPLAY_SIZE=$(numfmt --to=iec --suffix=B "$RAW_SIZE") | |
| echo "human_readable=$DISPLAY_SIZE" >> $GITHUB_OUTPUT | |
| - name: Create Docker image size report | |
| run: | | |
| { | |
| echo "## Docker Image Size Report" | |
| echo "" | |
| echo "**Backend:** ${{ steps.backend-size.outputs.human_readable }}" | |
| echo "**Frontend:** ${{ steps.frontend-size.outputs.human_readable }}" | |
| } >> $GITHUB_STEP_SUMMARY | |
| scan-production-images: | |
| name: Scan Production Images | |
| needs: | |
| - build-production-images | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Setup Trivy | |
| uses: aquasecurity/setup-trivy@e6c2c5e321ed9123bda567646e2f96565e34abe1 | |
| with: | |
| cache: true | |
| version: v0.62.1 | |
| - name: Scan backend image | |
| continue-on-error: true | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| exit-code: 1 | |
| image-ref: owasp/nest:backend-production | |
| skip-setup-trivy: true | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| - name: Scan frontend image | |
| continue-on-error: true | |
| uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 | |
| with: | |
| exit-code: 1 | |
| image-ref: owasp/nest:frontend-production | |
| skip-setup-trivy: true | |
| trivy-config: trivy.yaml | |
| trivyignores: trivyignore.yaml | |
| deploy-production-nest: | |
| name: Deploy Nest to Production | |
| env: | |
| ANSIBLE_HOST_KEY_CHECKING: false | |
| NEST_HOST_IP_ADDRESS: ${{ secrets.NEST_HOST_IP_ADDRESS }} | |
| NEST_SSH_PRIVATE_KEY_PATH: ${{ vars.NEST_SSH_PRIVATE_KEY_PATH }} | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.release_version }} | |
| environment: production | |
| if: | | |
| github.event_name == 'release' && | |
| github.event.action == 'published' | |
| needs: | |
| - scan-production-images | |
| - set-release-version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Prepare SSH key | |
| env: | |
| NEST_SSH_PRIVATE_KEY: ${{ secrets.NEST_SSH_PRIVATE_KEY }} | |
| NEST_SSH_PRIVATE_KEY_PATH: ${{ env.NEST_SSH_PRIVATE_KEY_PATH }} | |
| run: | | |
| SSH_KEY_PATH="${NEST_SSH_PRIVATE_KEY_PATH/#\~/$HOME}" | |
| mkdir -p -m 700 "$(dirname "$SSH_KEY_PATH")" | |
| umask 377 | |
| cat > "$SSH_KEY_PATH" <<EOF | |
| $NEST_SSH_PRIVATE_KEY | |
| EOF | |
| - name: Prepare secrets | |
| env: | |
| DJANGO_ALGOLIA_APPLICATION_ID: ${{ secrets.DJANGO_ALGOLIA_APPLICATION_ID }} | |
| DJANGO_ALGOLIA_WRITE_API_KEY: ${{ secrets.DJANGO_ALGOLIA_WRITE_API_KEY }} | |
| DJANGO_ALLOWED_HOSTS: ${{ secrets.DJANGO_ALLOWED_HOSTS }} | |
| DJANGO_AWS_ACCESS_KEY_ID: ${{ secrets.DJANGO_AWS_ACCESS_KEY_ID }} | |
| DJANGO_AWS_SECRET_ACCESS_KEY: ${{ secrets.DJANGO_AWS_SECRET_ACCESS_KEY }} | |
| DJANGO_CONFIGURATION: ${{ secrets.DJANGO_CONFIGURATION }} | |
| DJANGO_DB_HOST: ${{ secrets.DJANGO_DB_HOST }} | |
| DJANGO_DB_NAME: ${{ secrets.DJANGO_DB_NAME }} | |
| DJANGO_DB_PASSWORD: ${{ secrets.DJANGO_DB_PASSWORD }} | |
| DJANGO_DB_PORT: ${{ secrets.DJANGO_DB_PORT }} | |
| DJANGO_DB_USER: ${{ secrets.DJANGO_DB_USER }} | |
| DJANGO_GITHUB_APP_ID: ${{ secrets.DJANGO_GITHUB_APP_ID }} | |
| DJANGO_GITHUB_APP_INSTALLATION_ID: ${{ secrets.DJANGO_GITHUB_APP_INSTALLATION_ID }} | |
| DJANGO_OPEN_AI_SECRET_KEY: ${{ secrets.DJANGO_OPEN_AI_SECRET_KEY }} | |
| DJANGO_REDIS_HOST: ${{ secrets.DJANGO_REDIS_HOST }} | |
| DJANGO_REDIS_PASSWORD: ${{ secrets.DJANGO_REDIS_PASSWORD }} | |
| DJANGO_RELEASE_VERSION: ${{ env.RELEASE_VERSION }} | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| DJANGO_SENTRY_DSN: ${{ secrets.DJANGO_SENTRY_DSN }} | |
| DJANGO_SETTINGS_MODULE: ${{ secrets.DJANGO_SETTINGS_MODULE }} | |
| DJANGO_SLACK_BOT_TOKEN: ${{ secrets.DJANGO_SLACK_BOT_TOKEN }} | |
| DJANGO_SLACK_SIGNING_SECRET: ${{ secrets.DJANGO_SLACK_SIGNING_SECRET }} | |
| NEST_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NEST_GITHUB_APP_PRIVATE_KEY }} | |
| NEXT_SERVER_CSRF_URL: ${{ secrets.NEXT_SERVER_CSRF_URL }} | |
| NEXT_SERVER_GITHUB_CLIENT_ID: ${{ secrets.NEST_GITHUB_CLIENT_ID }} | |
| NEXT_SERVER_GITHUB_CLIENT_SECRET: ${{ secrets.NEST_GITHUB_CLIENT_SECRET }} | |
| NEXT_SERVER_GRAPHQL_URL: ${{ secrets.NEXT_SERVER_GRAPHQL_URL }} | |
| NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} | |
| NEXTAUTH_URL: ${{ secrets.VITE_API_URL }} | |
| SLACK_BOT_TOKEN_T04T40NHX: ${{ secrets.SLACK_BOT_TOKEN_T04T40NHX }} | |
| run: | | |
| # Backend | |
| umask 377 | |
| cat > .env.backend <<EOF | |
| DJANGO_ALGOLIA_APPLICATION_ID=$DJANGO_ALGOLIA_APPLICATION_ID | |
| DJANGO_ALGOLIA_WRITE_API_KEY=$DJANGO_ALGOLIA_WRITE_API_KEY | |
| DJANGO_ALLOWED_HOSTS=$DJANGO_ALLOWED_HOSTS | |
| DJANGO_AWS_ACCESS_KEY_ID=$DJANGO_AWS_ACCESS_KEY_ID | |
| DJANGO_AWS_SECRET_ACCESS_KEY=$DJANGO_AWS_SECRET_ACCESS_KEY | |
| DJANGO_CONFIGURATION=$DJANGO_CONFIGURATION | |
| DJANGO_DB_HOST=$DJANGO_DB_HOST | |
| DJANGO_DB_NAME=$DJANGO_DB_NAME | |
| DJANGO_DB_PASSWORD=$DJANGO_DB_PASSWORD | |
| DJANGO_DB_PORT=$DJANGO_DB_PORT | |
| DJANGO_DB_USER=$DJANGO_DB_USER | |
| DJANGO_GITHUB_APP_ID=$DJANGO_GITHUB_APP_ID | |
| DJANGO_GITHUB_APP_INSTALLATION_ID=$DJANGO_GITHUB_APP_INSTALLATION_ID | |
| DJANGO_OPEN_AI_SECRET_KEY=$DJANGO_OPEN_AI_SECRET_KEY | |
| DJANGO_REDIS_HOST=$DJANGO_REDIS_HOST | |
| DJANGO_REDIS_PASSWORD=$DJANGO_REDIS_PASSWORD | |
| DJANGO_RELEASE_VERSION=$DJANGO_RELEASE_VERSION | |
| DJANGO_SECRET_KEY=$DJANGO_SECRET_KEY | |
| DJANGO_SENTRY_DSN=$DJANGO_SENTRY_DSN | |
| DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE | |
| DJANGO_SLACK_BOT_TOKEN=$DJANGO_SLACK_BOT_TOKEN | |
| DJANGO_SLACK_SIGNING_SECRET=$DJANGO_SLACK_SIGNING_SECRET | |
| SLACK_BOT_TOKEN_T04T40NHX=$SLACK_BOT_TOKEN_T04T40NHX | |
| EOF | |
| # Cache | |
| umask 377 | |
| cat > .env.cache <<EOF | |
| REDIS_PASSWORD=$DJANGO_REDIS_PASSWORD | |
| EOF | |
| # Database | |
| umask 377 | |
| cat > .env.db <<EOF | |
| POSTGRES_DB=$DJANGO_DB_NAME | |
| POSTGRES_PASSWORD=$DJANGO_DB_PASSWORD | |
| POSTGRES_USER=$DJANGO_DB_USER | |
| EOF | |
| # Frontend | |
| umask 377 | |
| cat > .env.frontend <<EOF | |
| NEXT_SERVER_CSRF_URL=$NEXT_SERVER_CSRF_URL | |
| NEXT_SERVER_GITHUB_CLIENT_ID=$NEXT_SERVER_GITHUB_CLIENT_ID | |
| NEXT_SERVER_GITHUB_CLIENT_SECRET=$NEXT_SERVER_GITHUB_CLIENT_SECRET | |
| NEXT_SERVER_GRAPHQL_URL=$NEXT_SERVER_GRAPHQL_URL | |
| NEXTAUTH_SECRET=$NEXTAUTH_SECRET | |
| NEXTAUTH_URL=$NEXTAUTH_URL | |
| EOF | |
| # GitHub App private key | |
| umask 377 | |
| cat > .github.pem <<EOF | |
| "$NEST_GITHUB_APP_PRIVATE_KEY" | |
| EOF | |
| - name: Run Nest deploy | |
| working-directory: .github/ansible | |
| run: ansible-playbook -i inventory.yaml production/nest.yaml -e "github_workspace=$GITHUB_WORKSPACE" | |
| deploy-production-nest-proxy: | |
| name: Deploy Production Nest Proxy | |
| env: | |
| ANSIBLE_HOST_KEY_CHECKING: false | |
| PROXY_HOST_IP_ADDRESS: ${{ secrets.PROXY_HOST_IP_ADDRESS }} | |
| PROXY_SSH_PRIVATE_KEY_PATH: ${{ vars.PROXY_SSH_PRIVATE_KEY_PATH }} | |
| environment: production | |
| if: | | |
| github.event_name == 'release' && | |
| github.event.action == 'published' | |
| needs: | |
| - deploy-production-nest | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 | |
| - name: Prepare SSH key | |
| env: | |
| PROXY_SSH_PRIVATE_KEY: ${{ secrets.PROXY_SSH_PRIVATE_KEY }} | |
| PROXY_SSH_PRIVATE_KEY_PATH: ${{ env.PROXY_SSH_PRIVATE_KEY_PATH }} | |
| run: | | |
| SSH_KEY_PATH="${PROXY_SSH_PRIVATE_KEY_PATH/#\~/$HOME}" | |
| mkdir -p -m 700 "$(dirname "$SSH_KEY_PATH")" | |
| umask 377 | |
| cat > "$SSH_KEY_PATH" <<EOF | |
| $PROXY_SSH_PRIVATE_KEY | |
| EOF | |
| - name: Run proxy deploy | |
| working-directory: .github/ansible | |
| run: ansible-playbook -i inventory.yaml production/proxy.yaml -e "github_workspace=$GITHUB_WORKSPACE" |