diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..dacef15da --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# Node modules (will be installed in container) +node_modules +*/node_modules +**/node_modules + +# Build outputs +dist +build +coverage +*.tsbuildinfo + +# Development files +.git +.github +.vscode +*.log +# Note: We need yarn.lock and package-lock.json for dependency resolution +# *.lock + +# Docker +*.dockerignore +Dockerfile* +docker-compose* +.docker + +# Docker volumes +docker_data/ +docker_volumes/ + +# Development +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Cypress +cypress/videos +cypress/screenshots + +# Misc +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..b4ef6d10b --- /dev/null +++ b/.env.example @@ -0,0 +1,31 @@ +# Environment Variables for Docker Compose +# Copy this file to .env and modify as needed + +# Authentication Type (SNAuth or IdentityServer) +AUTH_TYPE=SNAuth + +# Node Environment +NODE_ENV=production + +# Ports +SENSENET_CLIENT_PORT=8080 +SENSENET_DEV_PORT=3000 + +# Development Settings (for hot reload) +CHOKIDAR_USEPOLLING=true +WATCHPACK_POLLING=true + +# Database Settings (if using database service) +# DB_PASSWORD=YourPassword123! +# DB_NAME=SenseNet +# DB_USER=sa + +# Redis Settings (if using redis service) +# REDIS_PASSWORD= + +# Traefik Settings (if using traefik service) +# TRAEFIK_DOMAIN=sensenet.local + +# Backend API URL (if connecting to external backend) +# REACT_APP_SERVICE_URL=https://dev.demo.sensenet.com +# REACT_APP_IDENTITY_SERVER_URL=https://is.demo.sensenet.com \ No newline at end of file diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 000000000..11acc91fe --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,65 @@ +name: Docker Image CI + +on: + push: + branches: + - "feature/docker-containerization" # Only containerization branch for now + pull_request: + types: [opened, synchronize, reopened, ready_for_review] # Skip draft PRs + branches: + - "develop" + - "main" + - "feature/sn-auth-package-extraimprovements" # Current dev branch with latest sn-auth + +jobs: + build: + runs-on: ubuntu-latest + # Skip draft PRs + if: github.event.pull_request.draft == false || github.event_name == 'push' + + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Check if Dockerfile exists + id: check_dockerfile + run: | + if [ -f "Dockerfile" ]; then + echo "dockerfile_exists=true" >> $GITHUB_OUTPUT + else + echo "dockerfile_exists=false" >> $GITHUB_OUTPUT + echo "⚠️ No Dockerfile found, skipping Docker build" + fi + + - name: Set up Docker metadata + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' + id: meta + uses: docker/metadata-action@v5 + with: + images: sensenetcsp/sn-client + tags: | + # Clean branch name (e.g., feature-docker-containerization) + type=ref,event=branch + # Branch name with SHA (e.g., feature-docker-containerization-abc1234) + type=ref,event=branch,suffix=-{{sha}} + # Latest tag for main branch + type=raw,value=latest,enable={{is_default_branch}} + # PR number for pull requests + type=ref,event=pr + + - name: Login to DockerHub + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + if: steps.check_dockerfile.outputs.dockerfile_exists == 'true' + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 000000000..059048134 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,129 @@ +# Docker Setup for SenseNet Client + +This guide explains how to run the SenseNet client application using Docker. + +## 🚀 Quick Start + +### Development (with hot reload) +```bash +docker-compose -f docker-compose.dev.yml up -d +``` +- **URL**: http://localhost:8080 +- **Hot reload**: ✅ File changes are instantly reflected +- **Use case**: Active development + +### Production +```bash +docker-compose -f docker-compose.prod.yml up -d +``` +- **URL**: http://localhost:8080 +- **Hot reload**: ❌ Static built files +- **Use case**: Testing production builds, deployment + +## 📁 Files Overview + +| File | Purpose | +|------|---------| +| `Dockerfile` | Single Docker image for both dev and prod | +| `docker-compose.dev.yml` | Development setup with volume mounts | +| `docker-compose.prod.yml` | Production setup without volume mounts | +| `.dockerignore` | Excludes unnecessary files from build context | + +## 🔧 How It Works + +### Development Mode +- **Volume mounting**: Your local code is mounted into the container +- **File watching**: Changes trigger automatic rebuilds +- **Command**: `yarn snapp start` (webpack dev server with hot reload) + +### Production Mode +- **Built files**: Uses pre-built static files inside the container +- **No volumes**: Container is self-contained +- **Command**: `yarn snapp start` (same command, but runs webpack dev server on built files) + +## 🛠️ Common Commands + +```bash +# Start development +docker-compose -f docker-compose.dev.yml up -d + +# Stop development +docker-compose -f docker-compose.dev.yml down + +# Rebuild and start (after dependency changes) +docker-compose -f docker-compose.dev.yml up --build -d + +# View logs +docker-compose -f docker-compose.dev.yml logs -f + +# Start production +docker-compose -f docker-compose.prod.yml up -d +``` + +## 🐳 Docker Images + +Automatic builds are available on DockerHub: + +```bash +# Latest development build +docker pull sensenetcsp/sn-client:feature-docker-containerization + +# Specific commit +docker pull sensenetcsp/sn-client:feature-docker-containerization-abc1234 + +# Production (when merged to main) +docker pull sensenetcsp/sn-client:latest +``` + +## ⚙️ Configuration + +### Environment Variables +Both compose files support these environment variables: + +- `NODE_ENV`: `development` or `production` +- `AUTH_TYPE`: `SNAuth` or `IdentityServer` +- `CHOKIDAR_USEPOLLING`: `true` (dev only, for file watching) +- `WATCHPACK_POLLING`: `true` (dev only, for webpack) + +### Port Configuration +- **Default**: Port 8080 for both dev and prod +- **Customizable**: Change the host port in docker-compose files + +## 🔍 Troubleshooting + +### Container won't start +```bash +# Check logs +docker-compose -f docker-compose.dev.yml logs + +# Rebuild from scratch +docker-compose -f docker-compose.dev.yml down +docker-compose -f docker-compose.dev.yml up --build +``` + +### Hot reload not working +- Ensure you're using the dev compose file +- Restart the container if file watching stops working + +### Port already in use +```bash +# Change the port in docker-compose file +ports: + - "3000:8080" # Use port 3000 instead of 8080 +``` + +## 📦 Build Process + +The Docker build process: +1. **Copy source code** (excluding files in `.dockerignore`) +2. **Install dependencies** with `yarn install` +3. **Build packages** with `yarn build` +4. **Start application** with `yarn snapp start` + +## 🚀 CI/CD + +GitHub Actions automatically builds and pushes Docker images when: +- Code is pushed to `feature/docker-containerization` +- Pull requests target `develop`, `main`, or `feature/sn-auth-package-extraimprovements` + +Images are tagged based on branch names and commit SHAs for easy identification. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..83bb18a13 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Development Dockerfile for SenseNet client with hot reload +FROM node:20-alpine + +# Set working directory +WORKDIR /app + +# Copy everything (dockerignore excludes unwanted files) +COPY . . + +# Install dependencies +RUN yarn install + +# Build packages (required for the app to work) +RUN yarn build + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 + +# Default command (overridden in docker-compose for hot reload) +CMD ["yarn", "snapp", "start"] diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..4c2a74a77 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,22 @@ +version: "3.8" + +services: + sensenet-dev: + build: + context: . + dockerfile: Dockerfile + container_name: sensenet-dev + ports: + - "8080:8080" + environment: + - NODE_ENV=development + - AUTH_TYPE=SNAuth + - CHOKIDAR_USEPOLLING=true + - WATCHPACK_POLLING=true + volumes: + - .:/app + - /app/node_modules + - /app/packages/*/node_modules + - /app/apps/*/node_modules + command: yarn snapp start + restart: unless-stopped diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 000000000..781e6904c --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,14 @@ +version: "3.8" + +services: + sensenet-app: + build: + context: . + dockerfile: Dockerfile + container_name: sensenet-app + ports: + - "8080:8080" + environment: + - NODE_ENV=production + - AUTH_TYPE=SNAuth + restart: unless-stopped