Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Use the "bullseye" based PHP image
FROM php:8.3-fpm-bullseye
# Use the "bookworm" based PHP image
# PHP 8.5 is supported by Mage-OS 3.0.0 / Magento 2.4.9 (framework requires ~8.3 || ~8.4 || ~8.5)
FROM php:8.5-fpm-bookworm

# Environment variables to allow non-interactive installation
ENV MYSQL_ROOT_PASSWORD=password
Expand Down Expand Up @@ -29,7 +30,7 @@ RUN apt-get update && apt-get install -y \
libmcrypt-dev \
supervisor \
nginx \
redis-server \
locales \
zip \
unzip \
git \
Expand All @@ -47,6 +48,14 @@ RUN apt-get update && apt-get install -y \
vim \
sudo

# Generate the en_US.UTF-8 locale (the Codespaces env sets LANG=en_US.UTF-8,
# and Valkey 8 fatally exits if that locale is not available)
RUN sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && \
locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8

# Install PHP extensions required for Magento
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-xpm && \
docker-php-ext-install -j$(nproc) \
Expand All @@ -63,39 +72,41 @@ RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-x
ftp \
xsl

# Install additional PHP extensions via PECL
RUN pecl install imagick redis xdebug && \
docker-php-ext-enable imagick redis xdebug
# Install imagick from source (builds cleanly against current PHP; the released
# PECL tarball has historically lagged new PHP versions)
RUN git clone https://github.com/Imagick/imagick --depth 1 /tmp/imagick && \
cd /tmp/imagick && phpize && ./configure && make && make install && \
rm -rf /tmp/imagick && docker-php-ext-enable imagick
RUN pecl install redis xdebug && docker-php-ext-enable redis xdebug

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Install MariaDB Server 10.6 (for Magento 2.4 compatibility)
# Install MariaDB Server 11.4 (for Magento 2.4.9 compatibility)
RUN apt-get update && apt-get install -y software-properties-common dirmngr \
&& curl -LsSO https://downloads.mariadb.com/MariaDB/mariadb_repo_setup \
&& chmod +x mariadb_repo_setup \
&& ./mariadb_repo_setup --mariadb-server-version="10.6" \
&& ./mariadb_repo_setup --mariadb-server-version="11.4" \
&& apt-get update \
&& echo "mariadb-server-10.6 mysql-server/root_password password ${MYSQL_ROOT_PASSWORD}" | debconf-set-selections \
&& echo "mariadb-server-10.6 mysql-server/root_password_again password ${MYSQL_ROOT_PASSWORD}" | debconf-set-selections \
&& echo "mariadb-server-11.4 mysql-server/root_password password ${MYSQL_ROOT_PASSWORD}" | debconf-set-selections \
&& echo "mariadb-server-11.4 mysql-server/root_password_again password ${MYSQL_ROOT_PASSWORD}" | debconf-set-selections \
&& apt-get install -y mariadb-server mariadb-client \
&& mkdir -p /var/run/mysqld \
&& chown mysql:mysql /var/run/mysqld \
&& rm mariadb_repo_setup

# Install Valkey 8 (Redis-compatible cache/session store for Magento 2.4.9)
RUN echo "deb http://deb.debian.org/debian bookworm-backports main" > /etc/apt/sources.list.d/backports.list && \
apt-get update && apt-get install -y -t bookworm-backports valkey-server

# Install Node.js and npm
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - && \
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && \
apt-get install -y nodejs

# Install Java, a dependency for OpenSearch
RUN apt-get update && apt-get install -y openjdk-17-jdk-headless
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

# Prepare for OpenSearch installation by adding the repository and key
RUN curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp | gpg --dearmor --batch --yes -o /usr/share/keyrings/opensearch-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring.gpg] https://artifacts.opensearch.org/releases/bundle/opensearch/2.x/apt stable main" | tee /etc/apt/sources.list.d/opensearch-2.x.list && \
apt-get update

# Install n98-magerun2
RUN wget https://files.magerun.net/n98-magerun2.phar && \
chmod +x ./n98-magerun2.phar && \
Expand Down
2 changes: 0 additions & 2 deletions .devcontainer/config/mysql.cnf
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_log_buffer_size = 32M
innodb_file_per_table = 1
max_allowed_packet = 128M
bind-address = 0.0.0.0
Expand Down
14 changes: 10 additions & 4 deletions .devcontainer/config/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,20 @@ http {
try_files $uri =404;
fastcgi_pass fastcgi_backend;

# This block is the key fix. It ensures the correct headers are passed to PHP-FPM.
# Include standard FastCGI parameters FIRST, then override.
# fastcgi_params sets HTTP_HOST=$host (localhost behind the Codespaces
# proxy) and SERVER_PORT=$server_port (8080). These must come before our
# overrides, otherwise they clobber them and Magento sees host "localhost"
# != base_url host, triggering an infinite redirect_to_base loop.
include fastcgi_params;

# Codespaces terminates TLS at its proxy and forwards as plain HTTP to
# localhost:8080, with the real host in X-Forwarded-Host. Force the values
# Magento needs to match the configured https base URL.
fastcgi_param HTTP_HOST $effective_host;
fastcgi_param HTTPS 'on';
fastcgi_param SERVER_PORT 443;
fastcgi_param REQUEST_SCHEME 'https';

# Include standard FastCGI parameters
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Expand Down
4 changes: 2 additions & 2 deletions .devcontainer/config/php-fpm.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
; Pid file
; Note: the default prefix is /var
; Default Value: none
pid = /tmp/php8.3-fpm.pid
pid = /tmp/php8.5-fpm.pid

; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; in a local file.
; Note: the default prefix is /var
; Default Value: log/php-fpm.log
error_log = /tmp/php8.3-fpm.log
error_log = /tmp/php8.5-fpm.log

; syslog_facility is used to specify what type of program is logging the
; message. This lets syslogd specify that messages from different facilities
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[program:redis]
command=/usr/bin/redis-server
[program:valkey]
command=/usr/bin/valkey-server
process_name=%(program_name)s
priority=1001
autostart=true
startretries=3
autorestart=true
user=root
user=root
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"8080": { "label": "Mage-OS/Magento" },
"8081": { "label": "PHP MyAdmin" },
"3306": { "label": "MySQL" },
"6379": { "label": "Redis" },
"6379": { "label": "Valkey" },
"1025": { "label": "Mailpit SMTP" },
"8025": { "label": "Mailpit Web" },
"9200": { "label": "OpenSearch" }
Expand All @@ -27,7 +27,7 @@
"HYVA_PROJECT_NAME": "${localEnv:HYVA_PROJECT_NAME:}",
"MAGENTO_COMPOSER_AUTH_USER": "${localEnv:MAGENTO_COMPOSER_AUTH_USER:}",
"MAGENTO_COMPOSER_AUTH_PASS": "${localEnv:MAGENTO_COMPOSER_AUTH_PASS:}",
"MAGENTO_VERSION": "${localEnv:MAGENTO_VERSION:2.4.7-p5}",
"MAGENTO_VERSION": "${localEnv:MAGENTO_VERSION:2.4.9}",
"MAGENTO_ADMIN_USERNAME": "${localEnv:MAGENTO_ADMIN_USERNAME:admin}",
"MAGENTO_ADMIN_PASSWORD": "${localEnv:MAGENTO_ADMIN_PASSWORD:password1}",
"MAGENTO_ADMIN_EMAIL": "${localEnv:MAGENTO_ADMIN_EMAIL:admin@example.com}",
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ start_container $OPENSEARCH_CONTAINER \
-e "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "DISABLE_INSTALL_DEMO_CONFIG=true" \
-e "plugins.security.disabled=true" \
opensearchproject/opensearch:2.19.2
opensearchproject/opensearch:3.0.0

# Start phpMyAdmin Container - connects to the main container via Docker bridge gateway
start_container $PHPMYADMIN_CONTAINER \
Expand Down
113 changes: 77 additions & 36 deletions .devcontainer/scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ show_ready_message() {
echo "Have an awesome time! 💙 Develo.co.uk"
}

# ======================================================================================
# Build the Hyvä theme CSS
# ======================================================================================
# We invoke the Tailwind build via npm directly instead of
# "n98-magerun2 dev:theme:build-hyva". That command forces TTY mode on its npm
# subprocess (failing with "TTY mode requires /dev/tty" in non-interactive
# container startup) and, without -p, runs Tailwind in watch mode which never
# returns. Running "npm run build" produces the same minified styles.css.
HYVA_TAILWIND_DIR="vendor/hyva-themes/magento2-default-theme/web/tailwind"
build_hyva_assets() {
echo "Building Hyvä theme CSS (Tailwind)..."
npm --prefix "${HYVA_TAILWIND_DIR}" install --no-audit --no-fund
# Tailwind v4's loader triggers Node's DEP0205 (module.register) deprecation
# warning on recent Node releases. It is harmless noise during a one-off CSS
# build, so silence deprecation warnings for this subprocess only.
NODE_OPTIONS="--no-deprecation" npm --prefix "${HYVA_TAILWIND_DIR}" run build
}

# ======================================================================================
# Supervisor Services (Nginx, MariaDB, Redis)
# ======================================================================================
Expand All @@ -33,7 +51,7 @@ sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/nginx.conf" /etc/nginx/ngi
sudo sed -i "s|__CODESPACES_REPO_ROOT__|${CODESPACES_REPO_ROOT}|g" /etc/nginx/nginx.conf
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/sp-php-fpm.conf" /etc/supervisor/conf.d/
sudo sed -i "s|\$CODESPACES_REPO_ROOT|${CODESPACES_REPO_ROOT}|g" /etc/supervisor/conf.d/sp-php-fpm.conf
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/sp-redis.conf" /etc/supervisor/conf.d/
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/sp-valkey.conf" /etc/supervisor/conf.d/
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/mysql.conf" /etc/supervisor/conf.d/
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/sp-nginx.conf" /etc/supervisor/conf.d/
sudo cp "${CODESPACES_REPO_ROOT}/.devcontainer/config/mysql.cnf" /etc/mysql/conf.d/
Expand All @@ -48,8 +66,7 @@ source "${CODESPACES_REPO_ROOT}/.devcontainer/scripts/start_services.sh"
if [ -f ".devcontainer/db-installed.flag" ]; then
echo "${PLATFORM_NAME} already installed, skipping installation/import."
if [ "${HYVA_LICENCE_KEY}" ]; then
echo "Building Hyvä theme assets..."
n98-magerun2 dev:theme:build-hyva frontend/Hyva/default
build_hyva_assets
echo "Hyvä theme configured successfully"
fi;
show_ready_message
Expand Down Expand Up @@ -114,17 +131,26 @@ else
if [ "${INSTALL_SAMPLE_DATA}" = "YES" ]; then
echo "============ Installing Sample Data =========="
echo "**** Deploying ${PLATFORM_NAME} sample data ****"
${COMPOSER_COMMAND} require ${PLATFORM_NAME}/module-bundle-sample-data ${PLATFORM_NAME}/module-widget-sample-data ${PLATFORM_NAME}/module-theme-sample-data ${PLATFORM_NAME}/module-catalog-sample-data ${PLATFORM_NAME}/module-customer-sample-data ${PLATFORM_NAME}/module-cms-sample-data ${PLATFORM_NAME}/module-catalog-rule-sample-data ${PLATFORM_NAME}/module-sales-rule-sample-data ${PLATFORM_NAME}/module-review-sample-data ${PLATFORM_NAME}/module-tax-sample-data ${PLATFORM_NAME}/module-sales-sample-data ${PLATFORM_NAME}/module-grouped-product-sample-data ${PLATFORM_NAME}/module-downloadable-sample-data ${PLATFORM_NAME}/module-msrp-sample-data ${PLATFORM_NAME}/module-configurable-sample-data ${PLATFORM_NAME}/module-product-links-sample-data ${PLATFORM_NAME}/module-wishlist-sample-data ${PLATFORM_NAME}/module-swatches-sample-data --no-update

php -d memory_limit=-1 bin/magento sampledata:deploy
${COMPOSER_COMMAND} update
# Require the sample data modules and install them via composer. We do
# NOT run "bin/magento sampledata:deploy" here: it bootstraps Magento
# before the app is installed (no env.php / empty generated/) and exits
# non-zero on PHP 8.4, aborting the script under "set -e". The composer
# require + update below pulls the same packages, and the sample data is
# loaded into the DB by setup:upgrade after setup:install.
# NOTE: ${PLATFORM_NAME}/sample-data-media is REQUIRED here. The sample-data
# modules ship only CSV fixtures + code; the actual product/CMS images live in
# the sample-data-media package (it is merely "suggest"ed by module-sample-data,
# so it is never pulled in automatically). Without it the catalog import cannot
# read pub/media/catalog/product and aborts, leaving most categories empty.
${COMPOSER_COMMAND} require ${PLATFORM_NAME}/module-bundle-sample-data ${PLATFORM_NAME}/module-widget-sample-data ${PLATFORM_NAME}/module-theme-sample-data ${PLATFORM_NAME}/module-catalog-sample-data ${PLATFORM_NAME}/module-customer-sample-data ${PLATFORM_NAME}/module-cms-sample-data ${PLATFORM_NAME}/module-catalog-rule-sample-data ${PLATFORM_NAME}/module-sales-rule-sample-data ${PLATFORM_NAME}/module-review-sample-data ${PLATFORM_NAME}/module-tax-sample-data ${PLATFORM_NAME}/module-sales-sample-data ${PLATFORM_NAME}/module-grouped-product-sample-data ${PLATFORM_NAME}/module-downloadable-sample-data ${PLATFORM_NAME}/module-msrp-sample-data ${PLATFORM_NAME}/module-configurable-sample-data ${PLATFORM_NAME}/module-product-links-sample-data ${PLATFORM_NAME}/module-wishlist-sample-data ${PLATFORM_NAME}/module-swatches-sample-data ${PLATFORM_NAME}/sample-data-media --no-update
${COMPOSER_COMMAND} update
echo "**** Sample data deployed successfully ****"
fi

# Decide whether to run a fresh install or import a database
if [ "${INSTALL_MAGENTO}" = "YES" ]; then
echo "============ Installing New ${PLATFORM_NAME} Instance ============"
mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "CREATE DATABASE IF NOT EXISTS magento2;"
mariadb -u root -p${MYSQL_ROOT_PASSWORD} -e "CREATE DATABASE IF NOT EXISTS magento2;"

url="https://${CODESPACE_NAME}-8080.app.github.dev/"
echo "Installing ${PLATFORM_NAME} with URL: $url"
Expand All @@ -148,23 +174,46 @@ else
--use-secure='1' \
--base-url-secure="$url" \
--use-secure-admin='1' \
--session-save='redis' \
--session-save-redis-host='127.0.0.1' \
--session-save-redis-port='6379' \
--cache-backend='redis' \
--cache-backend-redis-server='127.0.0.1' \
--cache-backend-redis-db='1' \
--page-cache='redis' \
--page-cache-redis-server='127.0.0.1' \
--page-cache-redis-db='2' \
--session-save='valkey' \
--session-save-valkey-host='127.0.0.1' \
--session-save-valkey-port='6379' \
--cache-backend='valkey' \
--cache-backend-valkey-server='127.0.0.1' \
--cache-backend-valkey-db='1' \
--page-cache='valkey' \
--page-cache-valkey-server='127.0.0.1' \
--page-cache-valkey-db='2' \
--search-engine='opensearch' \
--opensearch-host='localhost' \
--opensearch-port='9200'

# Run setup:upgrade if sample data was installed
if [ "${INSTALL_SAMPLE_DATA}" = "YES" ]; then
# The catalog product import reads images from pub/media/catalog/product, so the
# sample-data media MUST be staged (and readable) BEFORE setup:upgrade runs the
# import. Otherwise it fails with "File directory 'pub/media/catalog/product' is
# not readable" and only a handful of products are created.
SAMPLE_MEDIA_SOURCE="vendor/${PLATFORM_NAME}/sample-data-media"
mkdir -p pub/media/catalog/product
if [ -d "$SAMPLE_MEDIA_SOURCE" ]; then
echo "Staging sample data media into pub/media before import..."
rsync -a "${SAMPLE_MEDIA_SOURCE}/" pub/media/
else
echo "WARNING: ${SAMPLE_MEDIA_SOURCE} not found - product/CMS images will be missing."
fi
chmod -R a+rX pub/media

# The Media Gallery cms_*_save_after observers try to link every <img> in the
# sample-data CMS blocks/pages to a media_gallery_asset row. That table is only
# populated asynchronously by media-gallery:sync, so during import every image
# logs a critical "There is no such media asset" exception. Disable the media
# gallery for the import to silence them; it is re-enabled immediately after.
php -d memory_limit=-1 bin/magento config:set system/media_gallery/enabled 0

echo "============ Running setup:upgrade to install sample data =========="
php -d memory_limit=-1 bin/magento setup:upgrade

php -d memory_limit=-1 bin/magento config:set system/media_gallery/enabled 1
fi

if [ "${HYVA_LICENCE_KEY}" ] && [ "${HYVA_PROJECT_NAME}" ]; then
Expand Down Expand Up @@ -224,7 +273,7 @@ if [ "${HYVA_LICENCE_KEY}" ]; then
echo "Final Hyvä theme configuration..."

# Build Hyva theme assets
n98-magerun2 dev:theme:build-hyva frontend/Hyva/default
build_hyva_assets

# Deploy static content for Hyva theme
echo "Deploying static content for Hyvä theme..."
Expand Down Expand Up @@ -270,23 +319,15 @@ fi;


# ======================================================================================
# Fix for missing sample data media files
# Post-import media processing
# ======================================================================================
if [ "${INSTALL_SAMPLE_DATA}" = "YES" ]; then
SAMPLE_MEDIA_SOURCE="vendor/${PLATFORM_NAME}/sample-data-media"
MEDIA_DEST="pub/media"

if [ -d "$SAMPLE_MEDIA_SOURCE" ] && [ -w "$MEDIA_DEST" ]; then
echo "Found sample data media. Copying to pub/media..."
rsync -a "${SAMPLE_MEDIA_SOURCE}/" "${MEDIA_DEST}/"

if [ -f "bin/magento" ]; then
echo "Resizing product images and flushing cache..."
php -d memory_limit=-1 bin/magento catalog:image:resize
php -d memory_limit=-1 bin/magento cache:flush
echo "Sample data media fix applied."
fi
else
echo "Sample data media source not found or pub/media not writable. Skipping fix."
fi
# Sample data media is now staged into pub/media BEFORE setup:upgrade (see above), so
# here we only need to generate the resized product image cache and populate the media
# gallery index, then flush caches.
if [ "${INSTALL_SAMPLE_DATA}" = "YES" ] && [ -f "bin/magento" ]; then
echo "Resizing product images and syncing the media gallery..."
php -d memory_limit=-1 bin/magento catalog:image:resize
php -d memory_limit=-1 bin/magento media-gallery:sync || true
php -d memory_limit=-1 bin/magento cache:flush
echo "Sample data media processing complete."
fi
6 changes: 3 additions & 3 deletions .devcontainer/scripts/start_services.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ fi

# Wait for MariaDB
echo "Waiting for MySQL to be ready..."
if ! timeout 60 bash -c 'until sudo mysqladmin ping --silent; do echo "Waiting..." && sleep 2; done'; then
if ! timeout 60 bash -c 'until sudo mariadb-admin ping --silent; do echo "Waiting..." && sleep 2; done'; then
echo "Error: MySQL did not become available within 60 seconds."
exit 1
fi
echo "MySQL is ready!"

# Configure MySQL root user with password
echo "Configuring MySQL root user..."
sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; FLUSH PRIVILEGES;" 2>/dev/null || true
sudo mariadb -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; FLUSH PRIVILEGES;" 2>/dev/null || true

# Grant root access from any host for PHPMyAdmin
echo "Granting MySQL root access from any host..."
sudo mysql -e "CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;" 2>/dev/null || true
sudo mariadb -e "CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;" 2>/dev/null || true

# Wait for OpenSearch
echo "Waiting for OpenSearch to be ready..."
Expand Down
Loading