From 57dfa152d5ab4cb6eda769b2159b7e31ec63729f Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 22 Jan 2026 18:00:12 -0500 Subject: [PATCH 1/3] feat: add docker images --- .github/workflows/docker-publish.yml | 168 +++++++++++++++++++++++++++ README.md | 38 +++++- docker-compose.yml | 5 +- docs/deployment.md | 111 +++++++++++++++++- package.json | 2 +- packages/app/Dockerfile | 4 +- scripts/docker-build.sh | 20 ++++ 7 files changed, 337 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/docker-publish.yml create mode 100644 scripts/docker-build.sh diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..5473bc01 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,168 @@ +name: Build and Publish Docker Images + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: "Version tag (e.g., v1.0.0)" + required: true + type: string + +concurrency: + group: docker-${{ github.ref }} + cancel-in-progress: false + +env: + REGISTRY: ghcr.io + IMAGE_NAME_API: ${{ github.repository }}/api + IMAGE_NAME_APP: ${{ github.repository }}/app + +jobs: + check-release-type: + name: Check if this is an app release + runs-on: ubuntu-latest + outputs: + should-build: ${{ steps.check.outputs.should-build }} + version: ${{ steps.check.outputs.version }} + steps: + - name: Check release tag format + id: check + run: | + TAG="${{ github.event.release.tag_name || github.event.inputs.version }}" + if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "should-build=true" >> $GITHUB_OUTPUT + echo "version=$TAG" >> $GITHUB_OUTPUT + echo "✅ App release detected: $TAG" + elif [[ "$TAG" =~ ^@tuvixrss/ ]]; then + echo "should-build=false" >> $GITHUB_OUTPUT + echo "⏭️ Package release detected: $TAG - skipping Docker build" + else + echo "should-build=true" >> $GITHUB_OUTPUT + echo "version=$TAG" >> $GITHUB_OUTPUT + echo "⚠️ Unknown tag format: $TAG - proceeding with build" + fi + + build-and-push-api: + name: Build and Push API Image + runs-on: ubuntu-latest + needs: [check-release-type] + if: needs.check-release-type.outputs.should-build == 'true' + 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 Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push API image + uses: docker/build-push-action@v6 + with: + context: . + file: ./packages/api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + build-and-push-app: + name: Build and Push App Image + runs-on: ubuntu-latest + needs: [check-release-type] + if: needs.check-release-type.outputs.should-build == 'true' + 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 Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_APP }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push App image + uses: docker/build-push-action@v6 + with: + context: . + file: ./packages/app/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VITE_API_URL=http://localhost:3001/trpc + VITE_APP_VERSION=${{ needs.check-release-type.outputs.version }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + create-summary: + name: Create Deployment Summary + runs-on: ubuntu-latest + needs: [check-release-type, build-and-push-api, build-and-push-app] + if: needs.check-release-type.outputs.should-build == 'true' + steps: + - name: Generate summary + run: | + VERSION="${{ needs.check-release-type.outputs.version }}" + echo "# 🐳 Docker Images Published" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** \`$VERSION\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Images" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **API:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }}:$VERSION\`" >> $GITHUB_STEP_SUMMARY + echo "- **App:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_APP }}:$VERSION\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Usage" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo '```yaml' >> $GITHUB_STEP_SUMMARY + echo 'services:' >> $GITHUB_STEP_SUMMARY + echo ' api:' >> $GITHUB_STEP_SUMMARY + echo " image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }}:$VERSION" >> $GITHUB_STEP_SUMMARY + echo ' # ... rest of config' >> $GITHUB_STEP_SUMMARY + echo ' app:' >> $GITHUB_STEP_SUMMARY + echo " image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_APP }}:$VERSION" >> $GITHUB_STEP_SUMMARY + echo ' # ... rest of config' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index 80ed741b..e3f6f944 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,36 @@ Tuvix supports two deployment methods: See the **[Deployment Guide](./docs/deployment.md)** for detailed instructions. -> **📦 Docker Images Coming Soon:** Pre-built container images will be published to a container registry once the project reaches a stable release. For now, use the Dockerfiles and docker-compose scripts included in the repository. - ### Quick Start (Docker) +The docker-compose.yml works with both pre-built images and source builds: + +**Option 1: Pre-built Images (Recommended - Fast)** + +```bash +# Create directory and download files +mkdir TuvixRSS && cd TuvixRSS +curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/docker-compose.yml +curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/env.example +cp env.example .env + +# Edit .env and configure: +# 1. BETTER_AUTH_SECRET (generate: openssl rand -base64 32) +# 2. Admin credentials (ADMIN_USERNAME, ADMIN_EMAIL, ADMIN_PASSWORD) +nano .env + +# Pin to specific version (optional) +export VERSION=v0.6.1 # Or use 'latest' for newest + +# Pull images and start +docker compose pull +docker compose up -d + +# Access at http://localhost:5173 +``` + +**Option 2: Build from Source** + > **⚠️ Use a Release:** The `main` branch contains active development and is not guaranteed to be stable. Always use the latest release for self-hosting. Clone the repository and checkout the latest release: @@ -54,6 +80,14 @@ cp env.example .env # Edit .env and configure: # 1. BETTER_AUTH_SECRET (generate: openssl rand -base64 32) # 2. ADMIN_USERNAME, ADMIN_EMAIL, ADMIN_PASSWORD (for your admin user) +nano .env + +# Build and start (includes git version in settings) +pnpm run docker:build +docker compose up -d + +# Or build without version script +docker compose build docker compose up -d ``` diff --git a/docker-compose.yml b/docker-compose.yml index d6b887d7..94896bb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ services: api: + image: ghcr.io/techsquidtv/tuvixrss/api:${VERSION:-latest} build: context: . dockerfile: ./packages/api/Dockerfile - pull_policy: build container_name: tuvix-api restart: unless-stopped ports: @@ -46,12 +46,13 @@ services: - tuvix-network app: + image: ghcr.io/techsquidtv/tuvixrss/app:${VERSION:-latest} build: context: . dockerfile: ./packages/app/Dockerfile args: - VITE_API_URL=${VITE_API_URL:-http://localhost:3001/trpc} - pull_policy: build + - VITE_APP_VERSION=${VITE_APP_VERSION:-docker} container_name: tuvix-app restart: unless-stopped ports: diff --git a/docs/deployment.md b/docs/deployment.md index 09ad18c7..be75cf03 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -259,7 +259,73 @@ pnpm run pre-check ### Production Deployment -#### 1. Server Setup +The docker-compose.yml supports both pre-built images and source builds. Choose your preferred method: + +#### Option 1: Pre-built Images (Recommended) + +**Advantages:** + +- ✅ No build step required (faster deployment) +- ✅ Version tag embedded in image (shows in settings) +- ✅ Multi-arch support (amd64 & arm64) +- ✅ Consistent builds across environments + +**Setup:** + +```bash +# On your production server +mkdir TuvixRSS && cd TuvixRSS + +# Download docker-compose and env files +curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/docker-compose.yml +curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/env.example +cp env.example .env +vim .env # Configure your environment +``` + +**Pin to a specific version (recommended for production):** + +```bash +# Set version in .env file +echo "VERSION=v0.6.1" >> .env + +# Or export temporarily +export VERSION=v0.6.1 + +# Pull images and start +docker compose pull +docker compose up -d +``` + +**Deploy:** + +```bash +# Verify health +curl http://localhost:3001/health +curl http://localhost:5173/health + +# Monitor logs +docker compose logs -f +``` + +**Updates:** + +```bash +# Update to new version +export VERSION=v0.7.0 # Or update in .env +docker compose pull +docker compose up -d +``` + +#### Option 2: Build from Source + +**Advantages:** + +- ✅ Full control over build process +- ✅ Can modify code before deployment +- ✅ No external registry dependencies + +**Setup:** ```bash # On your production server @@ -304,7 +370,7 @@ ADMIN_PASSWORD= FETCH_INTERVAL_MINUTES=60 # How often to fetch RSS feeds ``` -#### 2. Deploy +**Deploy:** ```bash # Build and start @@ -319,7 +385,15 @@ curl http://localhost:5173/health docker compose logs -f ``` -#### 3. Database Backups +**Note:** When building from source, the version displayed in settings will default to "docker". To show the git version, set `VITE_APP_VERSION` before building: + +```bash +# Set version to git tag +export VITE_APP_VERSION=$(git describe --tags --always) +docker compose build +``` + +#### Database Backups ```bash # Backup database @@ -329,14 +403,32 @@ docker compose exec api cp /app/data/tuvix.db /app/data/backup-$(date +%Y%m%d).d cp ./data/tuvix.db ./data/backup-$(date +%Y%m%d).db ``` -#### 4. Updates +#### Updates + +**For Pre-built Images:** + +```bash +# Update version +export VERSION=v0.7.0 # Or update in .env + +# Pull and restart +docker compose pull +docker compose up -d +``` + +**For Source Builds:** ```bash # Pull latest code git pull origin main +# Or checkout specific release +git fetch --tags +git checkout v0.7.0 + # Rebuild and restart docker compose down +export VITE_APP_VERSION=$(git describe --tags --always) docker compose build docker compose up -d @@ -365,7 +457,8 @@ docker compose up -d - Multi-stage build with nginx - Build context: monorepo root (not `packages/app`) - Copies workspace files (`pnpm-workspace.yaml`, root `pnpm-lock.yaml`) -- Accepts VITE_API_URL build arg +- Accepts VITE_API_URL build arg (API endpoint for frontend) +- Accepts VITE_APP_VERSION build arg (version displayed in settings, defaults to "docker") - SPA routing support - Static asset caching - Health check on /health endpoint @@ -398,6 +491,7 @@ services: dockerfile: ./packages/app/Dockerfile args: - VITE_API_URL=${VITE_API_URL:-http://localhost:3001/trpc} + - VITE_APP_VERSION=${VITE_APP_VERSION:-docker} ports: - "5173:80" depends_on: @@ -405,6 +499,13 @@ services: condition: service_healthy ``` +**Build Arguments:** + +- `VITE_API_URL`: The API endpoint (defaults to `http://localhost:3001/trpc`) +- `VITE_APP_VERSION`: Version string shown in settings page (defaults to `docker`) + - Can be set to git commit SHA: `VITE_APP_VERSION=$(git rev-parse --short HEAD)` + - Or a version tag: `VITE_APP_VERSION=v1.2.3` + ### Monitoring & Troubleshooting #### Health Checks diff --git a/package.json b/package.json index 7a39d576..87f1bc8c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "dev:api": "cd packages/api && pnpm run dev", "dev:app": "cd packages/app && pnpm run dev", "dev": "concurrently \"pnpm:dev:api\" \"pnpm:dev:app\"", - "docker:build": "docker compose build", + "docker:build": "bash scripts/docker-build.sh", "docker:down": "docker compose down", "docker:logs": "docker compose logs -f", "docker:up": "docker compose up -d", diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index dd6c588e..83772447 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -23,9 +23,11 @@ RUN pnpm install --frozen-lockfile # Copy source code COPY packages/app ./packages/app -# Build argument for API URL +# Build arguments ARG VITE_API_URL=http://localhost:3001/trpc +ARG VITE_APP_VERSION ENV VITE_API_URL=${VITE_API_URL} +ENV VITE_APP_VERSION=${VITE_APP_VERSION} # Build the application RUN pnpm --filter @tuvixrss/app build diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh new file mode 100644 index 00000000..6cbc9e44 --- /dev/null +++ b/scripts/docker-build.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +# Get git version (tag or commit SHA) +VERSION=$(git describe --tags --always 2>/dev/null || echo "unknown") + +echo "Building TuvixRSS Docker images..." +echo "Version: $VERSION" + +# Export for docker-compose +export VITE_APP_VERSION="$VERSION" + +# Build images +docker compose build "$@" + +echo "" +echo "✅ Build complete!" +echo " Version: $VERSION" +echo "" +echo "To start: docker compose up -d" From fe3d8fd7817850693501afc3d0740a3d8543237a Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 22 Jan 2026 18:47:05 -0500 Subject: [PATCH 2/3] fix: resolve PR comments --- .github/workflows/docker-publish.yml | 4 ++-- README.md | 2 +- docker-compose.yml | 4 ++-- docs/deployment.md | 16 ++++++++-------- scripts/docker-build.sh | 1 + 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 5473bc01..acf18a6b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -75,7 +75,7 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=latest - name: Build and push API image uses: docker/build-push-action@v6 @@ -120,7 +120,7 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=latest - name: Build and push App image uses: docker/build-push-action@v6 diff --git a/README.md b/README.md index e3f6f944..f17104c9 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The docker-compose.yml works with both pre-built images and source builds: ```bash # Create directory and download files -mkdir TuvixRSS && cd TuvixRSS +mkdir Tuvix-RSS && cd Tuvix-RSS curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/docker-compose.yml curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/env.example cp env.example .env diff --git a/docker-compose.yml b/docker-compose.yml index 94896bb4..000b92d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: api: - image: ghcr.io/techsquidtv/tuvixrss/api:${VERSION:-latest} + image: ghcr.io/techsquidtv/tuvix-rss/api:${VERSION:-latest} build: context: . dockerfile: ./packages/api/Dockerfile @@ -46,7 +46,7 @@ services: - tuvix-network app: - image: ghcr.io/techsquidtv/tuvixrss/app:${VERSION:-latest} + image: ghcr.io/techsquidtv/tuvix-rss/app:${VERSION:-latest} build: context: . dockerfile: ./packages/app/Dockerfile diff --git a/docs/deployment.md b/docs/deployment.md index be75cf03..ddcb4670 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -140,8 +140,8 @@ TuvixRSS uses **Better Auth** for authentication, which manages user sessions vi ```bash # Clone repository -git clone https://github.com/yourusername/TuvixRSS.git -cd TuvixRSS +git clone https://github.com/TechSquidTV/Tuvix-RSS.git +cd Tuvix-RSS # Copy environment file cp .env.example .env @@ -274,7 +274,7 @@ The docker-compose.yml supports both pre-built images and source builds. Choose ```bash # On your production server -mkdir TuvixRSS && cd TuvixRSS +mkdir Tuvix-RSS && cd Tuvix-RSS # Download docker-compose and env files curl -O https://raw.githubusercontent.com/TechSquidTV/Tuvix-RSS/main/docker-compose.yml @@ -287,10 +287,10 @@ vim .env # Configure your environment ```bash # Set version in .env file -echo "VERSION=v0.6.1" >> .env +echo "VERSION=v0.7.0" >> .env # Or export temporarily -export VERSION=v0.6.1 +export VERSION=v0.7.0 # Pull images and start docker compose pull @@ -302,7 +302,7 @@ docker compose up -d ```bash # Verify health curl http://localhost:3001/health -curl http://localhost:5173/health +curl http://localhost:5173/health # app container listens on 8080, exposed on host as 5173 # Monitor logs docker compose logs -f @@ -379,7 +379,7 @@ docker compose up -d # Verify health curl http://localhost:3001/health -curl http://localhost:5173/health +curl http://localhost:5173/health # app container listens on 8080, exposed on host as 5173 # Monitor logs docker compose logs -f @@ -493,7 +493,7 @@ services: - VITE_API_URL=${VITE_API_URL:-http://localhost:3001/trpc} - VITE_APP_VERSION=${VITE_APP_VERSION:-docker} ports: - - "5173:80" + - "5173:8080" depends_on: api: condition: service_healthy diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh index 6cbc9e44..4709b4b6 100644 --- a/scripts/docker-build.sh +++ b/scripts/docker-build.sh @@ -8,6 +8,7 @@ echo "Building TuvixRSS Docker images..." echo "Version: $VERSION" # Export for docker-compose +export VERSION="$VERSION" export VITE_APP_VERSION="$VERSION" # Build images From 7e6b5ed777886c7d2578c2a780d0d461234a6cb9 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 22 Jan 2026 18:58:40 -0500 Subject: [PATCH 3/3] ci: docker prefer local build --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 000b92d8..5e4403b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ services: build: context: . dockerfile: ./packages/api/Dockerfile + pull_policy: build container_name: tuvix-api restart: unless-stopped ports: @@ -47,6 +48,7 @@ services: app: image: ghcr.io/techsquidtv/tuvix-rss/app:${VERSION:-latest} + pull_policy: build build: context: . dockerfile: ./packages/app/Dockerfile