Skip to content

Merge branch 'main' into ADM-709 #6630

Merge branch 'main' into ADM-709

Merge branch 'main' into ADM-709 #6630

name: Build and Deploy
on:
push:
branches: ["*"]
pull_request:
branches: ["main"]
workflow_dispatch:
jobs:
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:
scandir: "./ops"
fossa-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: FOSSA Check
run: |
curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install-latest.sh | bash
export FOSSA_API_KEY=${{secrets.FOSSA_API_KEY}}
fossa analyze
credential-check:
name: Credential Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
# - name: Trufflehog scan
# uses: trufflesecurity/trufflehog@main
# with:
# path: ./
# base: ${{ github.event.repository.default_branch }}
# head: HEAD
# extra_args: --only-verified
- name: Run Gitleaks check
run: |
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-v "${PWD}:/path" \
ghcr.io/gitleaks/gitleaks:latest \
detect \
--source="/path" \
-v --redact
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
- name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@master
with:
scan-type: "fs"
ignore-unfixed: true
exit-code: "1"
severity: "CRITICAL"
trivyignores: .trivyignore
- name: Run Trivy vulnerability scanner in IaC mode
uses: aquasecurity/trivy-action@master
with:
scan-type: "config"
exit-code: "1"
ignore-unfixed: true
severity: "CRITICAL"
trivyignores: .trivyignore
backend-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ vars.JAVA_VERSION }}
distribution: "adopt"
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up Gradle
uses: gradle/[email protected]
- name: Test and check
run: ./gradlew clean check
- name: Build
run: ./gradlew clean build
- name: Upload Test Report to Codacy
run: |
export CODACY_PROJECT_TOKEN=${{secrets.CODACY_PROJECT_TOKEN}}
bash <(curl -Ls https://coverage.codacy.com/get.sh)
backend-license-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ vars.JAVA_VERSION }}
distribution: "adopt"
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up Gradle
uses: gradle/[email protected]
- name: License check
run: ./gradlew clean checkLicense
- uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: backend-license-report
path: backend/build/reports/dependency-license/
retention-days: ${{ vars.RETENTION_DAYS }}
dot-star-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v4
- name: Java .*; check
shell: bash {0}
run: |
local result
result=$(grep -rin --exclude='*.svg' --exclude='*.png' --exclude='*.yaml' --exclude-dir='node_modules' '\.\*;' ./ || true)
if [ -n "$result" ]; then
echo "Error: Found files with [0-9]+px pattern:"
echo "$result"
exit 1
else
echo "No matching files found."
fi
px-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: CSS unit `px` check
shell: bash {0}
run: |
result=$(grep -rin --exclude='*.svg' --exclude='*.png' --exclude='*.yaml' --exclude-dir='node_modules' "[0-9]\+px" ./)
if [ -n "$result" ]; then
echo "Error: Found files with [0-9]+px pattern:"
echo "$result"
exit 1
else
echo "No matching files found."
fi
frontend-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
- name: Install & Lint
run: |
npm install -g pnpm
pnpm install --no-frozen-lockfile
pnpm lint
- name: Audit for vulnerabilities
run: pnpm dlx audit-ci@^6 --critical --report-type full
- name: Testing and coverage
run: |
pnpm coverage
- name: Building
run: pnpm build
- name: Upload Test Report to Codacy
run: |
export CODACY_PROJECT_TOKEN=${{secrets.CODACY_PROJECT_TOKEN}}
bash <(curl -Ls https://coverage.codacy.com/get.sh)
frontend-license-check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
- name: Install
run: |
npm install
- name: License compliance check
run: |
npm run license-compliance
# check-buildkite-status:
# if: ${{ github.event_name == 'pull_request' }}
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
#
# - name: Check BuildKite status
# run: |
# buildkite_status=$(curl -H "Authorization: Bearer ${{ secrets.BUILDKITE_TOKEN }}" "https://api.buildkite.com/v2/organizations/thoughtworks-Heartbeat/pipelines/heartbeat/builds?branch=main"| jq -r '.[0].state')
#
# if [ "$buildkite_status" != "passed" ]; then
# echo "BuildKite build failed. Cannot merge the PR."
# exit 1
# fi
deploy-infra:
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- fossa-check
- frontend-check
- px-check
- backend-check
- dot-star-check
- security-check
- shellcheck
- credential-check
- frontend-license-check
- backend-license-check
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4
# - name: Configure AWS credentials
# if: ${{ contains(github.event.head_commit.message, '[infra]') }}
# uses: aws-actions/configure-aws-credentials@v4
# with:
# role-to-assume: ${{ secrets.AWS_GITHUB_ACTION_ROLE }}
# aws-region: ${{ secrets.AWS_REGION}}
# role-session-name: MySessionName
# - name: Deploy infra
# if: ${{ contains(github.event.head_commit.message, '[infra]') }}
# env:
# SSHPublicKey: ${{ secrets.AWS_EC2_SSH_PUBLIC_KEY}}
# AWSHost: ${{ secrets.AWS_HOST }}
# AWSAccountId: ${{ secrets.AWS_ACCOUNT_ID }}
# AWSRegion: ${{ secrets.AWS_REGION }}
# BuildKiteToken: ${{ secrets.BUILDKITE_TOKEN }}
# SSHPrivateKey: ${{ secrets.AWS_EC2_SSH_PRIVATE_KEY}}
# run: |
# sh ./ops/infra/updateAwsResource.sh
build-backend:
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- deploy-infra
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_GITHUB_ACTION_ROLE }}
aws-region: ${{ secrets.AWS_REGION}}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push for Backend
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: heartbeat_backend
IMAGE_TAG: "hb${{ github.run_number }}"
run: |
docker build -t $REGISTRY/$REPOSITORY:latest ./ -f ./ops/infra/Dockerfile.backend
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG ./ -f ./ops/infra/Dockerfile.backend
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ steps.login-ecr.outputs.registry }}/heartbeat_backend:latest
format: "table"
exit-code: "1"
ignore-unfixed: true
severity: "CRITICAL,HIGH"
trivyignores: ".trivyignore"
# - name: Push for Backend
# env:
# REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# REPOSITORY: heartbeat_backend
# IMAGE_TAG: "hb${{ github.run_number }}"
# run: |
# docker push $REGISTRY/$REPOSITORY:latest
# docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
build-frontend:
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- deploy-infra
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_GITHUB_ACTION_ROLE }}
aws-region: ${{ secrets.AWS_REGION}}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push for Frontend
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: heartbeat_frontend
IMAGE_TAG: "hb${{ github.run_number }}"
run: |
docker build -t $REGISTRY/$REPOSITORY:latest ./ -f ./ops/infra/Dockerfile.frontend
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG ./ -f ./ops/infra/Dockerfile.frontend
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ steps.login-ecr.outputs.registry }}/heartbeat_frontend:latest
format: "table"
exit-code: "1"
ignore-unfixed: true
severity: "CRITICAL,HIGH"
trivyignores: ".trivyignore"
# - name: Push for Frontend
# env:
# REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# REPOSITORY: heartbeat_frontend
# IMAGE_TAG: "hb${{ github.run_number }}"
# run: |
# docker push $REGISTRY/$REPOSITORY:latest
# docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
build-mock-server:
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- deploy-infra
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_GITHUB_ACTION_ROLE }}
aws-region: ${{ secrets.AWS_REGION}}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag for MockServer
if: ${{ contains(github.event.head_commit.message, '[stub]') }}
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: heartbeat_stub
IMAGE_TAG: "hb${{ github.run_number }}"
run: |
docker build -t $REGISTRY/$REPOSITORY:latest ./ -f ./ops/infra/Dockerfile.stub
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG ./ -f ./ops/infra/Dockerfile.stub
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ steps.login-ecr.outputs.registry }}/heartbeat_stub:latest
format: "table"
exit-code: "1"
ignore-unfixed: true
severity: "CRITICAL,HIGH"
trivyignores: ".trivyignore"
# - name: Push for MockServer
# if: ${{ contains(github.event.head_commit.message, '[stub]') }}
# env:
# REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# REPOSITORY: heartbeat_stub
# IMAGE_TAG: "hb${{ github.run_number }}"
# run: |
# docker push $REGISTRY/$REPOSITORY:latest
# docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
deploy-e2e:
runs-on: ubuntu-latest
needs:
- build-backend
- build-frontend
steps:
- name: Checkout repo
# uses: actions/checkout@v4
run: echo "This is an empty step"
# - name: Update docker-compose.yaml
# run: |
# sed -i -e 's/heartbeat_backend:latest/${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}\/heartbeat_backend:hb${{ github.run_number }}/g' ops/infra/docker-compose.yml
# sed -i -e 's/heartbeat_frontend:latest/${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}\/heartbeat_frontend:hb${{ github.run_number }}/g' ops/infra/docker-compose.yml
# - name: Copy docker-compose to ec2
# uses: appleboy/scp-action@master
# with:
# host: ${{ secrets.AWS_EC2_IP_E2E }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# source: "./ops/infra/docker-compose.yml"
# target: "./"
# strip_components: 1
# - name: Deploy
# uses: appleboy/ssh-action@master
# env:
# REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# IMAGE_TAG: "hb${{ github.run_number }}"
# with:
# host: ${{ secrets.AWS_EC2_IP_E2E }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# script: |
# aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# cp "./ops/infra/docker-compose.yml" ./
# # docker-compose down
# if [[ -n $(docker images -f label=arch=Backend -q) ]]; then docker rmi -f $(docker images -f label=arch=Backend -q); fi
# if [[ -n $(docker images -f label=arch=Frontend -q) ]]; then docker rmi -f $(docker images -f label=arch=Frontend -q); fi
# docker pull $REGISTRY/heartbeat_backend:$IMAGE_TAG
# docker pull $REGISTRY/heartbeat_frontend:$IMAGE_TAG
# export MOCK_SERVER_URL=${{ secrets.AWS_EC2_IP_MOCK_SERVER }}:${{ secrets.AWS_EC2_IP_E2E_PORT }}
# export SPRING_PROFILES_ACTIVE="e2e"
# docker-compose up -d frontend
deploy-stub:
runs-on: ubuntu-latest
needs:
- build-mock-server
steps:
- name: Checkout repo
# uses: actions/checkout@v4
run: echo "This is an empty step"
# - name: Update docker-compose.yaml
# if: ${{ contains(github.event.head_commit.message, '[stub]') }}
# run: |
# sed -i -e 's/heartbeat_stub:latest/${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}\/heartbeat_stub:hb${{ github.run_number }}/g' ops/infra/docker-compose.yml
# mv ops/infra/docker-compose.yml ops/infra/docker-compose-stub.yml
# - name: Copy docker-compose to ec2
# uses: appleboy/scp-action@master
# if: ${{ contains(github.event.head_commit.message, '[stub]') }}
# with:
# host: ${{ secrets.AWS_EC2_IP_MOCK_SERVER }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# source: "./ops/infra/docker-compose-stub.yml"
# target: "./"
# strip_components: 1
# - name: Deploy
# if: ${{ contains(github.event.head_commit.message, '[stub]') }}
# uses: appleboy/ssh-action@master
# env:
# REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# IMAGE_TAG: "hb${{ github.run_number }}"
# with:
# host: ${{ secrets.AWS_EC2_IP_MOCK_SERVER }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# script: |
# aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# cp "./ops/infra/docker-compose-stub.yml" ./
# # docker-compose down stub
# if [[ -n $(docker images -f label=arch=stubs -q) ]]; then docker rmi -f $(docker images -f label=arch=stubs -q); fi
# docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}/heartbeat_stub:latest
# export MOCK_SERVER_PORT=${{ secrets.AWS_EC2_IP_E2E_PORT }}
# docker-compose -f docker-compose-stub.yml up -d stub
e2e:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
needs:
- deploy-e2e
- deploy-stub
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
node-version: ${{ vars.NODE_VERSION }}
- name: Install dependencies
run: |
npm install -g pnpm
pnpm install --no-frozen-lockfile
- name: Run E2E
env:
APP_ORIGIN: ${{ vars.APP_HTTP_SCHEDULE }}://${{ secrets.AWS_EC2_IP_E2E }}:${{ secrets.AWS_EC2_IP_E2E_FRONTEND_PORT }}
run: pnpm run e2e
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: frontend/cypress/videos/
retention-days: ${{ vars.RETENTION_DAYS }}
deploy:
runs-on: ubuntu-latest
needs:
- e2e
steps:
- name: Checkout repo
# uses: actions/checkout@v4
run: echo "This is an empty step"
# - name: Update docker-compose.yaml
# run: |
# sed -i -e 's/heartbeat_backend:latest/${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}\/heartbeat_backend:hb${{ github.run_number }}/g' ops/infra/docker-compose.yml
# sed -i -e 's/heartbeat_frontend:latest/${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}\/heartbeat_frontend:hb${{ github.run_number }}/g' ops/infra/docker-compose.yml
# - name: Copy docker-compose to ec2
# uses: appleboy/scp-action@master
# with:
# host: ${{ secrets.AWS_EC2_IP }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# source: "./ops/infra/docker-compose.yml"
# target: "./"
# strip_components: 1
# - name: Deploy
# uses: appleboy/ssh-action@master
# env:
# REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# IMAGE_TAG: "hb${{ github.run_number }}"
# with:
# host: ${{ secrets.AWS_EC2_IP }}
# username: ${{ secrets.AWS_USERNAME }}
# key: ${{ secrets.AWS_PRIVATE_KEY }}
# port: ${{ secrets.AWS_SSH_PORT }}
# script: |
# aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}
# cp "./ops/infra/docker-compose.yml" ./
# # docker-compose down
# if [[ -n $(docker images -f label=app=Heartbeat -q) ]]; then docker rmi -f $(docker images -f label=app=Heartbeat -q); fi
# docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}/heartbeat_backend:$IMAGE_TAG
# docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_HOST }}/heartbeat_frontend:$IMAGE_TAG
# docker-compose up -d frontend