This repository was archived by the owner on Aug 10, 2026. It is now read-only.
Repository navigation
Added node version issue fix #8
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: Deploy URL Bookmark Saver | |
| # Pipeline stages: | |
| # lint ──┬──→ build ──┐ | |
| # └──→ scan ──┴──→ deploy | |
| # | |
| # build and scan run in parallel after lint; deploy gates on both. | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - '.github/workflows/deploy-url-bookmark-saver.yml' | |
| - 'projects/03-url-bookmark-saver/**' | |
| pull_request: | |
| paths: | |
| - '.github/workflows/deploy-url-bookmark-saver.yml' | |
| - 'projects/03-url-bookmark-saver/**' | |
| workflow_dispatch: | |
| inputs: | |
| student_name: | |
| description: 'Deployment name prefix (e.g. john-smith). Overrides repo variable.' | |
| required: false | |
| default: '' | |
| aws_region: | |
| description: 'AWS region to deploy into' | |
| required: false | |
| default: 'us-east-1' | |
| aws_access_key_id: | |
| description: 'AWS Access Key ID — leave blank to use repo secret AWS_ACCESS_KEY_ID' | |
| required: false | |
| default: '' | |
| aws_secret_access_key: | |
| description: 'AWS Secret Access Key — leave blank to use repo secret AWS_SECRET_ACCESS_KEY' | |
| required: false | |
| default: '' | |
| env: | |
| PROJECT_DIR: projects/03-url-bookmark-saver | |
| AWS_REGION: ${{ inputs.aws_region || vars.URL_BOOKMARK_SAVER_AWS_REGION || 'us-east-1' }} | |
| STUDENT_NAME: ${{ inputs.student_name || vars.URL_BOOKMARK_SAVER_STUDENT_NAME }} | |
| # ── Stage 1: Lint ───────────────────────────────────────────────────────────── | |
| jobs: | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| continue-on-error: true # report lint issues without blocking build/scan/deploy | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 25 | |
| - uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_wrapper: false | |
| - name: Install frontend dependencies | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| run: npm install | |
| - name: ESLint — frontend | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| run: npm run lint | |
| - name: Terraform format check | |
| run: terraform fmt -check -recursive ${{ env.PROJECT_DIR }}/terraform | |
| - name: Terraform validate | |
| working-directory: ${{ env.PROJECT_DIR }}/terraform | |
| run: | | |
| terraform init -backend=false | |
| terraform validate | |
| # ── Stage 2a: Build ─────────────────────────────────────────────────────────── | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 25 | |
| - name: Install backend dependencies | |
| working-directory: ${{ env.PROJECT_DIR }}/backend | |
| run: npm install --omit=dev | |
| - name: Check Lambda syntax | |
| working-directory: ${{ env.PROJECT_DIR }}/backend | |
| run: node --check src/handler.js | |
| - name: Install frontend dependencies | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| run: npm install | |
| - name: Build frontend | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| env: | |
| NEXT_PUBLIC_API_URL: https://example.execute-api.us-east-1.amazonaws.com | |
| run: npm run build | |
| - name: TypeScript type check | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| run: npx tsc --noEmit | |
| # ── Stage 2b: Security Scan ─────────────────────────────────────────────────── | |
| scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| permissions: | |
| contents: read | |
| security-events: write # upload SARIF to GitHub Security tab | |
| actions: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 25 | |
| # ── Dependency vulnerability audit ────────────────────────────────────── | |
| - name: npm audit — backend | |
| working-directory: ${{ env.PROJECT_DIR }}/backend | |
| # Fail only on high/critical CVEs; warn on moderate | |
| run: | | |
| npm install --omit=dev | |
| npm audit --audit-level=high || true | |
| npm audit --audit-level=critical | |
| - name: npm audit — frontend | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| run: | | |
| npm install | |
| npm audit --audit-level=high || true | |
| npm audit --audit-level=critical | |
| # ── IaC security scan (Terraform) ─────────────────────────────────────── | |
| - name: Checkov — Terraform IaC scan | |
| id: checkov | |
| uses: bridgecrewio/checkov-action@v12 | |
| with: | |
| directory: ${{ env.PROJECT_DIR }}/terraform | |
| framework: terraform | |
| output_format: sarif | |
| output_file_path: checkov.sarif | |
| # Set soft_fail: false to hard-block deploys on IaC policy violations | |
| soft_fail: true | |
| - name: Upload Checkov results to Security tab | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: checkov.sarif | |
| category: checkov-terraform | |
| # ── SAST — CodeQL (JavaScript / TypeScript) ───────────────────────────── | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v3 | |
| with: | |
| languages: javascript-typescript | |
| queries: security-extended | |
| - name: Build for CodeQL | |
| working-directory: ${{ env.PROJECT_DIR }}/frontend | |
| env: | |
| NEXT_PUBLIC_API_URL: https://example.execute-api.us-east-1.amazonaws.com | |
| run: npm run build | |
| - name: Run CodeQL analysis | |
| uses: github/codeql-action/analyze@v3 | |
| with: | |
| category: codeql-javascript | |
| # ── Stage 3: Deploy ─────────────────────────────────────────────────────────── | |
| deploy: | |
| name: Deploy to AWS | |
| runs-on: ubuntu-latest | |
| needs: [build, scan] | |
| if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' | |
| permissions: | |
| contents: read | |
| id-token: write # required for OIDC | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 25 | |
| - uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_wrapper: false | |
| # Inputs (workflow_dispatch) take priority; falls back to repo secrets for push-triggered runs | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-region: ${{ env.AWS_REGION }} | |
| aws-access-key-id: ${{ inputs.aws_access_key_id || secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ inputs.aws_secret_access_key || secrets.AWS_SECRET_ACCESS_KEY }} | |
| - name: Validate deployment settings | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${STUDENT_NAME}" ]; then | |
| echo "::error::Provide student_name as a workflow input or set repo variable URL_BOOKMARK_SAVER_STUDENT_NAME." | |
| exit 1 | |
| fi | |
| aws sts get-caller-identity | |
| - name: Deploy application | |
| working-directory: ${{ env.PROJECT_DIR }} | |
| env: | |
| CI: true | |
| SEED_DEMO_DATA: ${{ vars.URL_BOOKMARK_SAVER_SEED_DEMO_DATA || 'true' }} | |
| DEMO_SEED_COUNT: ${{ vars.URL_BOOKMARK_SAVER_DEMO_SEED_COUNT || '18' }} | |
| run: bash scripts/deploy.sh --name "$STUDENT_NAME" --region "$AWS_REGION" | |
| - name: Publish deployment summary | |
| working-directory: ${{ env.PROJECT_DIR }}/terraform | |
| shell: bash | |
| run: | | |
| { | |
| echo "## URL Bookmark Saver — Deployed :rocket:" | |
| echo "" | |
| echo "| | |" | |
| echo "|---|---|" | |
| echo "| **Region** | ${AWS_REGION} |" | |
| echo "| **Name** | ${STUDENT_NAME} |" | |
| echo "| **Frontend** | $(terraform output -raw frontend_url) |" | |
| echo "| **API** | $(terraform output -raw api_url) |" | |
| echo "| **Dashboard** | $(terraform output -raw dashboard_url) |" | |
| } >> "$GITHUB_STEP_SUMMARY" |