diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..84a01e67b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,59 @@ +{ + "name": "BeWelcome Rox (Docker Master)", + "dockerComposeFile": [ + "../docker-compose.yml", + "../docker-compose.override.yml.dist" + ], + "service": "php", + "workspaceFolder": "/srv/bewelcome", + "overrideCommand": false, + "forwardPorts": [80, 1080, 9306, 9308], + "portsAttributes": { + "80": { + "label": "BeWelcome App (Nginx)", + "onAutoForward": "openBrowser" + }, + "1080": { + "label": "MailPit Web UI", + "onAutoForward": "notify" + }, + "9306": { + "label": "Manticore MySQL protocol", + "onAutoForward": "silent" + }, + "9308": { + "label": "Manticore HTTP API", + "onAutoForward": "silent" + } + }, + "hostRequirements": { + "cpus": 2, + "memory": "4gb", + "storage": "32gb" + }, + "customizations": { + "vscode": { + "extensions": [ + "bmewburn.vscode-intelephense-client", + "xdebug.php-debug", + "symfony-vscode.symfony-vscode", + "bradlc.vscode-tailwindcss", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "mikestead.dotenv", + "ms-azuretools.vscode-docker", + "GitHub.vscode-pull-request-github" + ], + "settings": { + "php.suggest.basic": false, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[php]": { + "editor.defaultFormatter": "bmewburn.vscode-intelephense-client" + }, + "intelephense.environment.phpVersion": "8.3.0", + "intelephense.files.maxSize": 5000000 + } + } + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..277d997c8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +* text=auto +*.sh text eol=lf +bin/console text eol=lf +.env text eol=lf diff --git a/Dockerfile b/Dockerfile index 6002873c6..6085d0285 100644 --- a/Dockerfile +++ b/Dockerfile @@ -184,7 +184,17 @@ FROM bewelcome_php AS bewelcome_php_dev # build for production ARG NODE_ENV=production +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + RUN set -eux; \ apk add --no-cache \ make \ - mysql-client + mysql-client \ + libstdc++ \ + gcompat \ + bash \ + nodejs \ + procps; \ + install-php-extensions xdebug + +COPY docker/php/conf.d/bewelcome.xdebug.ini $PHP_INI_DIR/conf.d/xdebug.ini diff --git a/behat.yml.dist b/behat.yml.dist index 5d70c2369..2590f9c02 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -2,7 +2,6 @@ default: suites: default: contexts: - - 'Alex\MailCatcher\Behat\MailCatcherContext': ~ - 'App\Tests\Behat\DatabaseContext': ~ - 'App\Tests\Behat\RefreshTokenContext': ~ - 'App\Tests\Behat\SecurityContext': ~ @@ -19,11 +18,4 @@ default: symfony: ~ 'Behatch\Extension': ~ 'FriendsOfBehat\SymfonyExtension': ~ - 'Alex\MailCatcher\Behat\MailCatcherExtension\Extension': - url: http://mailcatcher - purge_before_scenario: true -localhost: - extensions: - 'Alex\MailCatcher\Behat\MailCatcherExtension\Extension': - url: http://localhost:1080 diff --git a/composer.json b/composer.json index 57a319c18..3c90d96f2 100644 --- a/composer.json +++ b/composer.json @@ -92,7 +92,6 @@ "symfony/symfony": "*" }, "require-dev": { - "alexandresalome/mailcatcher": "^1.3", "behat/behat": "^3.7", "behatch/contexts": "^3.3", "dama/doctrine-test-bundle": "^6.3", diff --git a/composer.lock b/composer.lock index 6f483e659..78a21f63e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "80a687a6d3730b9e6a6074d478692314", + "content-hash": "9293e38faec90489d1977fb4f2f59d8f", "packages": [ { "name": "amphp/amp", @@ -11519,109 +11519,6 @@ ], "time": "2026-05-24T10:45:48+00:00" }, - { - "name": "symfony/serializer", - "version": "v5.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "460c5df9fb6c39d10d5b7f386e4feae4b6370221" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/460c5df9fb6c39d10d5b7f386e4feae4b6370221", - "reference": "460c5df9fb6c39d10d5b7f386e4feae4b6370221", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<4.4", - "symfony/property-access": "<5.4", - "symfony/property-info": "<5.4.24|>=6,<6.2.11", - "symfony/uid": "<5.3", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^5.4.26|^6.3", - "symfony/property-info": "^5.4.24|^6.2.11", - "symfony/uid": "^5.3|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0", - "symfony/var-exporter": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/cache-implementation": "For using the metadata cache.", - "symfony/config": "For using the XML mapping loader.", - "symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.", - "symfony/property-access": "For using the ObjectNormalizer.", - "symfony/property-info": "To deserialize relations.", - "symfony/var-exporter": "For using the metadata compiler.", - "symfony/yaml": "For using the default YAML mapping loader." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Serializer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/serializer/tree/v5.4.45" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:11:13+00:00" - }, { "name": "symfony/service-contracts", "version": "v2.5.4", @@ -13574,48 +13471,6 @@ } ], "packages-dev": [ - { - "name": "alexandresalome/mailcatcher", - "version": "v1.4.1", - "target-dir": "Alex/MailCatcher", - "source": { - "type": "git", - "url": "https://github.com/alexandresalome/mailcatcher.git", - "reference": "90ee943e91021a065f8ff9ed8e3f7b7d8ae7256d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/alexandresalome/mailcatcher/zipball/90ee943e91021a065f8ff9ed8e3f7b7d8ae7256d", - "reference": "90ee943e91021a065f8ff9ed8e3f7b7d8ae7256d", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=5.3.3", - "symfony/dom-crawler": "~2.3 || ~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0" - }, - "require-dev": { - "behat/behat": "~3.0", - "phpunit/phpunit": "~4.6", - "swiftmailer/swiftmailer": "~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Alex\\MailCatcher": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A library to access MailCatcher", - "support": { - "issues": "https://github.com/alexandresalome/mailcatcher/issues", - "source": "https://github.com/alexandresalome/mailcatcher/tree/v1.4.1" - }, - "time": "2024-03-29T15:42:38+00:00" - }, { "name": "behat/behat", "version": "v3.32.0", @@ -17570,7 +17425,7 @@ ], "aliases": [], "minimum-stability": "RC", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -17588,9 +17443,9 @@ "ext-xsl": "*", "ext-zip": "*" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { - "php": "8.2" + "php": "8.3" }, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 71195d3b2..83624c979 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -20,7 +20,7 @@ framework: #serializer: { enable_annotations: true } default_locale: "en" trusted_hosts: ~ - trusted_proxies: '%env(SYMFONY_TRUSTED_PROXIES)%' + trusted_proxies: '%env(string:default::SYMFONY_TRUSTED_PROXIES)%' trusted_headers: - x-forwarded-for - x-forwarded-host diff --git a/config/services.yaml b/config/services.yaml index acddb15f7..ab6463e53 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -40,6 +40,9 @@ services: # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name + App\Form\DataTransformer\: + resource: '../src/Form/DataTransformer/*' + App\: resource: '../src/*' exclude: '../src/{DependencyInjection,Entity,Migrations,Model/MemberDataExtractor/DoctrineExtractor.php,Twig,Tests,Kernel.php}' diff --git a/docker-compose.override.yml.dist b/docker-compose.override.yml.dist index 20411d1fd..839d4d8ba 100644 --- a/docker-compose.override.yml.dist +++ b/docker-compose.override.yml.dist @@ -2,6 +2,8 @@ # Not used by the beta or production stacks. services: php: + build: + target: bewelcome_php_dev volumes: - .:/srv/bewelcome:rw,cached - ./docker/php/conf.d/bewelcome.dev.ini:/usr/local/etc/php/conf.d/bewelcome.ini @@ -9,5 +11,11 @@ services: # - ./var:/srv/bewelcome/var:rw web: + ports: + - "80:80" volumes: - ./public:/srv/bewelcome/public:ro + + mailer: + ports: + - "1080:80" diff --git a/docker-compose.yml b/docker-compose.yml index 8f8f29efb..6b00322b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,10 @@ services: - ./data:/var/lib/manticore mailer: - image: tophfr/mailcatcher + image: axllent/mailpit + environment: + MP_SMTP_BIND_ADDR: "0.0.0.0:25" + MP_UI_BIND_ADDR: "0.0.0.0:80" volumes: db-data: {} diff --git a/docker/php/conf.d/bewelcome.xdebug.ini b/docker/php/conf.d/bewelcome.xdebug.ini new file mode 100644 index 000000000..20ce85ae0 --- /dev/null +++ b/docker/php/conf.d/bewelcome.xdebug.ini @@ -0,0 +1,6 @@ +; Xdebug configuration for development +; Disabled by default for performance. To enable, you can set XDEBUG_MODE=debug +xdebug.mode=off +xdebug.client_host=host.docker.internal +xdebug.start_with_request=yes +xdebug.discover_client_host=1 diff --git a/docker/php/docker-entrypoint.sh b/docker/php/docker-entrypoint.sh index 206d8fa49..542aca27d 100755 --- a/docker/php/docker-entrypoint.sh +++ b/docker/php/docker-entrypoint.sh @@ -74,16 +74,39 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then done if [ "$APP_ENV" != 'prod' ]; then - bin/console test:database:create --drop --force --no-interaction - - if [ -f docker/db/languages.sql ]; then - mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/languages.sql - fi - if [ -f docker/db/words.sql ]; then - mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/words.sql - fi - if [ -f docker/db/geonamesadminunits.sql ]; then - mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/geonamesadminunits.sql + if ! bin/console test:database:create --drop --force --no-interaction; then + echo "=========================================================" >&2 + echo " ERROR: Database creation failed!" >&2 + echo " Skipping SQL imports to avoid swallowing real errors." >&2 + echo " Please fix the schema and run bin/console test:database:create manually." >&2 + echo "=========================================================" >&2 + else + # Download seed files if not already present (e.g. on first DevContainer boot) + if [ ! -f docker/db/languages.sql ] && [ ! -f docker/db/languages.sql.bz2 ]; then + echo "Downloading languages.sql seed file..." + curl -sL http://downloads.bewelcome.org/for_developers/rox_test_db/languages.sql.bz2 -o docker/db/languages.sql.bz2 \ + && bunzip2 docker/db/languages.sql.bz2 \ + || echo "WARNING: Failed to download languages.sql.bz2" >&2 + elif [ -f docker/db/languages.sql.bz2 ] && [ ! -f docker/db/languages.sql ]; then + bunzip2 docker/db/languages.sql.bz2 || echo "WARNING: Failed to decompress languages.sql.bz2" >&2 + fi + if [ ! -f docker/db/words.sql ] && [ ! -f docker/db/words.sql.bz2 ]; then + echo "Downloading words.sql seed file..." + curl -sL http://downloads.bewelcome.org/for_developers/rox_test_db/words.sql.bz2 -o docker/db/words.sql.bz2 \ + && bunzip2 docker/db/words.sql.bz2 \ + || echo "WARNING: Failed to download words.sql.bz2" >&2 + elif [ -f docker/db/words.sql.bz2 ] && [ ! -f docker/db/words.sql ]; then + bunzip2 docker/db/words.sql.bz2 || echo "WARNING: Failed to decompress words.sql.bz2" >&2 + fi + if [ -f docker/db/languages.sql ]; then + mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/languages.sql || echo "ERROR: Failed to import languages.sql" >&2 + fi + if [ -f docker/db/words.sql ]; then + mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/words.sql || echo "ERROR: Failed to import words.sql" >&2 + fi + if [ -f docker/db/geonamesadminunits.sql ]; then + mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/geonamesadminunits.sql || echo "ERROR: Failed to import geonamesadminunits.sql" >&2 + fi fi fi