feat: Add DevContainer configuration for local development - #484
feat: Add DevContainer configuration for local development#484Ramjivan wants to merge 13 commits into
Conversation
🧪 How to Test This PRIf you want to help test this DevContainer setup, here is how you can do it quickly: Prerequisites:
Steps to test:
What to look out for:
|
- Add .devcontainer configuration for VS Code and Codespaces - Add xdebug to bewelcome_php_dev stage for local debugging
Neophytis
left a comment
There was a problem hiding this comment.
Good work on this — devcontainer support has been wanted for a while (closes #69) and the overall setup is clean. Two things before merging:
1. Target branch / base stack
This PR targets develop which uses FrankenPHP. Our actual beta and production Docker deployment runs on feature/docker-master (php-fpm + nginx). A devcontainer built against develop means contributors work against a runtime that doesn't match what gets deployed — which is the main thing a devcontainer should prevent.
I'd suggest rebasing against feature/docker-master and pointing dockerComposeFile at that branch's docker-compose.yml. The Codespaces support carries over unchanged and contributors get an environment that actually mirrors production.
2. Xdebug in the Dockerfile
Baking xdebug into the Dockerfile (disabled by default via xdebug.mode=off) is the right approach. It avoids recompiling the extension on every fresh container and keeps it opt-in with XDEBUG_MODE=debug. The postCreateCommand alternative would hit a compile penalty on every rebuild. Current approach is correct.
6b98962 to
1bbdcbe
Compare
|
Thanks Peter! Makes total sense. I've updated this PR:
This is now ready for review against the production-mirroring stack! |
|
I also just added a small |
|
@Ramjivan Seems the checks fail as they now use yarn again but develop moved to bun. Regarding FrankenPHP or nginx/PHP-FPM: FrankenPHP is likely faster and therefore the better choice in the future. Any way to set this up that it can be tested in parallel? Mailcatcher (even sj26/mailcatcher) doesn't get updates anymore (last push 2 years ago). MailPit had the last update 8 days ago and has an API which could be used for testing. |
|
@thisismeonmounteverest @Neophytis Yeah, the CI failures are just coming from the base To let us test both setups, I opened #486 pointing at Totally agree on swapping to MailPit. Want me to add that change into these PRs, or keep it in a separate PR? |
|
@Ramjivan I think we can switch to mailpit here. As it is not used for anything else than development. |
|
@Ramjivan I tried with Antigravity and building the container failed with a message that xdebug could not be setup/build. I also tried in PHPStorm (the IDE I normally use) and there it failed silently. So I asked the AI assistant there: "## Short answer |
|
I have pushed fixes for the remaining issues in this PR:
The Behat tests run cleanly against Mailpit! (Note: the local DB schema bug with |
|
Unfortunately, still no luck on Windows in WSL (and Antigravity). Can't even say what the error is now, but saw that the container gets build as production which doesn't make sense for a dev setup, does it? |
|
Good progress Ram — the mlocati 1. devcontainer is building the production stage, not the dev stage (critical) This is the root cause of Shevek's "container builds as production" observation. The fix is one line in services:
php:
build:
target: bewelcome_php_dev
2. UTF-8 BOM in The file starts with a BOM character ( 3. Duplicate The variable is defined twice — On FrankenPHP To be explicit: FrankenPHP is not the direction for Please test locally before requesting re-review Once the build target is fixed, please verify:
For WSL: I can test that side as well. |
|
@Neophytis I've addressed the feedback from your latest review and spent some time heavily debugging the DevContainer environment to ensure it's actually usable. Code Review Fixes:
1. Fixing the Container Crash-Loop (DB Bug Tolerance)
2. Alpine Linux vs. IDE Remote Servers
3. Antigravity IDE Compatibility & Workaround We reverted the hacks to keep the
Ready for the next round of peer reviews! |
|
As a separate discussion point for the team: The
Since we explicitly added native Alpine Are there any other plugins the team strongly recommends pre-installing for this repository to ensure a fully complete dev experience? Let me know your thoughts! |
|
@Neophytis I wanted to propose an alternative approach for our DevContainer setup that might elegantly solve the tension between "DevContainers must mirror production exactly" and "Developers need standard tooling for their IDEs to work." Right now, we are forcing developer tools into our An Alternative Approach: The Workspace ContainerInstead of injecting the IDE directly into our Alpine How it works:
The Result: This gives us the best of both worlds: a pristine, production-matching Alpine runtime, and a bulletproof, standard Debian developer experience. Real-World Context & DocumentationIn the wider PHP ecosystem, local development tools like DDEV and Lando solve this exact IDE compatibility problem by simply abandoning Alpine and forcing everything to run on heavy Debian/Ubuntu containers.
By using the multi-container approach instead of copying DDEV/Lando, we can provide that same robust Debian developer experience without sacrificing our pure Alpine production containers. This multi-container setup is officially documented and supported by the DevContainer specification for scenarios exactly like ours:
What do you think about exploring this for the DevContainer? |
Neophytis
left a comment
There was a problem hiding this comment.
Review summary
Tested the devcontainer stack locally — images build cleanly and PHP-FPM boots successfully. The core setup is solid, but there are several issues that prevent it from actually working out of the box for a developer. See inline comments for details.
Must-fix before merge:
- Port mappings missing — app and MailPit not reachable in browser
- MailPit port mismatch (configured on 80, devcontainer expects 1080)
- No step to download/decompress the SQL seed files (languages + words)
|| trueon SQL imports silently swallows real database errors
Should fix:
5. intelephense.environment.phpVersion set to 8.4.0 but Dockerfile uses PHP 8.3
6. consensus/behat-mailpit-extension: dev-main — floating version + GPL-3.0 licence conflict with the rest of the project (MIT/Apache)
7. App\Form\DataTransformer\ service registration is redundant and out of scope
Open question: The PR description mentions targeting develop — but this PR targets master. Which is correct?
| image: axllent/mailpit | ||
| environment: | ||
| MP_SMTP_BIND_ADDR: "0.0.0.0:25" | ||
| MP_UI_BIND_ADDR: "0.0.0.0:80" |
There was a problem hiding this comment.
MailPit's web UI is bound to port 80 here, but devcontainer.json lists port 1080 as "MailPit Web UI". These disagree — a developer hitting localhost:1080 will get nothing.
Either change this to MP_UI_BIND_ADDR: "0.0.0.0:1080", or keep port 80 internally and map host 1080 → container 80 in docker-compose.override.yml.dist (see that file's comment).
| services: | ||
| php: | ||
| build: | ||
| target: bewelcome_php_dev |
There was a problem hiding this comment.
Neither web (nginx) nor mailer (MailPit) exposes ports to the host. VS Code's forwardPorts forwards from inside the primary service (php), which doesn't listen on 80 or 1080, so both services are unreachable in the browser locally. Codespaces auto-scans all containers and handles this more gracefully, but local VS Code DevContainers will not forward these ports without explicit mappings.
Please add:
web:
ports:
- "80:80"
mailer:
ports:
- "1080:80"| "service": "php", | ||
| "workspaceFolder": "/srv/bewelcome", | ||
| "overrideCommand": false, | ||
| "forwardPorts": [80, 1080, 9306, 9308], |
There was a problem hiding this comment.
The seed SQL files (languages.sql, words.sql) are not in the repo — they are downloaded from downloads.bewelcome.org and decompressed by make install. A fresh devcontainer skips this entirely, so the imports are silently skipped (if [ -f ... ] guard passes quietly) and the language/words tables end up empty, breaking locale switching and member language preferences.
A postCreateCommand is needed, for example:
"postCreateCommand": "curl https://downloads.bewelcome.org/for_developers/rox_test_db/languages.sql.bz2 -o docker/db/languages.sql.bz2 && curl https://downloads.bewelcome.org/for_developers/rox_test_db/words.sql.bz2 -o docker/db/words.sql.bz2 && bunzip2 --force docker/db/languages.sql.bz2 docker/db/words.sql.bz2"| "[php]": { | ||
| "editor.defaultFormatter": "bmewburn.vscode-intelephense-client" | ||
| }, | ||
| "intelephense.environment.phpVersion": "8.4.0", |
There was a problem hiding this comment.
The Dockerfile uses FROM php:8.3.33-fpm-alpine3.24. Setting Intelephense to 8.4.0 means PHP 8.4-only syntax won't be flagged as errors in the editor, hiding real incompatibilities. Change to "8.3.0".
|
|
||
| if [ -f docker/db/languages.sql ]; then | ||
| mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/languages.sql | ||
| mysql $database_name -u $database_user -p$database_password -h $database_host < docker/db/languages.sql || true |
There was a problem hiding this comment.
The if [ -f ... ] guard already handles the missing-file case. Adding || true on the mysql imports means actual MariaDB errors (wrong schema, bad SQL, connection issues) are silently swallowed — a developer sees a clean boot but ends up with an empty or corrupt database with no indication of why.
At minimum, log before suppressing:
mysql ... < docker/db/languages.sql || echo 'WARNING: failed to import languages.sql'Same applies to the words.sql and geonamesadminunits.sql lines.
| "alexandresalome/mailcatcher": "^1.3", | ||
| "behat/behat": "^3.7", | ||
| "behatch/contexts": "^3.3", | ||
| "consensus/behat-mailpit-extension": "dev-main", |
There was a problem hiding this comment.
Two concerns:
-
Floating version:
dev-mainmeanscomposer updatewill pull whatever is on the extension'smainbranch at that moment. The lockfile pins commit89d3f16etoday, but this is fragile long-term. Pin to a stable tag or commit reference. -
Licence conflict:
consensus/behat-mailpit-extensionis GPL-3.0-or-later (copyleft), which conflicts with the rest of the project (MIT/Apache). Please check the licensing implications before merging — consider using a version-pinned fork or a differently-licenced alternative.
|
|
||
| # 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\: |
There was a problem hiding this comment.
App\Form\DataTransformer\ is already covered by the App\: catch-all below — DataTransformer is not in the exclude list, so this explicit registration is a no-op. It is also unrelated to the devcontainer work. If it fixes a real bug, it deserves its own PR with an explanation.
Move curl/bunzip2 download of languages.sql and words.sql into docker-entrypoint.sh so the files are fetched before the import guards run on first boot. The previous postCreateCommand approach downloaded the files after container startup had already completed, meaning the imports were always silently skipped on first DevContainer creation.
|
Great progress Ram — the port mappings, SQL error handling, and Intelephense version are all fixed. I tested locally: both the app (port 80) and MailPit (port 1080) are now reachable in the browser. Really close now, just a few small things left to tidy up: 1. Seed file download: change The download still fails on first boot, but it's not your code — # Change this in both curl calls:
curl -sL https://downloads.bewelcome.org/...
# to:
curl -sL http://downloads.bewelcome.org/...2. It looks like you removed The good news: the 3 existing feature tests ( # Remove these:
- 'Consensus\Behat\MailpitExtension\Context\MailpitContext': ~
# and:
'Consensus\Behat\MailpitExtension':
base_url: http://mailer:80
# (and the localhost equivalent)When someone writes a behat test that actually checks email, they can add back a properly-licensed extension at that point. 3. Two out-of-scope changes to clean up
Overall the devcontainer setup is solid — once these are addressed this should be good to merge! |
|
Hi @Neophytis, thanks for testing and for the quick feedback! All points are now addressed on
Ready for review! |
|
Pulled and retested — really close, one new blocker crept in and two minor cleanups left. Blocker: removing
This isn't your fault — you responded correctly to the review feedback, but the # config/packages/framework.yaml
trusted_proxies: '%env(default::SYMFONY_TRUSTED_PROXIES)%'With Minor: remove
Minor: This is an Xdebug 2 VS Code key — Xdebug 3 ignores it entirely. The One point from another review I want to flag as incorrect: Once those three things are done this is good to go — great work getting it this far! |
|
Almost there Ram! Three small things left: 1. One-word fix in The app fails to boot without trusted_proxies: '%env(SYMFONY_TRUSTED_PROXIES)%'to: trusted_proxies: '%env(default::SYMFONY_TRUSTED_PROXIES)%'Same for 2. Remove
3. Remove Dead Xdebug 2 setting, ignored by Xdebug 3. The That's it — nothing else needed! |
|
Hi @Neophytis, All 3 points have been addressed and pushed to
Everything is in place and verified locally! |
| "editor.defaultFormatter": "esbenp.prettier-vscode", | ||
| "[php]": { | ||
| "editor.defaultFormatter": "bmewburn.vscode-intelephense-client" | ||
| }, |
There was a problem hiding this comment.
Minor: phpVersion should be "8.2.0" to match the actual PHP version in the Docker image (the Dockerfile builds on PHP 8.2). Setting it to 8.3.0 will make Intelephense flag valid 8.2 code as errors.
| "service": "php", | ||
| "workspaceFolder": "/srv/bewelcome", | ||
| "overrideCommand": false, | ||
| "forwardPorts": [80, 1080, 9306, 9308], |
There was a problem hiding this comment.
macOS + Rancher Desktop note: forwardPorts causes port conflicts on macOS with Rancher Desktop (and likely other non-Docker-Desktop runtimes). Rancher exposes container ports via SSH tunnelling from its Lima VM, and VS Code's forwardPorts creates a competing localhost:PORT listener on top of that. Because the localhost-specific binding wins over the SSH tunnel's wildcard bind, connections get routed into the php container's localhost instead of the correct mailer/web container — and hang.
Workaround tested on macOS + Rancher Desktop: remove forwardPorts entirely. Ports with host mappings in the compose files are auto-detected by VS Code's Ports panel anyway; portsAttributes labels still apply. The forwardPorts array is only needed for ports that are not host-mapped in the compose file.
Works fine on WSL/Docker Desktop as-is — this is a macOS-specific Rancher Desktop behaviour. Worth adding a note to the README.
��This PR introduces a
devcontainersetup for BeWelcome Rox to standardise and simplify the developer onboarding experience, solving Issue #69.?? What this does:
docker-compose.ymlanddocker-compose.override.yml.dist. No new Docker networks or redundant configurations are introduced.docker/db/word.sqltodocker/db/words.sqlindocker-entrypoint.sh.?? Discussion Points for the Team:
I would love to get your thoughts on a couple of implementation details so we can ensure this is the right approach for everyone:
xdebugto thebewelcome_php_devstage in theDockerfiledirectly. This ensures the Docker layer caches the compilation and it is immediately available (set tooffby default for performance). Question: Are we comfortable adding this to the Dockerfile, or would you prefer it to be installed dynamically (e.g. via apostCreateCommandscript) to keep the Dockerfile completely untouched?devcontainer.jsonconfiguration also natively supports GitHub Codespaces! Since the repo is public, contributors can use it for free. Perhaps we should document this as an alternative inINSTALL.mdin the future?master. Does this align with our usual branching strategy for infrastructure enhancements?Closes #69