Skip to content

frontend-change

frontend-change #10

Workflow file for this run

name: Build, Push, and Deploy to EC2
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
inputs:
force_rebuild:
description: "Force rebuild of all Docker images"
required: false
default: false
type: boolean
permissions:
contents: read
env:
DOCKER_REGISTRY: docker.io
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
jobs:
detect-changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
backends: ${{ steps.set-services.outputs.backends }}
frontends: ${{ steps.set-services.outputs.frontends }}
has_backend_changes: ${{ steps.set-services.outputs.has_backend_changes }}
has_frontend_changes: ${{ steps.set-services.outputs.has_frontend_changes }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Check for changes
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
api-gateway:
- 'apps/api-gateway/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
auth-service:
- 'apps/auth-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
product-service:
- 'apps/product-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
order-service:
- 'apps/order-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
admin-service:
- 'apps/admin-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
chatting-service:
- 'apps/chatting-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
recommendation-service:
- 'apps/recommendation-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
logger-service:
- 'apps/logger-service/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
kafka-consumer:
- 'apps/kafka/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
user-ui:
- 'apps/user-ui/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
seller-ui:
- 'apps/seller-ui/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
admin-ui:
- 'apps/admin-ui/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.base.json'
- 'nx.json'
- name: Set services output
id: set-services
run: |
FORCE="${{ github.event.inputs.force_rebuild }}"
BACKENDS=()
FRONTENDS=()
if [ "$FORCE" = "true" ]; then
BACKENDS=("api-gateway" "auth-service" "product-service" "order-service" "admin-service" "chatting-service" "recommendation-service" "logger-service" "kafka-consumer")
FRONTENDS=("user-ui" "seller-ui" "admin-ui")
else
if [ "${{ steps.filter.outputs.api-gateway }}" = "true" ]; then BACKENDS+=("api-gateway"); fi
if [ "${{ steps.filter.outputs.auth-service }}" = "true" ]; then BACKENDS+=("auth-service"); fi
if [ "${{ steps.filter.outputs.product-service }}" = "true" ]; then BACKENDS+=("product-service"); fi
if [ "${{ steps.filter.outputs.order-service }}" = "true" ]; then BACKENDS+=("order-service"); fi
if [ "${{ steps.filter.outputs.admin-service }}" = "true" ]; then BACKENDS+=("admin-service"); fi
if [ "${{ steps.filter.outputs.chatting-service }}" = "true" ]; then BACKENDS+=("chatting-service"); fi
if [ "${{ steps.filter.outputs.recommendation-service }}" = "true" ]; then BACKENDS+=("recommendation-service"); fi
if [ "${{ steps.filter.outputs.logger-service }}" = "true" ]; then BACKENDS+=("logger-service"); fi
if [ "${{ steps.filter.outputs.kafka-consumer }}" = "true" ]; then BACKENDS+=("kafka-consumer"); fi
if [ "${{ steps.filter.outputs.user-ui }}" = "true" ]; then FRONTENDS+=("user-ui"); fi
if [ "${{ steps.filter.outputs.seller-ui }}" = "true" ]; then FRONTENDS+=("seller-ui"); fi
if [ "${{ steps.filter.outputs.admin-ui }}" = "true" ]; then FRONTENDS+=("admin-ui"); fi
fi
if [ ${#BACKENDS[@]} -eq 0 ]; then
echo "backends=[]" >> $GITHUB_OUTPUT
echo "has_backend_changes=false" >> $GITHUB_OUTPUT
else
JSON_BACKENDS=$(printf '%s\n' "${BACKENDS[@]}" | jq -R . | jq -s -c .)
echo "backends=$JSON_BACKENDS" >> $GITHUB_OUTPUT
echo "has_backend_changes=true" >> $GITHUB_OUTPUT
fi
if [ ${#FRONTENDS[@]} -eq 0 ]; then
echo "frontends=[]" >> $GITHUB_OUTPUT
echo "has_frontend_changes=false" >> $GITHUB_OUTPUT
else
JSON_FRONTENDS=$(printf '%s\n' "${FRONTENDS[@]}" | jq -R . | jq -s -c .)
echo "frontends=$JSON_FRONTENDS" >> $GITHUB_OUTPUT
echo "has_frontend_changes=true" >> $GITHUB_OUTPUT
fi
build-backend:
name: Build Backend ${{ matrix.service }}
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.has_backend_changes == 'true'
strategy:
fail-fast: false
matrix:
service: ${{ fromJson(needs.detect-changes.outputs.backends) }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Check if image already exists
id: check-image
run: |
SERVICE="${{ matrix.service }}"
REGISTRY_USER="${{ secrets.DOCKERHUB_USERNAME }}"
if [ -z "$REGISTRY_USER" ] || [ "${{ github.event_name }}" = "pull_request" ]; then
echo "exists=false" >> $GITHUB_OUTPUT
exit 0
fi
TAG="${REGISTRY_USER}/${SERVICE}:${{ github.sha }}"
if docker manifest inspect "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo ">>> Image $TAG already exists in Docker Hub. Skipping build."
else
echo "exists=false" >> $GITHUB_OUTPUT
echo ">>> Image $TAG does not exist in Docker Hub. Proceeding with build."
fi
- name: Set Dockerfile path
if: |
github.event.inputs.force_rebuild == 'true' ||
steps.check-image.outputs.exists != 'true'
id: vars
run: |
SERVICE="${{ matrix.service }}"
if [ "$SERVICE" = "kafka-consumer" ]; then
echo "dockerfile=apps/kafka/Dockerfile" >> $GITHUB_OUTPUT
else
echo "dockerfile=apps/$SERVICE/Dockerfile" >> $GITHUB_OUTPUT
fi
- name: Build and Push Image
if: |
github.event.inputs.force_rebuild == 'true' ||
steps.check-image.outputs.exists != 'true'
uses: docker/build-push-action@v5
with:
context: .
file: ${{ steps.vars.outputs.dockerfile }}
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:latest
${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.service }}
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
build-frontend:
name: Build Frontend ${{ matrix.service }}
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.has_frontend_changes == 'true'
strategy:
fail-fast: false
matrix:
service: ${{ fromJson(needs.detect-changes.outputs.frontends) }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Check if image already exists
id: check-image
run: |
SERVICE="${{ matrix.service }}"
REGISTRY_USER="${{ secrets.DOCKERHUB_USERNAME }}"
if [ -z "$REGISTRY_USER" ] || [ "${{ github.event_name }}" = "pull_request" ]; then
echo "exists=false" >> $GITHUB_OUTPUT
exit 0
fi
TAG="${REGISTRY_USER}/${SERVICE}:${{ github.sha }}"
if docker manifest inspect "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo ">>> Image $TAG already exists in Docker Hub. Skipping build."
else
echo "exists=false" >> $GITHUB_OUTPUT
echo ">>> Image $TAG does not exist in Docker Hub. Proceeding with build."
fi
- name: Build and Push Image
if: |
github.event.inputs.force_rebuild == 'true' ||
steps.check-image.outputs.exists != 'true'
uses: docker/build-push-action@v5
with:
context: .
file: apps/${{ matrix.service }}/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:latest
${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.service }}
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
deploy:
name: Deploy to EC2
runs-on: ubuntu-latest
needs: [detect-changes, build-backend, build-frontend]
if: |
always() &&
github.event_name != 'pull_request' &&
(needs.detect-changes.outputs.has_backend_changes == 'true' || needs.detect-changes.outputs.has_frontend_changes == 'true') &&
(needs.build-backend.result == 'success' || needs.build-backend.result == 'skipped') &&
(needs.build-frontend.result == 'success' || needs.build-frontend.result == 'skipped')
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up SSH Private Key
run: |
cat << 'EOF' > key.pem
${{ secrets.EC2_PRIVATE_KEY }}
EOF
chmod 600 key.pem
- name: Run Deployment Script
env:
EC2_HOST: ${{ secrets.EC2_HOST }}
SSH_KEY_PATH: ./key.pem
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
chmod +x scripts/deploy-production.sh
./scripts/deploy-production.sh