diff --git a/.env.example b/.env.example index 24c20d2..b97f9e5 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,11 @@ +COMPOSE_PROJECT_NAME=my-project +DOCKER_SERVER_NAME=my-project.local + APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true -APP_URL=https://my-project.test:30080 - -COMPOSE_PROJECT_NAME=my-project -DOCKER_MYSQL_LOCAL_PORT=33306 -DOCKER_NGINX_LOCAL_PORT=30080 -DOCKER_REDIS_LOCAL_PORT=36379 -DOCKER_SERVER_NAME=my-project.test - -SENTRY_LARAVEL_DSN= -SENTRY_TRACES_SAMPLE_RATE=1.0 -SENTRY_SEND_DEFAULT_PII=true +APP_URL=https://my-project.local LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null @@ -66,3 +59,7 @@ VITE_PUSHER_HOST="${PUSHER_HOST}" VITE_PUSHER_PORT="${PUSHER_PORT}" VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +SENTRY_LARAVEL_DSN= +SENTRY_TRACES_SAMPLE_RATE=1.0 +SENTRY_SEND_DEFAULT_PII=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04412ed..1a7eabe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: run: cp .env.example .env - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build Docker images - uses: docker/bake-action@v3 + uses: docker/bake-action@v4 with: load: true files: | @@ -36,7 +36,7 @@ jobs: run: docker compose up -d mysql mysql-test php-fpm --wait --no-build - name: Cache composer dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: vendor key: composer-${{ hashFiles('composer.lock') }} @@ -54,7 +54,7 @@ jobs: docker inspect "$PROJECT_NAME-mysql" - name: Cache npm dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: node_modules key: npm-${{ hashFiles('package-lock.json') }} @@ -65,6 +65,9 @@ jobs: - name: Install JS dependencies run: npm install && npm run build + - name: Run JS test suite + run: LARAVEL_BYPASS_ENV_CHECK=1 npm test + - name: Cache routes run: docker exec "$PROJECT_NAME-php-fpm" ./artisan route:cache @@ -72,7 +75,7 @@ jobs: run: docker exec "$PROJECT_NAME-php-fpm" ./artisan view:cache - name: Cache Larastan result cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .phpstan.cache key: "phpstan-result-cache-${{ github.run_id }}" # always unique key - always writes a new cache @@ -80,7 +83,7 @@ jobs: phpstan-result-cache- - name: Cache php-cs-fixer result cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .php-cs-fixer.cache key: "php-cs-fixer-result-cache-${{ github.run_id }}" # always unique key - always writes a new cache diff --git a/README.md b/README.md index f364007..cd712ad 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You can also remove: Modifications are needed to the following files already in a default Laravel install: * `.env.example` and `.env` - * copy from `APP_URL`, `COMPOSER_PROJECT_NAME`, `DOCKER_MYSQL_LOCAL_PORT`, `DOCKER_NGINX_LOCAL_PORT`, and `DOCKER_SERVER_NAME` + * copy from `APP_URL`, `COMPOSE_PROJECT_NAME`, and `DOCKER_SERVER_NAME` * copy `DB_HOST`, `DB_DATABASE`, `DB_USERNAME`, and `DB_PASSWORD` Update your Composer packages and scripts: diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md index 38ce672..36bc587 100644 --- a/README_TEMPLATE.md +++ b/README_TEMPLATE.md @@ -24,17 +24,20 @@ To get started with local development, follow these steps. Make sure you run all ### DNS resolution -> Decide on a local domain to use for this project. Substitute that any place you see `my-project.test` or `my-project` below. +> Decide on a local domain to use for this project. Substitute that any place you see `my-project.local` or `my-project` below. -By default, this project will run on the host `my-project.test`. This requires some sort of local DNS resolution for that hostname -to your localhost IP address. One easy way to do this for the entire `.test` top-level domain, is to run a lightweight tool -called `dnsmasq`. You can install it via Homebrew on a Mac with: `brew install dnsmasq`. +By default, this project will run on the host `my-project.local`. +OrbStack (see below) is already configured to serve on this top-level domain. + +If you're not using OrbStack, you will need some sort of local DNS resolution for that hostname to your localhost IP address. +One easy way to do this for the entire `.local` top-level domain, is to run a lightweight tool called `dnsmasq`. +You can install it via Homebrew on a Mac with: `brew install dnsmasq`. > If you've ever setup Valet, it already installed dnsmasq for you. You can verify if it's already installed by running > `brew services` and see if `dnsmasq` is listed. If you don't want to run `dnsmasq`, you can also add a manual DNS entry to your `/etc/hosts` file in the form: -`127.0.0.1 my-project.test` +`127.0.0.1 my-project.local` ### Setting up an SSL certificate @@ -56,22 +59,23 @@ Once `mkcert` is installed, we need to generate our local development root certi Then, generate the certificates for this project and put them into a location accessible to your docker setup: -`mkcert -cert-file docker/nginx/ssl.pem -key-file docker/nginx/key.pem my-project.test` +`mkcert -cert-file docker/vite/ssl.pem -key-file docker/vite/key.pem my-project.test` ### Node environment -The best option to ensure you're using the correct versions of Node and npm with this project is to install [Volta](https://volta.sh). Volta will read the pinned versions of Node and npm from the `package.json` so you can be sure you're using the correct versions. +The best option to ensure you're using the correct versions of Node and npm with this project is to install [Volta](https://volta.sh). +Volta will read the pinned versions of Node and npm from the `package.json` so you can be sure you're using the correct versions. ### Get the project running in Docker Docker is used for local development. It's self-contained, easy to set up, and matches the exact versions of key services -running in production. It requires that you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed. +running in production. We use [OrbStack](https://orbstack.dev) as our Docker engine, since it handles domain resolution and port mapping. +This config also works with [Docker Desktop](https://www.docker.com/products/docker-desktop/), but you may need to add explicit port mappings. **Setup the environment** -Make a copy of the example env file: `cp .env.example .env` +Open the `.env.example` file and update the `COMPOSE_PROJECT_NAME` and `DOCKER_SERVER_NAME` settings to match your project. -Open the `.env` file and review the settings prefixed with `DOCKER_`. The defaults should work, but if you want a different -host name, or to change the port numbers, make those modifications before continuing with the Docker setup. +Make a copy of the example env file: `cp .env.example .env` **Get Docker running** @@ -97,7 +101,7 @@ And then open `auth.json` and fill out the username and password values. **Normal project setup** With the certificates, our environment, and Docker setup, the rest of these steps will be typical steps for any Laravel -project. The one key difference is that instead of running tools like npm, composer and artisan directly, we need to run +project. The one key difference is that instead of running tools like composer and artisan directly, we need to run them from inside the container. This is very important. If we run the tools from our host environment, all the guarantees about versions of tooling will no longer apply. @@ -112,7 +116,7 @@ Run these commands to finish the local development setup * `docker/bin/artisan horizon:install` * `docker/bin/artisan migrate --seed` -You're good to go - surf to https://my-project.test:30080 (or a different host/port if you've configured it) +You're good to go - surf to https://my-project.local You can also use any normal database management tools and connect to the database using the port specified in `.env`. diff --git a/composer.json b/composer.json index 87124a8..0b6e3c2 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "name": "nocompromises/my-project", + "version": "1.0.0", "type": "project", "license": "proprietary", "require": { diff --git a/docker-compose.yml b/docker-compose.yml index b355282..b1599d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,51 +14,49 @@ ## - Make sure to enable the performance optimization for Macs, if you're running on macOS 12.2 or higher. ## From Docker Desktop, go to Settings, Experimental Features and enable both settings for virtualization and VirtioFS. ######################################################################################################################## -version: "3.9" - services: ## The App database mysql: container_name: "${COMPOSE_PROJECT_NAME}-mysql" - image: mysql:8.0.35 + image: mysql:8.0.37 volumes: - mysql-data:/var/lib/mysql + - ./docker/mysql/init:/docker-entrypoint-initdb.d environment: - - MYSQL_ROOT_PASSWORD=password - - MYSQL_DATABASE=app # update .env and .env.example if you change any of these values - - MYSQL_USER=app - - MYSQL_PASSWORD=app - ports: - - "${DOCKER_MYSQL_LOCAL_PORT}:3306" - command: - - "--character-set-server=utf8mb4" - - "--collation-server=utf8mb4_unicode_ci" - - "--default-authentication-plugin=mysql_native_password" + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: app # update .env and .env.example if you change any of these values + MYSQL_USER: app + MYSQL_PASSWORD: app + # Uncomment the command section if you need to deviate from MySQL defaults (like on a legacy database schema) + # command: + # - "--character-set-server=utf8mb4" + # - "--collation-server=utf8mb4_0900_ai_ci" + # - "--default-authentication-plugin=caching_sha2_password" restart: unless-stopped ## MySQL just for Unit Tests mysql-test: container_name: "${COMPOSE_PROJECT_NAME}-mysql-test" - image: mysql:8.0.35 + image: mysql:8.0.37 volumes: - mysql-test-data:/var/lib/mysql + - ./docker/mysql/init:/docker-entrypoint-initdb.d environment: - - MYSQL_ROOT_PASSWORD=password - - MYSQL_DATABASE=apptest # update phpunit.xml if you change any of these values - - MYSQL_USER=app - - MYSQL_PASSWORD=app - command: - - "--character-set-server=utf8mb4" - - "--collation-server=utf8mb4_unicode_ci" - - "--default-authentication-plugin=mysql_native_password" + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: apptest # update phpunit.xml if you change any of these values + MYSQL_USER: app + MYSQL_PASSWORD: app + # Uncomment the command section if you need to deviate from MySQL defaults (like on a legacy database schema) + # command: + # - "--character-set-server=utf8mb4" + # - "--collation-server=utf8mb4_0900_ai_ci" + # - "--default-authentication-plugin=caching_sha2_password" restart: unless-stopped redis: container_name: "${COMPOSE_PROJECT_NAME}-redis" - image: redis:7.0.5-alpine3.17 - ports: - - "${DOCKER_REDIS_LOCAL_PORT}:6379" + image: redis:7.2.5-alpine3.20 restart: unless-stopped ## NGINX to support the app, configured to use the php-fpm-debug server @@ -71,8 +69,8 @@ services: working_dir: /app volumes: - .:/app - ports: - - "${DOCKER_NGINX_LOCAL_PORT}:443" + labels: + - "dev.orbstack.domains=${DOCKER_SERVER_NAME}" depends_on: - php-fpm-debug restart: unless-stopped diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index 86dedad..37fdce5 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -1,5 +1,3 @@ -FROM nginx:1.22.0-alpine +FROM nginx:1.27.0-alpine3.19 COPY nginx.conf /etc/nginx/conf.d/default.conf -COPY ssl.pem /etc/nginx/ssl.pem -COPY key.pem /etc/nginx/key.pem diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 3dd45d5..5069a1c 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -1,19 +1,26 @@ server { - listen 443 ssl; - ssl_certificate /etc/nginx/ssl.pem; - ssl_certificate_key /etc/nginx/key.pem; + listen 80; root /app/public; + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; index index.php; - if (!-e $request_filename) { - rewrite ^.*$ /index.php last; + charset utf-8; + location / { + try_files $uri $uri/ /index.php?$query_string; } + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + error_page 404 /index.php; location ~ \.php$ { fastcgi_pass php-fpm-debug:9000; fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_read_timeout 300; } + location ~ /\.(?!well-known).* { + deny all; + } } diff --git a/docker/php-fpm/Dockerfile b/docker/php-fpm/Dockerfile index 139bdfe..7101069 100644 --- a/docker/php-fpm/Dockerfile +++ b/docker/php-fpm/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.2.12-fpm-alpine3.18 as base +FROM php:8.3.8-fpm-alpine3.20 as base RUN apk add --no-cache $PHPIZE_DEPS \ freetype-dev \ @@ -17,7 +17,7 @@ RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ RUN pecl install redis-6.0.2 && docker-php-ext-enable redis # install composer from the composer image -COPY --from=composer:2.6.5 /usr/bin/composer /usr/bin/composer +COPY --from=composer:2.7.7 /usr/bin/composer /usr/bin/composer COPY docker-php-file-uploads.ini /usr/local/etc/php/conf.d/docker-php-file-uploads.ini @@ -25,5 +25,5 @@ FROM base as debug # Supports xdebug RUN apk add --update linux-headers -RUN pecl install xdebug-3.2.2 && docker-php-ext-enable xdebug +RUN pecl install xdebug-3.3.2 && docker-php-ext-enable xdebug COPY docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini diff --git a/docker/php-fpm/docker-php-ext-xdebug.ini b/docker/php-fpm/docker-php-ext-xdebug.ini index 69b8397..63e4b0f 100644 --- a/docker/php-fpm/docker-php-ext-xdebug.ini +++ b/docker/php-fpm/docker-php-ext-xdebug.ini @@ -1,5 +1,5 @@ zend_extension=xdebug -xdebug.mode=debug +xdebug.mode=debug,develop xdebug.idekey=PHPSTORM xdebug.client_host=docker.for.mac.localhost memory_limit=512M diff --git a/docker/vite/.gitignore b/docker/vite/.gitignore new file mode 100644 index 0000000..cfaad76 --- /dev/null +++ b/docker/vite/.gitignore @@ -0,0 +1 @@ +*.pem diff --git a/package.json b/package.json index e479854..4a64e50 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "dev": "vite", "build": "vite build", "lint": "eslint ./resources/js", - "test": "vitest", - "test-with-coverage": "vitest run --coverage" + "test": "vitest --passWithNoTests", + "test-with-coverage": "vitest run --coverage --passWithNoTests" }, "devDependencies": { "@vitejs/plugin-vue": "^4.5.2", diff --git a/vite.config.js b/vite.config.js index 463a7fb..ed780b4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -26,15 +26,13 @@ export default defineConfig(({ mode }) => { if (mode === "development") { config.server = { - port: 30098, - strictPort: true, host: true, https: { - key: readFileSync("docker/nginx/key.pem"), - cert: readFileSync("docker/nginx/ssl.pem"), + key: readFileSync("docker/vite/key.pem"), + cert: readFileSync("docker/vite/ssl.pem"), }, hmr: { - host: "project.domain", + host: "my-project.test", }, }; }