diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..19bab33 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,208 @@ +name: CI/CD Pipeline for Node.js REST API + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +env: + AWS_REGION: us-east-1 + ECR_REPOSITORY: nodejs-rest-api + ECS_SERVICE: nodejs-rest-api-service + ECS_CLUSTER: nodejs-rest-api-cluster + ECS_TASK_DEFINITION: nodejs-rest-api-task + +permissions: + id-token: write + contents: read + +jobs: + lint: + name: Lint Code + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Setup Node.js + uses: actions/setup-node@60edb5dd545a775178fac7f3ce8f4f8db2c5c91e + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint --if-present + + - name: Run Prettier check + run: npm run format:check --if-present + + test: + name: Run Tests + runs-on: ubuntu-22.04 + needs: lint + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Setup Node.js + uses: actions/setup-node@60edb5dd545a775178fac7f3ce8f4f8db2c5c91e + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run unit tests + run: npm run test --if-present + + - name: Run integration tests + run: npm run test:integration --if-present + + - name: Generate coverage report + run: npm run test:coverage --if-present + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f87a6204de0b3bbce7 + with: + files: ./coverage/coverage-final.json + flags: unittests + fail_ci_if_error: false + + security-scan: + name: Security Scan + runs-on: ubuntu-22.04 + needs: lint + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Setup Node.js + uses: actions/setup-node@60edb5dd545a775178fac7f3ce8f4f8db2c5c91e + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run npm audit + run: npm audit --production || true + + - name: Run Snyk scan + uses: snyk/actions/node@b98d528857969a2c7d1d62a8e6a469e0521d658d + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high + continue-on-error: true + + build: + name: Build Application + runs-on: ubuntu-22.04 + needs: [lint, test] + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Setup Node.js + uses: actions/setup-node@60edb5dd545a775178fac7f3ce8f4f8db2c5c91e + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build application + run: npm run build --if-present + + - name: Upload build artifacts + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35165aff7ee4d3e0 + with: + name: build-artifacts + path: dist/ + retention-days: 1 + + build-and-push-image: + name: Build and Push Docker Image + runs-on: ubuntu-22.04 + needs: [build, security-scan] + if: github.event_name == 'push' + outputs: + image-uri: ${{ steps.image.outputs.image-uri }} + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Download build artifacts + uses: actions/download-artifact@65a9edc5881444af0b9a21b5bd54bp4562bebdf7 + with: + name: build-artifacts + path: dist/ + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e3a5de7a + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@2fc7aceee023b451c3dd37ef7147e18b74f7ac565 + + - name: Build, tag, and push image to Amazon ECR + id: image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest + echo "image-uri=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@595324a9b052078dec94873e36ab3f8ef055adff1 + with: + image-ref: ${{ steps.image.outputs.image-uri }} + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results + uses: github/codeql-action/upload-sarif@4dd8ab6fcee4130fa2422af25490ec3f58fe9534 + with: + sarif_file: 'trivy-results.sarif' + + deploy-to-ecs: + name: Deploy to AWS ECS + runs-on: ubuntu-22.04 + needs: build-and-push-image + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + environment: + name: production + url: https://api.example.com + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c7467393582ac550c2ba79612f + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e3a5de7a + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + + - name: Get task definition + run: | + aws ecs describe-task-definition \ + --task-definition $ECS_TASK_DEFINITION \ + --region $ \ No newline at end of file