Revert .gitlab-ci.yml to working configuration
Keep authentication fix in Program.cs but use original CI/CD pipeline configuration that builds from Dockerfile
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d31c0b4aeb
commit
c31df615ab
289
.gitlab-ci.yml
289
.gitlab-ci.yml
@ -1,265 +1,152 @@
|
|||||||
# GitLab CI/CD Pipeline for LittleShop
|
|
||||||
# Builds and deploys to Hostinger VPS
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DOCKER_HOST: unix:///var/run/docker.sock
|
DOCKER_HOST: unix:///var/run/docker.sock
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
# Registry configuration
|
|
||||||
REGISTRY_URL: "localhost:5000"
|
|
||||||
IMAGE_NAME: "littleshop"
|
|
||||||
# Hostinger deployment configuration
|
|
||||||
DEPLOY_HOST: "10.13.13.1"
|
|
||||||
DEPLOY_PORT: "2255"
|
|
||||||
CONTAINER_NAME: "littleshop-admin"
|
|
||||||
|
|
||||||
# Build from .NET source and create Docker image
|
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
image: mcr.microsoft.com/dotnet/sdk:9.0
|
image: docker:24
|
||||||
services:
|
|
||||||
- docker:24-dind
|
|
||||||
before_script:
|
|
||||||
- apt-get update && apt-get install -y docker.io
|
|
||||||
- docker --version
|
|
||||||
script:
|
script:
|
||||||
- echo "Building LittleShop application..."
|
- echo "Building LittleShop Docker image"
|
||||||
- cd LittleShop
|
- docker build -t localhost:5000/littleshop:latest .
|
||||||
- dotnet publish -c Production -o ../publish --verbosity minimal
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
# Create optimized Dockerfile
|
|
||||||
- |
|
- |
|
||||||
cat > Dockerfile << 'EOF'
|
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine
|
echo "Tagging as version $CI_COMMIT_TAG"
|
||||||
WORKDIR /app
|
docker tag localhost:5000/littleshop:latest localhost:5000/littleshop:$CI_COMMIT_TAG
|
||||||
COPY ./publish .
|
fi
|
||||||
|
- echo "Build complete"
|
||||||
# Create required directories and user
|
|
||||||
RUN mkdir -p /app/data /app/wwwroot/uploads && \
|
|
||||||
adduser -D -u 1658 appuser && \
|
|
||||||
chown -R appuser:appuser /app
|
|
||||||
|
|
||||||
USER appuser
|
|
||||||
EXPOSE 8080
|
|
||||||
ENTRYPOINT ["dotnet", "LittleShop.dll"]
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Build and tag Docker image
|
|
||||||
- export VERSION="${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
|
||||||
- docker build -t ${REGISTRY_URL}/${IMAGE_NAME}:${VERSION} .
|
|
||||||
- docker tag ${REGISTRY_URL}/${IMAGE_NAME}:${VERSION} ${REGISTRY_URL}/${IMAGE_NAME}:latest
|
|
||||||
|
|
||||||
# Save image for deployment
|
|
||||||
- docker save ${REGISTRY_URL}/${IMAGE_NAME}:${VERSION} -o littleshop.tar
|
|
||||||
- echo "Build complete for version ${VERSION}"
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- littleshop.tar
|
|
||||||
- publish/
|
|
||||||
expire_in: 1 hour
|
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||||
- if: '$CI_COMMIT_TAG'
|
- if: '$CI_COMMIT_TAG'
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
# Run tests
|
deploy:vps:
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
image: mcr.microsoft.com/dotnet/sdk:9.0
|
|
||||||
script:
|
|
||||||
- echo "Running tests..."
|
|
||||||
- cd LittleShop.Tests
|
|
||||||
- dotnet test --no-restore --verbosity normal || true
|
|
||||||
allow_failure: true
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
||||||
- if: '$CI_COMMIT_TAG'
|
|
||||||
|
|
||||||
# Deploy to Hostinger VPS
|
|
||||||
deploy:hostinger:
|
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: alpine:latest
|
image: docker:24
|
||||||
dependencies:
|
|
||||||
- build
|
|
||||||
before_script:
|
before_script:
|
||||||
- apk add --no-cache openssh-client sshpass curl
|
- apk add --no-cache openssh-client bash curl
|
||||||
# Setup SSH key if provided
|
- echo "$VPS_SSH_KEY_B64" | base64 -d > /tmp/deploy_key
|
||||||
- |
|
- chmod 600 /tmp/deploy_key
|
||||||
if [ -n "$HOSTINGER_SSH_KEY" ]; then
|
- mkdir -p ~/.ssh
|
||||||
echo "$HOSTINGER_SSH_KEY" | base64 -d > /tmp/hostinger_key
|
- chmod 700 ~/.ssh
|
||||||
chmod 600 /tmp/hostinger_key
|
- ssh-keyscan -p "$VPS_PORT" "$VPS_HOST" >> ~/.ssh/known_hosts
|
||||||
export SSH_CMD="ssh -i /tmp/hostinger_key"
|
|
||||||
export SCP_CMD="scp -i /tmp/hostinger_key"
|
|
||||||
else
|
|
||||||
export SSH_CMD="sshpass -p $DEPLOY_PASSWORD ssh"
|
|
||||||
export SCP_CMD="sshpass -p $DEPLOY_PASSWORD scp"
|
|
||||||
fi
|
|
||||||
script:
|
script:
|
||||||
- export VERSION="${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
- export VERSION="${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
||||||
- echo "Deploying version ${VERSION} to Hostinger..."
|
- echo "Deploying version $VERSION to VPS"
|
||||||
|
- echo "Building image from source..."
|
||||||
|
- docker build -t littleshop:$VERSION .
|
||||||
|
|
||||||
# Transfer Docker image to server
|
- echo "Copying image to VPS via SSH..."
|
||||||
- $SCP_CMD -P ${DEPLOY_PORT} -o StrictHostKeyChecking=no littleshop.tar ${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
|
- docker save littleshop:$VERSION | ssh -i /tmp/deploy_key -p "$VPS_PORT" "$VPS_USER@$VPS_HOST" "docker load"
|
||||||
|
|
||||||
# Deploy on server
|
- echo "Deploying on VPS..."
|
||||||
- |
|
- |
|
||||||
$SSH_CMD -p ${DEPLOY_PORT} -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} << DEPLOY_SCRIPT
|
ssh -i /tmp/deploy_key -p "$VPS_PORT" "$VPS_USER@$VPS_HOST" bash -s << EOF
|
||||||
set -e
|
set -e
|
||||||
|
export VERSION="$VERSION"
|
||||||
|
|
||||||
# Load Docker image
|
# Tag the image
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker load -i /tmp/littleshop.tar
|
docker tag littleshop:\$VERSION localhost:5000/littleshop:\$VERSION
|
||||||
|
docker tag littleshop:\$VERSION localhost:5000/littleshop:latest
|
||||||
|
|
||||||
# Stop and remove existing container
|
# Push to local registry
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker stop ${CONTAINER_NAME} 2>/dev/null || true
|
echo "Pushing to local Docker registry..."
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker rm ${CONTAINER_NAME} 2>/dev/null || true
|
docker push localhost:5000/littleshop:\$VERSION
|
||||||
|
docker push localhost:5000/littleshop:latest
|
||||||
|
|
||||||
# Run new container with authentication fix and all environment variables
|
# Navigate to deployment directory
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker run -d \
|
cd /opt/littleshop
|
||||||
--name ${CONTAINER_NAME} \
|
|
||||||
--restart unless-stopped \
|
|
||||||
-p 5100:8080 \
|
|
||||||
-v /var/opt/littleshop/data:/app/data \
|
|
||||||
-v /var/opt/littleshop/uploads:/app/wwwroot/uploads \
|
|
||||||
-e ASPNETCORE_ENVIRONMENT=Production \
|
|
||||||
-e WebPush__VapidPublicKey='BDJtQu7zV0H3KF4FkrZ8nPwP3YD_3cEz3hqJvQ6L_gvNpG8ANksQB-FZy2-PDmFAu6duiN4p3mkcNAGnN4YRbws' \
|
|
||||||
-e WebPush__VapidPrivateKey='Hm_ttUKUqoLn5R8WQP5O1SIGxm0kVJXMZGCPMD1tUDY' \
|
|
||||||
-e WebPush__VapidSubject='mailto:admin@littleshop.local' \
|
|
||||||
-e ConnectionStrings__DefaultConnection='Data Source=/app/data/littleshop-production.db' \
|
|
||||||
-e Jwt__Key='2D7B5FE9C4A3E1D8B6A947F2C8E5D3A1B9F7E4C2D8A6B3E9F1C7D5A2E8B4F6C9' \
|
|
||||||
-e Jwt__Audience='LittleShop-Production' \
|
|
||||||
-e Jwt__ExpiryInHours='24' \
|
|
||||||
-e Jwt__Issuer='LittleShop-Production' \
|
|
||||||
-e SilverPay__AllowUnsignedWebhooks='false' \
|
|
||||||
-e SilverPay__WebhookSecret='04126be1b2ca9a586aaf25670c0ddb7a9afa106158074605a1016a2889655c20' \
|
|
||||||
--health-cmd='curl -f http://localhost:8080/health || exit 1' \
|
|
||||||
--health-interval=30s \
|
|
||||||
--health-timeout=10s \
|
|
||||||
--health-retries=3 \
|
|
||||||
${REGISTRY_URL}/${IMAGE_NAME}:${VERSION}
|
|
||||||
|
|
||||||
# Wait for container health
|
# Force stop all littleshop containers (including orphans)
|
||||||
echo "Waiting for container to be healthy..."
|
echo "Stopping all littleshop containers..."
|
||||||
|
docker stop \$(docker ps -q --filter "name=littleshop") 2>/dev/null || true
|
||||||
|
docker rm \$(docker ps -aq --filter "name=littleshop") 2>/dev/null || true
|
||||||
|
|
||||||
|
# Stop services with compose (removes networks)
|
||||||
|
echo "Stopping compose services..."
|
||||||
|
docker-compose down --remove-orphans || true
|
||||||
|
|
||||||
|
# Prune unused Docker networks to avoid conflicts
|
||||||
|
echo "Cleaning up Docker networks..."
|
||||||
|
docker network prune -f || true
|
||||||
|
|
||||||
|
# Start services with new image
|
||||||
|
echo "Starting services with new image..."
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Wait for startup
|
||||||
|
echo "Waiting for services to start..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
echo "Running health checks..."
|
||||||
for i in 1 2 3 4 5 6; do
|
for i in 1 2 3 4 5 6; do
|
||||||
if echo "${DEPLOY_PASSWORD}" | sudo -S docker ps | grep -q "(healthy).*${CONTAINER_NAME}"; then
|
if curl -f -s http://localhost:5100/api/catalog/products > /dev/null 2>&1; then
|
||||||
echo "✅ Container is healthy"
|
echo "✅ Deployment successful - health check passed"
|
||||||
break
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Waiting for health check... attempt \$i/6"
|
echo "Health check attempt \$i/6 failed, waiting..."
|
||||||
sleep 10
|
sleep 10
|
||||||
done
|
done
|
||||||
|
|
||||||
# Test authentication redirect
|
echo "❌ Health check failed after deployment"
|
||||||
echo "Testing authentication redirect..."
|
docker logs littleshop-admin --tail 50
|
||||||
curl -I http://localhost:5100/Admin 2>/dev/null | head -15
|
exit 1
|
||||||
|
EOF
|
||||||
# Push to local registry for backup
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker push ${REGISTRY_URL}/${IMAGE_NAME}:${VERSION} 2>/dev/null || true
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker tag ${REGISTRY_URL}/${IMAGE_NAME}:${VERSION} ${REGISTRY_URL}/${IMAGE_NAME}:latest
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker push ${REGISTRY_URL}/${IMAGE_NAME}:latest 2>/dev/null || true
|
|
||||||
|
|
||||||
# Health check API
|
|
||||||
if curl -f -s http://localhost:5100/api/catalog/products > /dev/null 2>&1; then
|
|
||||||
echo "✅ Deployment successful - API health check passed"
|
|
||||||
else
|
|
||||||
echo "⚠️ API health check failed but container is running"
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker logs ${CONTAINER_NAME} --tail 20
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
rm -f /tmp/littleshop.tar
|
|
||||||
|
|
||||||
echo "Deployment of version ${VERSION} complete!"
|
|
||||||
DEPLOY_SCRIPT
|
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
url: http://${DEPLOY_HOST}:5100
|
url: http://hq.lan
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||||
when: manual # Require manual approval for production
|
when: on_success
|
||||||
- if: '$CI_COMMIT_TAG'
|
- if: '$CI_COMMIT_TAG'
|
||||||
when: manual
|
when: manual
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
# Rollback job
|
rollback:vps:
|
||||||
rollback:hostinger:
|
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
before_script:
|
before_script:
|
||||||
- apk add --no-cache openssh-client sshpass
|
- apk add --no-cache openssh-client bash
|
||||||
|
- echo "$VPS_SSH_KEY_B64" | base64 -d > /tmp/deploy_key
|
||||||
|
- chmod 600 /tmp/deploy_key
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- chmod 700 ~/.ssh
|
||||||
|
- ssh-keyscan -p "$VPS_PORT" "$VPS_HOST" >> ~/.ssh/known_hosts
|
||||||
script:
|
script:
|
||||||
- echo "Rolling back to previous version..."
|
- echo "Rolling back to previous version"
|
||||||
- |
|
- |
|
||||||
if [ -n "$HOSTINGER_SSH_KEY" ]; then
|
ssh -i /tmp/deploy_key -p "$VPS_PORT" "$VPS_USER@$VPS_HOST" bash -s << EOF
|
||||||
echo "$HOSTINGER_SSH_KEY" | base64 -d > /tmp/hostinger_key
|
|
||||||
chmod 600 /tmp/hostinger_key
|
|
||||||
SSH_CMD="ssh -i /tmp/hostinger_key"
|
|
||||||
else
|
|
||||||
SSH_CMD="sshpass -p $DEPLOY_PASSWORD ssh"
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
$SSH_CMD -p ${DEPLOY_PORT} -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} << ROLLBACK_SCRIPT
|
|
||||||
set -e
|
set -e
|
||||||
|
cd /opt/littleshop
|
||||||
|
|
||||||
# Get previous image
|
# Pull previous image
|
||||||
PREVIOUS_IMAGE=\$(echo "${DEPLOY_PASSWORD}" | sudo -S docker images ${REGISTRY_URL}/${IMAGE_NAME} --format "{{.Tag}}" | grep -v latest | head -2 | tail -1)
|
docker tag localhost:5000/littleshop:previous localhost:5000/littleshop:latest
|
||||||
|
|
||||||
if [ -z "\$PREVIOUS_IMAGE" ]; then
|
# Restart services
|
||||||
echo "❌ No previous image found for rollback"
|
echo "Restarting with previous version..."
|
||||||
exit 1
|
docker-compose down
|
||||||
fi
|
docker-compose up -d
|
||||||
|
|
||||||
echo "Rolling back to ${REGISTRY_URL}/${IMAGE_NAME}:\$PREVIOUS_IMAGE"
|
# Health check
|
||||||
|
|
||||||
# Stop current container
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker stop ${CONTAINER_NAME}
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker rm ${CONTAINER_NAME}
|
|
||||||
|
|
||||||
# Start with previous image
|
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker run -d \
|
|
||||||
--name ${CONTAINER_NAME} \
|
|
||||||
--restart unless-stopped \
|
|
||||||
-p 5100:8080 \
|
|
||||||
-v /var/opt/littleshop/data:/app/data \
|
|
||||||
-v /var/opt/littleshop/uploads:/app/wwwroot/uploads \
|
|
||||||
-e ASPNETCORE_ENVIRONMENT=Production \
|
|
||||||
-e WebPush__VapidPublicKey='BDJtQu7zV0H3KF4FkrZ8nPwP3YD_3cEz3hqJvQ6L_gvNpG8ANksQB-FZy2-PDmFAu6duiN4p3mkcNAGnN4YRbws' \
|
|
||||||
-e WebPush__VapidPrivateKey='Hm_ttUKUqoLn5R8WQP5O1SIGxm0kVJXMZGCPMD1tUDY' \
|
|
||||||
-e WebPush__VapidSubject='mailto:admin@littleshop.local' \
|
|
||||||
-e ConnectionStrings__DefaultConnection='Data Source=/app/data/littleshop-production.db' \
|
|
||||||
-e Jwt__Key='2D7B5FE9C4A3E1D8B6A947F2C8E5D3A1B9F7E4C2D8A6B3E9F1C7D5A2E8B4F6C9' \
|
|
||||||
-e Jwt__Audience='LittleShop-Production' \
|
|
||||||
-e Jwt__ExpiryInHours='24' \
|
|
||||||
-e Jwt__Issuer='LittleShop-Production' \
|
|
||||||
-e SilverPay__AllowUnsignedWebhooks='false' \
|
|
||||||
-e SilverPay__WebhookSecret='04126be1b2ca9a586aaf25670c0ddb7a9afa106158074605a1016a2889655c20' \
|
|
||||||
--health-cmd='curl -f http://localhost:8080/health || exit 1' \
|
|
||||||
--health-interval=30s \
|
|
||||||
--health-timeout=10s \
|
|
||||||
--health-retries=3 \
|
|
||||||
${REGISTRY_URL}/${IMAGE_NAME}:\$PREVIOUS_IMAGE
|
|
||||||
|
|
||||||
# Wait and check health
|
|
||||||
sleep 30
|
sleep 30
|
||||||
if curl -f -s http://localhost:5100/api/catalog/products > /dev/null 2>&1; then
|
if curl -f -s http://localhost:5100/api/catalog/products > /dev/null 2>&1; then
|
||||||
echo "✅ Rollback complete - service is healthy"
|
echo "✅ Rollback complete"
|
||||||
|
exit 0
|
||||||
else
|
else
|
||||||
echo "❌ Rollback health check failed"
|
echo "❌ Rollback health check failed"
|
||||||
echo "${DEPLOY_PASSWORD}" | sudo -S docker logs ${CONTAINER_NAME} --tail 50
|
docker logs littleshop-admin --tail 50
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
ROLLBACK_SCRIPT
|
EOF
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
when: manual
|
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
||||||
- if: '$CI_COMMIT_TAG'
|
- if: '$CI_COMMIT_TAG'
|
||||||
|
when: manual
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
Loading…
Reference in New Issue
Block a user