5 Commits
v1.0.0 ... main

Author SHA1 Message Date
zino
88e94557fb Do not ignore staticMethod.dynamicCall by default 2025-11-21 10:49:23 +01:00
zino
2212ca071a Update package name 2025-11-19 16:11:06 +01:00
zino
5ef73a8041 Add Docker-based dev/prod setup with zail helper scripts 2025-11-19 16:03:36 +01:00
zino
28647fce70 Sync composer.lock with composer.json 2025-11-16 00:37:28 +01:00
zino
a8c2d87c0b Do not migrate automatically 2025-11-16 00:26:03 +01:00
12 changed files with 484 additions and 15 deletions

47
.dockerignore Normal file
View File

@@ -0,0 +1,47 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.gitattributes
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.next
**/.cache
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose.y*ml
**/compose.*.y*ml
!**/composer.json
!**/composer.lock
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/vendor
**/.DS_Store
.editorconfig
.env.example
LICENSE
README.md
# Ignore development-related files and directories
# For development environment, we will use mount bind to mount the source code to the container
# So we don't need to copy the source code to the container for development
tests/
phpunit.xml
phpunit.xml.dist

View File

@@ -1,4 +1,5 @@
APP_NAME=Laravel APP_NAME="Laravel Starter Kit"
APP_NAME_SLUG=laravel-starter-kit
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
@@ -21,11 +22,11 @@ LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug LOG_LEVEL=debug
DB_CONNECTION=mariadb DB_CONNECTION=mariadb
DB_HOST=127.0.0.1 DB_HOST=mariadb
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=laravel-starter-kit DB_DATABASE=laravel-starter-kit
DB_USERNAME=root DB_USERNAME=laravel
DB_PASSWORD=password DB_PASSWORD=starter-secret
SESSION_DRIVER=database SESSION_DRIVER=database
SESSION_LIFETIME=120 SESSION_LIFETIME=120
@@ -37,7 +38,7 @@ BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local FILESYSTEM_DISK=local
QUEUE_CONNECTION=database QUEUE_CONNECTION=database
CACHE_STORE=database CACHE_STORE=file
# CACHE_PREFIX= # CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1 MEMCACHED_HOST=127.0.0.1
@@ -63,3 +64,9 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}" VITE_APP_NAME="${APP_NAME}"
# Docker: Sync user ID and group ID to avoid permission issues on development
UID=1000
GID=1000
XDEBUG_ENABLED=false

21
app/Jobs/TestLogJob.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class TestLogJob implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle(): void {
Log::info('TestLogJob executed (async): ' . now());
echo 'TestLogJob executed (async): ' . now() . PHP_EOL;
}
}

130
compose.yaml Normal file
View File

@@ -0,0 +1,130 @@
services:
php-fpm:
container_name: "${APP_NAME_SLUG:-laravel-starter-kit}-php-fpm"
build:
context: .
dockerfile: ./docker/common/php-fpm/Dockerfile
target: development
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
XDEBUG_ENABLED: ${XDEBUG_ENABLED:-true}
XDEBUG_MODE: develop,coverage,debug,profile
XDEBUG_HOST: ${XDEBUG_HOST:-host.docker.internal}
XDEBUG_IDE_KEY: ${XDEBUG_IDE_KEY:-DOCKER}
XDEBUG_LOG: /dev/stdout
XDEBUG_LOG_LEVEL: 0
env_file:
- .env
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ./:/var/www
networks:
- web
depends_on:
- mariadb
workspace:
container_name: "${APP_NAME_SLUG:-laravel-starter-kit}-workspace"
build:
context: .
dockerfile: ./docker/workspace/Dockerfile
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
XDEBUG_ENABLED: ${XDEBUG_ENABLED:-true}
XDEBUG_MODE: develop,coverage,debug,profile
XDEBUG_HOST: ${XDEBUG_HOST:-host.docker.internal}
XDEBUG_IDE_KEY: ${XDEBUG_IDE_KEY:-DOCKER}
XDEBUG_LOG: /dev/stdout
XDEBUG_LOG_LEVEL: 0
command: php artisan serve --host=0.0.0.0 --port=8000
ports:
- "8000:8000"
- "${VITE_PORT:-5173}:5173"
tty: true
stdin_open: true
env_file:
- .env
volumes:
- ./:/var/www
networks:
- web
depends_on:
- mariadb
scheduler:
container_name: "${APP_NAME_SLUG:-laravel-starter-kit}-scheduler"
build:
context: .
dockerfile: ./docker/common/php-fpm/Dockerfile
target: development
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
XDEBUG_ENABLED: ${XDEBUG_ENABLED:-true}
XDEBUG_MODE: develop,coverage,debug,profile
XDEBUG_HOST: ${XDEBUG_HOST:-host.docker.internal}
XDEBUG_IDE_KEY: ${XDEBUG_IDE_KEY:-DOCKER}
XDEBUG_LOG: /dev/stdout
XDEBUG_LOG_LEVEL: 0
command: sh -c "sleep 5 && php artisan schedule:work"
env_file:
- .env
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ./:/var/www
networks:
- web
depends_on:
- mariadb
queue-worker:
container_name: "${APP_NAME_SLUG:-laravel-starter-kit}-queue-worker"
build:
context: .
dockerfile: ./docker/common/php-fpm/Dockerfile
target: development
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
XDEBUG_ENABLED: ${XDEBUG_ENABLED:-true}
XDEBUG_MODE: develop,coverage,debug,profile
XDEBUG_HOST: ${XDEBUG_HOST:-host.docker.internal}
XDEBUG_IDE_KEY: ${XDEBUG_IDE_KEY:-DOCKER}
XDEBUG_LOG: /dev/stdout
XDEBUG_LOG_LEVEL: 0
command: sh -c "sleep 5 && php artisan queue:listen --timeout=0 -v"
env_file:
- .env
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ./:/var/www
networks:
- web
depends_on:
- mariadb
mariadb:
container_name: "${APP_NAME_SLUG:-laravel-starter-kit}-mariadb"
image: mariadb:latest
restart: unless-stopped
environment:
MARIADB_DATABASE: ${DB_DATABASE:-laravel}
MARIADB_USER: ${DB_USERNAME:-laravel}
MARIADB_PASSWORD: ${DB_PASSWORD:-secret}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
volumes:
- mariadb-data:/var/lib/mysql
ports:
- "${DB_PORT:-3306}:3306"
networks:
- web
networks:
web:
external: true
name: web
volumes:
mariadb-data:

View File

@@ -66,9 +66,7 @@
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
], ],
"post-create-project-cmd": [ "post-create-project-cmd": [
"@php artisan key:generate --ansi", "@php artisan key:generate --ansi"
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
], ],
"pre-package-uninstall": [ "pre-package-uninstall": [
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall" "Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"

8
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "371cadfa73bd8b1eeffb4f8056b83811", "content-hash": "0f770d255df260733746171900bcbf38",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -9906,12 +9906,12 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^8.2" "php": "^8.2"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.2.0" "plugin-api-version": "2.9.0"
} }

63
docker/bin/zail Executable file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -e
# Basis-Verzeichnis des Projekts ermitteln
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Unsere einzige Compose-Datei
COMPOSE_FILE="$BASE_DIR/compose.yaml"
# Standard-Service für Befehle wie "artisan", "npm", etc.
SERVICE="${ZAIL_SERVICE:-workspace}"
dc() {
docker compose -f "$COMPOSE_FILE" "$@"
}
if [[ "$#" -lt 1 ]]; then
echo "Usage: zail [up|down|stop|restart|ps|logs|bash|artisan|composer|npm|yarn|pnpm|<command>...]"
exit 1
fi
CMD="$1"
shift || true
case "$CMD" in
up)
dc up "$@"
;;
down)
dc down "$@"
;;
stop)
# Entspricht in etwa "sail stop"
dc stop "$@"
;;
restart)
dc down
dc up "$@"
;;
ps)
dc ps "$@"
;;
logs)
# Logs aller Services folgen
dc logs -f "$@"
;;
bash|shell|sh)
dc exec "$SERVICE" bash
;;
artisan)
dc exec "$SERVICE" php artisan "$@"
;;
composer)
dc exec "$SERVICE" composer "$@"
;;
npm|yarn|pnpm)
dc exec "$SERVICE" "$CMD" "$@"
;;
*)
# Fallback: beliebigen Befehl im Standard-Service ausführen
dc exec "$SERVICE" "$CMD" "$@"
;;
esac

View File

@@ -0,0 +1,97 @@
# docker/common/php-fpm/Dockerfile
# Stage 1: Build environment and Composer dependencies
FROM php:8.4-fpm AS builder
# Install system dependencies and PHP extensions required for Laravel + MySQL/PostgreSQL support
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
unzip \
libpq-dev \
libonig-dev \
libssl-dev \
libxml2-dev \
libcurl4-openssl-dev \
libicu-dev \
libzip-dev \
&& docker-php-ext-install -j"$(nproc)" \
pdo_mysql \
pdo_pgsql \
pgsql \
opcache \
intl \
zip \
bcmath \
soap \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Arbeitsverzeichnis
WORKDIR /var/www
# Kompletten Code kopieren (für composer-Skripte wie artisan package:discover)
COPY . /var/www
# Composer installieren und Dependencies holen
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist
# Stage 2: Development image (einziger Runtime-Stage)
FROM builder AS development
# Xdebug / Dev-Settings
ARG XDEBUG_ENABLED=true
ARG XDEBUG_MODE=develop,coverage,debug,profile
ARG XDEBUG_HOST=host.docker.internal
ARG XDEBUG_IDE_KEY=DOCKER
ARG XDEBUG_LOG=/dev/stdout
ARG XDEBUG_LOG_LEVEL=0
# User/Group-IDs für die Rechte-Synchronisierung
ARG UID=1000
ARG GID=1000
USER root
# Xdebug konfigurieren, falls aktiviert
RUN if [ "${XDEBUG_ENABLED}" = "true" ]; then \
pecl install xdebug && \
docker-php-ext-enable xdebug && \
{ \
echo "xdebug.mode=${XDEBUG_MODE}"; \
echo "xdebug.idekey=${XDEBUG_IDE_KEY}"; \
echo "xdebug.log=${XDEBUG_LOG}"; \
echo "xdebug.log_level=${XDEBUG_LOG_LEVEL}"; \
echo "xdebug.client_host=${XDEBUG_HOST}"; \
echo "xdebug.start_with_request=yes"; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ; \
fi
# User/Gruppe anlegen (oder existierende Gruppe wiederverwenden)
RUN if getent group "${GID}" >/dev/null; 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 \
&& 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
# Arbeitsverzeichnis
WORKDIR /var/www
# neuen Entry-Point für Dev-Container verwenden
COPY ./docker/php-fpm/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Zurück auf unprivilegierten User
USER www
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
EXPOSE 9000
CMD ["php-fpm"]

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env sh
set -e
APP_DIR=/var/www
if [ -n "${UID}" ] && [ -n "${GID}" ]; then
echo "Fixing file permissions with UID=${UID} and GID=${GID}..."
for path in storage bootstrap/cache; do
if [ -d "${APP_DIR}/${path}" ]; then
chown -R "${UID}:${GID}" "${APP_DIR}/${path}" || true
fi
done
fi
if [ -f "${APP_DIR}/artisan" ]; then
echo "Clearing configurations..."
php "${APP_DIR}/artisan" config:clear || true
php "${APP_DIR}/artisan" route:clear || true
php "${APP_DIR}/artisan" view:clear || true
fi
exec "$@"

View File

@@ -0,0 +1,84 @@
# docker/workspace/Dockerfile
# Use the official PHP CLI image as the base
FROM php:8.4-cli
# Set environment variables for user and group ID
ARG UID=1000
ARG GID=1000
ARG NODE_VERSION=22.0.0
# Install system dependencies and build libraries
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
unzip \
libpq-dev \
libonig-dev \
libssl-dev \
libxml2-dev \
libcurl4-openssl-dev \
libicu-dev \
libzip-dev \
&& docker-php-ext-install -j"$(nproc)" \
pdo_mysql \
pdo_pgsql \
pgsql \
opcache \
intl \
zip \
bcmath \
soap \
&& pecl install redis xdebug \
&& docker-php-ext-enable redis xdebug \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Xdebug-Args
ARG XDEBUG_ENABLED
ARG XDEBUG_MODE
ARG XDEBUG_HOST
ARG XDEBUG_IDE_KEY
ARG XDEBUG_LOG
ARG XDEBUG_LOG_LEVEL
# Configure Xdebug if enabled
RUN if [ "${XDEBUG_ENABLED}" = "true" ]; then \
docker-php-ext-enable xdebug && \
{ \
echo "xdebug.mode=${XDEBUG_MODE}"; \
echo "xdebug.idekey=${XDEBUG_IDE_KEY}"; \
echo "xdebug.log=${XDEBUG_LOG}"; \
echo "xdebug.log_level=${XDEBUG_LOG_LEVEL}"; \
echo "xdebug.client_host=${XDEBUG_HOST}"; \
echo "xdebug.start_with_request=yes"; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ; \
fi
# User/Gruppe anlegen
RUN if getent group "${GID}" >/dev/null; then \
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; \
fi \
&& usermod -aG sudo www \
&& echo 'www ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER www
# NVM + Node installieren
RUN export NVM_DIR="$HOME/.nvm" && \
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash && \
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
nvm install "${NODE_VERSION}" && \
nvm alias default "${NODE_VERSION}" && \
nvm use default
RUN echo 'export NVM_DIR="$HOME/.nvm"' >> /home/www/.bashrc && \
echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> /home/www/.bashrc && \
echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> /home/www/.bashrc
WORKDIR /var/www
ENTRYPOINT []
CMD ["bash"]

2
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{ {
"name": "laravel-starter-kit", "name": "www",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@@ -24,8 +24,7 @@ parameters:
ignoreErrors: ignoreErrors:
# It seems PHPStan > v2 cannot see Laravel's magic call methods in its static analysis, leading to # It seems PHPStan > v2 cannot see Laravel's magic call methods in its static analysis, leading to
# false positives "dynamic call to static method" errors. # false positives "dynamic call to static method" errors.
- #- identifier: staticMethod.dynamicCall
identifier: staticMethod.dynamicCall
# By default, PHPStan stores its cache files in sys_get_temp_dir() . '/phpstan' (usually /tmp/phpstan). You can override this by setting the tmpDir parameter. You can set this in extension settings as well. # By default, PHPStan stores its cache files in sys_get_temp_dir() . '/phpstan' (usually /tmp/phpstan). You can override this by setting the tmpDir parameter. You can set this in extension settings as well.
#tmpDir: /mnt/ramdisk #tmpDir: /mnt/ramdisk