feat: Add GitHub Actions workflows for frontend CI/CD

- Add lint.yml: TypeScript and ESLint checks
- Add build.yml: Vite application build pipeline
- Add docker-build.yml: Multi-arch container image builds
- Add deploy.yml: Production deployment to Docker Swarm
- Add dependabot.yml: Automated dependency updates

Follows Single Responsibility Principle with focused workflows.
Includes security best practices, caching, and deployment automation.
This commit is contained in:
Damir Mukimov 2025-11-27 04:47:20 +01:00
parent 4a23f496fa
commit 09a2d087e4
No known key found for this signature in database
GPG Key ID: 42996CC7C73BC750
5 changed files with 206 additions and 0 deletions

16
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,16 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"
include: "scope"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "deps"
include: "scope"

34
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Build
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
build-app:
name: Build Application
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build application
run: yarn build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: dist/
retention-days: 30

60
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Deploy
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
version:
description: "Version to deploy (e.g., v1.2.3)"
required: true
type: string
jobs:
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
environment:
name: production
url: https://tercul.example.com
steps:
- name: Check out code
uses: actions/checkout@v5
- name: Extract version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "VERSION=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
fi
- name: Deploy to Docker Swarm
env:
SWARM_HOST: ${{ secrets.SWARM_HOST }}
SWARM_SSH_KEY: ${{ secrets.SWARM_SSH_KEY }}
IMAGE_TAG: ${{ steps.version.outputs.VERSION }}
run: |
# Uncomment and configure for actual Docker Swarm deployment
# echo "$SWARM_SSH_KEY" > swarm_key
# chmod 600 swarm_key
# ssh -i swarm_key -o StrictHostKeyChecking=no \
# deploy@$SWARM_HOST \
# "docker service update \
# --image ghcr.io/${{ github.repository }}-frontend:${IMAGE_TAG} \
# tercul-frontend"
# rm swarm_key
echo "Deploying frontend version ${{ steps.version.outputs.VERSION }} to production"
echo "Image: ghcr.io/${{ github.repository }}-frontend:${IMAGE_TAG}"
- name: Deployment summary
run: |
echo "### Frontend Deployment Complete :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: ghcr.io/${{ github.repository }}-frontend:${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **Environment**: Production" >> $GITHUB_STEP_SUMMARY
echo "- **Deployed at**: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY

65
.github/workflows/docker-build.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Docker Build
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
jobs:
build-image:
name: Build Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Check out code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}-frontend
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=long
- name: Build and push
id: push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v3
with:
subject-name: ghcr.io/${{ github.repository }}-frontend
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

31
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Lint
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
typescript-lint:
name: TypeScript & ESLint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Type check
run: yarn check
- name: Lint
run: yarn lint
if: always()