Mark Shust 6 ヶ月 前
コミット
ae8b1e5829
54 ファイル変更1136 行追加167 行削除
  1. 1 1
      .github/workflows/build-elasticsearch-7-16.yml
  2. 1 1
      .github/workflows/build-elasticsearch-7-17.yml
  3. 1 1
      .github/workflows/build-elasticsearch-8-11.yml
  4. 1 1
      .github/workflows/build-elasticsearch-8-13.yml
  5. 1 1
      .github/workflows/build-elasticsearch-8-4.yml
  6. 1 1
      .github/workflows/build-elasticsearch-8-5.yml
  7. 1 1
      .github/workflows/build-elasticsearch-8-7.yml
  8. 1 1
      .github/workflows/build-nginx-1-18.yml
  9. 1 1
      .github/workflows/build-nginx-1-22.yml
  10. 1 1
      .github/workflows/build-nginx-1-24.yml
  11. 1 1
      .github/workflows/build-opensearch-1-2.yml
  12. 1 1
      .github/workflows/build-opensearch-2-12.yml
  13. 1 1
      .github/workflows/build-opensearch-2-5.yml
  14. 2 2
      .github/workflows/build-php-8-1.yml
  15. 2 2
      .github/workflows/build-php-8-2.yml
  16. 2 2
      .github/workflows/build-php-8-3.yml
  17. 1 1
      .github/workflows/build-rabbitmq-3-11.yml
  18. 1 1
      .github/workflows/build-rabbitmq-3-12.yml
  19. 1 1
      .github/workflows/build-rabbitmq-3-8.yml
  20. 1 1
      .github/workflows/build-rabbitmq-3-9.yml
  21. 1 1
      .github/workflows/build-ssh.yml
  22. 31 0
      CHANGELOG.md
  23. 39 14
      README.md
  24. 2 0
      compose/Makefile
  25. 230 58
      compose/bin/check-dependencies
  26. 7 12
      compose/bin/download
  27. 5 0
      compose/bin/ece-patches
  28. 1 1
      compose/bin/mysqldump
  29. 11 0
      compose/bin/setup
  30. 14 3
      compose/bin/setup-ssl
  31. 24 0
      compose/bin/test/unit
  32. 24 0
      compose/bin/test/unit-coverage
  33. 24 0
      compose/bin/test/unit-xdebug
  34. 2 4
      compose/bin/xdebug
  35. 9 1
      compose/compose.dev-linux.yaml
  36. 7 7
      compose/compose.healthcheck.yaml
  37. 5 3
      compose/compose.yaml
  38. 21 8
      images/php/8.1/Dockerfile
  39. 1 1
      images/php/8.1/conf/php-fpm.conf
  40. 31 8
      images/php/8.2/Dockerfile
  41. 1 1
      images/php/8.2/conf/php-fpm.conf
  42. 12 9
      images/php/8.3/Dockerfile
  43. 1 1
      images/php/8.3/conf/php-fpm.conf
  44. 124 0
      images/php/8.4/Dockerfile
  45. 2 0
      images/php/8.4/conf/blackfire.ini
  46. 4 0
      images/php/8.4/conf/msmtprc
  47. 34 0
      images/php/8.4/conf/php-fpm.conf
  48. 15 0
      images/php/8.4/conf/php.ini
  49. 5 0
      images/php/8.4/conf/spx.ini
  50. 413 0
      images/php/8.4/conf/www.conf
  51. 3 0
      images/rabbitmq/3.13/Dockerfile
  52. 1 0
      images/rabbitmq/3.13/conf/rabbitmq.conf
  53. 4 8
      lib/onelinesetup
  54. 5 4
      lib/template

+ 1 - 1
.github/workflows/build-elasticsearch-7-16.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/7.17
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-7-17.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/7.17
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-8-11.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/8.11
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-8-13.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/8.13
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-8-4.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/8.4
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-8-5.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/8.5
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-elasticsearch-8-7.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/elasticsearch/8.7
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-nginx-1-18.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/nginx/1.18
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-nginx-1-22.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/nginx/1.22
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-nginx-1-24.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/nginx/1.24
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-opensearch-1-2.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/opensearch/1.2
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-opensearch-2-12.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/opensearch/2.12
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-opensearch-2-5.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/opensearch/2.5
           platforms: linux/amd64,linux/arm64

+ 2 - 2
.github/workflows/build-php-8-1.yml

@@ -26,11 +26,11 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/php/8.1
           platforms: linux/amd64,linux/arm64
           push: true
           tags: |
             markoshust/magento-php:8.1-fpm
-            markoshust/magento-php:8.1-fpm-5
+            markoshust/magento-php:8.1-fpm-6

+ 2 - 2
.github/workflows/build-php-8-2.yml

@@ -26,11 +26,11 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/php/8.2
           platforms: linux/amd64,linux/arm64
           push: true
           tags: |
             markoshust/magento-php:8.2-fpm
-            markoshust/magento-php:8.2-fpm-4
+            markoshust/magento-php:8.2-fpm-5

+ 2 - 2
.github/workflows/build-php-8-3.yml

@@ -26,11 +26,11 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/php/8.3
           platforms: linux/amd64,linux/arm64
           push: true
           tags: |
             markoshust/magento-php:8.3-fpm
-            markoshust/magento-php:8.3-fpm-2
+            markoshust/magento-php:8.3-fpm-3

+ 1 - 1
.github/workflows/build-rabbitmq-3-11.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/rabbitmq/3.11
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-rabbitmq-3-12.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/rabbitmq/3.12
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-rabbitmq-3-8.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/rabbitmq/3.9
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-rabbitmq-3-9.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/rabbitmq/3.9
           platforms: linux/amd64,linux/arm64

+ 1 - 1
.github/workflows/build-ssh.yml

@@ -26,7 +26,7 @@ jobs:
       -
         name: Build and push
         id: docker_build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: images/php/8.2
           platforms: linux/amd64,linux/arm64

+ 31 - 0
CHANGELOG.md

@@ -4,6 +4,37 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [48.0.0] - 2024-11-27
+
+This release introduces significant enhancements to the development environment, including a new PHP 8.4 Docker image for early adopters and developers wanting to test future compatibility. The MageOS support has been substantially improved, and a new RabbitMQ 3.13 image has been added. Several quality-of-life improvements have been implemented, such as better unit testing tools, enhanced SSL generation for multi-site setups, and comprehensive documentation updates. A notable breaking change is the shift in the download command syntax - it now expects the edition type before the version number (ex: `bin/download community 2.4.7-p3` instead of the previous `bin/download 2.4.7 community`). Additionally, all Docker images have been updated to maintain parity with current Magento version requirements.
+
+### Added
+- New PHP 8.4 Docker image for development purposes [PR #1252](https://github.com/markshust/docker-magento/pull/1252)
+- New RabbitMQ 3.13 Docker image [PR #1252](https://github.com/markshust/docker-magento/pull/1252)
+- Backend login credentials and 2FA setup instructions to README [PR #1205](https://github.com/markshust/docker-magento/pull/1205)
+- PHP FTP extension to Docker setup [PR #1210](https://github.com/markshust/docker-magento/pull/1210)
+- PhpMyAdmin credentials to README [PR #1212](https://github.com/markshust/docker-magento/pull/1212)
+- `bin/test` helper scripts to execute unit tests [PR #1157](https://github.com/markshust/docker-magento/pull/1157)
+- `bin/ece-patches` command [PR #1171](https://github.com/markshust/docker-magento/pull/1171)
+- rsync to PHP images for Deployer support [PR #1163](https://github.com/markshust/docker-magento/pull/1163)
+- PhpMyAdmin for Linux [PR #1177](https://github.com/markshust/docker-magento/pull/1177)
+
+### Updated
+- Improved MageOS support [PR #1246](https://github.com/markshust/docker-magento/pull/1246)
+- System requirements helper script overhauled for easier use and maintenance [PR #1242](https://github.com/markshust/docker-magento/pull/1242)
+- Supported versions in `bin/check-dependencies` script [PR #1175](https://github.com/markshust/docker-magento/pull/1175)
+- Docker images for Magento version parity [PR #1252](https://github.com/markshust/docker-magento/pull/1252)
+- Bumped docker/build-push-action from 5 to 6 [PR #1174](https://github.com/markshust/docker-magento/pull/1174)
+- GitHub Actions to use release/next branch [PR #1254](https://github.com/markshust/docker-magento/pull/1254)
+- Speed improvement for `bin/xdebug` command [PR #1245](https://github.com/markshust/docker-magento/pull/1245)
+
+### Fixed
+- Code duplication [PR #1141](https://github.com/markshust/docker-magento/pull/1141)
+- Empty directory check moved to top of scripts [PR #1143](https://github.com/markshust/docker-magento/pull/1143)
+- Elasticsearch container issues [PR #1166](https://github.com/markshust/docker-magento/pull/1166)
+- Multi-site SSL generation support [PR #1200](https://github.com/markshust/docker-magento/pull/1200)
+- Various typos in files [PR #1238](https://github.com/markshust/docker-magento/pull/1238)
+
 ## [47.0.1] - 2024-04-25
 
 ### Fixed

+ 39 - 14
README.md

@@ -33,9 +33,10 @@ View Dockerfiles for the latest tags:
   - [`1.22`, `1.22-0`](images/nginx/1.22)
   - [`1.24`, `1.24-0`](images/nginx/1.24)
 - [markoshust/magento-php (Docker Hub)](https://hub.docker.com/r/markoshust/magento-php/)
-  - [`8.1-fpm`, `8.1-fpm-5`](images/php/8.1)
-  - [`8.2-fpm`, `8.2-fpm-4`](images/php/8.2)
-  - [`8.3-fpm`, `8.3-fpm-2`](images/php/8.3)
+  - [`8.1-fpm`, `8.1-fpm-6`](images/php/8.1)
+  - [`8.2-fpm`, `8.2-fpm-5`](images/php/8.2)
+  - [`8.3-fpm`, `8.3-fpm-3`](images/php/8.3)
+  - [`8.4-fpm-dev`](images/php/8.4)
 - [markoshust/magento-opensearch (Docker Hub)](https://hub.docker.com/r/markoshust/magento-opensearch/)
   - [`1.2`, `1.2-0`](images/opensearch/1.2)
   - [`2.5`, `2.5-1`](images/opensearch/2.5)
@@ -150,13 +151,28 @@ mkdir -p ~/Sites/magento
 cd $_
 
 # Run this automated one-liner from the directory you want to install your project.
-curl -s https://raw.githubusercontent.com/markshust/docker-magento/master/lib/onelinesetup | bash -s -- magento.test 2.4.7 community
+curl -s https://raw.githubusercontent.com/markshust/docker-magento/master/lib/onelinesetup | bash -s -- magento.test community 2.4.7-p3
 ```
 
-The `magento.test` above defines the hostname to use, and the `2.4.7` defines the Magento version to install. Note that since we need a write to `/etc/hosts` for DNS resolution, you will be prompted for your system password during setup.
+The `magento.test` above defines the hostname to use, `community` is the Magento edition, and the `2.4.7-p3` defines the Magento version to install. Note that since we need a write to `/etc/hosts` for DNS resolution, you will be prompted for your system password during setup.
 
 After the one-liner above completes running, you should be able to access your site at `https://magento.test`.
 
+## Accessing the Magento Backend
+
+After successfully installing the Magento environment, you can access the backend by following these steps:
+
+1. Open your web browser and go to the following URL: `https://magento.test/admin/`.
+
+2. Use the following default credentials to log in:
+- **Username:** `john.smith`
+- **Password:** `password123`
+
+3. Upon logging in, you might be prompted to configure Two-Factor Authentication (2FA). This emails you a code to log in with (which you can check with Mailcatcher by visiting `http://{yourdomain}:1080`). By default, the email address used for this purpose is:
+- **Email:** `john.smith@gmail.com`
+
+If you are testing in a local development environment and wish to disable 2FA, you can do so by installing [Mark's DisableTwoFactorAuth module](https://github.com/markshust/magento2-module-disabletwofactorauth).
+
 #### Install sample data
 
 After the above installation is complete, run the following lines to install sample data:
@@ -181,10 +197,10 @@ cd $_
 curl -s https://raw.githubusercontent.com/markshust/docker-magento/master/lib/template | bash
 
 # Download the version of Magento you want to use with:
-bin/download 2.4.7 community
-# You can specify the version and type (community, enterprise, mageos, mageos-nightly, mageos-mirror, mageos-hypernode-mirror, or mageos-maxcluster-mirror).
-# The mageos type is an alias for mageos-mirror.
-# If no arguments are passed, "2.4.7" and "community" are the default values used.
+bin/download community 2.4.7-p3
+# You can specify the edition (community, enterprise, mageos) and version (2.4.7-p3, 1.0.5, etc.)
+# If no arguments are passed in, the edition defaults to "community"
+# If no version is specified, it defaults to the most recent version defined in `bin/download`
 
 # or for Magento core development:
 # bin/start --no-dev
@@ -254,9 +270,9 @@ open https://magento.test
 
 ### Elasticsearch vs OpenSearch
 OpenSearch is set as the default search engine when setting up this project. Follow the instructions below if you want to use Elasticsearch instead:
-1. Comment out or remove the `opensearch` container in both the [`compose.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.yaml#L55-L66) and [`compose.healthcheck.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.healthcheck.yaml#L38-L43) files
-2. Uncomment the `elasticsearch` container in both the [`compose.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.yaml#L70-L81) and [`compose.healthcheck.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.healthcheck.yaml#L45-L50) files
-3. Update the `bin/setup-install` command to use the Elasticsearch ratther than OpenSearch. Change:
+1. Comment out or remove the `opensearch` container in both the [`compose.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.yaml#L69-L84) and [`compose.healthcheck.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.healthcheck.yaml#L36-L41) files
+2. Uncomment the `elasticsearch` container in both the [`compose.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.yaml#L86-L106) and [`compose.healthcheck.yaml`](https://github.com/markshust/docker-magento/blob/master/compose/compose.healthcheck.yaml#L43-L48) files
+3. Update the `bin/setup-install` command to use the Elasticsearch rather than OpenSearch. Change:
 
 ```
 --opensearch-host="$OPENSEARCH_HOST" \
@@ -306,7 +322,8 @@ It is recommended to keep your root docker config files in one repository, and y
 - `bin/devconsole`: Alias for `bin/n98-magerun2 dev:console`
 - `bin/docker-compose`: Support V1 (`docker-compose`) and V2 (`docker compose`) docker compose command, and use custom configuration files, such as `compose.yml` and `compose.dev.yml`
 - `bin/docker-stats`: Display container name and container ID, status for CPU, memory usage(in MiB and %), and memory limit of currently-running Docker containers.
-- `bin/download`: Download specific Magento version from Composer to the container, with optional arguments of the version (2.4.7 [default]) and type ("community" [default], "enterprise", or "mageos"). Ex. `bin/download 2.4.7 enterprise`
+- `bin/download`: Download specific Magento version from Composer to the container, with optional arguments of the type ("community" [default], "enterprise", or "mageos") and version ([default] is defined in `bin/download`). Ex. `bin/download mageos` or `bin/download enterprise 2.4.7-p3`
+- `bin/ece-patches`: Run the Cloud Patches CLI. Ex: `bin/ece-tools apply`
 - `bin/fixowns`: This will fix filesystem ownerships within the container.
 - `bin/fixperms`: This will fix filesystem permissions within the container.
 - `bin/grunt`: Run the grunt binary. Ex. `bin/grunt exec`
@@ -347,6 +364,9 @@ It is recommended to keep your root docker config files in one repository, and y
 - `bin/status`: Check the container status.
 - `bin/stop`: Stop all project containers.
 - `bin/stopall`: Stop all docker running containers
+- `bin/test/unit`: Run unit tests for a specific path. Ex. `bin/test/unit my-dir`
+- `bin/test/unit-coverage`: Generate unit tests coverage reports, saved to the folder `dev/tests/unit/report`. Ex. `bin/test/unit-coverage my-dir`
+- `bin/test/unit-xdebug`: Run unit tests with Xdebug. Ex. `bin/test/unit-xdebug my-dir`
 - `bin/update`: Update your project to the most recent version of `docker-magento`.
 - `bin/xdebug`: Disable or enable Xdebug. Accepts argument `disable`, `enable`, or `status`. Ex. `bin/xdebug enable`
 
@@ -419,7 +439,7 @@ Copy `src/auth.json.sample` to `src/auth.json`. Then, update the username and pa
 
 ### Email / Mailcatcher
 
-View emails sent locally through Mailcatcher by visiting [http://{yourdomain}:1080](http://{yourdomain}:1080). During development, it's easiest to test emails using a third-party module such as [Mageplaza's SMTP module](https://github.com/mageplaza/magento-2-smtp). In order to use mailcatcher, set the mailserver host to `mailcatcher` and set port to `1025`. Note that this port is different from the mailcatcher interface to read the emails.
+View emails sent locally through Mailcatcher by visiting [http://{yourdomain}:1080](http://{yourdomain}:1080). In order to use mailcatcher, set the mailserver host to `mailcatcher` and set port to `1025`. Note that this port (`1025`) is different from the mailcatcher interface to read the emails (`1080`).
 
 ### Redis
 
@@ -447,6 +467,11 @@ For more information about Redis usage with Magento, <a href="https://devdocs.ma
 
 PhpMyAdmin is built into the `compose.dev.yaml` file. Simply open `http://localhost:8080` in a web browser.
 
+These credentials can be used to log in to PhpMyAdmin:
+
+- **Username:** `magento`
+- **Password:** `magento`
+
 ### Xdebug & VS Code
 
 Install and enable the PHP Debug extension from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug).

+ 2 - 0
compose/Makefile

@@ -41,6 +41,7 @@ help:
 	@echo "$(call format,docker-compose,'Support V1 (`docker-compose`) and V2 (`docker compose`) docker compose command, and use custom configuration files.')"
 	@echo "$(call format,docker-stats,'Display status for CPU$(comma) memory usage$(comma) and memory limit of currently-running Docker containers.')"
 	@echo "$(call format,download,'Download & extract specific Magento version to the src directory.')"
+	@echo "$(call format,ece-patches,'Run the Cloud Patches CLI.')"
 	@echo "$(call format,fixowns,'This will fix filesystem ownerships within the container.')"
 	@echo "$(call format,fixperms,'This will fix filesystem permissions within the container.')"
 	@echo "$(call format,grunt,'Run the grunt binary.')"
@@ -81,6 +82,7 @@ help:
 	@echo "$(call format,status,'Check the container status.')"
 	@echo "$(call format,stop,'Stop all project containers.')"
 	@echo "$(call format,stopall,'Stop all docker running containers.')"
+	@echo "$(call format,test,'Run unit tests for a specific path.')"
 	@echo "$(call format,update,'Update your project to the latest version of docker-magento.')"
 	@echo "$(call format,xdebug,'Disable or enable Xdebug. Accepts argument `disable`, `enable`, or `status`.')"
 

+ 230 - 58
compose/bin/check-dependencies

@@ -7,68 +7,240 @@ YELLOW='\033[0;33m'
 BLUE='\033[0;34m'
 NC='\033[0m' # No Color
 
-# Declare arrays for storing common dependencies and Magento versions
-declare -a common_dependencies
-declare -a magento_versions=("2.4.7" "2.4.6-p5" "2.4.6-p4" "2.4.6-p3" "2.4.6-p2" "2.4.6-p1" "2.4.6" "2.4.5-p7" "2.4.5-p6" "2.4.5-p5" "2.4.5-p4" "2.4.5-p3" "2.4.5-p2" "2.4.5-p1" "2.4.5" "2.4.4-p8" "2.4.4-p7" "2.4.4-p6" "2.4.4-p5" "2.4.4-p4" "2.4.4-p3" "2.4.4-p2" "2.4.4-p1" "2.4.4" "2.4.3-p3" "2.4.2-p2")
+# Function to display the script header
+show_header() {
+    echo -e "${GREEN}============================================${NC}"
+    echo -e "${GREEN}   Magento 2 System Requirements Checker${NC}"
+    echo -e "${GREEN}============================================${NC}"
+    echo
+}
 
-# Assign common dependencies to corresponding Magento versions
-common_dependencies[0]="Composer:2.7 Elasticsearch:8.11 OpenSearch:2.12 MariaDB:10.6 MySQL:8.0 PHP:8.3,8.2 RabbitMQ:3.13 Redis:7.2 Varnish:7.5 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.11.20 'AWS-ElastiCache':Redis7.0 'AWS-Elasticsearch':-- 'AWS-OpenSearch':2.11"
-common_dependencies[1]="Composer:2.2 Elasticsearch:8.11,7.17 OpenSearch:2.12 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.12,3.11,3.9 Redis:7.0 Varnish:7.5 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[2]="Composer:2.2 Elasticsearch:8.11,7.17 OpenSearch:2.5 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[3]="Composer:2.2 Elasticsearch:8.5,7.17 OpenSearch:2.5 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[4]="Composer:2.2 Elasticsearch:8.5,7.17 OpenSearch:2.5 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[5]="Composer:2.2 Elasticsearch:8.5,7.17 OpenSearch:2.5 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[6]="Composer:2.2 Elasticsearch:8.4,7.17 OpenSearch:2.5 MariaDB:10.6 MySQL:8.0 PHP:8.2,8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.1 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.16 'AWS-ElastiCache':Redis6.2 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[7]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.3 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.11,3.9 Redis:7.0 Varnish:7.5 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[8]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.11,3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[9]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.11,3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[10]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.11,3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[11]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.11,3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[12]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.1 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[13]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.0 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':1.2"
-common_dependencies[14]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.0 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':1.2"
-common_dependencies[15]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.3 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:7.0 Varnish:7.5 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[16]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[17]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.24 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[18]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[19]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.3 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[20]="Composer:2.2 Elasticsearch:7.17 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.1 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':8.0 'AWS-S3':✔️ 'AWS-MQ':3.9.13 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':-- 'AWS-OpenSearch':1.2"
-common_dependencies[21]="Composer:2.1 Elasticsearch:7.16 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.0 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':5.7 'AWS-S3':✔️ 'AWS-MQ':3.8.11 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':--"
-common_dependencies[22]="Composer:2.1 Elasticsearch:7.16 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.0 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':5.7 'AWS-S3':✔️ 'AWS-MQ':3.8.11 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':--"
-common_dependencies[23]="Composer:2.1 Elasticsearch:7.16 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:8.1 RabbitMQ:3.9 Redis:6.2 Varnish:7.0 Apache:2.4 nginx:1.22 'AWS-Aurora-(MySQL)':5.7 'AWS-S3':✔️ 'AWS-MQ':3.8.11 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':--"
-common_dependencies[24]="Composer:1 Elasticsearch:7.16 OpenSearch:1.2 MariaDB:10.4 MySQL:8.0 PHP:7.4 RabbitMQ:3.8 Redis:6.0 Varnish:6.5 Apache:2.4 nginx:1.18 'AWS-Aurora-(MySQL)':5.7 'AWS-S3':✔️ 'AWS-MQ':3.8.11 'AWS-ElastiCache':Redis6 'AWS-Elasticsearch':7.9 'AWS-OpenSearch':--"
-common_dependencies[25]="Composer:1 Elasticsearch:7.9 OpenSearch:-- MariaDB:10.4 MySQL:8.0 PHP:7.4 RabbitMQ:3.8 Redis:6.0 Varnish:6.4 Apache:2.4 nginx:1.18 'AWS-Aurora-(MySQL)':-- 'AWS-S3':✔️ 'AWS-MQ':-- 'AWS-ElastiCache':-- 'AWS-Elasticsearch':-- 'AWS-OpenSearch':--"
+# Function to display version groups
+show_version_groups() {
+    echo -e "${BLUE}Available Magento version groups:${NC}"
+    echo "1) 2.4.7.x (2.4.7, 2.4.7-p1, 2.4.7-p2, 2.4.7-p3)"
+    echo "2) 2.4.6.x (2.4.6 through 2.4.6-p8)"
+    echo "3) 2.4.5.x (2.4.5 through 2.4.5-p10)"
+    echo "4) 2.4.4.x (2.4.4 through 2.4.4-p11)"
+    echo "5) 2.4.3.x (2.4.3-p3)"
+    echo "6) 2.4.2.x (2.4.2-p2)"
+    echo
+}
 
-# Function to print available Magento versions
-print_magento_versions() {
-    echo "Available Magento 2 versions:"
-    for version in "${magento_versions[@]}"; do
-        echo "$version"
-    done
+# Function to display requirements for a specific version group
+show_requirements() {
+    local version_group=$1
+    echo -e "${GREEN}System Requirements for Magento ${version_group}:${NC}"
+    echo -e "${YELLOW}Required Components:${NC}"
+    
+    case $version_group in
+        "2.4.7.x")
+            echo "• PHP: 8.3, 8.2"
+            echo "• Composer: 2.7"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.6"
+            echo "• Search: "
+            echo "  - Elasticsearch: 8.11"
+            echo "  - OpenSearch: 2.12"
+            echo "• Cache/Session:"
+            echo "  - Redis: 7.2"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.13"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.24"
+            echo "• Varnish: 7.5"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• Aurora (MySQL): 8.0"
+            echo "• S3: ✔️"
+            echo "• MQ: 3.11.20"
+            echo "• ElastiCache: Redis7.0"
+            echo "• OpenSearch: 2.11"
+            ;;
+        "2.4.6.x")
+            echo "• PHP: 8.2, 8.1"
+            echo "• Composer: 2.2"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.6"
+            echo "• Search: "
+            echo "  - Elasticsearch: 8.11, 7.17"
+            echo "  - OpenSearch: 2.12"
+            echo "• Cache/Session:"
+            echo "  - Redis: 7.0"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.13"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.24"
+            echo "• Varnish: 7.5"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• Aurora (MySQL): 8.0"
+            echo "• S3: ✔️"
+            echo "• MQ: 3.9.16"
+            echo "• ElastiCache: Redis6.2"
+            echo "• OpenSearch: 1.2"
+            ;;
+        "2.4.5.x")
+            echo "• PHP: 8.1"
+            echo "• Composer: 2.2"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.4"
+            echo "• Search: "
+            echo "  - Elasticsearch: 7.17"
+            echo "  - OpenSearch: 1.2"
+            echo "• Cache/Session:"
+            echo "  - Redis: 6.2"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.11, 3.9"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.24"
+            echo "• Varnish: 7.3"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• Aurora (MySQL): 8.0"
+            echo "• S3: ✔️"
+            echo "• MQ: 3.9.13"
+            echo "• ElastiCache: Redis6"
+            echo "• OpenSearch: 1.2"
+            ;;
+        "2.4.4.x")
+            echo "• PHP: 8.1"
+            echo "• Composer: 2.2"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.4"
+            echo "• Search: "
+            echo "  - Elasticsearch: 7.17"
+            echo "  - OpenSearch: 1.2"
+            echo "• Cache/Session:"
+            echo "  - Redis: 6.2"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.9"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.24"
+            echo "• Varnish: 7.3"
+            echo -e "${YELLOW}NOTE: Supports MySQL 8.0.0 through 8.0.28 only${NC}"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• Aurora (MySQL): 8.0"
+            echo "• S3: ✔️"
+            echo "• MQ: 3.9.13"
+            echo "• ElastiCache: Redis6"
+            echo "• OpenSearch: 1.2"
+            ;;
+        "2.4.3.x")
+            echo "• PHP: 7.4"
+            echo "• Composer: 1"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.4"
+            echo "• Search: "
+            echo "  - Elasticsearch: 7.16"
+            echo "  - OpenSearch: 1.2"
+            echo "• Cache/Session:"
+            echo "  - Redis: 6.0"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.8"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.18"
+            echo "• Varnish: 6.5"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• Aurora (MySQL): 5.7"
+            echo "• S3: ✔️"
+            echo "• MQ: 3.8.11"
+            echo "• ElastiCache: Redis6"
+            echo "• Elasticsearch: 7.9"
+            ;;
+        "2.4.2.x")
+            echo "• PHP: 7.4"
+            echo "• Composer: 1"
+            echo "• Database:"
+            echo "  - MySQL: 8.0"
+            echo "  - MariaDB: 10.4"
+            echo "• Search: "
+            echo "  - Elasticsearch: 7.9"
+            echo "• Cache/Session:"
+            echo "  - Redis: 6.0"
+            echo "• Message Queue:"
+            echo "  - RabbitMQ: 3.8"
+            echo "• Web Server:"
+            echo "  - Apache: 2.4"
+            echo "  - Nginx: 1.18"
+            echo "• Varnish: 6.4"
+            echo -e "${BLUE}Supported AWS Services:${NC}"
+            echo "• S3: ✔️"
+            echo "• Elasticsearch: 7.9"
+            ;;
+        *)
+            echo -e "${RED}Version group not found${NC}"
+            ;;
+    esac
 }
 
-# Function to print dependencies for a given Magento version
-print_dependencies() {
-    local version=$1
-    local index
-    for (( index=0; index<${#magento_versions[@]}; index++ )); do
-        if [[ "${magento_versions[$index]}" == "$version" ]]; then
-            echo -e "${GREEN}Recommended software dependencies:${NC}"
-            local dependencies=${common_dependencies[$index]}
-            IFS=' ' read -ra deps <<< "$dependencies"
-            for dep in "${deps[@]}"; do
-                echo "  • $dep"
-            done
-            return
-        fi
-    done
-    # If the provided Magento version is not recognized, prompt the user to choose a valid version
-    echo -e "${RED}Magento version not recognized. Please choose a valid version from the list below:${NC}"
-    print_magento_versions
-    echo -e "${BLUE}Additional info about system requirements of Magento in the DevDocs at ${YELLOW}https://experienceleague.adobe.com/en/docs/commerce-operations/installation-guide/system-requirements"
+# Function to show additional information
+show_additional_info() {
+    echo -e "${BLUE}Additional Information:${NC}"
+    echo "• Full system requirements documentation: https://experienceleague.adobe.com/docs/commerce-operations/installation-guide/system-requirements.html"
+    echo "• Magento DevDocs: https://developer.adobe.com/commerce/docs/"
+    echo "• Security Best Practices: https://experienceleague.adobe.com/docs/commerce-operations/implementation-playbook/best-practices/security/overview.html"
 }
 
-# Prompt the user to enter a Magento version and print its dependencies
-read -r -p "Enter the Magento 2 version you'd like to check dependencies on: " magento_version
+# Main script execution
+clear
+show_header
 
-print_dependencies "$magento_version"
+while true; do
+    show_version_groups
+    echo -e "${YELLOW}Options:${NC}"
+    echo "1-6) Select version group"
+    echo "i) Additional Information"
+    echo "q) Quit"
+    echo
+    read -r -p "Enter your choice: " choice
+    
+    case $choice in
+        q|Q) 
+            echo -e "${GREEN}Thank you for using the Magento System Requirements Checker!${NC}"
+            exit 0
+            ;;
+        i|I)
+            clear
+            show_additional_info
+            ;;
+        1) 
+            clear
+            show_requirements "2.4.7.x"
+            ;;
+        2)
+            clear
+            show_requirements "2.4.6.x"
+            ;;
+        3)
+            clear
+            show_requirements "2.4.5.x"
+            ;;
+        4)
+            clear
+            show_requirements "2.4.4.x"
+            ;;
+        5)
+            clear
+            show_requirements "2.4.3.x"
+            ;;
+        6)
+            clear
+            show_requirements "2.4.2.x"
+            ;;
+        *)
+            echo -e "${RED}Invalid choice. Please try again.${NC}"
+            ;;
+    esac
+    
+    echo
+    read -r -p "Press Enter to continue..."
+    clear
+done

+ 7 - 12
compose/bin/download

@@ -1,20 +1,19 @@
 #!/usr/bin/env bash
 
-VERSION=${1:-2.4.6-p4}
-EDITION=${2:-community}
+EDITION=${1:-community}
 
 # Define ANSI escape codes for colors
 YELLOW='\033[0;33m'
 BLUE='\033[0;34m'
 NC='\033[0m' # No Color
 
-bin/stop
-
 if [ -d "./src" ]; then
   echo "Error: The \"src\" directory is not empty. Please remove all contents within this directory and try again."
   exit 1
 fi
 
+bin/stop
+
 bin/start --no-dev
 [ $? != 0 ] && echo "Failed to start Docker services" && exit
 
@@ -22,15 +21,11 @@ bin/setup-composer-auth
 
 bin/fixowns
 
-if [ "$EDITION" == "mageos-nightly" ]; then
-  bin/clinotty composer create-project --stability alpha --repository-url=https://upstream-nightly.mage-os.org magento/project-community-edition .
-elif [ "$EDITION" == "mageos-mirror" ] || [ "$EDITION" == "mageos" ]; then
-  bin/clinotty composer create-project --repository-url=https://mirror.mage-os.org magento/project-community-edition="${VERSION}" .
-elif [ "$EDITION" == "mageos-hypernode-mirror" ]; then
-  bin/clinotty composer create-project --repository-url=https://mage-os.hypernode.com/mirror magento/project-community-edition="${VERSION}" .
-elif [ "$EDITION" == "mageos-maxcluster-mirror" ]; then
-  bin/clinotty composer create-project --repository-url=https://mage-os.maxcluster.net/mirror magento/project-community-edition="${VERSION}" .
+if [ "$EDITION" == "mageos" ]; then
+  VERSION=${2:-1.0.5}
+  bin/clinotty composer create-project --repository-url=https://repo.mage-os.org/ mage-os/project-community-edition="${VERSION}" .
 else
+  VERSION=${2:-2.4.7-p3}
   bin/clinotty composer create-project --repository=https://repo.magento.com/ magento/project-"${EDITION}"-edition="${VERSION}" .
 fi
 

+ 5 - 0
compose/bin/ece-patches

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+if ! bin/cliq ls vendor/bin/ece-patches; then
+    echo "The ece-patches tool is not installed. Please ensure you are using Adobe Commerce Cloud before running this command."
+fi
+bin/cli vendor/bin/ece-patches "$@"

+ 1 - 1
compose/bin/mysqldump

@@ -1,2 +1,2 @@
 #!/usr/bin/env bash
-bin/n98-magerun2 db:dump --stdout "$@"
+bin/n98-magerun2 db:dump --human-readable --stdout "$@"

+ 11 - 0
compose/bin/setup

@@ -1,5 +1,10 @@
 #!/usr/bin/env bash
 set -o errexit
+if [ -f "../env/magento.env" ]; then
+    source "../env/magento.env"
+else
+    echo "Warning: magento.env file not found."
+fi
 
 MEM_BYTES=$(docker info -f '{{.MemTotal}}')
 MEM_MB=$(( MEM_BYTES / 1000000 ))
@@ -53,3 +58,9 @@ cp -r .vscode src/
 
 echo "Docker development environment setup complete."
 echo "You may now access your Magento instance at https://${DOMAIN}/"
+
+echo "You may now access your Magento backend instance at https://${DOMAIN}/admin/"
+
+echo "Use the following default credentials to log in:"
+echo "Username: $MAGENTO_ADMIN_USER"
+echo "Password: $MAGENTO_ADMIN_PASSWORD"

+ 14 - 3
compose/bin/setup-ssl

@@ -6,9 +6,20 @@ if ! bin/docker-compose exec -T -u root app cat /root/.local/share/mkcert/rootCA
   bin/setup-ssl-ca
 fi
 
-# Generate the certificate for the specified domain
-DOMAIN_WITHOUT_PORT=$(echo "$@" | cut -d ':' -f1)
-bin/docker-compose exec -T -u root app mkcert -key-file nginx.key -cert-file nginx.crt "$DOMAIN_WITHOUT_PORT"
+# Initialize an empty array to hold the processed domains
+DOMAINS_WITHOUT_PORT=()
+
+# Loop through each domain
+for domain in "$@"; do
+  # Strip out the port number
+  DOMAIN_WITHOUT_PORT=$(echo "$domain" | cut -d ':' -f1)
+  # Append the processed domain to the array
+  DOMAINS_WITHOUT_PORT+=("$DOMAIN_WITHOUT_PORT")
+done
+
+# Use the array in the mkcert command
+bin/docker-compose exec -T -u root app mkcert -key-file nginx.key -cert-file nginx.crt "${DOMAINS_WITHOUT_PORT[@]}"
+
 echo "Moving key and cert to /etc/nginx/certs/..."
 bin/docker-compose exec -T -u root app chown app:app nginx.key nginx.crt
 bin/docker-compose exec -T -u root app mv nginx.key nginx.crt /etc/nginx/certs/

+ 24 - 0
compose/bin/test/unit

@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+display_help() {
+  echo -e "Description:
+  Run unit tests
+
+Usage:
+  bin/test/unit <unit_test_path>
+
+Arguments:
+  <unit_test_path> Specify a path to your test or folder with tests Ex: bin/test/unit app/code/Vendor/Module
+
+Options:
+  -h, --help       Display help message"
+}
+
+
+if [[ $1 == "-h" || $1 == "--help" ]]; then
+  display_help
+elif [[ -z $1 ]]; then
+  echo -e "Please specify a path to your test or folder with tests (ex. app/code/Vendor/Module)"
+else
+  bin/php vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist /var/www/html/"$1"
+fi

+ 24 - 0
compose/bin/test/unit-coverage

@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+display_help() {
+  echo -e "Description:
+  Generate unit tests coverage report in folder: dev/tests/unit/report
+
+Usage:
+  bin/test/unit-coverage <unit_test_path>
+
+Arguments:
+  <unit_test_path> Specify a path to your test or folder with tests Ex: bin/test/unit-coverage app/code/Vendor/Module
+
+Options:
+  -h, --help       Display help message"
+}
+
+if [[ $1 == "-h" || $1 == "--help" ]]; then
+  display_help
+elif [[ -z $1 ]]; then
+  echo -e "Please specify a path to your test or folder with tests (ex. app/code/Vendor/Module)"
+else
+  bin/php -d xdebug.mode=coverage vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist --coverage-html ./dev/tests/unit/report /var/www/html/"$1"
+  echo -e "Report path: dev/tests/unit/report"
+fi

+ 24 - 0
compose/bin/test/unit-xdebug

@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+display_help() {
+  echo -e "Description:
+  Run unit tests with xdebug (you need to turn on xdebug in IDE as usual)
+
+Usage:
+  bin/test/unit-xdebug <unit_test_path>
+
+Arguments:
+  <unit_test_path> Specify a path to your test or folder with tests Ex: bin/test/unit-xdebug app/code/Vendor/Module
+
+Options:
+  -h, --help       Display help message"
+}
+
+
+if [[ $1 == "-h" || $1 == "--help" ]]; then
+  display_help
+elif [[ -z $1 ]]; then
+  echo -e "Please specify a path to your test or folder with tests (ex. app/code/Vendor/Module)"
+else
+  bin/php -d xdebug.start_with_request=yes vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist /var/www/html/"$1"
+fi

+ 2 - 4
compose/bin/xdebug

@@ -21,8 +21,7 @@ xdebug_toggle() {
 xdebug_enable() {
     if [[ $S == 1 ]]; then
         bin/root sed -i -e 's/^xdebug.mode = off/xdebug.mode = debug/g' /usr/local/etc/php/php.ini
-        sleep 1
-        bin/restart phpfpm
+        bin/cli kill -USR2 1
         echo "Xdebug debug mode has been enabled."
     else
         echo "Xdebug debug mode is already enabled."
@@ -32,8 +31,7 @@ xdebug_enable() {
 xdebug_disable() {
     if [[ $S == 0 ]]; then
         bin/root sed -i -e 's/^xdebug.mode = debug/xdebug.mode = off/g' /usr/local/etc/php/php.ini
-        sleep 1
-        bin/restart phpfpm
+        bin/cli kill -USR2 1
         echo "Xdebug debug mode has been disabled."
     else
         echo "Xdebug debug mode is already disabled."

+ 9 - 1
compose/compose.dev-linux.yaml

@@ -5,4 +5,12 @@ services:
       - ./src:/var/www/html:cached
 
   phpfpm:
-    volumes: *appvolumes
+    volumes: *appvolumes    
+    
+  phpmyadmin:
+    image: linuxserver/phpmyadmin
+    env_file: env/db.env
+    ports:
+      - "8080:80"
+    depends_on:
+      - db

+ 7 - 7
compose/compose.healthcheck.yaml

@@ -1,7 +1,7 @@
 services:
   app:
     healthcheck:
-      test: 'curl --fail 127.0.0.1:8000'
+      test: "curl --fail 127.0.0.1:8000"
     depends_on:
       phpfpm:
         condition: service_started
@@ -21,39 +21,39 @@ services:
 
   db:
     healthcheck:
-      test: 'mysqladmin ping -h localhost -u root -pmagento'
+      test: "mysqladmin ping -h localhost -u root -pmagento"
       interval: 5s
       timeout: 5s
       retries: 30
 
   redis:
     healthcheck:
-      test: 'redis-cli ping || exit 1'
+      test: "redis-cli ping || exit 1"
       interval: 5s
       timeout: 5s
       retries: 30
 
   opensearch:
     healthcheck:
-      test: 'curl --fail opensearch:9200/_cat/health >/dev/null || exit 1'
+      test: "curl --fail opensearch:9200/_cat/health >/dev/null || exit 1"
       interval: 5s
       timeout: 5s
       retries: 30
 
   #elasticsearch:
   #  healthcheck:
-  #    test: 'curl --fail elasticsearch:9200/_cat/health >/dev/null || exit 1'
+  #    test: "curl --fail elasticsearch:9200/_cat/health >/dev/null || exit 1"
   #    interval: 5s
   #    timeout: 5s
   #    retries: 30
 
   rabbitmq:
     healthcheck:
-      test: 'rabbitmq-diagnostics -q ping'
+      test: "rabbitmq-diagnostics -q ping"
       interval: 5s
       timeout: 5s
       retries: 30
 
   mailcatcher:
     healthcheck:
-      test: 'wget --no-verbose --tries=1 --spider 127.0.0.1:1080 || exit 1'
+      test: "wget --no-verbose --tries=1 --spider 127.0.0.1:1080 || exit 1"

+ 5 - 3
compose/compose.yaml

@@ -1,7 +1,7 @@
 ## Mark Shust's Docker Configuration for Magento
 ## (https://github.com/markshust/docker-magento)
 ##
-## Version 47.0.1
+## Version 48.0.0
 
 ## To use SSH, see https://github.com/markshust/docker-magento#ssh
 ## Linux users, see https://github.com/markshust/docker-magento#linux
@@ -30,7 +30,7 @@ services:
       #- "host.docker.internal:host-gateway"
 
   phpfpm:
-    image: markoshust/magento-php:8.3-fpm-2
+    image: markoshust/magento-php:8.3-fpm-3
     volumes: *appvolumes
     env_file: env/phpfpm.env
     #extra_hosts: *appextrahosts
@@ -104,9 +104,11 @@ services:
   #    #- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
   #    ## Uncomment to increase the virtual memory map count
   #    #- "max_map_count=262144"
+  #    ## Uncomment the line below if ElasticSearch fails to start and stops after ~150 seconds with the error "container for service "elasticsearch" is unhealthy".
+  #    #- "xpack.security.enabled=false"
 
   rabbitmq:
-    image: markoshust/magento-rabbitmq:3.12-0
+    image: markoshust/magento-rabbitmq:3.13-0
     ports:
       - "15672:15672"
       - "5672:5672"

+ 21 - 8
images/php/8.1/Dockerfile

@@ -1,5 +1,5 @@
 FROM php:8.1-fpm-bookworm
-MAINTAINER Mark Shust <mark@shust.com>
+LABEL maintainer="Mark Shust <mark@shust.com>"
 
 ARG APP_ID=1000
 RUN groupadd -g "$APP_ID" app \
@@ -34,6 +34,7 @@ RUN apt-get update && apt-get install -y \
     msmtp \
     nodejs \
     procps \
+    rsync \
     strace \
     vim \
     zip \
@@ -41,14 +42,26 @@ RUN apt-get update && apt-get install -y \
   && rm -rf /var/lib/apt/lists/*
 
 RUN pecl channel-update pecl.php.net && pecl install \
-    imagick-3.7.0 \
-    redis-6.0.2 \
-    ssh2-1.3.1 \
-    swoole-5.1.1 \
-    xdebug-3.2.2 \
+    redis-6.1.0 \
+    ssh2-1.4.1 \
+    swoole-5.1.5 \
+    xdebug-3.3.2 \
   && pecl clear-cache \
   && rm -rf /tmp/pear
 
+RUN imagick_branch="28f27044e435a2b203e32675e942eb8de620ee58" \
+    && curl -L https://github.com/Imagick/imagick/archive/$imagick_branch.zip -o imagick.zip \
+    && unzip imagick.zip \
+    && rm imagick.zip \
+    && cd imagick-$imagick_branch \
+    && phpize \
+    && ./configure --with-php-config=/usr/local/bin/php-config \
+    && make \
+    && make install \
+    && echo "extension=imagick.so" >> $PHP_INI_DIR/conf.d/imagick.ini \
+    && cd .. \
+    && rm -rf imagick-$imagick_branch
+
 RUN docker-php-ext-configure \
     gd --with-freetype --with-jpeg --with-webp \
   && docker-php-ext-install \
@@ -86,7 +99,7 @@ RUN version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
     && mv /tmp/blackfire/blackfire-*.so $(php -r "echo ini_get ('extension_dir');")/blackfire.so \
     && rm -rf /tmp/blackfire /tmp/blackfire-probe.tar.gz
 
-RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
+RUN git clone --branch v0.4.17 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
     && cd /usr/lib/php-spx \
     && phpize \
     && ./configure \
@@ -94,7 +107,7 @@ RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php
     && make install
 
 RUN curl -sS https://getcomposer.org/installer | \
-  php -- --version=2.2.18 --install-dir=/usr/local/bin --filename=composer
+  php -- --version=2.2.24 --install-dir=/usr/local/bin --filename=composer
 
 COPY conf/blackfire.ini $PHP_INI_DIR/conf.d/blackfire.ini
 COPY conf/spx.ini $PHP_INI_DIR/conf.d/spx.ini

+ 1 - 1
images/php/8.1/conf/php-fpm.conf

@@ -1,4 +1,4 @@
-; This file was initially adapated from the output of: (on PHP 5.6)
+; This file was initially adapted from the output of: (on PHP 5.6)
 ;   grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default
 
 [global]

+ 31 - 8
images/php/8.2/Dockerfile

@@ -1,5 +1,5 @@
 FROM php:8.2-fpm-bookworm
-MAINTAINER Mark Shust <mark@shust.com>
+LABEL maintainer="Mark Shust <mark@shust.com>"
 
 ARG APP_ID=1000
 RUN groupadd -g "$APP_ID" app \
@@ -34,21 +34,43 @@ RUN apt-get update && apt-get install -y \
     msmtp \
     nodejs \
     procps \
+    rsync \
     strace \
     vim \
     zip \
     zlib1g-dev \
   && rm -rf /var/lib/apt/lists/*
 
+#RUN pecl channel-update pecl.php.net && pecl install \
+#    imagick-3.7.0 \
+#    redis-6.0.2 \
+#    ssh2-1.3.1 \
+#    swoole-5.1.1 \
+#    xdebug-3.2.2 \
+#  && pecl clear-cache \
+#  && rm -rf /tmp/pear
+
 RUN pecl channel-update pecl.php.net && pecl install \
-    imagick-3.7.0 \
-    redis-6.0.2 \
-    ssh2-1.3.1 \
-    swoole-5.1.1 \
-    xdebug-3.2.2 \
+    redis-6.1.0 \
+    ssh2-1.4.1 \
+    swoole-5.1.5 \
+    xdebug-3.3.2 \
   && pecl clear-cache \
   && rm -rf /tmp/pear
 
+RUN imagick_branch="28f27044e435a2b203e32675e942eb8de620ee58" \
+    && curl -L https://github.com/Imagick/imagick/archive/$imagick_branch.zip -o imagick.zip \
+    && unzip imagick.zip \
+    && rm imagick.zip \
+    && cd imagick-$imagick_branch \
+    && phpize \
+    && ./configure --with-php-config=/usr/local/bin/php-config \
+    && make \
+    && make install \
+    && echo "extension=imagick.so" >> $PHP_INI_DIR/conf.d/imagick.ini \
+    && cd .. \
+    && rm -rf imagick-$imagick_branch
+
 RUN docker-php-ext-configure \
     gd --with-freetype --with-jpeg --with-webp \
   && docker-php-ext-install \
@@ -56,6 +78,7 @@ RUN docker-php-ext-configure \
     bz2 \
     calendar \
     exif \
+    ftp \
     gd \
     gettext \
     intl \
@@ -86,7 +109,7 @@ RUN version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
     && mv /tmp/blackfire/blackfire-*.so $(php -r "echo ini_get ('extension_dir');")/blackfire.so \
     && rm -rf /tmp/blackfire /tmp/blackfire-probe.tar.gz
 
-RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
+RUN git clone --branch v0.4.17 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
     && cd /usr/lib/php-spx \
     && phpize \
     && ./configure \
@@ -94,7 +117,7 @@ RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php
     && make install
 
 RUN curl -sS https://getcomposer.org/installer | \
-  php -- --version=2.2.18 --install-dir=/usr/local/bin --filename=composer
+  php -- --version=2.7.9 --install-dir=/usr/local/bin --filename=composer
 
 COPY conf/blackfire.ini $PHP_INI_DIR/conf.d/blackfire.ini
 COPY conf/spx.ini $PHP_INI_DIR/conf.d/spx.ini

+ 1 - 1
images/php/8.2/conf/php-fpm.conf

@@ -1,4 +1,4 @@
-; This file was initially adapated from the output of: (on PHP 5.6)
+; This file was initially adapted from the output of: (on PHP 5.6)
 ;   grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default
 
 [global]

+ 12 - 9
images/php/8.3/Dockerfile

@@ -1,5 +1,5 @@
 FROM php:8.3-fpm-bookworm
-MAINTAINER Mark Shust <mark@shust.com>
+LABEL maintainer="Mark Shust <mark@shust.com>"
 
 ARG APP_ID=1000
 RUN groupadd -g "$APP_ID" app \
@@ -34,6 +34,7 @@ RUN apt-get update && apt-get install -y \
     msmtp \
     nodejs \
     procps \
+    rsync \
     strace \
     vim \
     zip \
@@ -41,24 +42,25 @@ RUN apt-get update && apt-get install -y \
   && rm -rf /var/lib/apt/lists/*
 
 RUN pecl channel-update pecl.php.net && pecl install \
-    redis-6.0.2 \
-    ssh2-1.3.1 \
-    swoole-5.1.1 \
+    redis-6.1.0 \
+    ssh2-1.4.1 \
+    swoole-5.1.5 \
     xdebug-3.3.2 \
   && pecl clear-cache \
   && rm -rf /tmp/pear
 
-RUN curl -L https://github.com/Imagick/imagick/archive/28f27044e435a2b203e32675e942eb8de620ee58.zip -o imagick.zip \
+RUN imagick_branch="28f27044e435a2b203e32675e942eb8de620ee58" \
+    && curl -L https://github.com/Imagick/imagick/archive/$imagick_branch.zip -o imagick.zip \
     && unzip imagick.zip \
     && rm imagick.zip \
-    && cd imagick-28f27044e435a2b203e32675e942eb8de620ee58 \
+    && cd imagick-$imagick_branch \
     && phpize \
     && ./configure --with-php-config=/usr/local/bin/php-config \
     && make \
     && make install \
     && echo "extension=imagick.so" >> $PHP_INI_DIR/conf.d/imagick.ini \
     && cd .. \
-    && rm -rf imagick-28f27044e435a2b203e32675e942eb8de620ee58
+    && rm -rf imagick-$imagick_branch
 
 RUN docker-php-ext-configure \
     gd --with-freetype --with-jpeg --with-webp \
@@ -67,6 +69,7 @@ RUN docker-php-ext-configure \
     bz2 \
     calendar \
     exif \
+    ftp \
     gd \
     gettext \
     intl \
@@ -97,7 +100,7 @@ RUN version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
     && mv /tmp/blackfire/blackfire-*.so $(php -r "echo ini_get ('extension_dir');")/blackfire.so \
     && rm -rf /tmp/blackfire /tmp/blackfire-probe.tar.gz
 
-RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
+RUN git clone --branch v0.4.17 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
     && cd /usr/lib/php-spx \
     && phpize \
     && ./configure \
@@ -105,7 +108,7 @@ RUN git clone --branch v0.4.15 --depth=1 https://github.com/NoiseByNorthwest/php
     && make install
 
 RUN curl -sS https://getcomposer.org/installer | \
-  php -- --version=2.6.6 --install-dir=/usr/local/bin --filename=composer
+  php -- --version=2.7.9 --install-dir=/usr/local/bin --filename=composer
 
 COPY conf/blackfire.ini $PHP_INI_DIR/conf.d/blackfire.ini
 COPY conf/spx.ini $PHP_INI_DIR/conf.d/spx.ini

+ 1 - 1
images/php/8.3/conf/php-fpm.conf

@@ -1,4 +1,4 @@
-; This file was initially adapated from the output of: (on PHP 5.6)
+; This file was initially adapted from the output of: (on PHP 5.6)
 ;   grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default
 
 [global]

+ 124 - 0
images/php/8.4/Dockerfile

@@ -0,0 +1,124 @@
+FROM php:8.4-fpm-bookworm
+LABEL maintainer="Mark Shust <mark@shust.com>"
+
+ARG APP_ID=1000
+RUN groupadd -g "$APP_ID" app \
+  && useradd -g "$APP_ID" -u "$APP_ID" -d /var/www -s /bin/bash app
+
+RUN mkdir -p /etc/nginx/html /var/www/html /sock \
+  && chown -R app:app /etc/nginx /var/www /usr/local/etc/php/conf.d /sock
+
+RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
+
+RUN apt-get update && apt-get install -y \
+    cron \
+    default-mysql-client \
+    git \
+    gnupg \
+    gzip \
+    libbz2-dev \
+    libfreetype6-dev \
+    libicu-dev \
+    libjpeg62-turbo-dev \
+    libmagickwand-dev \
+    libmcrypt-dev \
+    libonig-dev \
+    libpng-dev \
+    libsodium-dev \
+    libssh2-1-dev \
+    libwebp-dev \
+    libxslt1-dev \
+    libzip-dev \
+    lsof \
+    mailutils \
+    msmtp \
+    nodejs \
+    procps \
+    rsync \
+    strace \
+    vim \
+    zip \
+    zlib1g-dev \
+  && rm -rf /var/lib/apt/lists/*
+
+RUN pecl channel-update pecl.php.net \
+    && pecl install redis-6.1.0 \
+    && pecl install ssh2-1.4.1 \
+    && pecl install swoole-6.0.0RC1 \
+    && pecl install xdebug-3.4.0beta1 \
+    && pecl clear-cache \
+    && rm -rf /tmp/pear
+
+RUN imagick_branch="28f27044e435a2b203e32675e942eb8de620ee58" \
+    && curl -L https://github.com/Imagick/imagick/archive/$imagick_branch.zip -o imagick.zip \
+    && unzip imagick.zip \
+    && rm imagick.zip \
+    && cd imagick-$imagick_branch \
+    && phpize \
+    && ./configure --with-php-config=/usr/local/bin/php-config \
+    && make \
+    && make install \
+    && echo "extension=imagick.so" >> $PHP_INI_DIR/conf.d/imagick.ini \
+    && cd .. \
+    && rm -rf imagick-$imagick_branch
+
+RUN docker-php-ext-configure \
+    gd --with-freetype --with-jpeg --with-webp \
+  && docker-php-ext-install \
+    bcmath \
+    bz2 \
+    calendar \
+    exif \
+    ftp \
+    gd \
+    gettext \
+    intl \
+    mbstring \
+    mysqli \
+    opcache \
+    pcntl \
+    pdo_mysql \
+    soap \
+    sockets \
+    sodium \
+    sysvmsg \
+    sysvsem \
+    sysvshm \
+    xsl \
+    zip \
+  && docker-php-ext-enable \
+    imagick \
+    redis \
+    ssh2 \
+    xdebug
+
+# Temporary workaround for PHP 8.4
+RUN version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
+    && version=83 \
+    && architecture=$(uname -m) \
+    && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/$architecture/$version \
+    && mkdir -p /tmp/blackfire \
+    && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp/blackfire \
+    && mv /tmp/blackfire/blackfire-*.so $(php -r "echo ini_get ('extension_dir');")/blackfire.so \
+    && rm -rf /tmp/blackfire /tmp/blackfire-probe.tar.gz
+
+RUN git clone --branch v0.4.17 --depth=1 https://github.com/NoiseByNorthwest/php-spx.git /usr/lib/php-spx \
+    && cd /usr/lib/php-spx \
+    && phpize \
+    && ./configure \
+    && make \
+    && make install
+
+RUN curl -sS https://getcomposer.org/installer | \
+  php -- --version=2.7.9 --install-dir=/usr/local/bin --filename=composer
+
+COPY conf/blackfire.ini $PHP_INI_DIR/conf.d/blackfire.ini
+COPY conf/spx.ini $PHP_INI_DIR/conf.d/spx.ini
+COPY conf/msmtprc /etc/msmtprc
+COPY conf/php.ini $PHP_INI_DIR
+COPY conf/php-fpm.conf /usr/local/etc/
+COPY conf/www.conf /usr/local/etc/php-fpm.d/
+
+USER app:app
+VOLUME /var/www
+WORKDIR /var/www/html

+ 2 - 0
images/php/8.4/conf/blackfire.ini

@@ -0,0 +1,2 @@
+extension=blackfire.so
+blackfire.agent_socket=tcp://blackfire:8307

+ 4 - 0
images/php/8.4/conf/msmtprc

@@ -0,0 +1,4 @@
+account default
+host mailcatcher
+port 1025
+from "user@domain.com"

+ 34 - 0
images/php/8.4/conf/php-fpm.conf

@@ -0,0 +1,34 @@
+; This file was initially adapted from the output of: (on PHP 5.6)
+;   grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default
+
+[global]
+
+error_log = /proc/self/fd/2
+daemonize = no
+
+[www]
+
+; if we send this to /proc/self/fd/1, it never appears
+access.log = /proc/self/fd/2
+
+;user = app
+;group = app
+
+listen = /sock/docker.sock
+listen.owner = app
+listen.group = app
+listen.mode = 0660
+
+pm = dynamic
+pm.max_children = 10
+pm.start_servers = 4
+pm.min_spare_servers = 2
+pm.max_spare_servers = 6
+
+clear_env = no
+
+; Ensure worker stdout and stderr are sent to the main error log.
+catch_workers_output = yes
+
+; This needed to make PHP-SPX work in fpm mode.
+process.dumpable = yes

+ 15 - 0
images/php/8.4/conf/php.ini

@@ -0,0 +1,15 @@
+memory_limit = 4G
+max_execution_time = 1800
+zlib.output_compression = 1
+cgi.fix_pathinfo = 0
+date.timezone = UTC
+
+xdebug.mode = off
+xdebug.client_host = host.docker.internal
+xdebug.idekey = PHPSTORM
+
+upload_max_filesize = 100M
+post_max_size = 100M
+max_input_vars = 10000
+
+sendmail_path = "/usr/bin/msmtp -t"

+ 5 - 0
images/php/8.4/conf/spx.ini

@@ -0,0 +1,5 @@
+extension = /usr/lib/php-spx/modules/spx.so
+spx.http_enabled = 1
+spx.http_key = "dev"
+spx.http_ip_whitelist = "*"
+spx.data_dir = /var/www/spx_dumps

+ 413 - 0
images/php/8.4/conf/www.conf

@@ -0,0 +1,413 @@
+; Start a new pool named 'www'.
+; the variable $pool can we used in any directive and will be replaced by the
+; pool name ('www' here)
+[www]
+
+; Per pool prefix
+; It only applies on the following directives:
+; - 'access.log'
+; - 'slowlog'
+; - 'listen' (unixsocket)
+; - 'chroot'
+; - 'chdir'
+; - 'php_values'
+; - 'php_admin_values'
+; When not set, the global prefix (or NONE) applies instead.
+; Note: This directive can also be relative to the global prefix.
+; Default Value: none
+;prefix = /path/to/pools/$pool
+
+; Unix user/group of processes
+; Note: The user is mandatory. If the group is not set, the default user's group
+;       will be used.
+user = app
+group = app
+
+; The address on which to accept FastCGI requests.
+; Valid syntaxes are:
+;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4 address on
+;                            a specific port;
+;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
+;                            a specific port;
+;   'port'                 - to listen on a TCP socket to all addresses
+;                            (IPv6 and IPv4-mapped) on a specific port;
+;   '/path/to/unix/socket' - to listen on a unix socket.
+; Note: This value is mandatory.
+listen = 127.0.0.1:9000
+
+; Set listen(2) backlog.
+; Default Value: 511 (-1 on FreeBSD and OpenBSD)
+;listen.backlog = 511
+
+; Set permissions for unix socket, if one is used. In Linux, read/write
+; permissions must be set in order to allow connections from a web server. Many
+; BSD-derived systems allow connections regardless of permissions.
+; Default Values: user and group are set as the running user
+;                 mode is set to 0660
+;listen.owner = www-data
+;listen.group = www-data
+;listen.mode = 0660
+; When POSIX Access Control Lists are supported you can set them using
+; these options, value is a comma separated list of user/group names.
+; When set, listen.owner and listen.group are ignored
+;listen.acl_users =
+;listen.acl_groups =
+
+; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
+; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
+; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
+; must be separated by a comma. If this value is left blank, connections will be
+; accepted from any ip address.
+; Default Value: any
+;listen.allowed_clients = 127.0.0.1
+
+; Specify the nice(2) priority to apply to the pool processes (only if set)
+; The value can vary from -19 (highest priority) to 20 (lower priority)
+; Note: - It will only work if the FPM master process is launched as root
+;       - The pool processes will inherit the master process priority
+;         unless it specified otherwise
+; Default Value: no set
+; process.priority = -19
+
+; Choose how the process manager will control the number of child processes.
+; Possible Values:
+;   static  - a fixed number (pm.max_children) of child processes;
+;   dynamic - the number of child processes are set dynamically based on the
+;             following directives. With this process management, there will be
+;             always at least 1 children.
+;             pm.max_children      - the maximum number of children that can
+;                                    be alive at the same time.
+;             pm.start_servers     - the number of children created on startup.
+;             pm.min_spare_servers - the minimum number of children in 'idle'
+;                                    state (waiting to process). If the number
+;                                    of 'idle' processes is less than this
+;                                    number then some children will be created.
+;             pm.max_spare_servers - the maximum number of children in 'idle'
+;                                    state (waiting to process). If the number
+;                                    of 'idle' processes is greater than this
+;                                    number then some children will be killed.
+;  ondemand - no children are created at startup. Children will be forked when
+;             new requests will connect. The following parameter are used:
+;             pm.max_children           - the maximum number of children that
+;                                         can be alive at the same time.
+;             pm.process_idle_timeout   - The number of seconds after which
+;                                         an idle process will be killed.
+; Note: This value is mandatory.
+pm = dynamic
+
+; The number of child processes to be created when pm is set to 'static' and the
+; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
+; This value sets the limit on the number of simultaneous requests that will be
+; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
+; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
+; CGI. The below defaults are based on a server without much resources. Don't
+; forget to tweak pm.* to fit your needs.
+; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
+; Note: This value is mandatory.
+pm.max_children = 5
+
+; The number of child processes created on startup.
+; Note: Used only when pm is set to 'dynamic'
+; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
+pm.start_servers = 2
+
+; The desired minimum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.min_spare_servers = 1
+
+; The desired maximum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.max_spare_servers = 3
+
+; The number of seconds after which an idle process will be killed.
+; Note: Used only when pm is set to 'ondemand'
+; Default Value: 10s
+;pm.process_idle_timeout = 10s;
+
+; The number of requests each child process should execute before respawning.
+; This can be useful to work around memory leaks in 3rd party libraries. For
+; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
+; Default Value: 0
+;pm.max_requests = 500
+
+; The URI to view the FPM status page. If this value is not set, no URI will be
+; recognized as a status page. It shows the following informations:
+;   pool                 - the name of the pool;
+;   process manager      - static, dynamic or ondemand;
+;   start time           - the date and time FPM has started;
+;   start since          - number of seconds since FPM has started;
+;   accepted conn        - the number of request accepted by the pool;
+;   listen queue         - the number of request in the queue of pending
+;                          connections (see backlog in listen(2));
+;   max listen queue     - the maximum number of requests in the queue
+;                          of pending connections since FPM has started;
+;   listen queue len     - the size of the socket queue of pending connections;
+;   idle processes       - the number of idle processes;
+;   active processes     - the number of active processes;
+;   total processes      - the number of idle + active processes;
+;   max active processes - the maximum number of active processes since FPM
+;                          has started;
+;   max children reached - number of times, the process limit has been reached,
+;                          when pm tries to start more children (works only for
+;                          pm 'dynamic' and 'ondemand');
+; Value are updated in real time.
+; Example output:
+;   pool:                 www
+;   process manager:      static
+;   start time:           01/Jul/2011:17:53:49 +0200
+;   start since:          62636
+;   accepted conn:        190460
+;   listen queue:         0
+;   max listen queue:     1
+;   listen queue len:     42
+;   idle processes:       4
+;   active processes:     11
+;   total processes:      15
+;   max active processes: 12
+;   max children reached: 0
+;
+; By default the status page output is formatted as text/plain. Passing either
+; 'html', 'xml' or 'json' in the query string will return the corresponding
+; output syntax. Example:
+;   http://www.foo.bar/status
+;   http://www.foo.bar/status?json
+;   http://www.foo.bar/status?html
+;   http://www.foo.bar/status?xml
+;
+; By default the status page only outputs short status. Passing 'full' in the
+; query string will also return status for each pool process.
+; Example:
+;   http://www.foo.bar/status?full
+;   http://www.foo.bar/status?json&full
+;   http://www.foo.bar/status?html&full
+;   http://www.foo.bar/status?xml&full
+; The Full status returns for each process:
+;   pid                  - the PID of the process;
+;   state                - the state of the process (Idle, Running, ...);
+;   start time           - the date and time the process has started;
+;   start since          - the number of seconds since the process has started;
+;   requests             - the number of requests the process has served;
+;   request duration     - the duration in µs of the requests;
+;   request method       - the request method (GET, POST, ...);
+;   request URI          - the request URI with the query string;
+;   content length       - the content length of the request (only with POST);
+;   user                 - the user (PHP_AUTH_USER) (or '-' if not set);
+;   script               - the main script called (or '-' if not set);
+;   last request cpu     - the %cpu the last request consumed
+;                          it's always 0 if the process is not in Idle state
+;                          because CPU calculation is done when the request
+;                          processing has terminated;
+;   last request memory  - the max amount of memory the last request consumed
+;                          it's always 0 if the process is not in Idle state
+;                          because memory calculation is done when the request
+;                          processing has terminated;
+; If the process is in Idle state, then informations are related to the
+; last request the process has served. Otherwise informations are related to
+; the current request being served.
+; Example output:
+;   ************************
+;   pid:                  31330
+;   state:                Running
+;   start time:           01/Jul/2011:17:53:49 +0200
+;   start since:          63087
+;   requests:             12808
+;   request duration:     1250261
+;   request method:       GET
+;   request URI:          /test_mem.php?N=10000
+;   content length:       0
+;   user:                 -
+;   script:               /home/fat/web/docs/php/test_mem.php
+;   last request cpu:     0.00
+;   last request memory:  0
+;
+; Note: There is a real-time FPM status monitoring sample web page available
+;       It's available in: /usr/local/share/php/fpm/status.html
+;
+; Note: The value must start with a leading slash (/). The value can be
+;       anything, but it may not be a good idea to use the .php extension or it
+;       may conflict with a real PHP file.
+; Default Value: not set
+;pm.status_path = /status
+
+; The ping URI to call the monitoring page of FPM. If this value is not set, no
+; URI will be recognized as a ping page. This could be used to test from outside
+; that FPM is alive and responding, or to
+; - create a graph of FPM availability (rrd or such);
+; - remove a server from a group if it is not responding (load balancing);
+; - trigger alerts for the operating team (24/7).
+; Note: The value must start with a leading slash (/). The value can be
+;       anything, but it may not be a good idea to use the .php extension or it
+;       may conflict with a real PHP file.
+; Default Value: not set
+;ping.path = /ping
+
+; This directive may be used to customize the response of a ping request. The
+; response is formatted as text/plain with a 200 response code.
+; Default Value: pong
+;ping.response = pong
+
+; The access log file
+; Default: not set
+;access.log = log/$pool.access.log
+
+; The access log format.
+; The following syntax is allowed
+;  %%: the '%' character
+;  %C: %CPU used by the request
+;      it can accept the following format:
+;      - %{user}C for user CPU only
+;      - %{system}C for system CPU only
+;      - %{total}C  for user + system CPU (default)
+;  %d: time taken to serve the request
+;      it can accept the following format:
+;      - %{seconds}d (default)
+;      - %{miliseconds}d
+;      - %{mili}d
+;      - %{microseconds}d
+;      - %{micro}d
+;  %e: an environment variable (same as $_ENV or $_SERVER)
+;      it must be associated with embraces to specify the name of the env
+;      variable. Some exemples:
+;      - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
+;      - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
+;  %f: script filename
+;  %l: content-length of the request (for POST request only)
+;  %m: request method
+;  %M: peak of memory allocated by PHP
+;      it can accept the following format:
+;      - %{bytes}M (default)
+;      - %{kilobytes}M
+;      - %{kilo}M
+;      - %{megabytes}M
+;      - %{mega}M
+;  %n: pool name
+;  %o: output header
+;      it must be associated with embraces to specify the name of the header:
+;      - %{Content-Type}o
+;      - %{X-Powered-By}o
+;      - %{Transfert-Encoding}o
+;      - ....
+;  %p: PID of the child that serviced the request
+;  %P: PID of the parent of the child that serviced the request
+;  %q: the query string
+;  %Q: the '?' character if query string exists
+;  %r: the request URI (without the query string, see %q and %Q)
+;  %R: remote IP address
+;  %s: status (response code)
+;  %t: server time the request was received
+;      it can accept a strftime(3) format:
+;      %d/%b/%Y:%H:%M:%S %z (default)
+;      The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
+;      e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
+;  %T: time the log has been written (the request has finished)
+;      it can accept a strftime(3) format:
+;      %d/%b/%Y:%H:%M:%S %z (default)
+;      The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
+;      e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
+;  %u: remote user
+;
+; Default: "%R - %u %t \"%m %r\" %s"
+;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
+
+; The log file for slow requests
+; Default Value: not set
+; Note: slowlog is mandatory if request_slowlog_timeout is set
+;slowlog = log/$pool.log.slow
+
+; The timeout for serving a single request after which a PHP backtrace will be
+; dumped to the 'slowlog' file. A value of '0s' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_slowlog_timeout = 0
+
+; The timeout for serving a single request after which the worker process will
+; be killed. This option should be used when the 'max_execution_time' ini option
+; does not stop script execution for some reason. A value of '0' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_terminate_timeout = 0
+
+; Set open file descriptor rlimit.
+; Default Value: system defined value
+;rlimit_files = 1024
+
+; Set max core size rlimit.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+;rlimit_core = 0
+
+; Chroot to this directory at the start. This value must be defined as an
+; absolute path. When this value is not set, chroot is not used.
+; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
+; of its subdirectories. If the pool prefix is not set, the global prefix
+; will be used instead.
+; Note: chrooting is a great security feature and should be used whenever
+;       possible. However, all PHP paths will be relative to the chroot
+;       (error_log, sessions.save_path, ...).
+; Default Value: not set
+;chroot =
+
+; Chdir to this directory at the start.
+; Note: relative path can be used.
+; Default Value: current directory or / when chroot
+;chdir = /var/www
+
+; Redirect worker stdout and stderr into main error log. If not set, stdout and
+; stderr will be redirected to /dev/null according to FastCGI specs.
+; Note: on highloaded environement, this can cause some delay in the page
+; process time (several ms).
+; Default Value: no
+;catch_workers_output = yes
+
+; Clear environment in FPM workers
+; Prevents arbitrary environment variables from reaching FPM worker processes
+; by clearing the environment in workers before env vars specified in this
+; pool configuration are added.
+; Setting to "no" will make all environment variables available to PHP code
+; via getenv(), $_ENV and $_SERVER.
+; Default Value: yes
+;clear_env = no
+
+; Limits the extensions of the main script FPM will allow to parse. This can
+; prevent configuration mistakes on the web server side. You should only limit
+; FPM to .php extensions to prevent malicious users to use other extensions to
+; exectute php code.
+; Note: set an empty value to allow all extensions.
+; Default Value: .php
+;security.limit_extensions = .php .php3 .php4 .php5 .php7
+
+; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
+; the current environment.
+; Default Value: clean env
+;env[HOSTNAME] = $HOSTNAME
+;env[PATH] = /usr/local/bin:/usr/bin:/bin
+;env[TMP] = /tmp
+;env[TMPDIR] = /tmp
+;env[TEMP] = /tmp
+
+; Additional php.ini defines, specific to this pool of workers. These settings
+; overwrite the values previously defined in the php.ini. The directives are the
+; same as the PHP SAPI:
+;   php_value/php_flag             - you can set classic ini defines which can
+;                                    be overwritten from PHP call 'ini_set'.
+;   php_admin_value/php_admin_flag - these directives won't be overwritten by
+;                                     PHP call 'ini_set'
+; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
+
+; Defining 'extension' will load the corresponding shared extension from
+; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
+; overwrite previously defined php.ini values, but will append the new value
+; instead.
+
+; Note: path INI options can be relative and will be expanded with the prefix
+; (pool, global or /usr/local)
+
+; Default Value: nothing is defined by default except the values in php.ini and
+;                specified at startup with the -d argument
+;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
+;php_flag[display_errors] = off
+;php_admin_value[error_log] = /var/log/fpm-php.www.log
+;php_admin_flag[log_errors] = on
+;php_admin_value[memory_limit] = 32M

+ 3 - 0
images/rabbitmq/3.13/Dockerfile

@@ -0,0 +1,3 @@
+FROM rabbitmq:3.13-management-alpine
+
+COPY conf/rabbitmq.conf /etc/rabbitmq/rabbitmq.conf

+ 1 - 0
images/rabbitmq/3.13/conf/rabbitmq.conf

@@ -0,0 +1 @@
+vm_memory_high_watermark.absolute = 1GB

+ 4 - 8
lib/onelinesetup

@@ -2,16 +2,12 @@
 set -o errexit
 
 DOMAIN=${1:-magento.test}
-VERSION=${2:-2.4.7}
-EDITION=${3:-community}
-
-if [ -d "./bin" ]; then
-  echo "Error: The current directory is not empty. Please remove all contents within this directory and try again."
-  exit 1
-fi
+EDITION=${2}
+VERSION=${3}
 
 curl -s https://raw.githubusercontent.com/markshust/docker-magento/master/lib/template | bash
 
 # &&'s are used below otherwise onelinesetup script fails/errors after bin/download
-bin/download "${VERSION}" "${EDITION}" \
+# ${:+""} format only includes parameters if they were explicitly set
+bin/download ${EDITION:+"$EDITION"} ${VERSION:+"$VERSION"} \
   && bin/setup "${DOMAIN}"

+ 5 - 4
lib/template

@@ -1,14 +1,15 @@
 #!/usr/bin/env bash
-git init -qqq
-git remote add origin https://github.com/markshust/docker-magento
-git fetch origin -qqq
-git checkout origin/master -- compose
 
 if [ -d "./bin" ]; then
   echo "Error: The current directory is not empty. Please remove all contents within this directory and try again."
   exit 1
 fi
 
+git init -qqq
+git remote add origin https://github.com/markshust/docker-magento
+git fetch origin -qqq
+git checkout origin/master -- compose
+
 mv compose/* ./
 mv compose/.gitignore ./
 mv compose/.vscode ./