diff --git a/.dockerignore b/.dockerignore index a013931c..8c93c3af 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,18 +5,23 @@ .gitattributes .github/ .gitignore +.idea/ .sops.yaml -Makefile -README.md -readme.md -docker-compose.yml + +codestyle.php +docker-compose* +eslint.config.js +Taskfile.yml +node_modules/ docs/ phpstan* phpunit.xml.dist -.idea/ +public/hot +readme.md +renovate.json5 vendor/ -node_modules/ .composer/ environment/dev/ environment/prod/deployment/beta +!/environment/.docker diff --git a/.env.example b/.env.example index f2f9d67a..d659a639 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ APP_URL=https://website.blumilk.local.env APP_DOCKER_HOST_NAME=website.blumilk.local.env MAILPIT_DOCKER_HOST_NAME=website-mailpit.blumilk.local.env -VITE_DEV_SERVER_DOCKER_HOST_NAME=website-vite-dev-server.blumilk.local.env +VITE_LOCAL_SERVER_DOCKER_HOST_NAME=website-vite-local-server.blumilk.local.env MAPBOX_ACCESS_TOKEN= MAPBOX_STYLE= @@ -17,7 +17,7 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=pgsql -DB_HOST=website-db-dev +DB_HOST=website-db-local DB_PORT=5432 DB_DATABASE=website DB_USERNAME=website @@ -31,7 +31,7 @@ QUEUE_CONNECTION=redis SESSION_DRIVER=redis SESSION_LIFETIME=120 -REDIS_HOST=website-redis-dev +REDIS_HOST=website-redis-local REDIS_PASSWORD=null REDIS_PORT=6379 diff --git a/Makefile b/Makefile deleted file mode 100644 index 6802eade..00000000 --- a/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -export COMPOSE_DOCKER_CLI_BUILD = 1 -export DOCKER_BUILDKIT = 1 - -include .env - -MAKEFLAGS += --no-print-directory - -SHELL := /bin/bash - -DOCKER_COMPOSE_FILE = docker-compose.yml -DOCKER_COMPOSE_APP_CONTAINER = app -DOCKER_COMPOSE_DATABASE_CONTAINER = database - -CURRENT_USER_ID = $(shell id --user) -CURRENT_USER_GROUP_ID = $(shell id --group) -CURRENT_DIR = $(shell pwd) - -DATABASE_USERNAME=website -TEST_DATABASE_NAME=website-test - -init: check-env-file - @make build \ - && make run \ - && docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash "./environment/dev/scripts/init.sh" \ - && make create-test-db - -check-env-file: - @if [ ! -f ".env" ]; then \ - echo "Create .env file and adjust." ;\ - exit 1;\ - fi; \ - -build: - @docker compose --file ${DOCKER_COMPOSE_FILE} build --pull - -run: - @docker compose --file ${DOCKER_COMPOSE_FILE} up --detach - -stop: - @docker compose --file ${DOCKER_COMPOSE_FILE} stop - -restart: stop run - -shell: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash - -shell-root: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec ${DOCKER_COMPOSE_APP_CONTAINER} bash - -test: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} composer test - -fix: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'composer csf' - -analyse: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'composer analyse' - -dev: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} bash -c 'npm run dev' - -queue: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} php artisan queue:work - -create-test-db: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec ${DOCKER_COMPOSE_DATABASE_CONTAINER} bash -c 'createdb --username=${DATABASE_USERNAME} ${TEST_DATABASE_NAME} &> /dev/null && echo "Created database for tests (${TEST_DATABASE_NAME})." || echo "Database for tests (${TEST_DATABASE_NAME}) exists."' - -encrypt-beta-secrets: - @$(MAKE) encrypt-secrets SECRETS_ENV=beta - -decrypt-beta-secrets: - @$(MAKE) decrypt-secrets SECRETS_ENV=beta AGE_SECRET_KEY=${SOPS_AGE_BETA_SECRET_KEY} - -encrypt-prod-secrets: - @$(MAKE) encrypt-secrets SECRETS_ENV=prod - -decrypt-prod-secrets: - @$(MAKE) decrypt-secrets SECRETS_ENV=prod AGE_SECRET_KEY=${SOPS_AGE_PROD_SECRET_KEY} - -decrypt-secrets: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" --env SOPS_AGE_KEY=${AGE_SECRET_KEY} ${DOCKER_COMPOSE_APP_CONTAINER} \ - bash -c "echo 'Decrypting ${SECRETS_ENV} secrets' \ - && cd ./environment/prod/deployment/${SECRETS_ENV} \ - && sops --decrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets.decrypted .env.${SECRETS_ENV}.secrets \ - && echo 'Done'" - -encrypt-secrets: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} \ - bash -c "echo 'Encrypting ${SECRETS_ENV} secrets' \ - && cd ./environment/prod/deployment/${SECRETS_ENV} \ - && sops --encrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets .env.${SECRETS_ENV}.secrets.decrypted \ - && echo 'Done'" - -.PHONY: init check-env-file build run stop restart shell shell-root test fix create-test-db queue analyse encrypt-beta-secrets decrypt-beta-secrets encrypt-prod-secrets decrypt-prod-secrets decrypt-secrets encrypt-secrets dev diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 00000000..ab6da746 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,137 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +env: + COMPOSE_DOCKER_CLI_BUILD: 1 + DOCKER_BUILDKIT: 1 + CURRENT_USER_ID: + sh: id --user + DOCKER_COMPOSE_APP_CONTAINER: app + DOCKER_COMPOSE_DATABASE_CONTAINER: database + +dotenv: + - .env + +includes: + secops: ./environment/secops-Taskfile.yml + devops: ./environment/devops-Taskfile.yml + +tasks: + default: + desc: "List all available tasks" + cmds: + - task --list-all + + init-project: + desc: "Initialize the project" + aliases: [init] + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer install + - task: _set-app-key + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan migrate --seed + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan storage:link + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm install + - task: create-test-database + + dev: + desc: "Run Vite local dev server" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm run dev + + run-containers: + desc: "Run containers" + aliases: [run] + deps: [build] + preconditions: + - sh: test -f .env + msg: "Please create .env for app." + cmds: + - cmd: docker compose up --detach + + stop-containers: + desc: "Stop containers" + aliases: [stop] + cmds: + - cmd: docker compose stop + + restart-containers: + desc: "Restart containers" + aliases: [restart] + cmds: + - task: stop-containers + - task: run-containers + + build-containers: + desc: "Build containers" + aliases: [build] + cmds: + - cmd: docker compose build --pull + + shell-app: + desc: "Enter app shell" + aliases: [shell] + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER bash + + shell-app-root: + desc: "Enter app shell as root" + aliases: [shell-root] + deps: [run] + cmds: + - cmd: docker compose exec --user root $DOCKER_COMPOSE_APP_CONTAINER bash + + test: + desc: "Run tests" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer test + + fix: + desc: "Run fixers" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer fix + + analyse: + desc: "Run composer analyse" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer analyse + + type-check: + desc: "Run npm type-check" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm run type-check + + queue: + desc: "Run queue:work" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan queue:work + + create-test-database: + desc: "Create test database" + deps: [run] + aliases: [create-test-db] + vars: + TEST_DB_NAME: blumilk-website-test + cmds: + - cmd: docker compose exec $DOCKER_COMPOSE_DATABASE_CONTAINER bash -c "createdb --username=$DB_USERNAME {{ .TEST_DB_NAME }} && echo 'Created database for tests ({{ .TEST_DB_NAME }}).'" + ignore_error: true + + _set-app-key: + desc: "Create APP_KEY if not set" + internal: true + cmds: + - | + APP_KEY_VALUE=$(grep APP_KEY .env | cut --delimiter '=' --fields 2-) + if [ -z "${APP_KEY_VALUE}" ]; then + echo "APP_KEY is not set. Creating:" + docker compose exec $DOCKER_COMPOSE_APP_CONTAINER php artisan key:generate + fi diff --git a/docker-compose.yml b/docker-compose.yml index e4a4e121..91b3f4e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ networks: - website-dev: + website-local: driver: bridge traefik-proxy-blumilk-local-environment: external: true @@ -13,18 +13,15 @@ volumes: services: app: build: - context: ./environment/dev/app - dockerfile: Dockerfile + context: . + dockerfile: environment/.docker/app/Dockerfile + target: local args: - INSTALL_XDEBUG=${DOCKER_INSTALL_XDEBUG:-true} - USER_ID=${DOCKER_HOST_USER_ID:-1000} labels: - "traefik.enable=true" - "traefik.blumilk.local.environment=true" - # HTTP - - "traefik.http.routers.website-http-router.rule=Host(`${APP_DOCKER_HOST_NAME}`)" - - "traefik.http.routers.website-http-router.entrypoints=web" - - "traefik.http.routers.website-http-router.service=website-app" # HTTPS - "traefik.http.routers.website-https-router.rule=Host(`${APP_DOCKER_HOST_NAME}`)" - "traefik.http.routers.website-https-router.entrypoints=websecure" @@ -32,24 +29,23 @@ services: - "traefik.http.routers.website-https-router.service=website-app" # APP LOADBALANCER - "traefik.http.services.website-app.loadbalancer.server.port=80" - # VITE DEV SERVER - - "traefik.http.routers.website-vite-dev-server-https-router.rule=Host(`${VITE_DEV_SERVER_DOCKER_HOST_NAME}`)" - - "traefik.http.routers.website-vite-dev-server-https-router.entrypoints=websecure" - - "traefik.http.routers.website-vite-dev-server-https-router.tls=true" - - "traefik.http.routers.website-vite-dev-server-https-router.service=website-vite-dev-server" - - "traefik.http.services.website-vite-dev-server.loadbalancer.server.port=5173" - container_name: website-app-dev + # VITE LOCAL SERVER + - "traefik.http.routers.website-vite-local-server-https-router.rule=Host(`${VITE_LOCAL_SERVER_DOCKER_HOST_NAME}`)" + - "traefik.http.routers.website-vite-local-server-https-router.entrypoints=websecure" + - "traefik.http.routers.website-vite-local-server-https-router.tls=true" + - "traefik.http.routers.website-vite-local-server-https-router.service=website-vite-local-server" + - "traefik.http.services.website-vite-local-server.loadbalancer.server.port=5173" + container_name: website-app-local working_dir: /application volumes: - - ./environment/dev/app/nginx.conf:/etc/nginx/nginx.conf:ro - - ./environment/dev/app/php.ini:/usr/local/etc/php/conf.d/zzz-overrides.ini:ro - - ./environment/dev/app/php-fpm.conf:/usr/local/etc/php-fpm.d/zzz-overrides.conf:ro - - ./environment/dev/app/supervisord.conf:/etc/supervisor/custom-supervisord.conf:ro + - ./environment/local/app/php.ini:/usr/local/etc/php/conf.d/zzz-overrides.ini:ro + - ./environment/local/app/php-fpm.conf:/usr/local/etc/php-fpm.d/zzz-overrides.conf:ro + - ./environment/local/app/supervisord.conf:/etc/supervisor/custom-supervisord.conf:ro - .:/application ports: - ${DOCKER_APP_HOST_PORT:-63851}:80 networks: - - website-dev + - website-local - traefik-proxy-blumilk-local-environment restart: unless-stopped depends_on: @@ -58,7 +54,7 @@ services: database: image: postgres:16.3-alpine3.18@sha256:64e18e8fb3e9c9aac89ac590c5dd8306b862478404f76cd9b5f7720d012b4c47 - container_name: website-db-dev + container_name: website-db-local environment: - POSTGRES_USER=${DB_USERNAME} - POSTGRES_PASSWORD=${DB_PASSWORD} @@ -74,35 +70,35 @@ services: volumes: - website-postgres-data:/var/lib/postgresql/data networks: - - website-dev + - website-local restart: unless-stopped mailpit: - image: axllent/mailpit:v1.24.0@sha256:06cf27fde6ea653c68b5c80f7d5d7280d56d17248e34de4b4ef15f9f4f86662c - container_name: website-mailpit-dev - labels: - - "traefik.enable=true" - - "traefik.blumilk.local.environment=true" - - "traefik.http.routers.website-mailpit-http-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" - - "traefik.http.routers.website-mailpit-http-router.entrypoints=web" - - "traefik.http.routers.website-mailpit-https-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" - - "traefik.http.routers.website-mailpit-https-router.entrypoints=websecure" - - "traefik.http.routers.website-mailpit-https-router.tls=true" - - "traefik.http.services.website-mailpit.loadbalancer.server.port=8025" - networks: - - website-dev - - traefik-proxy-blumilk-local-environment - ports: - - ${DOCKER_MAILPIT_DASHBOARD_HOST_PORT:-34622}:8025 - restart: unless-stopped + image: axllent/mailpit:v1.24.0@sha256:06cf27fde6ea653c68b5c80f7d5d7280d56d17248e34de4b4ef15f9f4f86662c + container_name: website-mailpit-local + labels: + - "traefik.enable=true" + - "traefik.blumilk.local.environment=true" + - "traefik.http.routers.website-mailpit-http-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" + - "traefik.http.routers.website-mailpit-http-router.entrypoints=web" + - "traefik.http.routers.website-mailpit-https-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" + - "traefik.http.routers.website-mailpit-https-router.entrypoints=websecure" + - "traefik.http.routers.website-mailpit-https-router.tls=true" + - "traefik.http.services.website-mailpit.loadbalancer.server.port=8025" + networks: + - website-local + - traefik-proxy-blumilk-local-environment + ports: + - ${DOCKER_MAILPIT_DASHBOARD_HOST_PORT:-34622}:8025 + restart: unless-stopped redis: image: redis:7.2.3-alpine3.18@sha256:3ce533b2b057f74b235d1d8697ae08b1b6ff0a5e16827ea6a377b6365693c7ed - container_name: website-redis-dev + container_name: website-redis-local ports: - ${DOCKER_REDIS_HOST_PORT:-63852}:6379 volumes: - website-redis-data:/data networks: - - website-dev + - website-local restart: unless-stopped diff --git a/environment/.docker/app/Dockerfile b/environment/.docker/app/Dockerfile new file mode 100644 index 00000000..b2e34f7d --- /dev/null +++ b/environment/.docker/app/Dockerfile @@ -0,0 +1,166 @@ +FROM registry.blumilk.pl/internal-public/secops-tools-bin:v1.0.0 AS secops-tools-bin +FROM composer:2.8.6 AS composer +FROM node:22.14.0-bullseye-slim AS node + +FROM php:8.4.5-fpm-bookworm AS base + +COPY --from=composer /usr/bin/composer /usr/bin/composer + +# For other versions check: http://nginx.org/packages/mainline/debian/pool/nginx/n/nginx/ +ARG NGINX_VERSION="1.27.3-1~bookworm" + +# https://pecl.php.net/package/redis +# renovate: datasource=github-tags depName=phpredis/phpredis +ARG PHPREDIS_VERSION=6.1.0 + +RUN apt-get update \ + && apt-get install --assume-yes gpg \ + && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmour --output /etc/apt/trusted.gpg.d/apt.nginx.org.gpg > /dev/null \ + && echo "deb https://nginx.org/packages/mainline/debian bookworm nginx" | tee /etc/apt/sources.list.d/nginx.list \ + && apt-get update && apt-get install --assume-yes \ + nginx=${NGINX_VERSION} \ + libzip-dev \ + libpq-dev \ + libicu-dev \ + libjpeg-dev \ + libpng-dev \ + libwebp-dev \ + supervisor \ + cron \ + && pecl install \ + redis-${PHPREDIS_VERSION} \ + && docker-php-ext-configure \ + gd --with-jpeg --with-webp \ + && docker-php-ext-install \ + zip \ + pdo_pgsql \ + intl \ + gd \ + bcmath \ + calendar \ + opcache \ + pcntl \ + && docker-php-ext-enable \ + redis + +COPY ./environment/.docker/app/entrypoint.sh /entrypoint.sh +COPY ./environment/.docker/app/nginx.conf /etc/nginx/nginx.conf + +WORKDIR /application + +ENTRYPOINT ["/entrypoint.sh"] + +FROM base AS local + +COPY --from=secops-tools-bin /usr/local/bin/age /usr/local/bin/age-keygen /usr/local/bin/sops /usr/local/bin/ + +RUN mv "${PHP_INI_DIR}/php.ini-development" "${PHP_INI_DIR}/php.ini" + +ARG USER_NAME=host-user +ARG USER_ID=1000 +ARG PHP_FPM_GROUP=www-data + +RUN adduser \ + --disabled-password \ + --uid ${USER_ID} \ + ${USER_NAME} \ + && usermod \ + --append \ + --groups \ + ${PHP_FPM_GROUP} \ + ${USER_NAME} + +# Add node, npm, npx binaries +COPY --from=node --chown=${USER_NAME}:root /usr/local/lib/node_modules /usr/local/lib/node_modules +COPY --from=node --chown=${USER_NAME}:root /usr/local/bin/node /usr/local/bin/node +RUN ln --symbolic /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npm \ + && ln --symbolic /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npx + +# renovate: datasource=github-tags depName=xdebug/xdebug +ARG XDEBUG_VERSION=3.4.2 +ARG INSTALL_XDEBUG=false + +RUN if [ ${INSTALL_XDEBUG} = true ]; then \ + pecl install xdebug-${XDEBUG_VERSION} \ + && docker-php-ext-enable xdebug \ +;fi + +FROM composer AS vendor-build + +WORKDIR /app_composer_dependencies + +COPY composer.json composer.lock ./ + +RUN composer install \ + --no-interaction \ + --no-plugins \ + --no-scripts \ + --no-dev \ + --prefer-dist \ + --ignore-platform-reqs + +FROM node AS frontend-build + +WORKDIR /app_frontend_dependencies + +ARG ENVIRONMENT_ARG +ENV ENVIRONMENT=${ENVIRONMENT_ARG} + +# last position is target directory +COPY package.json \ + package-lock.json \ + postcss.config.js \ + vite.config.js \ + tsconfig.json \ + ./ + +COPY ./environment/${ENVIRONMENT}/app/vite.${ENVIRONMENT}.env .env + +RUN npm clean-install + +COPY resources/js ./resources/js/ +COPY resources/css ./resources/css/ +COPY resources/views ./resources/views/ + +RUN npm run build + +FROM base AS production + +ARG ENVIRONMENT_ARG +ENV ENVIRONMENT=${ENVIRONMENT_ARG} + +RUN docker-php-ext-install \ + opcache + +RUN mv "${PHP_INI_DIR}/php.ini-production" "${PHP_INI_DIR}/php.ini" + +COPY ./environment/${ENVIRONMENT}/app/php.ini ${PHP_INI_DIR}/conf.d/zzz-overrides-php.ini +COPY ./environment/${ENVIRONMENT}/app/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-overrides.conf +COPY ./environment/${ENVIRONMENT}/app/supervisord.conf /etc/supervisor/custom-supervisord.conf + +# Copy Frontend build +COPY --chown=www-data:www-data --from=frontend-build /app_frontend_dependencies/public/build ./public/build/ + +# Copy Composer dependencies +COPY --chown=www-data:www-data --from=vendor-build /app_composer_dependencies/vendor ./vendor/ + +# Copy App files +COPY --chown=www-data:www-data . . + +# Remove unnecessary files +RUN rm --recursive --force \ + .dockerignore \ + postcss.config.js \ + vite.config.js \ + tsconfig.json \ + eslint.config.js \ + environment/.docker + +RUN composer dump-autoload --optimize + +EXPOSE 80 + +ARG PROJECT_BUILD_VERSION_ARG +ENV PROJECT_BUILD_VERSION=${PROJECT_BUILD_VERSION_ARG} diff --git a/environment/.docker/app/entrypoint.sh b/environment/.docker/app/entrypoint.sh new file mode 100755 index 00000000..b951e259 --- /dev/null +++ b/environment/.docker/app/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# -e is for "automatic error detection", tell shell to abort any time an error occurred +set -e + +# bash is not responding to the sigterm and container always have 10 second timeout (when stop/restart) +# exec is related with +# https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop +# https://github.com/moby/moby/issues/3766 +# https://unix.stackexchange.com/a/196053 + +exec supervisord --configuration /etc/supervisor/custom-supervisord.conf diff --git a/environment/.docker/app/nginx.conf b/environment/.docker/app/nginx.conf new file mode 100644 index 00000000..ed2cc527 --- /dev/null +++ b/environment/.docker/app/nginx.conf @@ -0,0 +1,50 @@ +daemon off; + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + client_max_body_size 30M; + + server { + listen 80 default; + server_name blumilk-website-nginx; + + access_log /dev/stdout; + error_log /dev/stderr; + + root /application/public; + index index.php; + + if (!-e $request_filename) { + rewrite ^.*$ /index.php last; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + location /storage/ { + add_header 'Access-Control-Allow-Origin' '*'; + } + } +} diff --git a/environment/devops-Taskfile.yml b/environment/devops-Taskfile.yml new file mode 100644 index 00000000..c98201ed --- /dev/null +++ b/environment/devops-Taskfile.yml @@ -0,0 +1,10 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +tasks: + generate-age-keys: + desc: "Generate age keys" + cmds: + - cmd: docker run --rm registry.blumilk.pl/internal-public/secops-tools-bin:v1.0.0 age-keygen diff --git a/environment/local/app/php-fpm.conf b/environment/local/app/php-fpm.conf new file mode 100644 index 00000000..883caeca --- /dev/null +++ b/environment/local/app/php-fpm.conf @@ -0,0 +1,8 @@ +[global] +daemonize = no + +[www] +listen = /run/php-fpm.sock +listen.owner = nginx + +user = host-user diff --git a/environment/local/app/php.ini b/environment/local/app/php.ini new file mode 100644 index 00000000..8ffeea58 --- /dev/null +++ b/environment/local/app/php.ini @@ -0,0 +1,9 @@ +[PHP] +memory_limit = 256M + +[xdebug] +xdebug.client_host=xdebug://gateway +xdebug.client_port=9003 +xdebug.mode=develop,debug +xdebug.start_with_request=yes +xdebug.log_level=0 diff --git a/environment/local/app/supervisord.conf b/environment/local/app/supervisord.conf new file mode 100644 index 00000000..2acd0725 --- /dev/null +++ b/environment/local/app/supervisord.conf @@ -0,0 +1,22 @@ +; For more information on the config file, please see: +; http://supervisord.org/configuration.html + +[supervisord] +logfile = /var/log/supervisor/supervisord.log +pidfile = /var/run/supervisord.pid +user = root +nodaemon = true + +[program:nginx] +command = nginx +stdout_logfile = /dev/stdout +stdout_logfile_maxbytes = 0 +stderr_logfile = /dev/stderr +stderr_logfile_maxbytes = 0 + +[program:php-fpm] +command = php-fpm +stdout_logfile = /dev/stdout +stdout_logfile_maxbytes = 0 +stderr_logfile = /dev/stderr +stderr_logfile_maxbytes = 0 diff --git a/environment/local/app/vite.local.env b/environment/local/app/vite.local.env new file mode 100644 index 00000000..a7b2317c --- /dev/null +++ b/environment/local/app/vite.local.env @@ -0,0 +1 @@ +VITE_APP_NAME="blumilk-website" diff --git a/environment/secops-Taskfile.yml b/environment/secops-Taskfile.yml new file mode 100644 index 00000000..e4bac534 --- /dev/null +++ b/environment/secops-Taskfile.yml @@ -0,0 +1,38 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +tasks: + encrypt-staging-secrets: + desc: "Encrypt app staging secrets" + cmds: + - task: _encrypt-secrets + vars: + ENVIRONMENT: staging + + decrypt-staging-secrets: + desc: "Decrypt app staging secrets" + cmds: + - task: _decrypt-secrets + vars: + ENVIRONMENT: staging + AGE_SECRET_KEY: ${SOPS_AGE_STAGING_SECRET_KEY} + + _encrypt-secrets: + internal: true + cmds: + - cmd: echo "Encrypting {{ .ENVIRONMENT }} secrets" + - cmd: | + docker compose exec --user $CURRENT_USER_ID --workdir /application/environment/.deployment/{{ .ENVIRONMENT }} $DOCKER_COMPOSE_APP_CONTAINER \ + sops --encrypt --input-type=dotenv --output-type=dotenv --output .env.{{ .ENVIRONMENT }}.secrets .env.{{ .ENVIRONMENT }}.secrets.decrypted + - cmd: echo "Done" + + _decrypt-secrets: + internal: true + cmds: + - cmd: echo "Decrypting {{ .ENVIRONMENT }} secrets" + - cmd: | + docker compose exec --user $CURRENT_USER_ID --env SOPS_AGE_KEY={{ .AGE_SECRET_KEY }} --workdir /application/environment/.deployment/{{ .ENVIRONMENT }} $DOCKER_COMPOSE_APP_CONTAINER \ + sops --decrypt --input-type=dotenv --output-type=dotenv --output .env.{{ .ENVIRONMENT }}.secrets.decrypted .env.{{ .ENVIRONMENT }}.secrets + - cmd: echo 'Done' diff --git a/readme.md b/readme.md index 49d9d253..e325cd17 100644 --- a/readme.md +++ b/readme.md @@ -2,20 +2,99 @@ ### About application > Blumilk official website. -### Local development +--- +## Taskfile setup +### Linux + +If you don't have Task binary installed, you can install it by running command below. \ +If you don't want to install to `/usr/local/bin` (dir for all users in the system) change `-b` flag value. \ +Be sure that provided path is in system $PATH, that binary will be available in the terminal. +To check system paths type `$PATH` in the terminal. + +```shell +sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin v3.42.1 +``` +_-b sets bindir or installation directory, Defaults to ./bin_ \ +_-d turns on debug logging_ + +Other installation methods: https://taskfile.dev/installation \ +GitHub: https://github.com/go-task/task \ +Taskfile releases: https://github.com/go-task/task/releases + +# Task commands + +--- +To list all task commands just run: +```shell +task +``` +### Task commands completions: + +--- +Add this line to `.bashrc` if you are using bash: +``` +eval "$(task --completion bash)" ``` +For other shells see: \ +https://taskfile.dev/installation/#option-1-load-the-completions-in-your-shells-startup-config-recommended + +# Project initialization + +Before first use, project has to be initialized. + +First, prepare `.env` file +```shell cp .env.example .env -make init -make dev ``` -Application will be running under [localhost:34620](localhost:34620) and [http://website.blumilk.localhost/](http://website.blumilk.localhost/) in Blumilk traefik environment. If you don't have a Blumilk traefik environment set up yet, follow the instructions from this [repository](https://github.com/blumilksoftware/environment). + +To initialize project run: +```shell +task init +``` +This command will check if `.env` file exists. +Build and run containers. \ +Then it will: +- install composer dependencies +- generate `APP_KEY` if not set +- run migrations with seed +- link Laravel storage +- install npm dependencies +- create test database if not exist + +### Develop project + +To develop project run: +```shell +task dev +``` +This command will run Vite development server. \ +App will be available at: +- https://website.blumilk.local.env- if you ran Traefik in Blumilk environment. Don't forget to update hosts file. +- http://\:5173 - link will be displayed in console + +### Running tests + +You can run PHPUnit test cases + +``` +task test +``` + +### Code style check + +You can run PHP-CS-Fixer and ESLint: + +``` +task fix +``` + +Application will be running under [localhost:34620](localhost:34620) and [https://website.blumilk.local.env](https://website.blumilk.local.env) in Blumilk traefik environment. If you don't have a Blumilk traefik environment set up yet, follow the instructions from this [repository](https://github.com/blumilksoftware/environment). #### Commands Before running any of the commands below, you must run shell: ``` -make shell +task shell ``` - | Command | Task | |:------------------------|:--------------------------------------------| | `composer ` | Composer | @@ -33,9 +112,20 @@ make shell #### Containers -| service | container name | default host port | -|:-----------|:--------------------------|:--------------------------------| -| `app` | `website-app-dev` | [34620](http://localhost:34620) | -| `database` | `website-db-dev` | 34621 | -| `redis` | `website-redis-dev` | 34623 | -| `mailpit` | `website-mailpit-dev` | 34622 | +| service | container name | default host port | +|:-----------|:------------------------|:--------------------------------| +| `app` | `website-app-local` | [34620](http://localhost:34620) | +| `database` | `website-db-local` | 34621 | +| `redis` | `website-redis-local` | 34623 | +| `mailpit` | `website-mailpit-local` | 34622 | + +### Working with encrypted data + +To encrypt/decrypt environment secrets or json files, you can use task commands: \ +E.g.: `task secops:decrypt-beta-secrets` + +* secops:decrypt-beta-secrets: Decrypt app beta secrets +* secops:encrypt-beta-secrets: Encrypt app beta secrets + +Remember that decryption requires private key (e.g. `SOPS_AGE_BETA_SECRET_KEY` for beta environment) which should be set in `.env` file. +Encryption uses public key which is added in `.sops.yaml` file. diff --git a/vite.config.js b/vite.config.js index 90ae22d6..1b2fc6a2 100644 --- a/vite.config.js +++ b/vite.config.js @@ -12,7 +12,7 @@ export default ({ mode }) => { host: true, port: 5173, strictPort: true, - origin: 'https://' + process.env.VITE_DEV_SERVER_DOCKER_HOST_NAME, + origin: 'https://' + process.env.VITE_LOCAL_SERVER_DOCKER_HOST_NAME, cors: true, // Allow any origin }, resolve: {