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 : production
18+
19+ permissions :
20+ contents : read
21+ id-token : write
22+
23+ jobs :
24+ lint :
25+ runs-on : ubuntu-22.04
26+ steps :
27+ - name : Checkout code
28+ uses : actions/checkout@9bb56186c3b09b1872191b74733305c135a6d0e9
29+
30+ - name : Setup Node.js
31+ uses : actions/setup-node@60edb5dd545a775178fac7f3f667f4173c5c7605
32+ with :
33+ node-version : ' 18.18.0'
34+ cache : npm
35+
36+ - name : Install dependencies
37+ run : npm ci
38+
39+ - name : Run ESLint
40+ run : npm run lint --if-present
41+
42+ test :
43+ runs-on : ubuntu-22.04
44+ needs : lint
45+ steps :
46+ - name : Checkout code
47+ uses : actions/checkout@9bb56186c3b09b1872191b74733305c135a6d0e9
48+
49+ - name : Setup Node.js
50+ uses : actions/setup-node@60edb5dd545a775178fac7f3f667f4173c5c7605
51+ with :
52+ node-version : ' 18.18.0'
53+ cache : npm
54+
55+ - name : Install dependencies
56+ run : npm ci
57+
58+ - name : Run tests
59+ run : npm test --if-present
60+
61+ - name : Generate coverage report
62+ run : npm run test:coverage --if-present
63+
64+ - name : Upload coverage to Codecov
65+ uses : codecov/codecov-action@v3.1.4
66+ with :
67+ files : ./coverage/coverage-final.json
68+ flags : unittests
69+ fail_ci_if_error : false
70+
71+ build :
72+ runs-on : ubuntu-22.04
73+ needs : test
74+ if : github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
75+ outputs :
76+ image_uri : ${{ steps.image.outputs.uri }}
77+ steps :
78+ - name : Checkout code
79+ uses : actions/checkout@9bb56186c3b09b1872191b74733305c135a6d0e9
80+
81+ - name : Configure AWS credentials
82+ uses : aws-actions/configure-aws-credentials@v4.0.1
83+ with :
84+ role-to-assume : ${{ secrets.AWS_ROLE_TO_ASSUME }}
85+ aws-region : ${{ env.AWS_REGION }}
86+ role-duration-seconds : 900
87+
88+ - name : Login to Amazon ECR
89+ id : login-ecr
90+ uses : aws-actions/amazon-ecr-login@v2.0.1
91+
92+ - name : Build Docker image
93+ id : docker_build
94+ uses : docker/build-push-action@v5.1.0
95+ with :
96+ context : .
97+ push : false
98+ tags : |
99+ ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
100+ ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest
101+ cache-from : type=registry,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:buildcache
102+ cache-to : type=registry,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:buildcache,mode=max
103+ outputs : type=docker,dest=/tmp/image.tar
104+
105+ - name : Push Docker image to ECR
106+ run : |
107+ docker load --input /tmp/image.tar
108+ docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
109+ docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest
110+
111+ - name : Output image URI
112+ id : image
113+ run : echo "uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}" >> $GITHUB_OUTPUT
114+
115+ validate-terraform :
116+ runs-on : ubuntu-22.04
117+ needs : test
118+ steps :
119+ - name : Checkout code
120+ uses : actions/checkout@9bb56186c3b09b1872191b74733305c135a6d0e9
121+
122+ - name : Setup Terraform
123+ uses : hashicorp/setup-terraform@v2.1.0
124+ with :
125+ terraform_version : 1.6.0
126+
127+ - name : Terraform Format Check
128+ run : terraform fmt -check -recursive ./terraform
129+
130+ - name : Terraform Init
131+ run : cd terraform && terraform init -backend=false
132+
133+ - name : Terraform Validate
134+ run : cd terraform && terraform validate
135+
136+ - name : TFLint
137+ uses : terraform-linters/setup-tflint@v4.0.0
138+ with :
139+ tflint_version : v0.48.0
140+
141+ - name : Run TFLint
142+ run : cd terraform && tflint --init && tflint
143+
144+ deploy-to-ecs :
145+ runs-on : ubuntu-22.04
146+ needs : [build, validate-terraform]
147+ if : github.ref == 'refs/heads/main'
148+ environment : production
149+ steps :
150+ - name : Checkout code
151+ uses : actions/checkout@9bb56186c3b09b1872191b74733305c135a6d0e9
152+
153+ - name : Configure AWS credentials
154+ uses : aws-actions/configure-aws-credentials@v4.0.1
155+ with :
156+ role-to-assume : ${{ secrets.AWS_ROLE_TO_ASSUME }}
157+ aws-region : ${{ env.AWS_REGION }}
158+ role-duration-seconds : 1800
159+
160+ - name : Setup Terraform
161+ uses : hashicorp/setup-terraform@v2.1.0
162+ with :
163+ terraform_version : 1.6.0
164+
165+ - name : Terraform Init
166+ run : cd terraform && terraform init
167+
168+ - name : Terraform Plan
169+ run : cd terraform && terraform plan -var="image_tag=${{ needs.build.outputs.image_uri }}" -out=tfplan
170+
171+ - name : Terraform Apply
172+ if : github.ref == 'refs/heads/main'
173+ run : cd terraform && terraform apply -auto-approve tfplan
174+
175+ - name : Update ECS service
176+ run : |
177+ aws ecs update-service \
178+ --cluster ${{ env.ECS_CLUSTER }} \
179+ --service ${{ env.ECS_SERVICE }} \
180+ --force-new-deployment \
181+ --region ${{ env.AWS_REGION }}
182+
183+ - name : Wait for ECS service update
184+ run : |
185+ aws ecs wait services-stable \
186+ --cluster ${{ env.ECS_CLUSTER }} \
187+ --services ${{ env.ECS_SERVICE }} \
188+ --region ${{ env.AWS_REGION }}
189+
190+ notify :
191+ runs-on : ubuntu-22.04
192+ needs : [lint, test, build, validate-terraform, deploy-to-ecs]
193+ if : always()
194+ steps :
195+ - name : Slack notification on failure
196+ if : failure()
197+ uses : slackapi/slack-github-action@v1.24.0
198+ with :
199+ webhook-url : ${{ secrets.SLACK_WEBHOOK }}
200+ payload : |
201+ {
202+ "text": "GitHub Action failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
203+ }
204+
205+ - name : Slack notification on success
206+ if : success()
207+ uses : slackapi/slack-github-action@v1.24.0
208+ with :
209+ webhook-url : ${{ secrets.SLACK_WEBHOOK }}
210+ payload : |
211+ {
212+ "text": "GitHub Action succeeded: ${{ github.server_url }}/${{ github.repository }}/
0 commit comments