From d3c8cd36383e0de488a9165244993051b884c26d Mon Sep 17 00:00:00 2001 From: Aneesh Date: Sun, 16 Nov 2025 09:23:42 -0600 Subject: [PATCH] feat(custom-setup): add custom Docker setup with ERPNext and HRMS - Add CLAUDE.md with repository guidance for Claude Code - Add apps.json defining ERPNext and HRMS apps - Add build-custom-image.sh script for building custom images - Add docs/README-CUSTOM-SETUP.md with setup instructions - Update pwd.yml to use custom image and install HRMS app This enables a complete custom deployment with ERPNext (full ERP) and HRMS (HR management) using Docker containers. --- CLAUDE.md | 330 ++++++++++++++++++++++++++++++++++++ apps.json | 11 ++ build-custom-image.sh | 47 +++++ docs/README-CUSTOM-SETUP.md | 162 ++++++++++++++++++ pwd.yml | 36 +++- 5 files changed, 577 insertions(+), 9 deletions(-) create mode 100644 CLAUDE.md create mode 100644 apps.json create mode 100755 build-custom-image.sh create mode 100644 docs/README-CUSTOM-SETUP.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..8d60f77d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,330 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +This repository provides Docker containerization for Frappe Framework and ERPNext. It supports both development environments and production deployments through multiple compose configurations and image types. + +## Repository Architecture + +### Multi-Image Strategy + +There are **four distinct Dockerfile types** in `images/`: + +- **bench** (`images/bench/Dockerfile`): CLI-only setup for development/debugging +- **custom** (`images/custom/Containerfile`): Production-ready, built from Python base, uses `apps.json` for app installation +- **layered** (`images/layered/Containerfile`): Same as custom but based on prebuilt Docker Hub images for faster builds +- **production** (`images/production/Containerfile`): Quick-start image with only Frappe + ERPNext (not customizable) + +**Key principle**: `custom` and `layered` are for real deployments; `production` is for exploration only. + +### Multi-Service Architecture + +The base `compose.yaml` defines these core services: + +- **configurator**: Runs once on startup to configure `common_site_config.json` with database and Redis connection details, then exits +- **backend**: Werkzeug development server (Python) serving dynamic content +- **frontend**: Nginx reverse proxy serving static assets and routing requests +- **websocket**: Node.js Socket.IO server for real-time features +- **queue-short/queue-long**: Python RQ workers for background job processing +- **scheduler**: Python service running scheduled tasks + +**Additional services** come from compose overrides in `overrides/`: +- `compose.mariadb.yaml` / `compose.postgres.yaml`: Database services +- `compose.redis.yaml`: Redis cache and queue services +- `compose.proxy.yaml`: Traefik reverse proxy for multi-site hosting +- `compose.https.yaml`: SSL/TLS certificate management +- `compose.noproxy.yaml`: Direct port exposure (no proxy) for development + +### Compose File Composition Pattern + +The architecture uses Docker Compose overrides to build complete environments: + +```bash +docker compose \ + -f compose.yaml \ + -f overrides/compose.mariadb.yaml \ + -f overrides/compose.redis.yaml \ + -f overrides/compose.noproxy.yaml \ + config > compose.custom.yaml +``` + +This pattern allows mixing and matching services based on deployment needs. + +## Common Development Commands + +### Quick Testing Setup (pwd.yml) + +For rapid evaluation without local setup: + +```bash +# Start all services (uses frappe-custom:v15 image) +docker compose -f pwd.yml up -d + +# Monitor site creation (takes ~5 minutes) +docker compose -f pwd.yml logs -f create-site + +# Access at http://localhost:8080 +# Login: Administrator / admin +``` + +The `pwd.yml` file is a self-contained configuration that includes all services (MariaDB, Redis, workers, etc.) and automatically creates a site named "frontend". + +### Development Environment Setup + +Full development with hot-reload: + +```bash +# 1. Copy devcontainer configuration +cp -R devcontainer-example .devcontainer + +# 2. Open in VSCode with Dev Containers extension +# VSCode will detect .devcontainer and prompt to reopen in container + +# 3. Inside container, run automated installer +cd /workspace/development +python installer.py + +# Installer arguments: +# -j, --apps-json: Path to apps.json (default: apps-example.json) +# -b, --bench-name: Bench directory name (default: frappe-bench) +# -s, --site-name: Site name, must end with .localhost (default: development.localhost) +# -t, --frappe-branch: Frappe branch (default: version-15) +# -d, --db-type: Database type (mariadb or postgres) +# -a, --admin-password: Admin password (default: admin) +``` + +Development files are located in `development/frappe-bench/` (git-ignored directory). + +### Bench Commands (Inside Container) + +```bash +# Site management +bench new-site --mariadb-user-host-login-scope=% +bench list-sites +bench --site migrate + +# App management +bench get-app --branch +bench --site install-app +bench list-apps + +# Development +bench start # Start dev server with hot-reload +bench build # Build frontend assets +bench build --app # Build specific app + +# Database +bench mariadb # Open MariaDB console +bench --site backup --with-files + +# Debugging +bench --site console # Python REPL with Frappe context +``` + +## Building Custom Images + +### Creating apps.json + +Define custom apps to install in `apps.json`: + +```json +[ + { + "url": "https://github.com/frappe/erpnext", + "branch": "version-15" + }, + { + "url": "https://github.com/frappe/hrms", + "branch": "version-15" + } +] +``` + +### Build Process + +```bash +# 1. Encode apps.json +export APPS_JSON_BASE64=$(base64 -w 0 apps.json) + +# 2. Build image (example using layered) +docker build \ + --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ + --build-arg=FRAPPE_BRANCH=version-15 \ + --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --tag=custom:15 \ + --file=images/layered/Containerfile . + +# Or use docker-bake.hcl for multi-arch builds +docker buildx bake --no-cache +``` + +**Important build args**: +- `FRAPPE_PATH`: Frappe framework repo URL +- `FRAPPE_BRANCH`: Frappe framework branch +- `APPS_JSON_BASE64`: Base64-encoded apps.json +- `PYTHON_VERSION`: Python version (default: 3.11.6) +- `NODE_VERSION`: Node.js version (default: 20.19.2) + +## Testing and CI + +### Linting + +```bash +# Install pre-commit +pip install pre-commit +# or +brew install pre-commit + +# Setup hooks +pre-commit install + +# Run on all files +pre-commit run --all-files +``` + +### Integration Tests + +```bash +# Setup test environment +python3 -m venv venv +source venv/bin/activate +pip install -r requirements-test.txt + +# Run tests +pytest +``` + +## Site Operations + +### Creating a New Site + +```bash +# Basic site creation +docker compose exec backend \ + bench new-site --mariadb-user-host-login-scope=% \ + --db-root-password \ + --admin-password + +# For PostgreSQL +docker compose exec backend bench set-config -g root_login postgres +docker compose exec backend bench set-config -g root_password +docker compose exec backend \ + bench new-site --db-type postgres \ + --admin-password +``` + +**Note**: The `--mariadb-user-host-login-scope=%` option is critical for Docker networking - it allows database users to connect from any host (%). + +### Accessing Container Files + +```bash +# Enter backend container +docker compose exec backend bash + +# Key directories inside container: +# /home/frappe/frappe-bench/apps/ - All Frappe applications +# /home/frappe/frappe-bench/sites/ - Site data and configuration +# /home/frappe/frappe-bench/logs/ - Application logs + +# Copy files from container to host +docker compose cp backend:/home/frappe/frappe-bench/apps/my_app ./local-apps/ +``` + +## Environment Variables + +The main environment variables (from `example.env`): + +- `ERPNEXT_VERSION`: Version tag for ERPNext image +- `DB_HOST`, `DB_PORT`: External database connection (if not using compose.mariadb.yaml) +- `REDIS_CACHE`, `REDIS_QUEUE`: External Redis connection (if not using compose.redis.yaml) +- `FRAPPE_SITE_NAME_HEADER`: Override site resolution (default: `$$host`) +- `HTTP_PUBLISH_PORT`: HTTP port to publish (default: 8080) +- `LETSENCRYPT_EMAIL`: Email for Let's Encrypt certificates +- `SITES`: Comma-separated list of sites for SSL certificates + +See `docs/container-setup/env-variables.md` for complete reference. + +## Key Implementation Details + +### Service Startup Dependencies + +The `configurator` service MUST complete successfully before other services start. It writes database and Redis connection details to `sites/common_site_config.json`. The compose file uses: + +```yaml +x-depends-on-configurator: &depends_on_configurator + depends_on: + configurator: + condition: service_completed_successfully +``` + +### Nginx Configuration + +The `frontend` service uses `nginx-entrypoint.sh` which dynamically generates Nginx configuration from `resources/nginx-template.conf` using environment variables like `BACKEND`, `SOCKETIO`, `FRAPPE_SITE_NAME_HEADER`. + +### Volume Mounts + +- **sites**: Shared across all services, contains site data and configuration +- **logs**: Application logs (optional, for debugging) + +For development, bind mount `./development/frappe-bench` to `/workspace/development` in the container. + +## Platform-Specific Notes + +### ARM64 / Apple Silicon + +```bash +# Build multi-arch images +docker buildx bake --no-cache --set "*.platform=linux/arm64" + +# For pwd.yml on ARM64 +# 1. Add platform: linux/arm64 to all services +# 2. Replace version tags with :latest +``` + +Use `:cached` or `:delegated` volume flags on macOS for better performance. + +## Project Structure Reference + +``` +frappe_docker/ +├── compose.yaml # Base compose file (core services) +├── pwd.yml # Self-contained quick-start config +├── docker-bake.hcl # Buildx bake configuration +├── example.env # Environment variables template +├── images/ # Dockerfile definitions +│ ├── bench/ # CLI-only image +│ ├── custom/ # Production image (apps.json) +│ ├── layered/ # Fast-build production image +│ └── production/ # Quick-start image (Frappe + ERPNext only) +├── overrides/ # Compose file extensions +│ ├── compose.mariadb.yaml +│ ├── compose.redis.yaml +│ ├── compose.proxy.yaml +│ └── compose.https.yaml +├── resources/ # Runtime templates +│ ├── nginx-entrypoint.sh +│ └── nginx-template.conf +├── development/ # Dev environment (git-ignored) +│ └── installer.py # Automated setup script +├── devcontainer-example/ # VSCode devcontainer template +└── docs/ # Documentation +``` + +## Version Compatibility + +- **Frappe v14**: Python 3.10.13, Node v16 +- **Frappe v15**: Python 3.11.6, Node v20 (default) +- **Frappe v13**: Python 3.9.17, Node v14 + +When working with different versions, set `PYENV_VERSION` and use `nvm` to switch Node versions inside the container. + +## Important Development Notes + +1. **Site names must end with `.localhost`** for local development +2. **Default MariaDB root password is `123`** in development +3. **Default admin password is `admin`** when using installer.py +4. **Wait for configurator to exit** before creating sites or running migrations +5. **The `development/` directory is git-ignored** - all local development happens here +6. **Use `--mariadb-user-host-login-scope=%`** when creating sites in Docker to enable proper networking diff --git a/apps.json b/apps.json new file mode 100644 index 00000000..fc11f671 --- /dev/null +++ b/apps.json @@ -0,0 +1,11 @@ +[ + { + "url": "https://github.com/frappe/erpnext", + "branch": "version-15" + }, + { + "url": "https://github.com/frappe/hrms", + "branch": "version-15" + } +] + diff --git a/build-custom-image.sh b/build-custom-image.sh new file mode 100755 index 00000000..7d7b3d0b --- /dev/null +++ b/build-custom-image.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Build custom Frappe Docker image with ERPNext and HRMS +# This script builds a custom image using the apps.json file + +set -e + +echo "Building custom Frappe image with ERPNext and HRMS..." + +# Check if apps.json exists +if [ ! -f "apps.json" ]; then + echo "Error: apps.json file not found!" + exit 1 +fi + +# Encode apps.json to base64 +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + APPS_JSON_BASE64=$(base64 -i apps.json) +else + # Linux + APPS_JSON_BASE64=$(base64 -w 0 apps.json) +fi + +echo "Building image for AMD64 architecture (for Windows Server deployment)..." +echo "This may take 15-30 minutes depending on your system..." + +# Build the image using layered Containerfile +docker build \ + --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ + --build-arg=FRAPPE_BRANCH=version-15 \ + --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --platform=linux/amd64 \ + --tag=frappe-custom:v15 \ + --file=images/layered/Containerfile . + +echo "" +echo "✅ Build complete!" +echo "Image tagged as: frappe-custom:v15" +echo "" +echo "Next steps:" +echo "1. Run: docker compose -f pwd.yml up -d" +echo "2. Wait for all services to start (2-3 minutes)" +echo "3. Access ERPNext at: http://localhost:8080" +echo " Username: Administrator" +echo " Password: admin" + diff --git a/docs/README-CUSTOM-SETUP.md b/docs/README-CUSTOM-SETUP.md new file mode 100644 index 00000000..8fef20c9 --- /dev/null +++ b/docs/README-CUSTOM-SETUP.md @@ -0,0 +1,162 @@ +# Custom Frappe Setup with ERPNext and HRMS + +This setup includes a custom Docker image with two officially supported Frappe applications: +- **ERPNext** - Complete ERP solution (includes built-in CRM, Sales, Accounting, Inventory, and more) +- **HRMS** - Human Resource Management System + +## Prerequisites + +- Docker Desktop (for Mac/Windows) or Docker Engine (for Linux) +- At least 4GB of free RAM +- 10GB of free disk space + +## Quick Start + +### Step 1: Build the Custom Image + +Run the build script to create a custom Docker image with all three apps: + +```bash +./build-custom-image.sh +``` + +**Note:** This will take 15-30 minutes depending on your system. The script will: +- Read the `apps.json` file containing ERPNext and HRMS +- Build a custom Docker image tagged as `frappe-custom:v15` +- Configure it for ARM64 architecture (Apple Silicon) + +### Step 2: Start the Services + +Once the build is complete, start all services: + +```bash +docker compose -f pwd.yml up -d +``` + +### Step 3: Wait for Initialization + +The first startup takes 2-3 minutes as it: +- Initializes the database +- Creates a new site called "frontend" +- Installs ERPNext and HRMS apps + +You can monitor the progress with: + +```bash +docker compose -f pwd.yml logs -f create-site +``` + +Wait until you see a message indicating the site has been created successfully. + +### Step 4: Access the System + +Once ready, access the system at: + +**URL:** http://localhost:8080 + +**Login Credentials:** +- Username: `Administrator` +- Password: `admin` + +## What's Included + +### ERPNext +Full-featured ERP with modules for: +- **CRM** - Lead Management, Opportunities, Sales Pipeline +- **Sales & Purchase** - Quotations, Orders, Invoices +- **Accounting** - General Ledger, Accounts Payable/Receivable, Financial Reports +- **Inventory Management** - Stock, Warehouses, Serial/Batch Numbers +- **Manufacturing** - BOM, Work Orders, Production Planning +- **Projects** - Project Management, Tasks, Timesheets +- **Assets** - Asset Management, Maintenance +- And much more... + +### HRMS +Human Resource Management with: +- **Employee Management** - Employee Records, Organizational Chart +- **Attendance & Leave** - Attendance Tracking, Leave Management +- **Payroll** - Salary Structure, Payroll Processing +- **Recruitment** - Job Openings, Job Applications +- **Performance Management** - Appraisals, Goals +- **Expense Claims** - Employee Advances, Expense Claims + +## Managing the Setup + +### Stop Services +```bash +docker compose -f pwd.yml down +``` + +### View Logs +```bash +# All services +docker compose -f pwd.yml logs -f + +# Specific service +docker compose -f pwd.yml logs -f backend +``` + +### Restart Services +```bash +docker compose -f pwd.yml restart +``` + +### Remove Everything (including data) +```bash +docker compose -f pwd.yml down -v +``` + +## Troubleshooting + +### Build Fails +- Ensure you have a stable internet connection +- Check that Docker has enough resources allocated (4GB+ RAM) +- Try running the build script again + +### Services Won't Start +- Check if ports 8080 is already in use: `lsof -i :8080` +- Ensure Docker is running: `docker ps` +- Check logs: `docker compose -f pwd.yml logs` + +### Can't Access at localhost:8080 +- Wait 2-3 minutes after starting services +- Check if the create-site service completed: `docker compose -f pwd.yml ps` +- Verify the frontend service is running: `docker compose -f pwd.yml logs frontend` + +## Architecture Notes + +This setup uses: +- **MariaDB 10.6** for the database +- **Redis** for caching and job queues +- **Nginx** as the web server +- **Node.js** for real-time websockets +- **Python** for the backend application + +All services run in separate containers and communicate over a Docker network. + +## Customization + +To add or remove apps, edit the `apps.json` file and rebuild: + +```bash +# Edit apps.json to add more Frappe apps +nano apps.json + +# Rebuild the image +./build-custom-image.sh + +# Restart services +docker compose -f pwd.yml down +docker compose -f pwd.yml up -d +``` + +**Note:** ERPNext already includes comprehensive CRM functionality. If you need a standalone modern CRM, consider using Frappe CRM separately instead of with ERPNext to avoid feature overlap. + +## Support + +For issues specific to: +- **Frappe Framework:** https://github.com/frappe/frappe +- **ERPNext:** https://github.com/frappe/erpnext +- **HRMS:** https://github.com/frappe/hrms +- **Docker Setup:** https://github.com/frappe/frappe_docker + diff --git a/pwd.yml b/pwd.yml index 7dfeae15..37c223e4 100644 --- a/pwd.yml +++ b/pwd.yml @@ -2,7 +2,9 @@ version: "3" services: backend: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -18,7 +20,9 @@ services: MARIADB_ROOT_PASSWORD: admin configurator: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -47,7 +51,9 @@ services: - logs:/home/frappe/frappe-bench/logs create-site: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -77,10 +83,11 @@ services: fi done; echo "sites/common_site_config.json found"; - bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend; + bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --install-app hrms --set-default frontend; db: image: mariadb:10.6 + platform: linux/amd64 networks: - frappe_network healthcheck: @@ -102,7 +109,9 @@ services: - db-data:/var/lib/mysql frontend: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network depends_on: @@ -128,7 +137,9 @@ services: - "8080:8080" queue-long: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -147,7 +158,9 @@ services: FRAPPE_REDIS_QUEUE: redis://redis-queue:6379 queue-short: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -167,6 +180,7 @@ services: redis-queue: image: redis:6.2-alpine + platform: linux/amd64 networks: - frappe_network deploy: @@ -184,7 +198,9 @@ services: condition: on-failure scheduler: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: @@ -198,7 +214,9 @@ services: - logs:/home/frappe/frappe-bench/logs websocket: - image: frappe/erpnext:v15.88.1 + image: frappe-custom:v15 + pull_policy: never + platform: linux/amd64 networks: - frappe_network deploy: