From d0da82efbacf3379c12e1a1dc92f7b078fbcb150 Mon Sep 17 00:00:00 2001 From: y9938 Date: Wed, 10 Dec 2025 19:37:35 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=BF=D0=B5=D1=80=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20Docker-=D0=BE=D0=BA?= =?UTF-8?q?=D1=80=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20Laravel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 2 +- .editorconfig | 18 +++++++++++ .env.example | 5 --- .gitignore | 46 +++++++++++++++++++++++++++ Makefile | 56 +++++++++++++++++++++++++++++++++ README.md | 12 +++++-- compose.yaml | 13 +++++--- docker/Dockerfile | 31 +++++++++++++------ docker/docker-entrypoint.sh | 52 ------------------------------- docker/entrypoint.sh | 57 ++++++++++++++++++++++++++++++++++ docker/php-fpm.conf | 10 ------ run.sh | 62 ------------------------------------- 12 files changed, 218 insertions(+), 146 deletions(-) create mode 100644 .editorconfig create mode 100644 Makefile delete mode 100755 docker/docker-entrypoint.sh create mode 100755 docker/entrypoint.sh delete mode 100644 docker/php-fpm.conf delete mode 100755 run.sh diff --git a/.dockerignore b/.dockerignore index f514cf0..32ce416 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ * -!docker/docker-entrypoint.sh +!docker/entrypoint.sh !docker/php-fpm.conf diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..432737a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[*.sh] +indent_size = 2 diff --git a/.env.example b/.env.example index 44959e6..03b3616 100644 --- a/.env.example +++ b/.env.example @@ -6,10 +6,5 @@ DB_USERNAME=laravel DB_PASSWORD=secret DB_ROOT_PASSWORD=secret -# Docker -PHP_VERSION=8.3-fpm -IMAGE_NAME=dev-pj1-laravel -IMAGE_TAG=${PHP_VERSION} - APP_UID=1000 APP_GID=1000 diff --git a/.gitignore b/.gitignore index 5504403..877d5c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,47 @@ +# Environment files +.env +.env.backup +.env.production +auth.json +Homestead.yaml +Homestead.json + +# Vendor / dependencies +/vendor +/composer/ +.composer-hash + +# Laravel storage and cache +/storage/*.key +/storage/pail +/storage/framework/* +!/storage/framework/.gitignore +/storage/logs/* +!/storage/logs/.gitignore +/public/storage + +# Build artifacts +/public/build +/public/hot +/bootstrap/cache/* +!/bootstrap/cache/.gitignore + +# Testing / cache +.phpunit.result.cache /.phpunit.cache + +# Editor / IDE +/.idea +/.vscode +.phpactor.json +.scribe/ +.scribe-hash + +# System / logs +*.log +.DS_Store +Thumbs.db +.bash_history /bootstrap/ssr /node_modules /public/build @@ -26,3 +69,6 @@ yarn-error.log /.nova /.vscode /.zed + +.composer-hash +.scribe-hash diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..686d7d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +-include .env +APP_UID ?= 1000 +APP_GID ?= 1000 + +IMAGE_NAME := laravel-setup-php-fpm +IMAGE_TAG := latest +IMAGE := $(IMAGE_NAME):$(IMAGE_TAG) +TAR_FILE := $(IMAGE_NAME)_$(IMAGE_TAG).tar.gz + +.PHONY: help build load shell docs + +help: + @echo "Usage:" + @echo " make [SOURCE=...]" + @echo "" + @echo "Targets:" + @echo " build Build Docker image and save to archive" + @echo " load Load Docker image from archive, URL, or build if missing" + @echo " Optional: SOURCE=url_or_file" + @echo " shell Enter php-fpm container as www" + @echo " docs Regenerate API documentation" + + +build: + @echo "Building image..." + docker build -f docker/Dockerfile --build-arg UID=$(APP_UID) --build-arg GID=$(APP_GID) -t $(IMAGE) . + @echo "Saving image to $(TAR_FILE)..." + docker save $(IMAGE) | gzip > $(TAR_FILE) + +load: +ifdef SOURCE + @if echo "$(SOURCE)" | grep -qE '^https?://'; then \ + echo "Downloading and loading image from $(SOURCE)..."; \ + curl -L "$(SOURCE)" -o $(TAR_FILE); \ + docker load < $(TAR_FILE); \ + else \ + echo "Loading image from file $(SOURCE)..."; \ + docker load < $(SOURCE); \ + fi +else + @if [ -f "$(TAR_FILE)" ]; then \ + echo "Loading image from archive $(TAR_FILE)..."; \ + docker load < $(TAR_FILE); \ + else \ + echo "No archive found; building image..."; \ + docker build -f docker/Dockerfile --build-arg UID=$(APP_UID) --build-arg GID=$(APP_GID) -t $(IMAGE) .; \ + fi +endif + +shell: + @echo "Entering php-fpm container as www..." + docker compose exec --user www php-fpm bash + +docs: + @echo "Regenerating API documentation..." + docker compose exec --user www php-fpm php artisan scribe:generate diff --git a/README.md b/README.md index 84a644d..9040ae3 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,18 @@ - NGINX, MySQL, phpMyAdmin -## Создание проекта: +## Create a project: ```bash cp .env.example .env # !change .env for yourself -./run.sh build docker compose up -d php-fpm docker compose exec php-fpm bash +# or `make shell` ``` +Rewrite below for yourself: + ```bash composer global require laravel/installer @@ -22,3 +24,9 @@ laravel new example-app mv example-app/* example-app/.* ./ rmdir example-app ``` + +## Quick Actions + +```bash +make help +``` diff --git a/compose.yaml b/compose.yaml index 22dbd33..3eb0d5b 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,3 +1,5 @@ +name: laravel-setup + services: web: image: nginx:latest @@ -13,15 +15,16 @@ services: condition: service_started php-fpm: - image: "${IMAGE_NAME}:${IMAGE_TAG}" - user: "${APP_UID}:${APP_GID}" + build: + context: . + dockerfile: docker/Dockerfile + args: + UID: ${APP_UID} + GID: ${APP_GID} env_file: - .env volumes: - ./:/var/www - depends_on: - mysql: - condition: service_started mysql: image: mysql:8.0 diff --git a/docker/Dockerfile b/docker/Dockerfile index 2bae4a1..4dbbfc9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,23 +1,36 @@ -ARG PHP_VERSION=8.3-fpm -FROM php:${PHP_VERSION} +FROM php:8.3-fpm RUN apt-get update && apt-get install -y --no-install-recommends \ libpng-dev libonig-dev libxml2-dev libzip-dev \ - zip unzip git \ + zip unzip git gosu \ netcat-traditional \ && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \ && apt-get clean && rm -rf /var/lib/apt/lists/* COPY --from=composer:latest /usr/bin/composer /usr/bin/composer -COPY --chown=www-data:www-data ./docker/docker-entrypoint.sh /usr/local/bin/ -COPY ./docker/php-fpm.conf /usr/local/etc/php-fpm.d/zz-docker.conf +ARG UID +ARG GID -RUN chmod +x /usr/local/bin/docker-entrypoint.sh +# Create a new user with the specified UID and GID, reusing an existing group if GID exists +# and update php-fpm to use the new user and group +RUN if getent group ${GID}; then \ + group_name=$(getent group ${GID} | cut -d: -f1); \ + useradd -m -u ${UID} -g ${GID} -s /bin/bash www; \ + else \ + groupadd -g ${GID} www && \ + useradd -m -u ${UID} -g www -s /bin/bash www; \ + group_name=www; \ + fi + +# Update php-fpm to use the new user and group +RUN sed -i "s/user = www-data/user = www/g" /usr/local/etc/php-fpm.d/www.conf && \ + sed -i "s/group = www-data/group = $group_name/g" /usr/local/etc/php-fpm.d/www.conf + +COPY ./docker/entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/entrypoint.sh -USER www-data WORKDIR /var/www - EXPOSE 9000 -ENTRYPOINT ["docker-entrypoint.sh"] +ENTRYPOINT ["entrypoint.sh"] CMD ["php-fpm"] diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh deleted file mode 100755 index 9c5e6ab..0000000 --- a/docker/docker-entrypoint.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -set -e - -APP_ENV=${APP_ENV:-unknown} -echo ">> Running in $APP_ENV mode" - -# Only for Laravel project -if [ -f "artisan" ]; then - - echo ">> Ensuring composer dependencies are up to date..." - if [ "$APP_ENV" = "production" ]; then - composer install --no-dev --optimize-autoloader - else - composer install --optimize-autoloader - fi - - if [ -f ".env" ] && grep -q '^APP_KEY=$' .env; then - echo ">> Generating application key..." - php artisan key:generate --ansi - fi - - echo ">> Waiting for MySQL..." - while ! nc -z mysql 3306; do sleep 1; done - - echo ">> Running migrations..." - php artisan migrate --force || true - - if [ ! -L "public/storage" ] && [ -d "storage/app/public" ]; then - echo ">> Creating storage link..." - php artisan storage:link - fi - - if [ "$APP_ENV" = "local" ]; then - echo ">> Running seeders..." - php artisan db:seed --force || true - - if composer show knuckleswtf/scribe > /dev/null 2>&1; then - echo ">> Generating API documentation..." - php artisan scribe:generate --no-interaction || echo ">> Documentation generation failed, continuing..." - else - echo ">> Scribe not installed, skipping documentation generation" - fi - fi - - if [ "$APP_ENV" = "production" ]; then - php artisan optimize - fi -else - echo ">> Not a Laravel project" -fi - -exec "$@" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..60938c7 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +echo ">> Running dev setup..." + +# Only for Laravel project +if [ ! -f "artisan" ]; then + echo ">> Not a Laravel project" + exec "$@" +fi + +if [ ! -f .composer-hash ] || ! sha1sum -c .composer-hash > /dev/null 2>&1; then + echo ">> composer.json or composer.lock changed, installing dependencies..." + gosu www composer install --optimize-autoloader --no-interaction + sha1sum composer.json composer.lock > .composer-hash +else + echo ">> Composer dependencies up to date (hash match), skipping install." +fi + +if [ -f ".env" ] && grep -q '^APP_KEY=$' .env; then + echo ">> Generating application key..." + gosu www php artisan key:generate --ansi +fi + +echo ">> Waiting for MySQL..." +while ! nc -z mysql 3306; do sleep 1; done + +echo ">> Running migrations..." +gosu www php artisan migrate --force + +if [ ! -L "public/storage" ] && [ -d "storage/app/public" ]; then + echo ">> Creating storage link..." + gosu www php artisan storage:link +fi + +echo ">> Running seeders..." +gosu www php artisan db:seed --force + +if composer show knuckleswtf/scribe > /dev/null 2>&1; then + # Define all directories/files Scribe cares about + SCRIBE_SOURCES="config/scribe.php routes/ app/Http/Controllers/ app/Http/Requests/ app/Models/" + + # Create combined hash of all .php files in those paths + CURRENT_HASH=$(find $SCRIBE_SOURCES -type f -name "*.php" -exec sha1sum {} + | sha1sum) + + if [ ! -f .scribe-hash ] || [ "$CURRENT_HASH" != "$(cat .scribe-hash)" ]; then + echo ">> Generating API documentation..." + gosu www php artisan scribe:generate --no-interaction || true + echo "$CURRENT_HASH" > .scribe-hash + else + echo ">> API docs up to date, skipping Scribe generation." + fi +else + echo ">> Scribe not installed, skipping API docs generation" +fi + +exec "$@" diff --git a/docker/php-fpm.conf b/docker/php-fpm.conf deleted file mode 100644 index 061471c..0000000 --- a/docker/php-fpm.conf +++ /dev/null @@ -1,10 +0,0 @@ -[global] -daemonize = no - -[www] -listen = 9000 -pm = dynamic -pm.max_children = 5 -pm.start_servers = 2 -pm.min_spare_servers = 1 -pm.max_spare_servers = 3 diff --git a/run.sh b/run.sh deleted file mode 100755 index ea6bda6..0000000 --- a/run.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Help -if [[ $# -eq 0 ]]; then - echo "Usage: $0 {build|load} [source]" - echo " build - Build image and save archive" - echo " load [source] - Load image from:" - echo " (no arg) - Use archive or build from source" - echo " URL - download from URL then load" - echo " file - load from specified file via docker load command" - echo "" - echo "Archive location: \${IMAGE_NAME}_\$IMAGE_TAG.tar.gz in current directory" - exit 0 -fi - -# Load config -[[ ! -f .env ]] && cp .env.example .env -source .env - -# Validate -if [[ -z "${IMAGE_NAME:-}" ]] || [[ -z "${IMAGE_TAG:-}" ]] || [[ -z "${PHP_VERSION:-}" ]]; then - echo "Error: IMAGE_NAME or IMAGE_TAG or PHP_VERSION not set in .env" - echo "Please add to .env (check vars in .env.example):" - echo "PHP_VERSION=8.3-fpm # Used in Dockerfile" - echo "IMAGE_NAME=your-image-name" - echo "IMAGE_TAG=\${PHP_VERSION} # Or custom tag" - exit 1 -fi - -IMAGE="$IMAGE_NAME:$IMAGE_TAG" -TAR_FILE="${IMAGE_NAME}_$IMAGE_TAG.tar.gz" - -ACTION="${1:-}" -SOURCE="${2:-}" - -case "$ACTION" in - build) - docker build -f docker/Dockerfile --build-arg PHP_VERSION=$PHP_VERSION -t "$IMAGE" . - docker save "$IMAGE" | gzip > "$TAR_FILE" - ;; - load) - if [[ -f "$TAR_FILE" ]]; then - docker load < "$TAR_FILE" - elif [[ -n "$SOURCE" ]]; then - if [[ "$SOURCE" =~ ^https?:// ]]; then - curl -L "$SOURCE" -o "$TAR_FILE" - docker load < "$TAR_FILE" - else - # Handle local file path - docker load < "$SOURCE" - fi - else - docker build -f docker/Dockerfile --build-arg PHP_VERSION=$PHP_VERSION -t "$IMAGE" . - fi - ;; - *) - echo "Error: Unknown action '$ACTION'" - echo "Usage: $0 {build|load} [source]" - exit 1 - ;; -esac