Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
name: CI/CD Pipeline - Node.js API to AWS ECS

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

permissions:
contents: read
id-token: write

jobs:
lint-and-test:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1

- name: Setup Node.js
uses: actions/setup-node@v4.0.2
with:
node-version: '18.x'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run linter
run: npm run lint

- name: Run tests
run: npm run test

- name: Generate coverage report
run: npm run test:coverage
continue-on-error: true

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3.1.5
with:
files: ./coverage/coverage-final.json
flags: unittests
fail_ci_if_error: false

build-and-push:
needs: lint-and-test
runs-on: ubuntu-22.04
if: github.event_name == 'push'
outputs:
image-uri: ${{ steps.image.outputs.image-uri }}
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@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@v2.0.1

- name: Build Docker image
id: docker-build
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker tag $REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $REGISTRY/$ECR_REPOSITORY:latest

- name: Push image to Amazon ECR
id: image
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker push $REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $REGISTRY/$ECR_REPOSITORY:latest
echo "image-uri=$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

- name: Create image definitions JSON
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
printf '[{"name":"%s","imageUri":"%s"}]' $ECS_TASK_DEFINITION $REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG > image-definitions.json

- name: Upload image definitions
uses: actions/upload-artifact@v4.3.1
with:
name: image-definitions
path: image-definitions.json
retention-days: 1

deploy-staging:
needs: build-and-push
runs-on: ubuntu-22.04
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
environment:
name: staging
url: https://staging-api.example.com
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1

- name: Download image definitions
uses: actions/download-artifact@v4.1.0
with:
name: image-definitions

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ env.AWS_REGION }}

- name: Get current task definition
run: |
aws ecs describe-task-definition --task-definition $ECS_TASK_DEFINITION --query taskDefinition > task-definition.json

- name: Update ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0
with:
task-definition: task-definition.json
container-name: ${{ env.ECS_TASK_DEFINITION }}
image: ${{ needs.build-and-push.outputs.image-uri }}

- name: Register new task definition
uses: aws-actions/amazon-ecs-register-task-definition@v1.3.0
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}

- name: Deploy to ECS (Staging)
uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.11
with:
service: ${{ env.ECS_SERVICE }}-staging
cluster: ${{ env.ECS_CLUSTER }}-staging
task-definition: ${{ steps.task-def.outputs.task-definition }}
wait-for-service-stability: true

deploy-production:
needs: build-and-push
runs-on: ubuntu-22.04
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@v4.1.1

- name: Download image definitions
uses: actions/download-artifact@v4.1.0
with:
name: image-definitions

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ env.AWS_REGION }}

- name: Get current task definition
run: |
aws ecs describe-task-definition --task-definition $ECS_TASK_DEFINITION --query taskDefinition > task-definition.json

- name: Update ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0
with:
task-definition: task-definition.json
container-name: ${{ env.ECS_TASK_DEFINITION }}
image: ${{ needs.build-and-push.outputs.image-uri }}

- name: Register new task definition
uses: aws-actions/amazon-ecs-register-task-definition@v1.3.0
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}

- name: Deploy to ECS (Production)
uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.11
with:
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
task-definition: ${{ steps.task-def.outputs.task-definition }}
wait-for-service-stability: true

terraform-plan:
runs-on: ubuntu-22.04
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ secrets.AWS_ROLE