From b73d22ac32778a77e062c9f4dee099c6923a1254 Mon Sep 17 00:00:00 2001 From: Mohan Veerachamy <76078705+mohanranjith@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:18:37 -0500 Subject: [PATCH] ci: add CI/CD pipeline via OpsTools --- .github/workflows/ci-cd.yml | 184 ++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 .github/workflows/ci-cd.yml diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..b754ea6 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,184 @@ +name: Node.js API CI/CD Pipeline + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +env: + AWS_REGION: us-east-1 + ECR_REPOSITORY: node-api + ECS_SERVICE: node-api-service + ECS_CLUSTER: node-api-cluster + ECS_TASK_DEFINITION: node-api-task + +jobs: + lint-test: + name: Lint and Test + runs-on: ubuntu-22.04 + strategy: + matrix: + node-version: [18.x, 20.x] + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@60edb5dd545a775178fac7f3a11ecda6e5a4f69f # v4.0.2 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint --if-present + + - name: Run tests + run: npm test --if-present + + - name: Generate coverage report + run: npm run coverage --if-present + continue-on-error: true + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@125fc84a3e5e4183e5e9c34b23b9ef1d16cbf484 # v3.1.6 + if: always() + with: + files: ./coverage/coverage-final.json + fail_ci_if_error: false + + security-scan: + name: Security Scan + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@6e7b7d1fd3e4692731fcf9cdff0f8244cfad8103 # v0.24.0 + with: + scan-type: fs + scan-ref: . + format: sarif + output: trivy-results.sarif + + - name: Upload Trivy results to GitHub Security tab + uses: github/codeql-action/upload-sarif@cf7e9f23492505046de19cd2d8569798fc6f4eac # v3.25.4 + with: + sarif_file: trivy-results.sarif + + build-image: + name: Build and Push Docker Image + runs-on: ubuntu-22.04 + needs: [lint-test, security-scan] + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') + permissions: + id-token: write + contents: read + outputs: + image-uri: ${{ steps.image.outputs.image-uri }} + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Configure AWS credentials via OIDC + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e93febb1 # v4.0.2 + 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@062b18b96a7aff0 # v2.0.1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16eca30d26365 # v3.0.0 + + - name: Build and push Docker image + id: docker-build + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + with: + context: . + push: true + tags: | + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }} + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') + VCS_REF=${{ github.sha }} + + - name: Output image URI + id: image + run: | + echo "image-uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}" >> $GITHUB_OUTPUT + + terraform-plan: + name: Terraform Plan + runs-on: ubuntu-22.04 + needs: build-image + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + permissions: + id-token: write + contents: read + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Configure AWS credentials via OIDC + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e93febb1 # v4.0.2 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2.0.3 + with: + terraform_version: 1.7.0 + + - name: Terraform Format Check + run: terraform fmt -check -recursive ./terraform + + - name: Terraform Init + run: cd terraform && terraform init -backend-config="bucket=${{ secrets.TERRAFORM_STATE_BUCKET }}" -backend-config="key=prod/terraform.tfstate" -backend-config="region=${{ env.AWS_REGION }}" + + - name: Terraform Validate + run: cd terraform && terraform validate + + - name: Terraform Plan + id: plan + run: | + cd terraform && terraform plan \ + -var="image_uri=${{ needs.build-image.outputs.image-uri }}" \ + -var="environment=staging" \ + -out=tfplan + continue-on-error: true + + - name: Comment Terraform Plan on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const fs = require('fs'); + const plan = fs.readFileSync('terraform/tfplan', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## Terraform Plan\n\`\`\`\n${plan}\n\`\`\`` + }); + + deploy-staging: + name: Deploy to Staging + runs-on: ubuntu-22.04 + needs: [build-image, terraform-plan] + if: github.event_name == ' \ No newline at end of file