diff --git a/.gitea/workflows/build-push.yml b/.gitea/workflows/build-push.yml new file mode 100644 index 0000000..20fd983 --- /dev/null +++ b/.gitea/workflows/build-push.yml @@ -0,0 +1,57 @@ +name: Build and Push Docker Image + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: 'Docker image tag (e.g., v1.0.0, latest)' + required: false + default: 'latest' + push_to_registry: + description: 'Push to registry?' + required: false + default: 'true' + +jobs: + build-and-push: + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Registry + uses: docker/login-action@v2 + with: + registry: ${{ secrets.DOCKER_REGISTRY_URL }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web:buildcache,mode=max diff --git a/.gitea/workflows/deploy-dev.yml b/.gitea/workflows/deploy-dev.yml new file mode 100644 index 0000000..ea8b559 --- /dev/null +++ b/.gitea/workflows/deploy-dev.yml @@ -0,0 +1,140 @@ +name: Deploy Development + +on: + push: + branches: + - dev + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-latest + + steps: + # 1️⃣ Checkout code + - name: Checkout code + shell: sh + run: | + git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo + cd /workspace/repo + git checkout $GITHUB_REF_NAME + + # 2️⃣ Build image + - name: Build Docker image + shell: sh + run: | + cd /workspace/repo + docker build -t slipmatz-web:dev . + docker save slipmatz-web:dev | gzip > slipmatz-web_dev.tar.gz + + # 3️⃣ Setup SSH + - name: Setup SSH + shell: sh + env: + DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts + + # 4️⃣ Upload artifacts + - name: Upload image and compose + shell: sh + env: + DEPLOY_USER: ${{ secrets.DEPLOY_USER }} + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + run: | + scp -i ~/.ssh/id_ed25519 \ + /workspace/repo/slipmatz-web_dev.tar.gz \ + /workspace/repo/docker-compose.yml \ + ${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/ + + # 5️⃣ Deploy on server + - name: Deploy on server + shell: sh + env: + DEPLOY_USER: ${{ secrets.DEPLOY_USER }} + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + + run: | + ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF' + set -e + + DEPLOY_DIR="/var/www/apps/slipmatz-web_dev" + mkdir -p "$DEPLOY_DIR" + + echo "📦 Loading image" + docker load < /tmp/slipmatz-web_dev.tar.gz + + echo "🧹 Removing old slipmatz-web images" + docker images | grep slipmatz-web | grep -v "$(docker images slipmatz-web:dev -q)" | awk '{print $3}' | xargs -r docker rmi -f || true + + echo "📄 Updating compose file" + cp /tmp/docker-compose.yml "$DEPLOY_DIR/" + + cd "$DEPLOY_DIR" + + echo "🔍 Checking .env file" + if [ ! -f .env ]; then + echo "❌ .env file not found at $DEPLOY_DIR/.env" + echo "Please create it first with required variables:" + echo " - NUXT_PUBLIC_*, STRIPE_SECRET_KEY" + exit 1 + fi + + echo "🔧 Fixing .env permissions" + sudo chown $USER:$USER .env + sudo chmod 600 .env + + echo "🌐 Ensure networks" + docker network inspect traefik-public >/dev/null 2>&1 || \ + docker network create traefik-public + docker network inspect crew-app-net >/dev/null 2>&1 || \ + docker network create crew-app-net + + echo "🚀 Starting containers (env vars from .env file)" + export COMPOSE_PROJECT_NAME=slipmatz-web-dev + docker compose down || true + docker compose up -d --remove-orphans + + echo "🧪 Testing container health" + sleep 15 + if ! docker ps | grep -q slipmatz-web; then + echo "❌ Container not running" + docker compose logs --tail=50 + exit 1 + fi + + echo "🎉 Dev deployment complete!" + docker compose ps + + echo "🧹 Cleanup" + rm -f /tmp/slipmatz-web_dev.tar.gz /tmp/docker-compose.yml + + echo "🗑️ Aggressive Docker cleanup to reclaim space" + docker image prune -af --filter "until=24h" || true + docker container prune -f || true + docker volume prune -f || true + docker builder prune -af --filter "until=48h" || true + + echo "💾 Docker space usage:" + docker system df + + echo "✅ Dev deployment completed successfully" + EOF + + # 6️⃣ Verify deployment + - name: Verify deployment + shell: sh + env: + DEPLOY_USER: ${{ secrets.DEPLOY_USER }} + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + run: | + sleep 5 + ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST \ + "docker ps | grep slipmatz-web || exit 1" diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..86fd13a --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,89 @@ +name: Deploy Production + +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-latest + + steps: + - name: Checkout code + shell: sh + run: | + git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo || true + cd /workspace/repo + git fetch origin $GITHUB_REF_NAME + git checkout $GITHUB_REF_NAME + git pull origin $GITHUB_REF_NAME + + - name: Build Docker Image + shell: sh + run: | + cd /workspace/repo + docker build -t slipmatz-web:latest . + docker save slipmatz-web:latest | gzip > slipmatz-web.tar.gz + + - name: Setup SSH and Deploy + shell: sh + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + echo "$PROD_DEPLOY_SSH_KEY" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keygen -y -f ~/.ssh/deploy_key > /dev/null 2>&1 || { echo "Error: Invalid SSH key format"; exit 1; } + + cd /workspace/repo + scp -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key slipmatz-web.tar.gz docker-compose.yml "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST:/tmp/" + + ssh -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST" " + DEPLOY_DIR='/var/www/apps/slipmatz-web' + mkdir -p \$DEPLOY_DIR + cd /tmp + docker load < slipmatz-web.tar.gz + + echo 'Removing old slipmatz-web images' + docker images | grep slipmatz-web | grep -v "\$(docker images slipmatz-web:latest -q)" | awk '{print \$3}' | xargs -r docker rmi -f || true + + cp docker-compose.yml \$DEPLOY_DIR/ + cd \$DEPLOY_DIR + + # .env file should already exist on server with all required variables + # Required: NUXT_PUBLIC_*, STRIPE_SECRET_KEY, etc. + # If it doesn't exist, deployment will fail (this is intentional for security) + + docker compose down || true + docker image prune -f + docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public + export DOMAIN=slipmatz.com + docker compose up -d + sleep 10 + rm -f /tmp/slipmatz-web.tar.gz /tmp/docker-compose.yml + + echo 'Aggressive Docker cleanup to reclaim space' + docker image prune -af --filter "until=24h" || true + docker container prune -f || true + docker volume prune -f || true + docker builder prune -af --filter "until=48h" || true + echo 'Docker space usage:' + docker system df + + echo 'Production deployment completed successfully!' + echo 'Application available at: https://slipmatz.com' + " + env: + PROD_DEPLOY_SSH_KEY: ${{ secrets.PROD_DEPLOY_SSH_KEY }} + PROD_DEPLOY_USER: ${{ secrets.PROD_DEPLOY_USER }} + PROD_DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }} + + - name: Health Check + shell: sh + run: | + sleep 10 + curl -f https://slipmatz.com || exit 1 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml deleted file mode 100644 index 92b8c62..0000000 --- a/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Build and Push Docker Image - -on: - push: - branches: - - main - - dev - - 'feat/**' - - 'fix/**' - tags: - - 'v*' - pull_request: - branches: - - main - - dev - workflow_dispatch: - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build-and-push: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} - - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64 - 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 - - # - name: Deploy to production server - # if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' - # uses: appleboy/ssh-action@v1.2.0 - # with: - # host: ${{ secrets.SERVER_HOST }} - # username: ${{ secrets.SERVER_USER }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # port: ${{ secrets.SERVER_PORT || 22 }} - # script: | - # cd ${{ secrets.SERVER_PATH || '/root/crew-infrastructure' }} - # export SLIPMATZ_WEB_TAG=main - # docker compose pull slipmatz_web - # docker compose up -d slipmatz_web - # docker compose ps slipmatz_web diff --git a/docker-compose.yml b/docker-compose.yml index 0508e3f..1c2907c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,21 @@ services: - NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.slipmatz-web.rule=Host(`${DOMAIN:-slipmatz.com}`)" + - "traefik.http.routers.slipmatz-web.entrypoints=websecure" + - "traefik.http.routers.slipmatz-web.tls=true" + - "traefik.http.routers.slipmatz-web.tls.certresolver=le" + - "traefik.http.services.slipmatz-web.loadbalancer.server.port=3000" + # HTTP to HTTPS redirect + - "traefik.http.routers.slipmatz-web-http.rule=Host(`${DOMAIN:-slipmatz.com}`)" + - "traefik.http.routers.slipmatz-web-http.entrypoints=web" + - "traefik.http.routers.slipmatz-web-http.middlewares=https-redirect" + - "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https" networks: + - traefik-public + - crew-app-net - slipmatz-network healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"] @@ -30,5 +44,9 @@ services: start_period: 40s networks: + traefik-public: + external: true + crew-app-net: + external: true slipmatz-network: driver: bridge