wexample-wex-addon-dev-php 12.3.1__tar.gz → 12.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/PKG-INFO +6 -6
  2. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/README.md +3 -3
  3. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/pyproject.toml +3 -3
  4. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/file → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/deployment}/__init__.py +0 -0
  5. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/deployment/step/symfony_env_local_deployment_step.py +288 -0
  6. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/composer → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/formatter}/__init__.py +0 -0
  7. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/php_addon_manager.py +7 -0
  8. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/resources/docker/Dockerfile.roave +31 -0
  9. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/composer/commands → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services}/__init__.py +0 -0
  10. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/composer/commands/service → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/composer}/__init__.py +0 -0
  11. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/laravel → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/composer/commands/service}/__init__.py +0 -0
  12. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/laravel/commands/db → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/laravel}/__init__.py +0 -0
  13. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/phpmyadmin → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/php/ai}/__init__.py +0 -0
  14. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/php/ai/shared}/__init__.py +0 -0
  15. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands}/__init__.py +0 -0
  16. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/ai → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands/runtime}/__init__.py +0 -0
  17. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/wordpress → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/commands/translations}/__init__.py +0 -0
  18. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/docker/docker-compose.yml +24 -0
  19. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/workdir → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/knowledge}/__init__.py +0 -0
  20. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/samples/env/dev/docker/docker-compose.yml +8 -0
  21. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/samples/env/local/docker/docker-compose.yml +11 -0
  22. wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/samples/env/prod/docker/docker-compose.yml +8 -0
  23. {wexample_wex_addon_dev_php-12.3.1/tests/workdir → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/wordpress}/__init__.py +0 -0
  24. wexample_wex_addon_dev_php-12.3.1/tests/.gitkeep → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/workdir/__init__.py +0 -0
  25. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/workdir/php_package_workdir.py +75 -0
  26. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/workdir/php_workdir.py +77 -10
  27. wexample_wex_addon_dev_php-12.5.0/tests/.gitkeep +0 -0
  28. wexample_wex_addon_dev_php-12.5.0/tests/workdir/__init__.py +0 -0
  29. wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/resources/docker/Dockerfile.roave +0 -22
  30. wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/docker/docker-compose.yml +0 -6
  31. wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/samples/env/dev/docker/docker-compose.yml +0 -4
  32. wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/samples/env/local/docker/docker-compose.yml +0 -8
  33. wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/samples/env/prod/docker/docker-compose.yml +0 -4
  34. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/__init__.py +0 -0
  35. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/__pycache__/__init__.py +0 -0
  36. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/config_value/__init__.py +0 -0
  37. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/config_value/php_package_readme_config_value.py +0 -0
  38. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/const/__init__.py +0 -0
  39. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/const/tags.py +0 -0
  40. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/formatter → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/deployment/step}/__init__.py +0 -0
  41. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/file}/__init__.py +0 -0
  42. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/file/php_composer_json_file.py +0 -0
  43. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/formatter/php_code_formatter.py +0 -0
  44. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/py.typed +0 -0
  45. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/resources/.wex.yml +0 -0
  46. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/resources/design_rules/php_code.md +0 -0
  47. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/resources/writing_rules/php_code.md +0 -0
  48. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/frankenphp → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/composer/commands}/__init__.py +0 -0
  49. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/composer/commands/service/install_local.py +0 -0
  50. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/composer/commands/service/refresh_lock.py +0 -0
  51. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/composer/docker/.wex.yml +0 -0
  52. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/composer/docker/docker-compose.yml +0 -0
  53. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/composer/service.yml +0 -0
  54. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/laravel/commands → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/frankenphp}/__init__.py +0 -0
  55. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/docker/.wex.yml +0 -0
  56. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/docker/docker-compose.yml +0 -0
  57. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/.wex.yml +0 -0
  58. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/caddy/.wex.yml +0 -0
  59. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/caddy/Caddyfile +0 -0
  60. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/docker/.wex.yml +0 -0
  61. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/docker/docker-compose.yml +0 -0
  62. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/dev/docker/.wex.yml +0 -0
  63. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/dev/docker/docker-compose.yml +0 -0
  64. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/local/docker/.wex.yml +0 -0
  65. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/local/docker/docker-compose.yml +0 -0
  66. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/prod/docker/.wex.yml +0 -0
  67. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/env/prod/docker/docker-compose.yml +0 -0
  68. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/php/.wex.yml +0 -0
  69. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/samples/php/web.ini +0 -0
  70. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/frankenphp/service.yml +0 -0
  71. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/app_service.py +0 -0
  72. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/laravel/commands}/__init__.py +0 -0
  73. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/laravel/commands/service → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/laravel/commands/db}/__init__.py +0 -0
  74. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/db/migrate.yml +0 -0
  75. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/php → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance}/__init__.py +0 -0
  76. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/disable.yml +0 -0
  77. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/enable.yml +0 -0
  78. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/php/ai → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/laravel/commands/service}/__init__.py +0 -0
  79. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/service/install_local.py +0 -0
  80. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/commands/service/refresh_lock.py +0 -0
  81. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/docker/.wex.yml +0 -0
  82. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/docker/docker-compose.yml +0 -0
  83. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/.wex.yml +0 -0
  84. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/docker/docker-compose.yml +0 -0
  85. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/env/dev/docker/docker-compose.yml +0 -0
  86. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/samples/env/prod/docker/docker-compose.yml +0 -0
  87. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/laravel/service.yml +0 -0
  88. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/php/ai/shared → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/php}/__init__.py +0 -0
  89. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/ai/shared/context.j2 +0 -0
  90. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/ai/shared/php.j2 +0 -0
  91. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/docker/.wex.yml +0 -0
  92. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/docker/docker-compose.yml +0 -0
  93. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/.wex.yml +0 -0
  94. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/caddy/.wex.yml +0 -0
  95. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/caddy/Caddyfile +0 -0
  96. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/docker/.wex.yml +0 -0
  97. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/docker/docker-compose.yml +0 -0
  98. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/dev/docker/.wex.yml +0 -0
  99. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/dev/docker/docker-compose.yml +0 -0
  100. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/local/docker/.wex.yml +0 -0
  101. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/local/docker/docker-compose.yml +0 -0
  102. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/prod/docker/.wex.yml +0 -0
  103. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/env/prod/docker/docker-compose.yml +0 -0
  104. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/php/.wex.yml +0 -0
  105. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/samples/php/web.ini +0 -0
  106. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/php/service.yml +0 -0
  107. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/phpmyadmin/commands/runtime → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/phpmyadmin}/__init__.py +0 -0
  108. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/docker/.wex.yml +0 -0
  109. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/docker/docker-compose.yml +0 -0
  110. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/env/.wex.yml +0 -0
  111. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/env/local/docker/docker-compose.yml +0 -0
  112. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/phpmyadmin/service.yml +0 -0
  113. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/ai/agents → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony}/__init__.py +0 -0
  114. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/ai}/__init__.py +0 -0
  115. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/ai/shared → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/ai/agents}/__init__.py +0 -0
  116. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/commands → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor}/__init__.py +0 -0
  117. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor/agent.yml +0 -0
  118. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/agents/editor/context.j2 +0 -0
  119. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/commands/service → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/ai/shared}/__init__.py +0 -0
  120. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/ai/shared/context.j2 +0 -0
  121. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/app_service.py +0 -0
  122. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/commands/translations → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/commands}/__init__.py +0 -0
  123. {wexample_wex_addon_dev_php-12.3.1/src/wexample_wex_addon_dev_php/services/symfony/knowledge → wexample_wex_addon_dev_php-12.5.0/src/wexample_wex_addon_dev_php/services/symfony/commands/service}/__init__.py +0 -0
  124. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/deploy.py +0 -0
  125. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/install_local.py +0 -0
  126. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/service/watch.py +0 -0
  127. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/commands/translations/translate.py +0 -0
  128. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/docker/.wex.yml +0 -0
  129. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/knowledge/entity.md +0 -0
  130. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/.wex.yml +0 -0
  131. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/.wex.yml +0 -0
  132. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/docker-compose.yml +0 -0
  133. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/env.build +0 -0
  134. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/.wex.yml +0 -0
  135. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/Dockerfile.dev +0 -0
  136. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/docker/images/Dockerfile.release +0 -0
  137. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/dev/docker/.wex.yml +0 -0
  138. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/local/docker/.wex.yml +0 -0
  139. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/samples/env/prod/docker/.wex.yml +0 -0
  140. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/symfony/service.yml +0 -0
  141. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/__init__.py +0 -0
  142. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/service/__init__.py +0 -0
  143. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/url/__init__.py +0 -0
  144. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/commands/url/replace.py +0 -0
  145. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/docker/.wex.yml +0 -0
  146. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/docker/docker-compose.yml +0 -0
  147. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/.wex.yml +0 -0
  148. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/docker/docker-compose-cli.yml +0 -0
  149. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/docker/docker-compose.yml +0 -0
  150. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/dev/docker/docker-compose.yml +0 -0
  151. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/local/wp-cli/.gitkeep +0 -0
  152. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/samples/env/prod/docker/docker-compose.yml +0 -0
  153. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/services/wordpress/service.yml +0 -0
  154. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/workdir/php_packages_suite_workdir.py +0 -0
  155. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/src/wexample_wex_addon_dev_php/workdir/php_symfony_workdir.py +0 -0
  156. {wexample_wex_addon_dev_php-12.3.1 → wexample_wex_addon_dev_php-12.5.0}/tests/workdir/test_php_package_workdir.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-wex-addon-dev-php
3
- Version: 12.3.1
3
+ Version: 12.5.0
4
4
  Summary: Extends wex with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers
5
5
  Author-Email: weeger <contact@wexample.com>
6
6
  License: MIT
@@ -13,8 +13,8 @@ Requires-Dist: attrs>=23.1.0
13
13
  Requires-Dist: cattrs>=23.1.0
14
14
  Requires-Dist: wexample-filestate-php>=6.4.0
15
15
  Requires-Dist: wexample-filestate-symfony>=2.0.0
16
- Requires-Dist: wexample-wex-addon-ai>=16.1.0
17
- Requires-Dist: wexample-wex-addon-app>=32.1.0
16
+ Requires-Dist: wexample-wex-addon-ai>=17.0.0
17
+ Requires-Dist: wexample-wex-addon-app>=32.3.0
18
18
  Requires-Dist: wexample-wex-addon-dev-javascript>=8.2.0
19
19
  Provides-Extra: dev
20
20
  Requires-Dist: pytest; extra == "dev"
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  # wex_addon_dev_php
25
25
 
26
- Version: 12.3.1
26
+ Version: 12.5.0
27
27
 
28
28
  `wex_addon_dev_php` extends the wex CLI with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers. It is aimed at PHP developers who manage their apps through wex and need framework-aware automation — dependency wiring, migration runs, lock-file refreshes — executed directly inside the running service containers. The addon registers four workdir types (`php`, `php-package`, `php-packages-suite`, `php-symfony`) and contributes its commands through the standard `PhpAddonManager` plugin point.
29
29
 
@@ -275,8 +275,8 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
275
275
  - cattrs: >=23.1.0
276
276
  - wexample-filestate-php: >=6.4.0
277
277
  - wexample-filestate-symfony: >=2.0.0
278
- - wexample-wex-addon-ai: >=16.1.0
279
- - wexample-wex-addon-app: >=32.1.0
278
+ - wexample-wex-addon-ai: >=17.0.0
279
+ - wexample-wex-addon-app: >=32.3.0
280
280
  - wexample-wex-addon-dev-javascript: >=8.2.0
281
281
 
282
282
  ## Versioning & Compatibility Policy
@@ -1,6 +1,6 @@
1
1
  # wex_addon_dev_php
2
2
 
3
- Version: 12.3.1
3
+ Version: 12.5.0
4
4
 
5
5
  `wex_addon_dev_php` extends the wex CLI with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers. It is aimed at PHP developers who manage their apps through wex and need framework-aware automation — dependency wiring, migration runs, lock-file refreshes — executed directly inside the running service containers. The addon registers four workdir types (`php`, `php-package`, `php-packages-suite`, `php-symfony`) and contributes its commands through the standard `PhpAddonManager` plugin point.
6
6
 
@@ -252,8 +252,8 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
252
252
  - cattrs: >=23.1.0
253
253
  - wexample-filestate-php: >=6.4.0
254
254
  - wexample-filestate-symfony: >=2.0.0
255
- - wexample-wex-addon-ai: >=16.1.0
256
- - wexample-wex-addon-app: >=32.1.0
255
+ - wexample-wex-addon-ai: >=17.0.0
256
+ - wexample-wex-addon-app: >=32.3.0
257
257
  - wexample-wex-addon-dev-javascript: >=8.2.0
258
258
 
259
259
  ## Versioning & Compatibility Policy
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-wex-addon-dev-php"
9
- version = "12.3.1"
9
+ version = "12.5.0"
10
10
  description = "Extends wex with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers"
11
11
  authors = [
12
12
  { name = "weeger", email = "contact@wexample.com" },
@@ -22,8 +22,8 @@ dependencies = [
22
22
  "cattrs>=23.1.0",
23
23
  "wexample-filestate-php>=6.4.0",
24
24
  "wexample-filestate-symfony>=2.0.0",
25
- "wexample-wex-addon-ai>=16.1.0",
26
- "wexample-wex-addon-app>=32.1.0",
25
+ "wexample-wex-addon-ai>=17.0.0",
26
+ "wexample-wex-addon-app>=32.3.0",
27
27
  "wexample-wex-addon-dev-javascript>=8.2.0",
28
28
  ]
29
29
 
@@ -0,0 +1,288 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, ClassVar
4
+
5
+ from wexample_wex_addon_app.const.deployment import (
6
+ DEPLOYMENT_STATUS_KO,
7
+ DEPLOYMENT_STATUS_OK,
8
+ DEPLOYMENT_STATUS_UNKNOWN,
9
+ )
10
+ from wexample_wex_addon_app.deployment.abstract_deployment_step import (
11
+ AbstractDeploymentStep,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from wexample_wex_addon_app.helper.remote import ResolvedRemote
16
+ from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
17
+
18
+ # What a Symfony app's runtime always needs from the host, whatever `env.build` says:
19
+ # the build env reaches no service and holds no secret, by its own contract.
20
+ ALWAYS_FROM_THE_HOST: tuple[str, ...] = ("APP_SECRET", "DATABASE_URL")
21
+
22
+ # Config only these envs load: what it reads is never asked of a deployed host.
23
+ NOT_DEPLOYED_ENVS: tuple[str, ...] = ("dev", "test")
24
+
25
+ # A variable named so is a key the app signs with: made on the host, never asked.
26
+ SECRET_SUFFIX = "_SECRET"
27
+
28
+
29
+ def _keys(content: str, names_only: bool = False) -> set[str]:
30
+ keys = set()
31
+ for line in content.splitlines():
32
+ line = line.strip()
33
+ if not line or line.startswith("#"):
34
+ continue
35
+ name = line if names_only else line.partition("=")[0]
36
+ name = name.removeprefix("export ").strip()
37
+ if name and (names_only or "=" in line):
38
+ keys.add(name)
39
+ return keys
40
+
41
+
42
+ def _quote(value: str) -> str:
43
+ """A value as Symfony's Dotenv reads it back literally: single quotes take nothing
44
+ for a variable, and are only left for double ones when the value holds one."""
45
+ if "'" not in value:
46
+ return f"'{value}'"
47
+ escaped = value.replace("\\", "\\\\").replace('"', '\\"').replace("$", "\\$")
48
+ return f'"{escaped}"'
49
+
50
+
51
+ def _without_env_blocks(content: str) -> str:
52
+ """The config with its `when@dev:` and `when@test:` blocks cut out."""
53
+ kept: list[str] = []
54
+ skipping = False
55
+ for line in content.splitlines():
56
+ if line and not line[0].isspace() and not line.startswith("#"):
57
+ skipping = line.rstrip().rstrip(":") in {
58
+ f"when@{env}" for env in NOT_DEPLOYED_ENVS
59
+ }
60
+ if not skipping:
61
+ kept.append(line)
62
+ return "\n".join(kept)
63
+
64
+
65
+ class SymfonyEnvLocalDeploymentStep(AbstractDeploymentStep):
66
+ """The host's checkout has a `.env.local` giving every variable the app reads that the
67
+ image does not carry — the image's `.env` is `.wex/docker/env.build`, there to let the
68
+ kernel boot during the build and nothing more.
69
+
70
+ The variables are read off the app's own config (`%env(…)%`), so the list follows the
71
+ app; one read with a `default::` fallback, or only by `dev`/`test` config, is not
72
+ required. Only the names are looked at on the host, never the values.
73
+
74
+ Written when there is none, the way one copies a `.env.dist`: the signing secrets
75
+ generated, every other value the local `.env` gives, made the host's — the local
76
+ domain becomes the env's, and a secret the host generated for itself — a database
77
+ password — is named `${KEY}` and filled in on the host from its own env file, so it
78
+ never travels and nobody has to go and read it. Nothing is asked: what is copied as
79
+ it is gets named, for the operator to correct on the host if it must differ.
80
+ """
81
+
82
+ LABEL: ClassVar[str] = "Symfony .env.local"
83
+ NAME: ClassVar[str] = "symfony_env_local"
84
+ PER_ENV: ClassVar[bool] = True
85
+ REQUIRES: ClassVar[tuple[str, ...]] = ("remote_app_dir",)
86
+
87
+ @staticmethod
88
+ def _build_env_keys(workdir: ManagedWorkdir) -> set[str]:
89
+ path = workdir.get_path() / ".wex" / "docker" / "env.build"
90
+ if not path.is_file():
91
+ return set()
92
+ return _keys(path.read_text(encoding="utf-8"))
93
+
94
+ @staticmethod
95
+ def _exists(remote: ResolvedRemote) -> bool:
96
+ import shlex
97
+
98
+ from wexample_wex_addon_app.helper.remote import remote_run
99
+
100
+ path = shlex.quote(f"{remote['path']}/.env.local")
101
+ return remote_run(remote, f"sudo -n test -f {path}").returncode == 0
102
+
103
+ @staticmethod
104
+ def _host_keys(remote: ResolvedRemote) -> set[str] | None:
105
+ """The names the host's `.env.local` defines — values cut off on the host, so
106
+ none ever travels."""
107
+ import shlex
108
+
109
+ from wexample_wex_addon_app.helper.remote import remote_run
110
+
111
+ path = shlex.quote(f"{remote['path']}/.env.local")
112
+ result = remote_run(
113
+ remote,
114
+ f"sudo -n sh -c 'test -f {path} && cut -d= -f1 {path} || true'",
115
+ )
116
+ if result.returncode != 0:
117
+ return None
118
+ return _keys(result.stdout + "\n", names_only=True)
119
+
120
+ @staticmethod
121
+ def _local_values(workdir: ManagedWorkdir) -> dict[str, str]:
122
+ """What the app's local `.env` gives — offered to the operator, never sent as is."""
123
+ path = workdir.get_path() / ".env"
124
+ if not path.is_file():
125
+ return {}
126
+ values = {}
127
+ for line in path.read_text(encoding="utf-8").splitlines():
128
+ line = line.strip()
129
+ if not line or line.startswith("#") or "=" not in line:
130
+ continue
131
+ key, _, value = line.removeprefix("export ").partition("=")
132
+ value = value.strip()
133
+ if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\"":
134
+ value = value[1:-1]
135
+ values[key.strip()] = value
136
+ return values
137
+
138
+ def applies_to(self, workdir: ManagedWorkdir) -> bool:
139
+ from wexample_wex_addon_app.app_addon_manager import AppAddonManager
140
+
141
+ if self.kernel is None:
142
+ return False
143
+ services = AppAddonManager.from_kernel(self.kernel).get_app_services(workdir)
144
+ return any(service.name == "symfony" for service in services)
145
+
146
+ def apply(
147
+ self, workdir: ManagedWorkdir, remote: ResolvedRemote | None, io: Any = None
148
+ ) -> str:
149
+ import secrets
150
+ import shlex
151
+
152
+ from wexample_wex_addon_app.helper.deployment import DEPLOYMENT_HOST_USER
153
+ from wexample_wex_addon_app.helper.remote import remote_run
154
+
155
+ defaults = self._host_defaults(workdir, remote)
156
+ values: dict[str, str] = {}
157
+ generated: list[str] = []
158
+ for key in self._needed(workdir):
159
+ if key.endswith(SECRET_SUFFIX):
160
+ values[key] = secrets.token_hex(32)
161
+ generated.append(key)
162
+ else:
163
+ # Written even empty: Symfony refuses a variable that is not there, and
164
+ # an empty one is how an app says « not used here ».
165
+ values[key] = defaults.get(key, "")
166
+
167
+ path = f"{remote['path']}/.env.local"
168
+ # Through stdin, never a command line: a secret must not show in the host's
169
+ # process list nor in a shell history. The `${KEY}` are resolved there.
170
+ result = remote_run(
171
+ remote,
172
+ f"sudo -n -u {DEPLOYMENT_HOST_USER} python3 -c {shlex.quote(_HOST_WRITER)} "
173
+ f"{shlex.quote(remote['path'] + '/.wex/local/env.yml')} {shlex.quote(path)}",
174
+ input="".join(f"{key}={_quote(value)}\n" for key, value in values.items()),
175
+ )
176
+ if result.returncode != 0:
177
+ raise RuntimeError(
178
+ f"Writing {path} on {remote['host']} failed: {result.stderr.strip()[:300]}"
179
+ )
180
+
181
+ made = f", generated {', '.join(generated)} (kept there, not shown)" if generated else ""
182
+ empty = [key for key, value in values.items() if not value]
183
+ copied = [
184
+ key for key, value in values.items()
185
+ if value and key not in generated and value == self._local_values(workdir).get(key)
186
+ ]
187
+ rest = (f" Copied as they are locally: {', '.join(copied)}." if copied else "") + (
188
+ f" Left empty: {', '.join(empty)}." if empty else ""
189
+ )
190
+ return f"Wrote {path} on {remote['host']}{made}.{rest}"
191
+
192
+ def can_apply(self, workdir: ManagedWorkdir, remote: ResolvedRemote | None) -> bool:
193
+ # Written when there is none, never over one: an existing file is the host's.
194
+ return self._host_keys(remote) == set() and not self._exists(remote)
195
+
196
+ def check(self, workdir: ManagedWorkdir, remote: ResolvedRemote | None) -> tuple[str, str]:
197
+ present = self._host_keys(remote)
198
+ if present is None:
199
+ return DEPLOYMENT_STATUS_UNKNOWN, "could not read it"
200
+ if not present and not self._exists(remote):
201
+ return DEPLOYMENT_STATUS_KO, f"no {remote['path']}/.env.local"
202
+ missing = [key for key in self._needed(workdir) if key not in present]
203
+ if missing:
204
+ return DEPLOYMENT_STATUS_KO, f"no value for {', '.join(missing)}"
205
+ return DEPLOYMENT_STATUS_OK, f"{remote['path']}/.env.local"
206
+
207
+ def hint(self, workdir: ManagedWorkdir, remote: ResolvedRemote | None) -> str:
208
+ present = self._host_keys(remote) or set()
209
+ missing = [key for key in self._needed(workdir) if key not in present]
210
+ return (
211
+ f"Give {remote['path']}/.env.local on {remote['host']} (owned by www-data) the "
212
+ f"host's values for: {', '.join(missing)} — and mount it into the container, "
213
+ "the image only carries the build env."
214
+ )
215
+
216
+ def _host_defaults(
217
+ self, workdir: ManagedWorkdir, remote: ResolvedRemote
218
+ ) -> dict[str, str]:
219
+ """The local `.env` values, with what is this machine's replaced by the host's:
220
+ the domain, and every generated secret by a `${KEY}` the host resolves."""
221
+ import yaml
222
+ from wexample_wex_addon_app.helper.deployment import deployment_declared_vars
223
+
224
+ local_env = workdir.get_path() / ".wex" / "local" / "env.yml"
225
+ local_secrets = (
226
+ yaml.safe_load(local_env.read_text(encoding="utf-8")) or {}
227
+ if local_env.is_file()
228
+ else {}
229
+ )
230
+ replacements: dict[str, str] = {}
231
+ for key, meta in deployment_declared_vars(workdir, self.kernel).items():
232
+ value = local_secrets.get(key)
233
+ if isinstance(meta, dict) and meta.get("generated") and value:
234
+ replacements[str(value)] = "${" + key + "}"
235
+ domain = workdir.get_config().search("domain").get_str_or_none()
236
+ env_domain = (
237
+ workdir.get_config(env_name=remote["env"]).search("domain").get_str_or_none()
238
+ )
239
+ if domain and env_domain:
240
+ replacements[domain] = env_domain
241
+
242
+ defaults = {}
243
+ for key, value in self._local_values(workdir).items():
244
+ for old, new in replacements.items():
245
+ value = value.replace(old, new)
246
+ defaults[key] = value
247
+ return defaults
248
+
249
+ def _needed(self, workdir: ManagedWorkdir) -> list[str]:
250
+ """The variables the app's config reads that the build env does not give, plus
251
+ those it never gives for real."""
252
+ import re
253
+
254
+ config = workdir.get_path() / "config"
255
+ referenced: set[str] = set()
256
+ for path in config.rglob("*.yaml"):
257
+ if any(part in NOT_DEPLOYED_ENVS for part in path.relative_to(config).parts):
258
+ continue
259
+ content = _without_env_blocks(path.read_text(encoding="utf-8"))
260
+ for match in re.finditer(r"%env\(([^)]+)\)%", content):
261
+ processors, _, key = match.group(1).rpartition(":")
262
+ # `default::KEY` falls back on its own: the app runs without it.
263
+ if "default:" not in f"{processors}:":
264
+ referenced.add(key)
265
+ built = self._build_env_keys(workdir)
266
+ needed = {key for key in referenced if key not in built} | set(ALWAYS_FROM_THE_HOST)
267
+ return sorted(needed)
268
+
269
+
270
+ # Run on the host: fills every `${KEY}` of the content on stdin from the host's env
271
+ # file, then writes it for the owner alone. A key the env file lacks stays as it is.
272
+ _HOST_WRITER = """
273
+ import os, re, sys
274
+ values = {}
275
+ with open(sys.argv[1]) as env:
276
+ for line in env:
277
+ match = re.match(r"^([A-Za-z_][A-Za-z0-9_]*):\\s*(.*?)\\s*$", line)
278
+ if match:
279
+ values[match[1]] = match[2].strip("\\"'")
280
+ content = re.sub(
281
+ r"\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}",
282
+ lambda m: values.get(m[1], m[0]),
283
+ sys.stdin.read(),
284
+ )
285
+ os.umask(0o077)
286
+ with open(sys.argv[2], "w") as target:
287
+ target.write(content)
288
+ """
@@ -20,6 +20,13 @@ class PhpAddonManager(FormatterContributingAddonMixin, AbstractAddonManager):
20
20
 
21
21
  return wexample_wex_addon_dev_php
22
22
 
23
+ def get_deployment_step_classes(self) -> list[type]:
24
+ from wexample_wex_addon_dev_php.deployment.step.symfony_env_local_deployment_step import (
25
+ SymfonyEnvLocalDeploymentStep,
26
+ )
27
+
28
+ return [SymfonyEnvLocalDeploymentStep]
29
+
23
30
  def get_formatter_classes(self) -> list[type[AbstractFormatter]]:
24
31
  from wexample_wex_addon_dev_php.formatter.php_code_formatter import (
25
32
  PhpCodeFormatter,
@@ -0,0 +1,31 @@
1
+ FROM php:8.5-cli
2
+
3
+ RUN apt-get update && apt-get install -y \
4
+ git \
5
+ unzip \
6
+ libicu-dev \
7
+ libzip-dev \
8
+ libpng-dev \
9
+ && rm -rf /var/lib/apt/lists/* \
10
+ && docker-php-ext-install bcmath gd intl zip \
11
+ # A test suite compiling a Symfony container outgrows the CLI's 128M.
12
+ && echo 'memory_limit=1G' > /usr/local/etc/php/conf.d/memory.ini
13
+
14
+ # The publish runs under sudo, so the container runs as root while the mounted
15
+ # repositories belong to the user: git refuses them as of dubious ownership,
16
+ # Roave fails, and every change in src/ reads as a breaking one.
17
+ RUN git config --system --add safe.directory '*'
18
+
19
+ RUN groupadd -g 1000 appuser && \
20
+ useradd -m -u 1000 -g 1000 -s /bin/bash appuser
21
+
22
+ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
23
+
24
+ USER appuser
25
+ RUN composer global require roave/backward-compatibility-check
26
+
27
+ ENV PATH="/home/appuser/.composer/vendor/bin:${PATH}"
28
+
29
+ WORKDIR /var/www/html
30
+
31
+ CMD ["tail", "-f", "/dev/null"]
@@ -0,0 +1,24 @@
1
+ services:
2
+ # What a host runs: the release image CI built, the code inside it and nothing
3
+ # mounted over it. APP_RELEASE_IMAGE is computed by `app::config/build` from the
4
+ # app's origin (or `docker.release_image`); BRANCH is the env's. The host's
5
+ # secrets reach it through the env composes (see samples/env/dev and prod).
6
+ symfony:
7
+ container_name: ${APP_PROJECT_NAME}_symfony
8
+ image: ${APP_RELEASE_IMAGE}:${BRANCH}
9
+ environment:
10
+ - VIRTUAL_HOST=${APP_DOMAINS_STRING}
11
+ - VIRTUAL_PORT=80
12
+ extends:
13
+ file: ${SERVICE_DEFAULT_COMPOSE}
14
+ service: default
15
+
16
+ # What a developer runs: the generic php image with the checkout mounted over
17
+ # /var/www/html, Caddyfile and php.ini bound from the app. Only the local env
18
+ # extends it (samples/env/local) — a service declared here starts nothing on its
19
+ # own, these files are only ever reached through `extends`.
20
+ symfony_local:
21
+ container_name: ${APP_PROJECT_NAME}_symfony
22
+ extends:
23
+ file: ${SERVICE_PHP_COMPOSE}
24
+ service: php
@@ -0,0 +1,8 @@
1
+ services:
2
+ symfony:
3
+ environment:
4
+ - LETSENCRYPT_HOST=${APP_DOMAINS_STRING}
5
+ volumes:
6
+ # The host's secrets, untracked in its checkout: the image only carries the
7
+ # build env (.wex/docker/env.build).
8
+ - ${APP_PATH}.env.local:/var/www/html/.env.local:ro
@@ -0,0 +1,11 @@
1
+ services:
2
+ # Local runs the image built from Dockerfile.dev with the checkout mounted —
3
+ # `symfony_local` — never the release image the base compose points at.
4
+ symfony:
5
+ extends:
6
+ file: ${SERVICE_SYMFONY_COMPOSE}
7
+ service: symfony_local
8
+ image: ${APP_NAME}:local
9
+ build:
10
+ context: ${APP_PATH}
11
+ dockerfile: .wex/docker/images/Dockerfile.dev
@@ -0,0 +1,8 @@
1
+ services:
2
+ symfony:
3
+ environment:
4
+ - LETSENCRYPT_HOST=${APP_DOMAINS_STRING}
5
+ volumes:
6
+ # The host's secrets, untracked in its checkout: the image only carries the
7
+ # build env (.wex/docker/env.build).
8
+ - ${APP_PATH}.env.local:/var/www/html/.env.local:ro
@@ -188,3 +188,78 @@ class PhpPackageWorkdir(WithRunnerWorkdirMixin, PhpWorkdir):
188
188
 
189
189
  # Uses git repo to deploy packages.
190
190
  self.push_to_deployment_remote()
191
+
192
+ def _test_run_without_container(self) -> None:
193
+ """In the php image of the runners, the dependencies included.
194
+
195
+ php is not on the host, and neither are the dependencies: a package's
196
+ vendor/ lives in a Docker volume of its own, mounted where the tests
197
+ expect it, so the host keeps no copy of it for an editor to index. The
198
+ volume is refilled only when composer.json changed since; no lock is
199
+ written, the package's own being left as it is.
200
+
201
+ The packages' directory is mounted whole: the local siblings are linked
202
+ into vendor/ by relative paths (composer path repositories). Run as the
203
+ real user, so what the tests write stays the user's under a sudo publish.
204
+ """
205
+ import hashlib
206
+ import os
207
+
208
+ from wexample_helpers.helper.user import user_get_real_gid, user_get_real_uid
209
+
210
+ runner = self._get_or_create_runner("roave")
211
+ runner.build()
212
+
213
+ user = f"{user_get_real_uid()}:{user_get_real_gid()}"
214
+ path = self.get_path()
215
+ path_hash = hashlib.md5(str(path).encode()).hexdigest()[:8]
216
+ vendor_volume = f"wex-php-vendor-{path.name}-{path_hash}"
217
+ cache_volume = "wex-php-composer-cache"
218
+
219
+ # The mount point: Docker would create it as root otherwise. Created by
220
+ # the user already when wex itself does not run as root.
221
+ vendor_dir = path / "vendor"
222
+ if not vendor_dir.exists():
223
+ vendor_dir.mkdir()
224
+ if os.geteuid() == 0:
225
+ os.chown(vendor_dir, user_get_real_uid(), user_get_real_gid())
226
+
227
+ # A new volume belongs to root; the tests run as the user.
228
+ self.shell_run_for_app(
229
+ cmd=[
230
+ "docker", "run", "--rm", "--user", "0",
231
+ "-v", f"{vendor_volume}:/vendor",
232
+ "-v", f"{cache_volume}:/cache",
233
+ runner.image_name,
234
+ "chown", user, "/vendor", "/cache",
235
+ ]
236
+ )
237
+
238
+ packages_dir = "/var/www/packages"
239
+ package_dir = f"{packages_dir}/{path.name}"
240
+ script = (
241
+ "set -e; "
242
+ 'sum=$(sha1sum composer.json | cut -d" " -f1); '
243
+ 'if [ "$(cat vendor/.wex-composer-json 2>/dev/null)" != "$sum" ]; then '
244
+ "composer config --global lock false; "
245
+ "composer update --no-interaction --no-progress --no-scripts; "
246
+ 'echo "$sum" > vendor/.wex-composer-json; '
247
+ "fi; "
248
+ # The run history is not written into the package; every package
249
+ # requires PHPUnit ^13, which names the option so.
250
+ "exec vendor/bin/phpunit --do-not-record-test-run-history"
251
+ )
252
+ self.shell_run_for_app(
253
+ cmd=[
254
+ "docker", "run", "--rm", "--user", user,
255
+ "-e", "HOME=/tmp",
256
+ "-e", "COMPOSER_HOME=/tmp/composer",
257
+ "-e", "COMPOSER_CACHE_DIR=/cache",
258
+ "-v", f"{path.parent}:{packages_dir}",
259
+ "-v", f"{vendor_volume}:{package_dir}/vendor",
260
+ "-v", f"{cache_volume}:/cache",
261
+ "-w", package_dir,
262
+ runner.image_name,
263
+ "sh", "-c", script,
264
+ ]
265
+ )