1+ name : Node.js API CI/CD Pipeline
2+
3+ on :
4+ push :
5+ branches :
6+ - main
7+ - develop
8+ pull_request :
9+ branches :
10+ - main
11+ - develop
12+
13+ env :
14+ AWS_REGION : us-east-1
15+ ECR_REPOSITORY : node-api
16+ ECS_SERVICE : node-api-service
17+ ECS_CLUSTER : node-api-cluster
18+ ECS_TASK_DEFINITION : node-api-task
19+
20+ jobs :
21+ lint-test :
22+ name : Lint and Test
23+ runs-on : ubuntu-22.04
24+ strategy :
25+ matrix :
26+ node-version : [18.x, 20.x]
27+ steps :
28+ - name : Checkout code
29+ uses : actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
30+
31+ - name : Setup Node.js ${{ matrix.node-version }}
32+ uses : actions/setup-node@60edb5dd545a775178fac7f3a11ecda6e5a4f69f # v4.0.2
33+ with :
34+ node-version : ${{ matrix.node-version }}
35+ cache : npm
36+
37+ - name : Install dependencies
38+ run : npm ci
39+
40+ - name : Run linter
41+ run : npm run lint --if-present
42+
43+ - name : Run tests
44+ run : npm test --if-present
45+
46+ - name : Generate coverage report
47+ run : npm run coverage --if-present
48+ continue-on-error : true
49+
50+ - name : Upload coverage to Codecov
51+ uses : codecov/codecov-action@125fc84a3e5e4183e5e9c34b23b9ef1d16cbf484 # v3.1.6
52+ if : always()
53+ with :
54+ files : ./coverage/coverage-final.json
55+ fail_ci_if_error : false
56+
57+ security-scan :
58+ name : Security Scan
59+ runs-on : ubuntu-22.04
60+ steps :
61+ - name : Checkout code
62+ uses : actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
63+
64+ - name : Run Trivy vulnerability scanner
65+ uses : aquasecurity/trivy-action@6e7b7d1fd3e4692731fcf9cdff0f8244cfad8103 # v0.24.0
66+ with :
67+ scan-type : fs
68+ scan-ref : .
69+ format : sarif
70+ output : trivy-results.sarif
71+
72+ - name : Upload Trivy results to GitHub Security tab
73+ uses : github/codeql-action/upload-sarif@cf7e9f23492505046de19cd2d8569798fc6f4eac # v3.25.4
74+ with :
75+ sarif_file : trivy-results.sarif
76+
77+ build-image :
78+ name : Build and Push Docker Image
79+ runs-on : ubuntu-22.04
80+ needs : [lint-test, security-scan]
81+ if : github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
82+ permissions :
83+ id-token : write
84+ contents : read
85+ outputs :
86+ image-uri : ${{ steps.image.outputs.image-uri }}
87+ steps :
88+ - name : Checkout code
89+ uses : actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
90+
91+ - name : Configure AWS credentials via OIDC
92+ uses : aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e93febb1 # v4.0.2
93+ with :
94+ role-to-assume : ${{ secrets.AWS_ROLE_TO_ASSUME }}
95+ aws-region : ${{ env.AWS_REGION }}
96+
97+ - name : Login to Amazon ECR
98+ id : login-ecr
99+ uses : aws-actions/amazon-ecr-login@062b18b96a7aff0 # v2.0.1
100+
101+ - name : Set up Docker Buildx
102+ uses : docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16eca30d26365 # v3.0.0
103+
104+ - name : Build and push Docker image
105+ id : docker-build
106+ uses : docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0
107+ with :
108+ context : .
109+ push : true
110+ tags : |
111+ ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
112+ ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest
113+ cache-from : type=gha
114+ cache-to : type=gha,mode=max
115+ build-args : |
116+ BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
117+ VCS_REF=${{ github.sha }}
118+
119+ - name : Output image URI
120+ id : image
121+ run : |
122+ echo "image-uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}" >> $GITHUB_OUTPUT
123+
124+ terraform-plan :
125+ name : Terraform Plan
126+ runs-on : ubuntu-22.04
127+ needs : build-image
128+ if : github.event_name == 'push' && github.ref == 'refs/heads/develop'
129+ permissions :
130+ id-token : write
131+ contents : read
132+ pull-requests : write
133+ steps :
134+ - name : Checkout code
135+ uses : actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
136+
137+ - name : Configure AWS credentials via OIDC
138+ uses : aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e93febb1 # v4.0.2
139+ with :
140+ role-to-assume : ${{ secrets.AWS_ROLE_TO_ASSUME }}
141+ aws-region : ${{ env.AWS_REGION }}
142+
143+ - name : Setup Terraform
144+ uses : hashicorp/setup-terraform@v2.0.3
145+ with :
146+ terraform_version : 1.7.0
147+
148+ - name : Terraform Format Check
149+ run : terraform fmt -check -recursive ./terraform
150+
151+ - name : Terraform Init
152+ run : cd terraform && terraform init -backend-config="bucket=${{ secrets.TERRAFORM_STATE_BUCKET }}" -backend-config="key=prod/terraform.tfstate" -backend-config="region=${{ env.AWS_REGION }}"
153+
154+ - name : Terraform Validate
155+ run : cd terraform && terraform validate
156+
157+ - name : Terraform Plan
158+ id : plan
159+ run : |
160+ cd terraform && terraform plan \
161+ -var="image_uri=${{ needs.build-image.outputs.image-uri }}" \
162+ -var="environment=staging" \
163+ -out=tfplan
164+ continue-on-error : true
165+
166+ - name : Comment Terraform Plan on PR
167+ if : github.event_name == 'pull_request'
168+ uses : actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
169+ with :
170+ script : |
171+ const fs = require('fs');
172+ const plan = fs.readFileSync('terraform/tfplan', 'utf8');
173+ github.rest.issues.createComment({
174+ issue_number: context.issue.number,
175+ owner: context.repo.owner,
176+ repo: context.repo.repo,
177+ body: `## Terraform Plan\n\`\`\`\n${plan}\n\`\`\``
178+ });
179+
180+ deploy-staging :
181+ name : Deploy to Staging
182+ runs-on : ubuntu-22.04
183+ needs : [build-image, terraform-plan]
184+ if : github.event_name == '
0 commit comments