From ab17324f8533dc09e9515ead52231cf35633022a Mon Sep 17 00:00:00 2001 From: Mike Johnston Date: Mon, 30 Mar 2026 22:00:36 -0400 Subject: [PATCH] feat: add deployment workflows for web, Android, Docker, macOS, and Linux --- .gitea/workflows/build-android.yml | 81 +++++++++++++ .gitea/workflows/build-docker.yml | 43 +++++++ .gitea/workflows/build-linux.yml | 78 ++++++++++++ .gitea/workflows/build-mac.yml | 52 ++++++++ .gitea/workflows/build-web.yml | 89 ++++++++++++++ .gitea/workflows/rebuild-prod.yaml | 89 ++++++++++++++ .github/workflows/build-web.yml | 89 ++++++++++++++ docker-compose.prod.yml | 66 ++++++++++ docs/WEB-DEPLOYMENT.md | 189 +++++++++++++++++++++++++++++ nginx/nginx.conf | 60 +++++++++ scripts/web-deploy.ps1 | 51 ++++++++ scripts/web-deploy.sh | 51 ++++++++ web-compose.yml | 33 +++++ 13 files changed, 971 insertions(+) create mode 100644 .gitea/workflows/build-android.yml create mode 100644 .gitea/workflows/build-docker.yml create mode 100644 .gitea/workflows/build-linux.yml create mode 100644 .gitea/workflows/build-mac.yml create mode 100644 .gitea/workflows/build-web.yml create mode 100644 .gitea/workflows/rebuild-prod.yaml create mode 100644 .github/workflows/build-web.yml create mode 100644 docker-compose.prod.yml create mode 100644 docs/WEB-DEPLOYMENT.md create mode 100644 nginx/nginx.conf create mode 100644 scripts/web-deploy.ps1 create mode 100644 scripts/web-deploy.sh create mode 100644 web-compose.yml diff --git a/.gitea/workflows/build-android.yml b/.gitea/workflows/build-android.yml new file mode 100644 index 0000000..d0b8faa --- /dev/null +++ b/.gitea/workflows/build-android.yml @@ -0,0 +1,81 @@ +name: Build Android APK + +on: + workflow_dispatch: + inputs: + tag: + description: 'Version tag (e.g. 2.0.0)' + required: false + default: '2.0.0' + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android platform tools + run: | + sdkmanager "platforms;android-34" "build-tools;34.0.0" + + - name: Install Capacitor dependencies + working-directory: apps/android + run: npm install + + - name: Add Android platform + working-directory: apps/android + run: npx cap add android || echo "Android platform already exists" + + - name: Sync Capacitor + working-directory: apps/android + run: npx cap sync android + + - name: Make gradlew executable + working-directory: apps/android/android + run: chmod +x gradlew + + - name: Build debug APK + working-directory: apps/android/android + run: ./gradlew assembleDebug + + - name: Rename APK + run: | + VERSION="${{ github.event.inputs.tag }}" + cp apps/android/android/app/build/outputs/apk/debug/app-debug.apk \ + "dibby-wemo-$VERSION-android.apk" + + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: dibby-wemo-android-apk + path: dibby-wemo-*.apk + retention-days: 30 + + - name: Upload to GitHub Release + if: github.event.inputs.tag != '' + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ github.event.inputs.tag }} + files: dibby-wemo-*.apk + fail_on_unmatched_files: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitea/workflows/build-docker.yml b/.gitea/workflows/build-docker.yml new file mode 100644 index 0000000..68b5565 --- /dev/null +++ b/.gitea/workflows/build-docker.yml @@ -0,0 +1,43 @@ +name: Build & Push Docker Image + +on: + workflow_dispatch: + inputs: + tag: + description: 'Docker image tag (e.g. 2.0.0)' + required: true + default: '2.0.0' + +jobs: + build-docker: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ghcr.io/k0rb3nd4ll4s/dibby-wemo-manager:${{ github.event.inputs.tag }} + ghcr.io/k0rb3nd4ll4s/dibby-wemo-manager:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitea/workflows/build-linux.yml b/.gitea/workflows/build-linux.yml new file mode 100644 index 0000000..72f76a1 --- /dev/null +++ b/.gitea/workflows/build-linux.yml @@ -0,0 +1,78 @@ +name: Build & Upload Linux Packages + +on: + workflow_dispatch: + inputs: + tag: + description: 'Release tag to upload assets to (e.g. v2.0.0)' + required: true + default: 'v2.0.0' + +jobs: + build-linux: + runs-on: ubuntu-22.04 + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Linux build tools + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq rpm fakeroot dpkg libarchive-tools + + - name: Install workspace dependencies + run: npm install --legacy-peer-deps + + - name: Install app-builder to system PATH + run: | + # The binary is bundled in the npm package for the current platform. + # Copy it to /usr/local/bin so USE_SYSTEM_APP_BUILDER=true can find it. + BINARY=$(node -e "console.log(require('./node_modules/app-builder-bin').appBuilderPath)") + echo "app-builder binary: $BINARY" + chmod +x "$BINARY" + sudo cp "$BINARY" /usr/local/bin/app-builder + app-builder --version + + - name: Vite build + run: npx electron-vite build + working-directory: apps/desktop + + - name: Bundle standalone scheduler + run: node scripts/bundle-standalone.js + working-directory: apps/desktop + + - name: Build Linux x64 packages + run: npx electron-builder --linux --x64 --publish never + working-directory: apps/desktop + env: + CSC_IDENTITY_AUTO_DISCOVERY: "false" + USE_SYSTEM_APP_BUILDER: "true" + + - name: Build Linux arm64 packages + run: npx electron-builder --linux --arm64 --publish never + working-directory: apps/desktop + env: + CSC_IDENTITY_AUTO_DISCOVERY: "false" + USE_SYSTEM_APP_BUILDER: "true" + + - name: List build output + run: ls -lh apps/desktop/dist/ + + - name: Upload Linux packages to release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.inputs.tag }} + files: | + apps/desktop/dist/*.AppImage + apps/desktop/dist/*.deb + apps/desktop/dist/*.rpm + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitea/workflows/build-mac.yml b/.gitea/workflows/build-mac.yml new file mode 100644 index 0000000..9f69076 --- /dev/null +++ b/.gitea/workflows/build-mac.yml @@ -0,0 +1,52 @@ +name: Build & Upload macOS Package + +on: + workflow_dispatch: + inputs: + tag: + description: 'Release tag to upload assets to (e.g. v2.0.0)' + required: true + default: 'v2.0.0' + +jobs: + build-mac: + runs-on: macos-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install workspace dependencies + run: npm install --legacy-peer-deps + + - name: Vite build + run: npx electron-vite build + working-directory: apps/desktop + + - name: Bundle standalone scheduler + run: node scripts/bundle-standalone.js + working-directory: apps/desktop + + - name: Build macOS packages + run: npx electron-builder --mac --publish never + working-directory: apps/desktop + env: + CSC_IDENTITY_AUTO_DISCOVERY: "false" + + - name: List build output + run: ls -lh apps/desktop/dist/ + + - name: Upload macOS packages to release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.inputs.tag }} + files: apps/desktop/dist/*.dmg + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitea/workflows/build-web.yml b/.gitea/workflows/build-web.yml new file mode 100644 index 0000000..eefa693 --- /dev/null +++ b/.gitea/workflows/build-web.yml @@ -0,0 +1,89 @@ +# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions +name: Build Images and Deploy +run-name: ${{ gitea.actor }} is building new PROD images and redeploying the existing stack 🚀 +on: + push: + # not working right now https://github.com/actions/runner/issues/2324 + # paths-ignore: + # - **.yml + branches: + - main + +env: + STACK_NAME: dibbly + DOT_ENV: ${{ secrets.PROD_ENV }} + PORTAINER_TOKEN: ${{ vars.PORTAINER_TOKEN }} + PORTAINER_API_URL: https://portainer.dev.nervesocket.com/api + ENDPOINT_NAME: "mini" #sometimes "primary" + IMAGE_TAG: "reg.dev.nervesocket.com/dibbly:latest" + +jobs: + Update-PROD-Stack: + runs-on: ubuntu-latest + steps: + # if: contains(github.event.pull_request.head.ref, 'init-stack') + - name: Checkout + uses: actions/checkout@v4 + with: + ref: main + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push PROD Docker image + run: | + echo $DOT_ENV | base64 -d > .env + docker buildx build --push -f Dockerfile -t $IMAGE_TAG . + + - name: Get the endpoint ID + # Usually ID is 1, but you can get it from the API. Only skip this if you are VERY sure. + run: | + ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/endpoints" | jq -r ".[] | select(.Name==\"$ENDPOINT_NAME\") | .Id") + echo "ENDPOINT_ID=$ENDPOINT_ID" >> $GITHUB_ENV + echo "Got stack Endpoint ID: $ENDPOINT_ID" + + - name: Fetch stack ID from Portainer + run: | + STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks" | jq -r ".[] | select(.Name==\"$STACK_NAME\" and .EndpointId==$ENDPOINT_ID) | .Id") + + echo "STACK_ID=$STACK_ID" >> $GITHUB_ENV + echo "Got stack ID: $STACK_ID matched with Endpoint ID: $ENDPOINT_ID" + + - name: Fetch Stack + run: | + # Get the stack details (including env vars) + STACK_DETAILS=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks/$STACK_ID") + + # Extract environment variables from the stack + echo "$STACK_DETAILS" | jq -r '.Env' > stack_env.json + + echo "Existing stack environment variables:" + cat stack_env.json + + - name: Redeploy stack in Portainer + run: | + # Read stack file content + STACK_FILE_CONTENT=$(echo "$( .env + docker buildx build --push -f Dockerfile -t $IMAGE_TAG . + + - name: Get the endpoint ID + # Usually ID is 1, but you can get it from the API. Only skip this if you are VERY sure. + run: | + ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/endpoints" | jq -r ".[] | select(.Name==\"$ENDPOINT_NAME\") | .Id") + echo "ENDPOINT_ID=$ENDPOINT_ID" >> $GITHUB_ENV + echo "Got stack Endpoint ID: $ENDPOINT_ID" + + - name: Fetch stack ID from Portainer + run: | + STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks" | jq -r ".[] | select(.Name==\"$STACK_NAME\" and .EndpointId==$ENDPOINT_ID) | .Id") + + echo "STACK_ID=$STACK_ID" >> $GITHUB_ENV + echo "Got stack ID: $STACK_ID matched with Endpoint ID: $ENDPOINT_ID" + + - name: Fetch Stack + run: | + # Get the stack details (including env vars) + STACK_DETAILS=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks/$STACK_ID") + + # Extract environment variables from the stack + echo "$STACK_DETAILS" | jq -r '.Env' > stack_env.json + + echo "Existing stack environment variables:" + cat stack_env.json + + - name: Redeploy stack in Portainer + run: | + # Read stack file content + STACK_FILE_CONTENT=$(echo "$( .env + docker buildx build --push -f Dockerfile -t $IMAGE_TAG . + + - name: Get the endpoint ID + # Usually ID is 1, but you can get it from the API. Only skip this if you are VERY sure. + run: | + ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/endpoints" | jq -r ".[] | select(.Name==\"$ENDPOINT_NAME\") | .Id") + echo "ENDPOINT_ID=$ENDPOINT_ID" >> $GITHUB_ENV + echo "Got stack Endpoint ID: $ENDPOINT_ID" + + - name: Fetch stack ID from Portainer + run: | + STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks" | jq -r ".[] | select(.Name==\"$STACK_NAME\" and .EndpointId==$ENDPOINT_ID) | .Id") + + echo "STACK_ID=$STACK_ID" >> $GITHUB_ENV + echo "Got stack ID: $STACK_ID matched with Endpoint ID: $ENDPOINT_ID" + + - name: Fetch Stack + run: | + # Get the stack details (including env vars) + STACK_DETAILS=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks/$STACK_ID") + + # Extract environment variables from the stack + echo "$STACK_DETAILS" | jq -r '.Env' > stack_env.json + + echo "Existing stack environment variables:" + cat stack_env.json + + - name: Redeploy stack in Portainer + run: | + # Read stack file content + STACK_FILE_CONTENT=$(echo "$( $null 2>&1 +} catch { + Write-Host "❌ Docker is not running. Please start Docker Desktop first." -ForegroundColor Red + exit 1 +} + +# Build the Docker image +Write-Host "📦 Building Docker image..." -ForegroundColor Blue +docker build -t reg.dev.nervesocket.com/dibbly:latest . + +# Stop existing container if running +Write-Host "🛑 Stopping existing container..." -ForegroundColor Yellow +docker-compose -f web-compose.yml down 2>$null + +# Start the service +Write-Host "🔄 Starting web service..." -ForegroundColor Blue +docker-compose -f web-compose.yml up -d + +# Wait for service to be ready +Write-Host "⏳ Waiting for service to be ready..." -ForegroundColor Yellow +Start-Sleep -Seconds 10 + +# Check if service is running +$serviceStatus = docker-compose -f web-compose.yml ps +if ($serviceStatus -match "Up") { + Write-Host "✅ Dibby Wemo Manager is now running!" -ForegroundColor Green + Write-Host "" + Write-Host "🌐 Access the web interface at:" -ForegroundColor Cyan + Write-Host " http://localhost:3456" + Write-Host "" + Write-Host "📱 Mobile-friendly URL:" -ForegroundColor Cyan + $localIP = (Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias "Ethernet*","Wi-Fi*" | Where-Object { $_.IPAddress -notlike "169.*" -and $_.IPAddress -notlike "127.*" } | Select-Object -First 1).IPAddress + Write-Host " http://$($localIP):3456" + Write-Host "" + Write-Host "📊 View logs:" -ForegroundColor Cyan + Write-Host " docker-compose -f web-compose.yml logs -f" + Write-Host "" + Write-Host "🛑 Stop service:" -ForegroundColor Cyan + Write-Host " docker-compose -f web-compose.yml down" +} else { + Write-Host "❌ Failed to start the service. Check logs:" -ForegroundColor Red + docker-compose -f web-compose.yml logs + exit 1 +} diff --git a/scripts/web-deploy.sh b/scripts/web-deploy.sh new file mode 100644 index 0000000..b0e3f13 --- /dev/null +++ b/scripts/web-deploy.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Dibby Wemo Manager - Web Deployment Script +# This script builds and deploys the web version using Docker Compose + +set -e + +echo "🚀 Deploying Dibby Wemo Manager (Web Version)..." + +# Check if Docker is running +if ! docker info > /dev/null 2>&1; then + echo "❌ Docker is not running. Please start Docker first." + exit 1 +fi + +# Build the Docker image (if needed) +echo "📦 Building Docker image..." +docker build -t reg.dev.nervesocket.com/dibbly:latest . + +# Stop existing container if running +echo "🛑 Stopping existing container..." +docker-compose -f web-compose.yml down || true + +# Start the service +echo "🔄 Starting web service..." +docker-compose -f web-compose.yml up -d + +# Wait for service to be ready +echo "⏳ Waiting for service to be ready..." +sleep 10 + +# Check if service is running +if docker-compose -f web-compose.yml ps | grep -q "Up"; then + echo "✅ Dibby Wemo Manager is now running!" + echo "" + echo "🌐 Access the web interface at:" + echo " http://localhost:3456" + echo "" + echo "📱 Mobile-friendly URL:" + echo " http://$(hostname -I | awk '{print $1}'):3456" + echo "" + echo "📊 View logs:" + echo " docker-compose -f web-compose.yml logs -f" + echo "" + echo "🛑 Stop service:" + echo " docker-compose -f web-compose.yml down" +else + echo "❌ Failed to start the service. Check logs:" + docker-compose -f web-compose.yml logs + exit 1 +fi diff --git a/web-compose.yml b/web-compose.yml new file mode 100644 index 0000000..aede496 --- /dev/null +++ b/web-compose.yml @@ -0,0 +1,33 @@ +version: '3.8' + +services: + dibbly-web: + image: reg.dev.nervesocket.com/dibbly:latest + container_name: dibbly-wemo-manager + restart: unless-stopped + ports: + - "3456:3456" + volumes: + - dibbly-data:/data + environment: + - DATA_DIR=/data + - PORT=3456 + networks: + - dibbly-network + # Use host networking on Linux for Wemo SSDP discovery + # Comment out the network_mode line below if not on Linux + # network_mode: host + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3456/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +volumes: + dibbly-data: + driver: local + +networks: + dibbly-network: + driver: bridge