π fix(server): fix server ci version on healthcheck #41
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 }} | |
rm -f package-lock.json | |
npm install --package-lock-only | |
npm ci | |
- 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@v4 | |
with: | |
files: ./${{ matrix.package }}/coverage/lcov.info | |
flags: ${{ matrix.package }} | |
name: ${{ matrix.package }}-coverage | |
token: ${{ secrets.CODECOV_TOKEN }} | |
fail_ci_if_error: false | |
# Release with semantic versioning (only on main branch) | |
release: | |
name: π Release | |
runs-on: ubuntu-latest | |
needs: test-and-lint | |
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 | |
SEMANTIC_RELEASE: true | |
run: | | |
npm run release > semantic-release-output.txt 2>&1 | |
cat semantic-release-output.txt | |
# Check if a new release was published | |
if grep -q "Published release" semantic-release-output.txt; then | |
echo "new-release-published=true" >> $GITHUB_OUTPUT | |
# Extract version from the output | |
VERSION=$(grep "Published release" semantic-release-output.txt | sed -n 's/.*Published release \([0-9.]*\).*/\1/p') | |
echo "new-release-version=$VERSION" >> $GITHUB_OUTPUT | |
echo "β New release published: $VERSION" | |
else | |
echo "new-release-published=false" >> $GITHUB_OUTPUT | |
echo "No new release published" | |
fi | |
# Build applications (only if new release was published) | |
build: | |
name: ποΈ Build | |
runs-on: ubuntu-latest | |
needs: release | |
if: needs.release.outputs.new-release-published == 'true' | |
strategy: | |
matrix: | |
package: [client, server] | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
with: | |
ref: ${{ github.ref }} # Ensure we get the latest commit with updated version | |
- 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: Get package versions | |
run: | | |
echo "client_version=$(jq -r .version client/package.json)" >> $GITHUB_OUTPUT | |
echo "server_version=$(jq -r .version server/package.json)" >> $GITHUB_OUTPUT | |
id: set_versions | |
- name: π¨ Build Version Info | |
env: | |
BUILD_NUMBER: ${{ github.run_number }} | |
NODE_ENV: production | |
# Use the released version (should match package.json after semantic-release) | |
APP_VERSION: ${{ needs.release.outputs.new-release-version }} | |
SERVER_VERSION: ${{ needs.release.outputs.new-release-version }} | |
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 }} | |
EXPO_PUBLIC_API_URL: http://56.228.14.41 | |
EXPO_PUBLIC_APP_VERSION: ${{ needs.release.outputs.new-release-version }} | |
EXPO_PUBLIC_BUILD_NUMBER: ${{ github.run_number }} | |
EXPO_PUBLIC_BUILD_DATE: ${{ github.event.head_commit.timestamp }} | |
EXPO_PUBLIC_COMMIT_HASH: ${{ github.sha }} | |
EXPO_PUBLIC_BUILD_ENV: production | |
run: | | |
cd client | |
echo "\n--- VERSION INFO ---" | |
echo "Released Version: ${{ needs.release.outputs.new-release-version }}" | |
echo "Package.json Version: $(jq -r .version package.json)" | |
echo "\n--- ENVIRONMENT VARIABLES ---" | |
printenv | grep -E "(EXPO_PUBLIC_|NODE_ENV)" | sort | |
echo "\n--- constants/version.ts (before build-version) ---" | |
cat constants/version.ts || echo "File not found" | |
npm run build:version | |
echo "\n--- constants/version.ts (after build-version) ---" | |
cat constants/version.ts | |
npm run build | |
- name: ποΈ Build Server | |
if: matrix.package == 'server' | |
env: | |
NODE_ENV: production | |
SERVER_VERSION: ${{ needs.release.outputs.new-release-version }} | |
BUILD_NUMBER: ${{ github.run_number }} | |
BUILD_DATE: ${{ github.event.head_commit.timestamp }} | |
COMMIT_HASH: ${{ github.sha }} | |
run: | | |
cd server | |
echo "\n--- VERSION INFO ---" | |
echo "Released Version: ${{ needs.release.outputs.new-release-version }}" | |
echo "Package.json Version: $(jq -r .version package.json)" | |
echo "\n--- ENVIRONMENT VARIABLES ---" | |
printenv | grep -E "(SERVER_VERSION|BUILD_|NODE_ENV)" | sort | |
echo "\n--- src/common/config/version.ts (before build-version) ---" | |
cat src/common/config/version.ts || echo "File not found" | |
npm run build:version | |
echo "\n--- src/common/config/version.ts (after build-version) ---" | |
cat src/common/config/version.ts | |
npm run build | |
- name: π€ Upload Build Artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.package }}-build-${{ needs.release.outputs.new-release-version }} | |
path: | | |
${{ matrix.package }}/dist | |
${{ matrix.package }}/web/dist | |
${{ matrix.package }}/package.json | |
${{ matrix.package }}/package-lock.json | |
retention-days: 30 | |
# Deploy to AWS (only if new release was published) | |
deploy: | |
name: οΏ½ Deploy | |
runs-on: ubuntu-latest | |
needs: [release, build] | |
if: needs.release.outputs.new-release-published == 'true' | |
environment: production | |
steps: | |
- name: π₯ Checkout | |
uses: actions/checkout@v3 | |
with: | |
ref: ${{ github.ref }} # Ensure we get the latest commit | |
- 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-${{ needs.release.outputs.new-release-version }}/web/dist" ]; then | |
aws s3 sync "./artifacts/client-build-${{ needs.release.outputs.new-release-version }}/web/dist/" s3://${{ secrets.S3_BUCKET }} --delete --cache-control "no-cache, no-store, must-revalidate" | |
echo "β Client deployed to S3 with version ${{ needs.release.outputs.new-release-version }}" | |
else | |
echo "β Client build not found for version ${{ needs.release.outputs.new-release-version }}" | |
ls -la ./artifacts/ || echo "No artifacts directory" | |
fi | |
# Deploy Server to EC2 | |
- name: οΏ½ Build Server Docker Image | |
run: | | |
# Copy server build artifacts to server directory | |
if [ -d "./artifacts/server-build-${{ needs.release.outputs.new-release-version }}/dist" ]; then | |
cp -r "./artifacts/server-build-${{ needs.release.outputs.new-release-version }}/dist" server/ | |
echo "β Server build artifacts copied" | |
else | |
echo "β Server build artifacts not found" | |
ls -la ./artifacts/ | |
exit 1 | |
fi | |
cd server | |
# Update package-lock.json to fix npm ci sync issues | |
rm -f package-lock.json | |
npm install --package-lock-only | |
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 with versioned tag | |
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 (keep latest 3 versions) | |
sudo docker image prune -f | |
sudo docker images lab1-todoapp-server --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}" | tail -n +2 | head -n -3 | awk '{print $3}' | xargs -r sudo docker rmi || echo "No old images to remove" | |
- name: β Deployment Summary | |
run: | | |
echo "π Deployment completed for version ${{ needs.release.outputs.new-release-version }}" | |
echo "π¦ Released Version: ${{ needs.release.outputs.new-release-version }}" | |
echo "ποΈ Build Number: ${{ github.run_number }}" | |
echo "π Client: http://${{ secrets.S3_BUCKET }}.s3-website.eu-north-1.amazonaws.com" | |
echo "π₯οΈ Server: http://${{ secrets.EC2_HOST }}/api" | |
echo "π Health endpoint: http://${{ secrets.EC2_HOST }}/api/health" | |
- name: π Health Check | |
run: | | |
echo "β³ Waiting 30 seconds for server to start..." | |
sleep 30 | |
echo "π Checking server health..." | |
if curl -f -s http://${{ secrets.EC2_HOST }}/api/health; then | |
echo "β Server health check passed" | |
echo "π Checking version endpoint..." | |
curl -s http://${{ secrets.EC2_HOST }}/api/version || echo "Version endpoint not available" | |
else | |
echo "β Server health check failed" | |
echo "π Checking server logs..." | |
exit 1 | |
fi |