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
49 changes: 41 additions & 8 deletions .ddev/commands/web/install-magento
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ cd /var/www/html || exit 1
# global config
MAGENTO_FOLDER="magento"

# Guard: verify the MageForge bind-mount is active.
# The Docker bind-mount (../src → .../MageForge) is a kernel-level mount established
# when the containers start. If magento/ was deleted while DDEV was running, the mount
# becomes orphaned: its inode is gone, and recreating the directory produces a new inode
# not covered by the old mount. The module source would then be invisible to Magento.
# The only fix is a container restart, which re-establishes the mount on the new inode.
if [[ ! -f "${MAGENTO_FOLDER}/app/code/OpenForgeProject/MageForge/registration.php" ]]; then
echo "ERROR: The MageForge bind-mount is not active."
echo ""
echo "This happens when magento/ was deleted while DDEV was still running."
echo ""
echo "Fix: run 'ddev restart', then re-run 'ddev install-magento'."
exit 1
fi

# check if Magento is already installed
if [[ -f "${MAGENTO_FOLDER}/bin/magento" ]]; then
echo "Magento is already installed. Skipping install-magento."
Expand All @@ -35,6 +50,7 @@ if [[ ! -d "${MAGENTO_FOLDER}" ]]; then
mkdir -p "${MAGENTO_FOLDER}"
fi


# copy everything from magento-temp into magento folder
cp -a magento-temp/. "${MAGENTO_FOLDER}/"

Expand Down Expand Up @@ -63,6 +79,11 @@ rm -f package-lock.json # remove basic package-lock.json to avoid conflicts
# create missing local-themes.js file for grunt tasks
echo 'module.exports = {};' >dev/tools/grunt/configs/local-themes.js

# Require Hyvä theme and install MageForge module deps before setup:install so that
# both are included in the initial schema-upgrade pass (avoiding a redundant second pass).
composer require 'hyva-themes/magento2-default-theme'
/var/www/html/.ddev/commands/web/install-module-deps

# install magento
bin/magento setup:install \
--admin-email=admin@mageforge.ddev.site \
Expand All @@ -87,15 +108,27 @@ bin/magento deploy:mode:set developer
# disable 2FA
bin/magento module:disable Magento_TwoFactorAuth Magento_AdminAdobeImsTwoFactorAuth

# Enable MageForge: the module is bind-mounted under app/code/ and therefore not registered
# via Composer autoload. bin/magento module:enable discovers it through the component registrar
# (registration.php), writes the entry to app/etc/config.php, and exits non-zero on any error.
bin/magento module:enable OpenForgeProject_MageForge
Comment thread
dermatz marked this conversation as resolved.

Comment thread
dermatz marked this conversation as resolved.
# install sample data
bin/magento sampledata:deploy

# require Hyvä theme
composer require 'hyva-themes/magento2-default-theme'

# enable mageforge module (source is bind-mounted via .ddev/docker-compose.mageforge-source.yaml
# into app/code/OpenForgeProject/MageForge — no Composer installation needed)
bin/magento module:enable OpenForgeProject_MageForge

# run setup upgrade to apply schema and module registration
# run setup upgrade to apply sample data module schemas
bin/magento setup:upgrade

# Set Hyvä Default as the active storefront theme.
# The theme_id is a database-generated integer, so look it up after setup:upgrade
# has registered all themes into the theme table.
HYVA_THEME_ID=$(mysql --host=db --user=db --password=db db \
--skip-column-names --silent \
--execute="SELECT theme_id FROM theme WHERE theme_path = 'Hyva/default' LIMIT 1" 2>/dev/null) || HYVA_THEME_ID=""
if [[ -n "$HYVA_THEME_ID" ]]; then
echo "Setting Hyvä Default (ID: ${HYVA_THEME_ID}) as storefront theme..."
bin/magento config:set design/theme/theme_id "$HYVA_THEME_ID"
bin/magento cache:clean config
else
echo "WARNING: Hyvä Default theme not found in database, skipping theme activation."
fi
75 changes: 75 additions & 0 deletions .ddev/commands/web/install-module-deps
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash

## Description: Install MageForge module dependencies into the Magento vendor directory
## Usage: install-module-deps
## Example: ddev install-module-deps

# The module source is bind-mounted (not Composer-installed), so its third-party
# dependencies are not resolved automatically. Read them from the module's composer.json
# and install the non-Magento/non-PHP ones into the Magento vendor.

MAGENTO_DIR="/var/www/html/magento"
MODULE_COMPOSER_JSON="/var/www/html/composer.json"

# Skip if Magento is not yet installed (e.g. on initial ddev start before install-magento)
if [[ ! -f "${MAGENTO_DIR}/vendor/autoload.php" ]]; then
exit 0
fi

cd "${MAGENTO_DIR}" || exit 1

php -r "
\$manifest = json_decode(file_get_contents('${MODULE_COMPOSER_JSON}'), true);
if (\$manifest === null) {
fwrite(STDERR, 'ERROR: Could not parse module composer.json at ${MODULE_COMPOSER_JSON}' . PHP_EOL);
exit(1);
}
\$deps = [];
foreach (\$manifest['require'] ?? [] as \$package => \$constraint) {
// Skip php, magento/*, and Composer platform packages (ext-*, lib-*, composer-*-api).
// Platform packages have no vendor/ directory and would cause repeated failed installs.
if (\$package === 'php'
|| str_starts_with(\$package, 'magento/')
|| str_starts_with(\$package, 'ext-')
|| str_starts_with(\$package, 'lib-')
|| \$package === 'composer-runtime-api'
|| \$package === 'composer-plugin-api') {
continue;
}
\$deps[\$package] = \$constraint;
}
\$missingConstraints = [];
\$missingNames = [];
// Read Magento's composer.json once to compare declared constraints.
\$magentoManifest = is_file('composer.json')
? json_decode(file_get_contents('composer.json'), true)
: null;
foreach (\$deps as \$package => \$constraint) {
\$vendorDir = 'vendor/' . \$package;
\$needsInstall = !is_dir(\$vendorDir);
if (!\$needsInstall) {
// Check whether Magento's composer.json already declares the exact required
// constraint. This detects constraint bumps in the module (e.g. ^0.3 → ^0.4)
// and triggers a re-install when the constraint changes.
\$declaredConstraint = \$magentoManifest['require'][\$package]
?? \$magentoManifest['require-dev'][\$package]
?? null;
if (\$declaredConstraint !== \$constraint) {
\$needsInstall = true;
}
}
if (\$needsInstall) {
\$missingConstraints[] = escapeshellarg(\"\$package:\$constraint\");
\$missingNames[] = escapeshellarg(\$package);
}
}
if (\$missingConstraints) {
echo 'Installing MageForge module dependencies: ' . implode(', ', \$missingNames) . PHP_EOL;
// Add constraints without resolving first, then update only the new packages.
// This prevents Composer from touching unrelated Magento core dependencies.
passthru('composer require --no-interaction --no-update ' . implode(' ', \$missingConstraints), \$exitCode);
if (\$exitCode !== 0) { exit(\$exitCode); }
passthru('composer update --no-interaction --with-dependencies ' . implode(' ', \$missingNames), \$exitCode);
exit(\$exitCode);
}
"
11 changes: 8 additions & 3 deletions .ddev/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ database:
version: "10.6"
hooks:
pre-start:
# Pre-create parent directories for the MageForge bind-mount on the host.
# Docker creates missing mount-target parents as root; by creating them here
# Pre-create the MageForge bind-mount target directory on the host.
# Docker creates missing mount-target paths as root; by creating them here
# (as the host user) they get the correct ownership for the DDEV web container.
- exec-host: mkdir -p magento/app/code/OpenForgeProject
# MageForge/ is the actual bind-mount target (../src → .../MageForge), so we
# must create it explicitly – creating only the parent is not enough.
- exec-host: mkdir -p magento/app/code/OpenForgeProject/MageForge
post-start:
- exec-host: ddev npx skills experimental_install
# Install MageForge module dependencies (e.g. laravel/prompts) that are not
# resolved automatically because the module is bind-mounted, not Composer-installed.
- exec: /var/www/html/.ddev/commands/web/install-module-deps
use_dns_when_possible: true
composer_root: magento
composer_version: "2"
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@

MageForge is a powerful CLI toolkit for Magento 2 front-end development. It simplifies theme building workflows, supports multiple theme types (Magento Standard, Hyvä, TailwindCSS, custom), and includes developer tools like the Frontend Inspector.

## Get your Merch

[![Mageforge Hero](./.github/assets/MageForce-Merch.jpg)](https://mageforge.myspreadshop.de/)

## Table of Contents

- [Requirements](#requirements)
Expand All @@ -33,6 +29,8 @@ MageForge is a powerful CLI toolkit for Magento 2 front-end development. It simp

## Supported Theme Types

![Mageforge Hero](./.github/assets/cli-chooser.png)

| Theme Type | Support Status |
| ------------------------------- | ---------------------------------------------------------- |
| Magento Standard | ✅ Supported |
Expand Down
Loading