diff --git a/.docker/dev/dev-entrypoint.sh b/.docker/dev/dev-entrypoint.sh new file mode 100755 index 000000000..c2700b426 --- /dev/null +++ b/.docker/dev/dev-entrypoint.sh @@ -0,0 +1,78 @@ +#!/bin/sh +set -eu + +APP_DIR="/var/www/html" +DEV_HOME="/var/www/partdb-dev-home" + +prepare_runtime() +{ + # Development-only: newly created files are writable by all users, + # and newly created directories are writable/traversable by all users. + umask 000 + + mkdir -p \ + "$DEV_HOME" \ + "$DEV_HOME/.composer" \ + "$DEV_HOME/.cache/composer" \ + "$DEV_HOME/.cache/yarn" \ + "$APP_DIR/vendor" \ + "$APP_DIR/node_modules" \ + "$APP_DIR/var/cache" \ + "$APP_DIR/var/log" \ + "$APP_DIR/var/db" \ + "$APP_DIR/var/share" \ + "$APP_DIR/uploads" \ + "$APP_DIR/public/media" \ + "$APP_DIR/public/build" \ + "$APP_DIR/public/bundles" + + chown -R www-data:www-data "$DEV_HOME" + + # Development-only permission workaround for Linux bind mounts. + chmod 777 \ + "$APP_DIR/vendor" \ + "$APP_DIR/node_modules" \ + "$APP_DIR/var/cache" \ + "$APP_DIR/var/log" \ + "$APP_DIR/var/db" \ + "$APP_DIR/var/share" \ + "$APP_DIR/uploads" \ + "$APP_DIR/public/media" \ + "$APP_DIR/public/build" \ + "$APP_DIR/public/bundles" + + export HOME="$DEV_HOME" + export COMPOSER_HOME="$DEV_HOME/.composer" + export COMPOSER_CACHE_DIR="$DEV_HOME/.cache/composer" + export YARN_CACHE_FOLDER="$DEV_HOME/.cache/yarn" + + mkdir -p \ + "$DEV_HOME" \ + "$DEV_HOME/.composer" \ + "$DEV_HOME/.cache/composer" \ + "$DEV_HOME/.cache/yarn" + + chown -R www-data:www-data "$DEV_HOME" + + git config --system --get-all safe.directory 2>/dev/null \ + | grep -Fxq "$APP_DIR" \ + || git config --system --add safe.directory "$APP_DIR" +} + +start_server() +{ + "php-fpm${PHP_VERSION}" -F & + exec /usr/local/bin/apache2-foreground +} + +prepare_runtime + +case "${1:-}" in + apache2-foreground|/usr/local/bin/apache2-foreground) + start_server + ;; + + *) + exec gosu www-data "$@" + ;; +esac diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5994a115d..30740702f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,7 +28,10 @@ Interesting folders are: * `tests/`: Tests that can be run by PHPUnit. ## Development environment -For setting up a development environment, you will need to install PHP, Composer, a database server (MySQL or MariaDB) and yarn (which needs a Node.js environment). +For contributors who prefer a Docker-based development environment, see +[DEVELOPMENT.md](DEVELOPMENT.md). + +For setting up a native PHP development environment, you will need to install PHP, Composer, a database server (MySQL or MariaDB) and yarn (which needs a Node.js environment). * Copy `.env` to `.env.local` and change `APP_ENV` to `APP_ENV=dev`. That way you will get development tools (Symfony profiler) and other features that will simplify development. * Run `composer install` (without -o) to install PHP dependencies and `yarn install` to install frontend dependencies. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..caf230f1c --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,409 @@ +# Development + +This document describes how to set up a complete Docker-based development environment for Part-DB. + +> **Note** +> +> The standard native PHP development workflow is documented in `CONTRIBUTING.md`. This document provides an alternative Docker-based workflow that requires only Docker and Docker Compose. + +--- + +# Requirements + +The following software is required: + +* Docker +* Docker Compose +* Git + +No local installation of PHP, Composer, Node.js, Yarn or a database server is required. + +--- + +# Development Environment + +The development environment consists of two Docker images: + +* **Dockerfile** – the upstream production image used as the development base. +* **Dockerfile.dev** – a lightweight development image layered on top of the production image that provides: + + * Composer development support + * Node.js and Yarn + * Live frontend rebuilding + * Development PHP configuration + * Automatic permission handling for bind-mounted source code + +The development environment uses: + +* SQLite database +* Symfony development mode +* Webpack Encore live asset rebuilding (`yarn watch`) + +All application source code remains on the host machine. + +--- + +# Initial Setup + +Clone the repository: + +```bash +git clone https://github.com/Part-DB/Part-DB-server.git +cd Part-DB-server +``` + +Build the upstream production base image: + +```bash +docker build --file Dockerfile --tag partdb-local-dev-base . +``` + +Build the development image: + +```bash +docker compose -f compose.dev.yaml build +``` + +Install Composer dependencies: + +```bash +docker compose -f compose.dev.yaml run --rm partdb composer install +``` + +Verify that Symfony is correctly configured: + +```bash +docker compose -f compose.dev.yaml run --rm partdb php bin/console about +``` + +The output should report: + +```text +Environment dev +Debug true +``` + +Build the frontend assets: + +```bash +docker compose -f compose.dev.yaml run --rm assets \ + sh -lc 'yarn install --network-timeout 600000 && yarn build' +``` + +Create the development database: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + php bin/console doctrine:migrations:migrate +``` + +During the initial migration an administrator account is created. + +The output contains the generated password: + +```text +[warning] The initial password for the "admin" user is: ******** +``` + +Record this password for the initial login. + +Start the development environment: + +```bash +docker compose -f compose.dev.yaml up -d partdb assets +``` + +Confirm both services are running: + +```bash +docker compose -f compose.dev.yaml ps +``` + +Inspect the application logs: + +```bash +docker compose -f compose.dev.yaml logs --tail=100 partdb +``` + +Inspect the frontend build logs: + +```bash +docker compose -f compose.dev.yaml logs --tail=100 assets +``` + +Open Part-DB in your workstation browser: + +``` +http://localhost:8080/ +``` + +--- + +# Daily Development Workflow + +Start the development environment: + +```bash +docker compose -f compose.dev.yaml up -d +``` + +Restart the services: + +```bash +docker compose -f compose.dev.yaml restart +``` + +Check service status: + +```bash +docker compose -f compose.dev.yaml ps +``` + +Watch the application log: + +```bash +docker compose -f compose.dev.yaml logs -f partdb +``` + +Watch the frontend asset compiler: + +```bash +docker compose -f compose.dev.yaml logs -f assets +``` + +Stop the development environment: + +```bash +docker compose -f compose.dev.yaml down +``` + +--- + +# Composer + +Install or update PHP dependencies: + +```bash +docker compose -f compose.dev.yaml run --rm partdb composer install +``` + +Run any Composer command: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + composer +``` + +Examples: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + composer update + +docker compose -f compose.dev.yaml run --rm partdb \ + composer require vendor/package +``` + +--- + +# Symfony Console + +Run any Symfony command: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + php bin/console +``` + +Examples: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + php bin/console cache:clear + +docker compose -f compose.dev.yaml run --rm partdb \ + php bin/console about +``` + +--- + +# Frontend Development + +The `assets` service runs `yarn watch` and automatically rebuilds frontend assets +whenever CSS or TypeScript files change. + +To monitor the asset compiler: + +```bash +docker compose -f compose.dev.yaml logs -f assets +``` + +To perform a one-off production build: + +```bash +docker compose -f compose.dev.yaml run --rm assets yarn build +``` + +--- + +# Testing + +To run phpunit testing: + +```bash +docker compose -f compose.dev.yaml run --rm partdb php bin/phpunit +``` + + +To run code coverage test: + +```bash +docker compose -f compose.dev.yaml run --rm \ + -e XDEBUG_MODE=coverage partdb php bin/phpunit --coverage-text \ + +``` + +Examples: + +```bash +docker compose -f compose.dev.yaml run --rm partdb php bin/phpunit \ + tests/Services/ImportExportSystem/BOMImporterTest.php + +docker compose -f compose.dev.yaml run --rm -e XDEBUG_MODE=coverage \ + partdb php bin/phpunit --coverage-text \ + tests/Services/ImportExportSystem/BOMImporterTest.php +``` + +--- + +# Database + +The development environment uses SQLite. + +The database is stored on the host at: + +``` +var/db/app.db +``` + +The database is **not** removed when containers are recreated. + +Run migrations only when: + +* creating a new development environment +* new Doctrine migrations have been added +* the SQLite database has been deleted + +Run migrations: + +```bash +docker compose -f compose.dev.yaml run --rm partdb \ + php bin/console doctrine:migrations:migrate +``` + +--- + +# Logs + +Application logs: + +```bash +docker compose -f compose.dev.yaml logs -f partdb +``` + +Frontend logs: + +```bash +docker compose -f compose.dev.yaml logs -f assets +``` + +--- + +# Docker Images + +The development environment uses two images. + +## Base image + +Built from the upstream Dockerfile: + +```bash +docker build --file Dockerfile --tag partdb-local-dev-base . +``` + +This image only needs rebuilding when: + +* the upstream Dockerfile changes +* PHP packages change +* system packages change + +## Development image + +Built from `Dockerfile.dev`: + +```bash +docker compose -f compose.dev.yaml build +``` + +Rebuild this image after changing: + +* `Dockerfile.dev` +* `compose.dev.yaml` +* `.docker/dev/` + +--- + +# Troubleshooting + +## Rebuild the development image + +```bash +docker compose -f compose.dev.yaml build +docker compose -f compose.dev.yaml up -d --force-recreate +``` + +## View container logs + +```bash +docker compose -f compose.dev.yaml logs -f partdb + +docker compose -f compose.dev.yaml logs -f assets +``` + +## Remove containers + +```bash +docker compose -f compose.dev.yaml down +``` + +## Clean generated files + +```bash +rm -rf \ + vendor \ + node_modules \ + var/cache \ + var/log \ + var/db \ + var/share \ + public/build \ + public/bundles +``` + +The tracked files under `uploads/` and `public/media/` should not be removed. + +--- + +# Notes + +The Docker development environment intentionally differs from the native development workflow described in `CONTRIBUTING.md`. + +Specifically: + +* No `.env.local` file is required. +* Symfony configuration is supplied through Docker environment variables. +* No local PHP installation is required. +* No local Composer installation is required. +* No local Node.js or Yarn installation is required. +* No local database server is required. +* Live frontend rebuilding is provided automatically by the `assets` service. diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 000000000..780b093de --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,58 @@ +# syntax=docker/dockerfile:1 + +FROM partdb-local-dev-base + +ARG PHP_VERSION=8.4 +ARG NODE_VERSION=22 + +USER root + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + gosu \ + passwd \ + php8.4-xdebug \ + && curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" \ + | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && npm install --global yarn@1.22.22 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /var/www/partdb-dev-home \ + && chown -R www-data:www-data /var/www/partdb-dev-home \ + && usermod --home /var/www/partdb-dev-home www-data + +RUN printf '%s\n' \ + 'display_errors=On' \ + 'display_startup_errors=On' \ + 'error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED' \ + 'opcache.enable=0' \ + 'opcache.enable_cli=0' \ + 'opcache.validate_timestamps=1' \ + 'realpath_cache_ttl=0' \ + 'memory_limit=512M' \ + 'max_execution_time=120' \ + > "/etc/php/${PHP_VERSION}/fpm/conf.d/99-partdb-development.ini" \ + && cp \ + "/etc/php/${PHP_VERSION}/fpm/conf.d/99-partdb-development.ini" \ + "/etc/php/${PHP_VERSION}/cli/conf.d/99-partdb-development.ini" + +COPY .docker/dev/dev-entrypoint.sh /usr/local/bin/partdb-dev-entrypoint + +RUN chmod 0755 /usr/local/bin/partdb-dev-entrypoint + +ENV PHP_VERSION="${PHP_VERSION}" +ENV APP_ENV=dev +ENV APP_DEBUG=1 +ENV NODE_ENV=development + +WORKDIR /var/www/html + +ENTRYPOINT ["partdb-dev-entrypoint"] +CMD ["/usr/local/bin/apache2-foreground"] + +EXPOSE 80 diff --git a/compose.dev.yaml b/compose.dev.yaml new file mode 100644 index 000000000..b3b074313 --- /dev/null +++ b/compose.dev.yaml @@ -0,0 +1,59 @@ +name: partdb-dev + +services: + partdb: + build: + context: . + dockerfile: Dockerfile.dev + args: + PHP_VERSION: "8.4" + NODE_VERSION: "22" + + image: partdb-dev:local + container_name: partdb-dev + + ports: + - "8080:80" + + environment: + APP_ENV: dev + APP_DEBUG: "1" + + DATABASE_URL: "sqlite:///%kernel.project_dir%/var/db/app.db" + + APP_SECRET: "partdb-development-secret" + + DEFAULT_LANG: en + DEFAULT_TIMEZONE: UTC + BASE_CURRENCY: USD + INSTANCE_NAME: "Part-DB Development" + + CHECK_FOR_UPDATES: "0" + USE_GRAVATAR: "0" + ALLOW_ATTACHMENT_DOWNLOADS: "0" + + volumes: + # The complete checked-out working tree, including vendor, + # node_modules, generated assets and development data. + - .:/var/www/html + + restart: unless-stopped + + assets: + image: partdb-dev:local + container_name: partdb-assets + + working_dir: /var/www/html + + environment: + NODE_ENV: development + + volumes: + - .:/var/www/html + + command: + - yarn + - watch + + init: true + restart: unless-stopped