<?php namespace LakeDrops\Docker4Drupal; use Composer\Package\PackageInterface; use Composer\Composer; use Composer\IO\IOInterface; use Composer\Script\Event as ScriptEvent; use LakeDrops\Component\Dotenv\Dotenv; use LakeDrops\DockerTraefik\Traefik; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Yaml\Yaml; class Handler { /** * @var \Composer\Composer */ protected $composer; /** * @var \Composer\IO\IOInterface */ protected $io; /** * @var PackageInterface */ protected $drupalCorePackage; /** * Handler constructor. * * @param Composer $composer * @param IOInterface $io */ public function __construct(Composer $composer, IOInterface $io) { $this->composer = $composer; $this->io = $io; } /** * Look up the Drupal core package object, or return it from where we cached * it in the $drupalCorePackage field. * * @return PackageInterface */ protected function getDrupalCorePackage() { if (!isset($this->drupalCorePackage)) { $this->drupalCorePackage = $this->getPackage('drupal/core'); } return $this->drupalCorePackage; } /** * Retrieve a package from the current composer process. * * @param string $name * Name of the package to get from the current composer installation. * * @return PackageInterface */ protected function getPackage($name) { return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*'); } /** * Configure Drupal Project for Docker. * * @param ScriptEvent $event * @param bool $overwrite */ public function configureProject($event, $overwrite = FALSE) { // We only do the fancy stuff for developers if (!$event->isDevMode()) { return; } $options = $this->getOptions(); $fs = new Filesystem(); $installationManager = $this->composer->getInstallationManager(); if (isset($options['webroot'])) { if (!$fs->exists($options['webroot'])) { return; } $webRoot = $options['webroot']; } else { $drupalCorePackage = $this->getDrupalCorePackage(); if (!$drupalCorePackage) { // We are called too early, Drupal core is not available yet. return; } $corePath = $installationManager->getInstallPath($drupalCorePackage); // Directory where Drupal's index.php is located $webRoot = dirname($corePath); } // Directory where the root project is being created $projectRoot = getcwd(); // Directory where this plugin is being installed $pluginRoot = $installationManager->getInstallPath($this->getPackage('lakedrops/docker4drupal')); // If the d8-project-scaffold plugin is present we only execute this one // if $force is TRUE. This way we can make sure that we get executed after // d8-project-scaffold. $settingsPath = $webRoot . '/sites/default'; if ($this->getPackage('lakedrops/d8-project-scaffold')) { if (!$fs->exists($projectRoot . '/settings/default')) { return; } $settingsPath = 'settings/default'; } // Provide all the required files $twig_loader = new \Twig_Loader_Array([]); $twig = new \Twig_Environment($twig_loader); $options['webRoot'] = $webRoot . '/'; $orig_ignored = FALSE; foreach ($this->getFiles($projectRoot, $webRoot, $settingsPath) as $template => $def) { $file = $def['dest'] . '/' . $template; if ($overwrite || !$fs->exists($file)) { $twig_loader->setTemplate($template, file_get_contents($pluginRoot . '/templates/' . $template . '.twig')); $rendered = $twig->render($template, $options); if (!empty($def['add2yaml']) && isset($options[$template])) { $yaml = Yaml::parse($rendered); $yaml = array_merge_recursive($yaml, $options[$template]); $rendered = Yaml::dump($yaml, 9, 2); } if ($fs->exists($file)) { if (md5_file($file) == md5($rendered)) { continue; } $orig_file = $file . '.orig'; if ($fs->exists($orig_file)) { $fs->remove($orig_file); } $fs->rename($file, $orig_file); if (!$orig_ignored) { $this->git('ignore *.orig'); $orig_ignored = TRUE; } } else { $this->git('ignore ' . $template); } file_put_contents($file, $rendered); } if (isset($def['link']) && ($def['link'] != $settingsPath)) { $link = $def['link'] . '/' . $template; if (!$fs->exists($link)) { $rel = substr($fs->makePathRelative($file, $projectRoot . '/' . $link), 3, -1); $fs->symlink($rel, $link); } } $fs->chmod($file, 0664); } // Make sure that settings.docker.php gets called from settings.php $settingsPhpFile = $settingsPath . '/settings.php'; if ($fs->exists(($settingsPhpFile))) { $settingsPhp = file_get_contents($settingsPhpFile); if (strpos($settingsPhp, 'settings.docker.php') === FALSE) { $settingsPhp .= "\n\nif (file_exists(__DIR__ . '/settings.docker.php')) {\n include __DIR__ . '/settings.docker.php';\n}\n"; file_put_contents($settingsPhpFile, $settingsPhp); } } $traefik = new Traefik($options['projectname']); $traefik->update(); } protected function getFiles($projectRoot, $webRoot, $settingsPath) { return [ 'settings.docker.php' => [ 'dest' => $projectRoot . '/' . $settingsPath, 'link' => $webRoot . '/sites/default', ], 'docker-compose.yml' => [ 'dest' => $projectRoot, 'add2yaml' => TRUE, ], 'aliases.drushrc.php' => [ 'dest' => $projectRoot . '/drush', ], 'drushrc.php' => [ 'dest' => $projectRoot . '/drush', ], 'aliases.yml' => [ 'dest' => $projectRoot . '/drush', 'add2yaml' => TRUE, ], 'drush.yml' => [ 'dest' => $projectRoot . '/drush', 'add2yaml' => TRUE, ], ]; } /** * Retrieve excludes from optional "extra" configuration. * * @return array */ protected function getOptions() { $projectname = str_replace([' ', '-', '_', '.'], '', basename(getcwd())); $env = new Dotenv('docker4drupal', $this->io); $env->put('COMPOSE_PROJECT_NAME', $projectname); $extra = $this->composer->getPackage()->getExtra() + ['docker4drupal' => []]; $options = NestedArray::mergeDeep([ 'projectname' => $projectname, 'docker0' => [ 'ip' => $this->getLocalIpv4('docker0'), ], 'live' => [ 'root' => '', 'uri' => '', 'host' => '', 'user' => $env->receive('live_host_username', 'Remote username for host of the live site', getenv('USER')), ], 'drupal' => [ 'version' => '8', ], 'php' => [ 'version' => '7.0', 'xdebug' => 1, ], 'webserver' => [ 'type' => 'nginx', 'version' => '1.13', ], 'varnish' => [ 'enable' => 0, ], 'redis' => [ 'version' => '4.0', ], 'adminer' => [ 'version' => '4.3', ], 'solr' => [ 'enable' => 0, 'version' => '6.6', ], 'node' => [ 'enable' => 0, 'key' => '', 'path' => '', ], 'memcached' => [ 'enable' => 0, ], 'rsyslog' => [ 'enable' => 0, ], 'athenapdf' => [ 'enable' => 0, 'key' => '', ], 'blackfire' => [ 'enable' => 0, 'id' => '', 'token' => '', ], 'webgrind' => [ 'enable' => 0, ], ], $extra['docker4drupal']); return $options; } /** * Wrapper for git command in the root directory. * * @param $command * Git command name, arguments and/or options. */ protected function git($command) { passthru(escapeshellcmd('git -c "user.email=d8-project@lakedrops.com" ' . $command)); } private function getLocalIpv4($interface = NULL) { $out = explode(PHP_EOL, shell_exec('LC_ALL=C /sbin/ifconfig')); $local_addrs = array(); $ifname = 'unknown'; foreach ($out as $str) { $matches = array(); if (preg_match('/^([a-z0-9]+)(:\d{1,2})?(\s)+Link/', $str, $matches)) { $ifname = $matches[1]; if(strlen($matches[2])>0) { $ifname .= $matches[2]; } } elseif (preg_match('/inet addr:((?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:[.](?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3})\s/', $str, $matches)) { $local_addrs[$ifname] = $matches[1]; } } if (!isset($interface)) { return $local_addrs; } return isset($local_addrs[$interface]) ? $local_addrs[$interface] : '127.0.0.1'; } }