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);

jurgenhaas
committed
foreach ($this->getFiles($projectRoot, $webRoot, $settingsPath) as $template => $def) {
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.
*
* @return array
* List of files.
*/
protected function getFiles($projectRoot, $webRoot, $settingsPath): 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,
* 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' => $this->isLocalDevMode() ? 1 : 0,
256
257
258
259
260
261
262
263
264
265
266
267
268
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
],
'webserver' => [
'type' => 'apache',
],
'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.
}