name: CI on: push: branches: - main pull_request: env: IMAGE_NAME: ${{ vars.IMAGE_NAME }} REGISTRY_LOCATION: ${{ vars.REGISTRY_LOCATION }} PORT: ${{ vars.PORT }} TEST_PORT: ${{ vars.TEST_PORT }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} PROD_SERVER_HOST: ${{ secrets.PROD_SERVER_HOST }} DEPLOY_USER: ${{ secrets.DEPLOY_USER }} jobs: lint: name: ✅ Lint & Validate runs-on: ubuntu-latest steps: - name: đŸ“Ĩ Checkout repository uses: actions/checkout@v4 - name: 🔧 Validate HTML run: | echo "Basic HTML validation" test -f index.html - name: 🐋 Validate Dockerfile run: | dockerfilelint Dockerfile || true build: name: Build & Push Docker Image runs-on: ubuntu-latest needs: lint steps: - name: đŸ“Ĩ Checkout repository uses: actions/checkout@v4 - name: 🔖 Set image metadata run: | echo "RUN_NUMBER=${GITEA_RUN_NUMBER}" >> $GITEA_ENV - name: 🐋 Build Docker image run: | docker build \ -t $IMAGE_NAME:$RUN_NUMBER \ -t $IMAGE_NAME:latest \ . - name: â„šī¸ Image info run: docker images | grep $IMAGE_NAME - name: 🔑 Login to registry run: | echo "$REGISTRY_TOKEN" | docker login $REGISTRY_LOCATION \ -u "$REGISTRY_USER" --password-stdin - name: đŸˇī¸ Build images run: | docker tag $IMAGE_NAME:latest $REGISTRY_LOCATION/$IMAGE_NAME:latest docker tag $IMAGE_NAME:$RUN_NUMBER $REGISTRY_LOCATION/$IMAGE_NAME:build-$RUN_NUMBER - name: 🚀 Push images run: | docker push $REGISTRY_LOCATION/$IMAGE_NAME:latest docker push $REGISTRY_LOCATION/$IMAGE_NAME:build-$RUN_NUMBER deploy: name: 🚀 Deploy image to prod runs-on: ubuntu-latest needs: build steps: - name: 🔑 Setup SSH run: | mkdir -p ~/.ssh echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H $PROD_SERVER_HOST >> ~/.ssh/known_hosts - name: 🔑 Login to registry run: | echo "$REGISTRY_TOKEN" | docker login $REGISTRY_LOCATION \ -u "$REGISTRY_USER" --password-stdin - name: đŸšĸ Deploy container run: | ssh $DEPLOY_USER@$PROD_SERVER_HOST << 'EOF' set -e NEW_IMAGE_NAME="$REGISTRY_LOCATION/$IMAGE_NAME:build-$RUN_NUMBER" OLD_CONTAINER_NAME=$(docker inspect $IMAGE_NAME --format='{{.Config.Image}}') NEW_CONTAINER_IMAGE="$IMAGE_NAME:build-$RUN_NUMBER" echo "Pulling image $NEW_IMAGE_NAME" docker pull "$NEW_IMAGE_NAME" echo "Starting new test container" docker run -d \ --name $NEW_CONTAINER_IMAGE \ --health-cmd="wget -qO- http://localhost:$TEST_PORT/health || exit 1" \ --health-interval=5s \ --health-retries=5 \ --health-timeout=2s \ --health-start-period=5s \ -p $TEST_PORT:80 \ "$NEW_CONTAINER_IMAGE" echo "Waiting for healthcheck..." for i in {1..15}; do STATUS=$(docker inspect --format='{{.State.Health.Status}}' $NEW_CONTAINER_IMAGE) [ "$STATUS" = "healthy" ] && break [ "$STATUS" = "unhealthy" ] && break sleep 2 done docker stop $NEW_CONTAINER_IMAGE docker image rm $NEW_CONTAINER_IMAGE if [ "$STATUS" = "healthy" ]; then echo "✅ Container is healthy - starting deployment" docker stop $OLD_CONTAINER_NAME docker run -d \ --name $NEW_CONTAINER_IMAGE \ --health-cmd="wget -qO- http://localhost:$PORT/health || exit 1" \ --health-interval=5s \ --health-retries=5 \ --health-timeout=2s \ --health-start-period=5s \ -p $PORT:80 \ "$NEW_CONTAINER_IMAGE" sleep 10 DEPLOYMENT_STATUS=$(docker inspect --format='{{.State.Health.Status}}' $NEW_CONTAINER_IMAGE) if [ "$DEPLOYMENT_STATUS" != "healthy" ]; then echo "❌ Deployment failed — rolling back" docker logs $NEW_CONTAINER_IMAGE docker rm -f $NEW_CONTAINER_IMAGE if [ -n "$OLD_CONTAINER_NAME" ]; then docker run -d \ --name $OLD_CONTAINER_NAME \ --health-cmd="wget -qO- http://localhost:$PORT/health || exit 1" \ --health-interval=5s \ --health-retries=5 \ --health-timeout=2s \ --health-start-period=5s \ -p $PORT:80 \ "$OLD_CONTAINER_NAME" echo "❌ Deployment failed — ✅ rollback successfull" fi exit 1 fi docker image rm $OLD_CONTAINER_NAME echo "✅ Deployment successfull" else echo "❌ Deployment failed — â¤ī¸ healthcheck failed on the test container" fi EOF