Newer
Older
use LakeDrops\Component\Composer\BaseHandler;
use LakeDrops\Component\Dotenv\Dotenv;
use LakeDrops\DockerTraefik\Traefik;

jurgenhaas
committed
use Symfony\Component\Yaml\Yaml;
/**
* Class Handler.
*
* @package LakeDrops\Docker4Drupal
*/
class Handler extends BaseHandler {
/**
* @var array
*/
protected $options;
* @param bool $overwrite
*
* @throws \Twig_Error_Loader
* @throws \Twig_Error_Runtime
* @throws \Twig_Error_Syntax
public function configureProject($overwrite = FALSE) {
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);
$webRoot = dirname($corePath);
$pluginRoot = $installationManager->getInstallPath($this->getPackage('lakedrops/docker4drupal'));

jurgenhaas
committed
// 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';
}
$twig_loader = new \Twig_Loader_Array([]);
$twig = new \Twig_Environment($twig_loader);
foreach ($this->getFiles($projectRoot, $webRoot, $settingsPath, $options) as $template => $def) {
if (isset($def['condition']) && !$def['condition']) {
continue;
}
if (!$fs->exists($def['dest'])) {
$fs->mkdir($def['dest']);
}
$twig_loader->setTemplate($template, $template);
$filename = $twig->render($template, $options);
$file = $def['dest'] . '/' . $filename;
if (($overwrite && empty($def['add2git'])) || !$fs->exists($file)) {
$twig_loader->setTemplate($filename, file_get_contents($pluginRoot . '/templates/' . ($def['source'] ?? '') . $template . '.twig'));
$rendered = $twig->render($filename, $options);
if (!empty($def['add2yaml']) && isset($options[$filename])) {
$yaml = Yaml::parse($rendered);
/** @noinspection SlowArrayOperationsInLoopInspection */
$rendered = Yaml::dump($yaml, 9, 2);
// Render the string again so that custom content can also use variables
$twig_loader->setTemplate($filename, $rendered);
$rendered = $twig->render($filename, $options);
continue;
}
$orig_file = $file . '.orig';
if ($fs->exists($orig_file)) {
$fs->remove($orig_file);
}
$fs->rename($file, $orig_file);
if (!$orig_ignored) {
if (empty($def['add2git'])) {
if (isset($def['link']) && ($def['link'] !== $settingsPath)) {
$link = $def['link'] . '/' . $filename;
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.

jurgenhaas
committed
$settingsPhpFile = $settingsPath . '/settings.php';

jurgenhaas
committed
$settingsPhp = file_get_contents($settingsPhpFile);
if (strpos($settingsPhp, 'settings.docker.php') === FALSE) {

jurgenhaas
committed
$settingsPhp .= "\n\nif (file_exists(__DIR__ . '/settings.docker.php')) {\n include __DIR__ . '/settings.docker.php';\n}\n";

jurgenhaas
committed
file_put_contents($settingsPhpFile, $settingsPhp);
}
$this->gitIgnore('tests/backstop/backstop_data/bitmaps_test');
$this->gitIgnore('tests/backstop/backstop_data/html_report');
$this->gitLFS('tests/backstop/**/*.png');
$traefik = new Traefik($options['projectname']);
$traefik->update();
// Set permissions, see https://wodby.com/stacks/drupal/docs/local/permissions
exec('setfacl -dR -m u:$(whoami):rwX -m u:82:rwX -m u:100:rX -m g::rwX ' . $projectRoot . ' >/dev/null 2>&1');
exec('setfacl -R -m u:$(whoami):rwX -m u:82:rwX -m u:100:rX -m g::rwX ' . $projectRoot . ' >/dev/null 2>&1');
/**
* List of files and settings on how to handle them.
*
* @param string $projectRoot
* Name of the project's root directory.
* @param string $webRoot
* Name of the web's root directory.
* @param string $settingsPath
* Name of the settings directory.
* @param array $options
* Keyed array with all current options.
protected function getFiles($projectRoot, $webRoot, $settingsPath, $options): array {

jurgenhaas
committed
'dest' => $projectRoot . '/' . $settingsPath,
'add2yaml' => TRUE,

jurgenhaas
committed
'drushrc.php' => [
'dest' => $projectRoot . '/drush',
],
'default.site.yml' => [
'dest' => $projectRoot . '/drush/sites',
'add2yaml' => TRUE,
],
'drush.yml' => [
'dest' => $projectRoot . '/drush',
'add2yaml' => TRUE,
'wkhtmltox.sh' => [
'dest' => $projectRoot . '/.docker-init',
],
'backstop.json' => [
'source' => 'tests/backstop/',
'dest' => $projectRoot . '/tests/backstop',
'add2yaml' => TRUE,
'add2git' => TRUE,
],
'vhost.conf' => [
'dest' => $projectRoot . '/apache',
'condition' => $options['webserver']['overwriteconfig'],
],
* Retrieve options from composer.json "extra" configuration.
* @param null $key
* Optional name of an option to be received instead of the full set of options.
*
if ($this->options === NULL) {
$env = new Dotenv('docker4drupal', $this->io);
$projectname = getenv('COMPOSE_PROJECT_NAME');
if (empty($projectname)) {$projectname = str_replace([' ', '-', '_', '.'], '', basename(getcwd()));
$env->put('COMPOSE_PROJECT_NAME', $projectname);
}
$extra = $this->composer->getPackage()->getExtra() + ['docker4drupal' => []];
$options = NestedArray::mergeDeep([
'projectname' => $projectname,
'ci_home' => '/home/gitlab-runner',
'ip' => ($this->isCiContext() || $this->isLocalDevMode()) ?
$this->getDockerGateway() :
$this->getLocalIpv4('docker0'),
],
'live' => [
'root' => '',
'uri' => '',
'host' => '',
'user' => $env->receive('live_host_username', 'Remote username for host of the live site', getenv('USER')),
],
'drush' => [
'sql' => [
'tables' => [
'structure' => [
'cache',
'cache_*',
'history',
'search_*',
'sessions',
'watchdog',
],
'skip' => [
'migration_*',
],
],
],
'drupal' => [
'version' => '8',
],
'php' => [

jurgenhaas
committed
'version' => $env->receiveGlobal('PHP_VERSION', 'PHP version', '7.2'),
'xdebug' => $env->receiveGlobal('PHP_DEBUG', 'PHP debug', '0'),
],
'webserver' => [
'type' => 'apache',
'overwriteconfig' => (bool) $env->receiveGlobal('WEBSERVER_CONFIG_OVERWRITE', 'Overwrite webserver config (0 or 1)', '0'),
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
],
'varnish' => [
'enable' => 0,
],
'redis' => [
'version' => '4.0',
],
'dbbrowser' => [
'type' => 'pma',
],
'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,
],
'wkhtmltox' => [
'enable' => 0,
],
], $extra['docker4drupal']);
if ($this->isCiContext() || $this->isLocalDevMode()) {
$projectRoot = $this->getDockerMountSource(getenv('CI_PROJECT_DIR'));
// Check if SSH auth sockets are supported.
$ssh_auth_sock = getenv('SSH_AUTH_SOCK');
$options['php']['ssh'] = !empty($ssh_auth_sock);
if ($options['php']['ssh']) {
$options['php']['ssh_auth_sock'] = ($this->isCiContext() || $this->isLocalDevMode()) ?
$this->getDockerMountSource('/ssh-agent') :
'$SSH_AUTH_SOCK';
}
$options['projectroot'] = $projectRoot;
$this->options = $env->replaceEnvironmentVariables($options);
$env->put('PHP_VERSION', $this->options['php']['version'], TRUE);
}
if ($key !== NULL) {
return $this->options[$key];
}
return $this->options;
/**
* Determine local ipv4 address.
*
* @param string|null $interface
* The name of the interface for which to determine the ipv4 address.
*
* @return string|array
* The ipv4 address(es).
*/
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];
}
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 $local_addrs[$interface] ?? '127.0.0.1';
/**
* @return string
*/
private function getDockerGateway(): string {
$container = $this->readContainerConfig();
return $container['NetworkSettings']['Gateway'];
}
/**
* @param $projectRoot
*
* @return string
*/
private function getDockerMountSource($projectRoot): string {
$currentDir = getcwd();
$container = $this->readContainerConfig();
foreach ($container['Mounts'] as $mount) {
if (empty($projectRoot)) {
if ($currentDir === $mount['Destination']) {
return $mount['Source'];
}
}
else if (strpos($projectRoot, $mount['Destination']) === 0) {
return $mount['Source'] . substr($projectRoot, strlen($mount['Destination']));
}
}
return getcwd();
}
private function readContainerConfig() {
try {
$output = [];
exec('basename "$(cat /proc/1/cpuset)"', $output);
$id = reset($output);
$output = [];
exec('docker container inspect ' . $id, $output);
return json_decode(implode('', $output), TRUE)[0];
}
catch (\Exception $ex) {
// Ignore.
}
return [
'NetworkSettings' => [
'Gateway' => '127.0.0.1',
],
'Mounts' => [],
];