π fix: resolve semantic-release pre-commit hook conflicts in CI/CD #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: π CI/CD Pipeline | |
on: | |
push: | |
branches: [main] | |
pull_request: | |
branches: [main] | |
workflow_dispatch: | |
permissions: | |
contents: write | |
issues: write | |
pull-requests: write | |
id-token: write | |
jobs: | |
# Test & Lint both client and server | |
test-and-lint: | |
name: π§ͺ Test & Lint | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
package: [client, server] | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
- name: π¦ Setup Node.js | |
uses: actions/setup-node@v3 | |
with: | |
node-version: '20' | |
cache: 'npm' | |
cache-dependency-path: '${{ matrix.package }}/package-lock.json' | |
- name: π₯ Install Dependencies | |
run: | | |
cd ${{ matrix.package }} | |
npm ci --include=optional || npm ci --omit=optional || (rm -rf node_modules package-lock.json && npm install --omit=optional) | |
- name: π Lint | |
run: | | |
cd ${{ matrix.package }} | |
npm run lint | |
- name: π§ͺ Test | |
run: | | |
cd ${{ matrix.package }} | |
npm run test:ci || npm run test:coverage || npm run test | |
- name: π Upload Coverage | |
uses: codecov/codecov-action@v3 | |
if: matrix.package == 'server' | |
with: | |
files: ./server/coverage/lcov.info | |
flags: ${{ matrix.package }} | |
name: ${{ matrix.package }}-coverage | |
fail_ci_if_error: false | |
# Build applications (only on main branch) | |
build: | |
name: ποΈ Build | |
runs-on: ubuntu-latest | |
needs: test-and-lint | |
if: github.ref == 'refs/heads/main' | |
strategy: | |
matrix: | |
package: [client, server] | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
- name: π¦ Setup Node.js | |
uses: actions/setup-node@v3 | |
with: | |
node-version: '20' | |
cache: 'npm' | |
cache-dependency-path: '${{ matrix.package }}/package-lock.json' | |
- name: π₯ Install Dependencies | |
run: | | |
cd ${{ matrix.package }} | |
npm ci --include=optional || npm ci --omit=optional || (rm -rf node_modules package-lock.json && npm install --omit=optional) | |
- name: π¨ Build Version Info | |
env: | |
BUILD_NUMBER: ${{ github.run_number }} | |
NODE_ENV: production | |
run: | | |
cd ${{ matrix.package }} | |
npm run build:version | |
- name: ποΈ Build Client | |
if: matrix.package == 'client' | |
env: | |
NODE_ENV: production | |
EXPO_PUBLIC_SUPABASE_URL: ${{ secrets.EXPO_PUBLIC_SUPABASE_URL }} | |
EXPO_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.EXPO_PUBLIC_SUPABASE_ANON_KEY }} | |
run: | | |
cd client | |
npm run build:web | |
- name: ποΈ Build Server | |
if: matrix.package == 'server' | |
run: | | |
cd server | |
npm run build | |
- name: π€ Upload Build Artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.package }}-build | |
path: | | |
${{ matrix.package }}/dist | |
${{ matrix.package }}/web/dist | |
${{ matrix.package }}/package.json | |
${{ matrix.package }}/package-lock.json | |
retention-days: 30 | |
# Release with semantic versioning (only on main) | |
release: | |
name: π Release | |
runs-on: ubuntu-latest | |
needs: build | |
if: github.ref == 'refs/heads/main' | |
permissions: | |
contents: write | |
issues: write | |
pull-requests: write | |
id-token: write | |
outputs: | |
new-release-published: ${{ steps.semantic.outputs.new-release-published }} | |
new-release-version: ${{ steps.semantic.outputs.new-release-version }} | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
with: | |
fetch-depth: 0 | |
persist-credentials: false | |
- name: π¦ Setup Node.js | |
uses: actions/setup-node@v3 | |
with: | |
node-version: '20' | |
cache: 'npm' | |
- name: π₯ Install Dependencies | |
run: npm ci | |
- name: π§ Configure Git | |
run: | | |
git config --global user.name "github-actions[bot]" | |
git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git | |
- name: π Semantic Release | |
id: semantic | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
GIT_AUTHOR_NAME: github-actions[bot] | |
GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
GIT_COMMITTER_NAME: github-actions[bot] | |
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
run: npm run release | |
# Deploy to AWS (only if new release) | |
deploy: | |
name: π Deploy | |
runs-on: ubuntu-latest | |
needs: release | |
if: needs.release.outputs.new-release-published == 'true' | |
environment: production | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
- name: π₯ Download Build Artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: ./artifacts | |
- name: π§ Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v2 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
aws-region: ${{ secrets.AWS_REGION }} | |
# Deploy Client to S3 | |
- name: π Deploy Client to S3 | |
run: | | |
if [ -d "./artifacts/client-build/web/dist" ]; then | |
aws s3 sync ./artifacts/client-build/web/dist/ s3://${{ secrets.S3_BUCKET }} --delete | |
echo "β Client deployed to S3" | |
else | |
echo "β Client build not found" | |
fi | |
# Deploy Server to EC2 | |
- name: π¦ Build Server Docker Image | |
run: | | |
cd server | |
npm ci --include=optional || npm ci --omit=optional || (rm -rf node_modules package-lock.json && npm install --omit=optional) | |
docker build -t lab1-todoapp-server:${{ needs.release.outputs.new-release-version }} --target production . | |
docker save lab1-todoapp-server:${{ needs.release.outputs.new-release-version }} | gzip > lab1-todoapp-server.tar.gz | |
- name: π Deploy Server to EC2 | |
uses: appleboy/[email protected] | |
with: | |
host: ${{ secrets.EC2_HOST }} | |
username: ${{ secrets.EC2_USER }} | |
key: ${{ secrets.EC2_PRIVATE_KEY }} | |
source: "server/lab1-todoapp-server.tar.gz" | |
target: "/home/ubuntu/" | |
- name: π Update Server on EC2 | |
uses: appleboy/[email protected] | |
with: | |
host: ${{ secrets.EC2_HOST }} | |
username: ${{ secrets.EC2_USER }} | |
key: ${{ secrets.EC2_PRIVATE_KEY }} | |
script: | | |
# Load new image | |
cd /home/ubuntu | |
sudo docker load < server/lab1-todoapp-server.tar.gz | |
# Stop and remove old container | |
sudo docker stop lab1-todoapp-server || true | |
sudo docker rm lab1-todoapp-server || true | |
# Run new container | |
sudo docker run -d \ | |
--name lab1-todoapp-server \ | |
--restart unless-stopped \ | |
-p 80:3000 \ | |
-e NODE_ENV=production \ | |
-e PORT=3000 \ | |
-e SUPABASE_DB_URL="${{ secrets.SUPABASE_DB_URL }}" \ | |
-e SUPABASE_KEY="${{ secrets.SUPABASE_KEY }}" \ | |
-e SUPABASE_URL="${{ secrets.SUPABASE_URL }}" \ | |
-e ALLOWED_ORIGINS="http://56.228.14.41,https://lab1.warteamx.com,http://lab1-todoapp.s3-website.eu-north-1.amazonaws.com" \ | |
lab1-todoapp-server:${{ needs.release.outputs.new-release-version }} | |
# Clean up old images | |
sudo docker image prune -f | |
- name: β Deployment Summary | |
run: | | |
echo "π Deployment completed for version ${{ needs.release.outputs.new-release-version }}" | |
echo "π Client: http://${{ secrets.S3_BUCKET }}.s3-website.eu-north-1.amazonaws.com" | |
echo "π₯οΈ Server: http://${{ secrets.EC2_HOST }}/api" | |
- name: π Health Check | |
run: | | |
sleep 30 | |
if curl -f http://${{ secrets.EC2_HOST }}/api/health; then | |
echo "β Server health check passed" | |
else | |
echo "β Server health check failed" | |
exit 1 | |
fi |