From 6536ec32c9e174cb7ee3a82874b41bfc25e82d90 Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Tue, 26 Aug 2025 08:19:58 +0200 Subject: [PATCH] Update to use github container repository --- .github/workflows/docker-publish.yml | 114 ++++++++++ DOCKER_PUBLIC_SETUP.md | 297 +++++++++++++++++++++++++++ README.md | 78 +++++++ deploy-public.bat | 115 +++++++++++ deploy-public.sh | 114 ++++++++++ docker-compose.public.yml | 73 +++++++ 6 files changed, 791 insertions(+) create mode 100644 .github/workflows/docker-publish.yml create mode 100644 DOCKER_PUBLIC_SETUP.md create mode 100644 deploy-public.bat create mode 100644 deploy-public.sh create mode 100644 docker-compose.public.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..f999ad5 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,114 @@ +name: Build and Publish Docker Image + +on: + push: + branches: [ main ] + tags: [ 'v*' ] + pull_request: + branches: [ main ] + release: + types: [ published ] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + include: + - name: amd64 + platform: linux/amd64 + - name: arm64 + platform: linux/arm64 + - name: armv7 + platform: linux/arm/v7 + + 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 + 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 + 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: ${{ matrix.platform }} + 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 + + # Multi-platform manifest job + manifest: + needs: build + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + permissions: + contents: read + packages: write + + steps: + - 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 }} + 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: Create and push manifest + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/DOCKER_PUBLIC_SETUP.md b/DOCKER_PUBLIC_SETUP.md new file mode 100644 index 0000000..4f5615c --- /dev/null +++ b/DOCKER_PUBLIC_SETUP.md @@ -0,0 +1,297 @@ +# TimeTracker Public Docker Image Setup + +This guide explains how to set up and use the public Docker image for TimeTracker, which provides faster deployment and consistent builds across different environments. + +## 🚀 Quick Start + +### Prerequisites + +- Docker and Docker Compose installed +- GitHub account (for accessing the container registry) +- Basic knowledge of Docker commands + +### Step 1: Enable GitHub Container Registry + +1. **Push your code to GitHub** (if not already done) +2. **The GitHub Actions workflow will automatically build and publish images** when you: + - Push to the `main` branch + - Create a release with a `v*` tag + - Create a pull request (builds but doesn't publish) + +### Step 2: Deploy Using Public Image + +#### Option A: Automated Deployment Script + +**Linux/macOS:** +```bash +git clone https://github.com/yourusername/TimeTracker.git +cd TimeTracker +./deploy-public.sh +``` + +**Windows:** +```cmd +git clone https://github.com/yourusername/TimeTracker.git +cd TimeTracker +deploy-public.bat +``` + +#### Option B: Manual Deployment + +1. **Clone the repository:** + ```bash + git clone https://github.com/yourusername/TimeTracker.git + cd TimeTracker + ``` + +2. **Set your GitHub repository:** + ```bash + export GITHUB_REPOSITORY="yourusername/timetracker" + ``` + +3. **Create environment file:** + ```bash + cp env.example .env + # Edit .env with your configuration + ``` + +4. **Pull and run the image:** + ```bash + docker pull ghcr.io/$GITHUB_REPOSITORY:latest + docker-compose -f docker-compose.public.yml up -d + ``` + +## 🔧 Configuration + +### Environment Variables + +Create a `.env` file with your configuration: + +```bash +# Required +SECRET_KEY=your-secure-random-string-here +ADMIN_USERNAMES=admin,manager + +# Optional +TZ=Europe/Brussels +CURRENCY=EUR +ROUNDING_MINUTES=1 +SINGLE_ACTIVE_TIMER=true +ALLOW_SELF_REGISTER=true +IDLE_TIMEOUT_MINUTES=30 + +# Database (using PostgreSQL from docker-compose) +DATABASE_URL=postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker +``` + +### Docker Compose Configuration + +The `docker-compose.public.yml` file includes: + +- **TimeTracker App**: Main application container +- **PostgreSQL Database**: Persistent data storage +- **Caddy Reverse Proxy**: Optional HTTPS support + +## 📦 Available Images + +### Image Tags + +- `latest` - Latest build from main branch +- `v1.0.0` - Specific version releases +- `main-abc123` - Build from specific commit SHA + +### Supported Architectures + +- `linux/amd64` - Intel/AMD 64-bit processors +- `linux/arm64` - ARM 64-bit (Apple Silicon, ARM servers) +- `linux/arm/v7` - ARM 32-bit (Raspberry Pi 3/4) + +## 🔄 Updating + +### Automatic Updates + +The public image is automatically updated when you push to the main branch. To update your deployment: + +```bash +# Pull the latest image +docker pull ghcr.io/yourusername/timetracker:latest + +# Restart the containers +docker-compose -f docker-compose.public.yml up -d +``` + +### Manual Updates + +For specific versions: + +```bash +# Pull a specific version +docker pull ghcr.io/yourusername/timetracker:v1.0.0 + +# Update docker-compose.public.yml to use the specific tag +# Then restart +docker-compose -f docker-compose.public.yml up -d +``` + +## 🛠️ Troubleshooting + +### Common Issues + +#### 1. Image Not Found + +**Error:** `manifest for ghcr.io/username/timetracker:latest not found` + +**Solution:** +- Ensure the GitHub Actions workflow has run successfully +- Check that your repository name is correct +- Verify the image exists in your GitHub Packages + +#### 2. Permission Denied + +**Error:** `denied: permission_denied` + +**Solution:** +- Ensure your GitHub repository is public, or +- Use a personal access token for private repositories + +#### 3. Architecture Mismatch + +**Error:** `no matching manifest for linux/arm64` + +**Solution:** +- The image supports multiple architectures automatically +- If you're on ARM64, the correct image will be pulled automatically + +### Debugging + +#### Check Image Details + +```bash +# List available images +docker images ghcr.io/yourusername/timetracker + +# Inspect image details +docker inspect ghcr.io/yourusername/timetracker:latest +``` + +#### View Logs + +```bash +# Application logs +docker-compose -f docker-compose.public.yml logs app + +# Database logs +docker-compose -f docker-compose.public.yml logs db + +# All logs +docker-compose -f docker-compose.public.yml logs -f +``` + +#### Health Check + +```bash +# Check if the application is running +curl http://localhost:8080/_health + +# Check container status +docker-compose -f docker-compose.public.yml ps +``` + +## 🔒 Security Considerations + +### Public vs Private Images + +- **Public repositories**: Images are publicly accessible +- **Private repositories**: Require authentication to pull images + +### Authentication for Private Repositories + +If your repository is private, you'll need to authenticate: + +```bash +# Login to GitHub Container Registry +echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin + +# Or use a personal access token +docker login ghcr.io -u USERNAME -p YOUR_TOKEN +``` + +### Environment Variables + +- Never commit sensitive environment variables to version control +- Use `.env` files (already in `.gitignore`) +- Consider using Docker secrets for production deployments + +## 📊 Monitoring + +### Health Checks + +The application includes built-in health checks: + +```bash +# Manual health check +curl -f http://localhost:8080/_health + +# Docker health check status +docker ps --format "table {{.Names}}\t{{.Status}}" +``` + +### Logs + +```bash +# Follow application logs +docker-compose -f docker-compose.public.yml logs -f app + +# Export logs for analysis +docker-compose -f docker-compose.public.yml logs app > timetracker.log +``` + +## 🚀 Production Deployment + +### Recommended Setup + +1. **Use specific version tags** instead of `latest` +2. **Set up proper environment variables** +3. **Configure HTTPS** using the Caddy reverse proxy +4. **Set up monitoring** and log aggregation +5. **Regular backups** of the PostgreSQL database + +### Example Production Configuration + +```yaml +# docker-compose.prod.yml +version: '3.8' +services: + app: + image: ghcr.io/yourusername/timetracker:v1.0.0 + environment: + - SECRET_KEY=${SECRET_KEY} + - ADMIN_USERNAMES=${ADMIN_USERNAMES} + - TZ=${TZ} + volumes: + - app_data:/data + - ./logs:/app/logs + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/_health"] + interval: 30s + timeout: 10s + retries: 3 +``` + +## 🤝 Contributing + +To contribute to the Docker image setup: + +1. **Fork the repository** +2. **Make your changes** +3. **Test the Docker build locally** +4. **Submit a pull request** + +The GitHub Actions workflow will automatically test your changes and build new images. + +## 📚 Additional Resources + +- [GitHub Container Registry Documentation](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) +- [Docker Compose Documentation](https://docs.docker.com/compose/) +- [TimeTracker Main Documentation](README.md) diff --git a/README.md b/README.md index 8349e7a..28ae67c 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,50 @@ A robust, self-hosted time tracking application designed for teams and freelance - Comprehensive time reports with export capabilities - Visual breakdowns of time allocation and productivity +## 🐳 Docker Images + +### Public Docker Image + +TimeTracker provides pre-built Docker images available on **GitHub Container Registry (GHCR)**: + +```bash +# Pull the latest image +docker pull ghcr.io/yourusername/timetracker:latest + +# Run with docker-compose +docker-compose -f docker-compose.public.yml up -d + +# Or run directly +docker run -d \ + --name timetracker \ + -p 8080:8080 \ + -e SECRET_KEY=your-secret-key \ + -e ADMIN_USERNAMES=admin \ + ghcr.io/yourusername/timetracker:latest +``` + +**Available Tags:** +- `latest` - Latest stable build from main branch +- `v1.0.0` - Specific version releases +- `main-abc123` - Build from specific commit + +**Supported Architectures:** +- `linux/amd64` - Intel/AMD 64-bit +- `linux/arm64` - ARM 64-bit (Apple Silicon, ARM servers) +- `linux/arm/v7` - ARM 32-bit (Raspberry Pi 3/4) + +### Building Your Own Image + +For custom modifications or development: + +```bash +# Build locally +docker build -t timetracker . + +# Run with docker-compose +docker-compose up -d +``` + ## 🚀 Quick Start ### Prerequisites @@ -83,6 +127,40 @@ A robust, self-hosted time tracking application designed for teams and freelance ### Installation +#### Option 1: Using Public Docker Image (Recommended) + +**Fastest deployment with pre-built images:** + +1. **Clone the repository:** + ```bash + git clone https://github.com/yourusername/TimeTracker.git + cd TimeTracker + ``` + +2. **Run the deployment script:** + ```bash + # Linux/macOS + ./deploy-public.sh + + # Windows + deploy-public.bat + ``` + +3. **Access the application:** + ``` + http://your-pi-ip:8080 + ``` + +**Benefits:** +- ✅ No build time required +- ✅ Consistent builds across environments +- ✅ Automatic updates when you push to main +- ✅ Multi-architecture support (AMD64, ARM64, ARMv7) + +#### Option 2: Build from Source + +**For development or custom modifications:** + 1. **Clone the repository:** ```bash git clone https://github.com/yourusername/TimeTracker.git diff --git a/deploy-public.bat b/deploy-public.bat new file mode 100644 index 0000000..90d73c1 --- /dev/null +++ b/deploy-public.bat @@ -0,0 +1,115 @@ +@echo off +setlocal enabledelayedexpansion + +echo 🚀 Time Tracker Public Image Deployment +echo ======================================= + +REM Check if Docker is installed +docker --version >nul 2>&1 +if errorlevel 1 ( + echo ❌ Docker is not installed. Please install Docker Desktop first: + echo https://www.docker.com/products/docker-desktop/ + pause + exit /b 1 +) + +REM Check if Docker Compose is installed +docker-compose --version >nul 2>&1 +if errorlevel 1 ( + echo ❌ Docker Compose is not installed. Please install Docker Compose first. + pause + exit /b 1 +) + +echo ✅ Docker and Docker Compose are installed + +REM Get GitHub repository from git remote or prompt user +for /f "tokens=*" %%i in ('git remote get-url origin 2^>nul ^| sed "s/.*github\.com[:/]\([^/]*\/[^/]*\)\.git/\1/"') do set GITHUB_REPO=%%i + +if "%GITHUB_REPO%"=="" ( + echo ⚠️ Could not detect GitHub repository from git remote + set /p GITHUB_REPO="Enter your GitHub repository (e.g., username/timetracker): " +) + +REM Export for docker-compose +set GITHUB_REPOSITORY=%GITHUB_REPO% + +echo 📦 Using public image: ghcr.io/%GITHUB_REPOSITORY% + +REM Create necessary directories +echo 📁 Creating directories... +if not exist "data" mkdir data +if not exist "logs" mkdir logs +if not exist "backups" mkdir backups + +REM Copy environment file if it doesn't exist +if not exist ".env" ( + echo 📝 Creating .env file from template... + copy env.example .env + echo ⚠️ Please edit .env file with your configuration before starting + echo Key settings to review: + echo - SECRET_KEY: Change this to a secure random string + echo - ADMIN_USERNAMES: Set your admin usernames + echo - TZ: Set your timezone + echo - CURRENCY: Set your currency +) else ( + echo ✅ .env file already exists +) + +REM Pull the latest image +echo 📥 Pulling latest Time Tracker image... +docker pull ghcr.io/%GITHUB_REPOSITORY%:latest + +REM Start the application using public image +echo 🚀 Starting Time Tracker with public image... +docker-compose -f docker-compose.public.yml up -d + +REM Wait for application to start +echo ⏳ Waiting for application to start... +timeout /t 10 /nobreak >nul + +REM Check if application is running +curl -f http://localhost:8080/_health >nul 2>&1 +if errorlevel 1 ( + echo ❌ Application failed to start. Check logs with: + echo docker-compose -f docker-compose.public.yml logs + pause + exit /b 1 +) else ( + echo ✅ Time Tracker is running successfully! + echo. + echo 🌐 Access the application at: + echo http://localhost:8080 + echo. + echo 📋 Next steps: + echo 1. Open the application in your browser + echo 2. Log in with your admin username + echo 3. Create your first project + echo 4. Start tracking time! + echo. + echo 🔧 Useful commands: + echo View logs: docker-compose -f docker-compose.public.yml logs -f + echo Stop app: docker-compose -f docker-compose.public.yml down + echo Restart: docker-compose -f docker-compose.public.yml restart + echo Update: docker pull ghcr.io/%GITHUB_REPOSITORY%:latest ^&^& docker-compose -f docker-compose.public.yml up -d +) + +REM Optional: Enable TLS with reverse proxy +set /p ENABLE_TLS="🔒 Enable HTTPS with reverse proxy? (y/N): " +if /i "%ENABLE_TLS%"=="y" ( + echo 🔒 Starting with TLS support... + docker-compose -f docker-compose.public.yml --profile tls up -d + echo ✅ HTTPS enabled! Access at: + echo https://localhost +) + +echo. +echo 🎉 Deployment complete! +echo. +echo 💡 Benefits of using the public image: +echo - Faster deployment (no build time) +echo - Consistent builds across environments +echo - Automatic updates when you push to main +echo - Multi-architecture support (AMD64, ARM64, ARMv7) + +pause diff --git a/deploy-public.sh b/deploy-public.sh new file mode 100644 index 0000000..dc790db --- /dev/null +++ b/deploy-public.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Time Tracker Public Image Deployment Script +# This script deploys Time Tracker using the pre-built public Docker image + +set -e + +echo "🚀 Time Tracker Public Image Deployment" +echo "=======================================" + +# Check if Docker is installed +if ! command -v docker &> /dev/null; then + echo "❌ Docker is not installed. Please install Docker first:" + echo " curl -fsSL https://get.docker.com -o get-docker.sh" + echo " sudo sh get-docker.sh" + echo " sudo usermod -aG docker $USER" + exit 1 +fi + +# Check if Docker Compose is installed +if ! command -v docker-compose &> /dev/null; then + echo "❌ Docker Compose is not installed. Please install Docker Compose first:" + echo " sudo apt-get update" + echo " sudo apt-get install docker-compose-plugin" + exit 1 +fi + +echo "✅ Docker and Docker Compose are installed" + +# Get GitHub repository from git remote or prompt user +GITHUB_REPO=$(git remote get-url origin 2>/dev/null | sed 's/.*github\.com[:/]\([^/]*\/[^/]*\)\.git/\1/' || echo "") + +if [ -z "$GITHUB_REPO" ]; then + echo "⚠️ Could not detect GitHub repository from git remote" + read -p "Enter your GitHub repository (e.g., username/timetracker): " GITHUB_REPO +fi + +# Export for docker-compose +export GITHUB_REPOSITORY="$GITHUB_REPO" + +echo "📦 Using public image: ghcr.io/$GITHUB_REPOSITORY" + +# Create necessary directories +echo "📁 Creating directories..." +mkdir -p data logs backups + +# Copy environment file if it doesn't exist +if [ ! -f .env ]; then + echo "📝 Creating .env file from template..." + cp env.example .env + echo "⚠️ Please edit .env file with your configuration before starting" + echo " Key settings to review:" + echo " - SECRET_KEY: Change this to a secure random string" + echo " - ADMIN_USERNAMES: Set your admin usernames" + echo " - TZ: Set your timezone" + echo " - CURRENCY: Set your currency" +else + echo "✅ .env file already exists" +fi + +# Pull the latest image +echo "📥 Pulling latest Time Tracker image..." +docker pull "ghcr.io/$GITHUB_REPOSITORY:latest" + +# Start the application using public image +echo "🚀 Starting Time Tracker with public image..." +docker-compose -f docker-compose.public.yml up -d + +# Wait for application to start +echo "⏳ Waiting for application to start..." +sleep 10 + +# Check if application is running +if curl -f http://localhost:8080/_health > /dev/null 2>&1; then + echo "✅ Time Tracker is running successfully!" + echo "" + echo "🌐 Access the application at:" + echo " http://$(hostname -I | awk '{print $1}'):8080" + echo "" + echo "📋 Next steps:" + echo " 1. Open the application in your browser" + echo " 2. Log in with your admin username" + echo " 3. Create your first project" + echo " 4. Start tracking time!" + echo "" + echo "🔧 Useful commands:" + echo " View logs: docker-compose -f docker-compose.public.yml logs -f" + echo " Stop app: docker-compose -f docker-compose.public.yml down" + echo " Restart: docker-compose -f docker-compose.public.yml restart" + echo " Update: docker pull ghcr.io/$GITHUB_REPOSITORY:latest && docker-compose -f docker-compose.public.yml up -d" +else + echo "❌ Application failed to start. Check logs with:" + echo " docker-compose -f docker-compose.public.yml logs" + exit 1 +fi + +# Optional: Enable TLS with reverse proxy +read -p "🔒 Enable HTTPS with reverse proxy? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "🔒 Starting with TLS support..." + docker-compose -f docker-compose.public.yml --profile tls up -d + echo "✅ HTTPS enabled! Access at:" + echo " https://$(hostname -I | awk '{print $1}')" +fi + +echo "" +echo "🎉 Deployment complete!" +echo "" +echo "💡 Benefits of using the public image:" +echo " - Faster deployment (no build time)" +echo " - Consistent builds across environments" +echo " - Automatic updates when you push to main" +echo " - Multi-architecture support (AMD64, ARM64, ARMv7)" diff --git a/docker-compose.public.yml b/docker-compose.public.yml new file mode 100644 index 0000000..81bda61 --- /dev/null +++ b/docker-compose.public.yml @@ -0,0 +1,73 @@ +services: + app: + image: ghcr.io/${GITHUB_REPOSITORY:-yourusername/timetracker}:latest + container_name: timetracker-app + environment: + - TZ=${TZ:-Europe/Brussels} + - CURRENCY=${CURRENCY:-EUR} + - ROUNDING_MINUTES=${ROUNDING_MINUTES:-1} + - SINGLE_ACTIVE_TIMER=${SINGLE_ACTIVE_TIMER:-true} + - ALLOW_SELF_REGISTER=${ALLOW_SELF_REGISTER:-true} + - IDLE_TIMEOUT_MINUTES=${IDLE_TIMEOUT_MINUTES:-30} + - ADMIN_USERNAMES=${ADMIN_USERNAMES:-admin} + - SECRET_KEY=${SECRET_KEY:-your-secret-key-change-this} + - DATABASE_URL=postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker + - LOG_FILE=/app/logs/timetracker.log + ports: + - "8080:8080" + volumes: + - app_data:/data + - ./logs:/app/logs + depends_on: + - db + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/_health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + db: + image: postgres:16-alpine + container_name: timetracker-db + environment: + - POSTGRES_DB=${POSTGRES_DB:-timetracker} + - POSTGRES_USER=${POSTGRES_USER:-timetracker} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-timetracker} + - TZ=${TZ:-Europe/Brussels} + volumes: + - db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + # Optional reverse proxy for TLS on LAN + proxy: + image: caddy:2-alpine + container_name: timetracker-proxy + volumes: + - ./docker/Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + ports: + - "80:80" + - "443:443" + depends_on: + - app + restart: unless-stopped + profiles: + - tls + +volumes: + app_data: + driver: local + db_data: + driver: local + caddy_data: + driver: local + caddy_config: + driver: local