From 6f84827d6e0f9174f66cf821de3625b20b53ef99 Mon Sep 17 00:00:00 2001
From: jurgenhaas <juergen@paragon-es.de>
Date: Mon, 3 Apr 2017 11:51:12 +0200
Subject: [PATCH] Initial code

---
 .editorconfig                      |  17 +++
 .gitignore                         |   2 +
 composer.json                      |  43 +++++++
 src/Handler.php                    | 194 +++++++++++++++++++++++++++++
 src/Plugin.php                     |  71 +++++++++++
 templates/docker-compose.yml.twig  | 112 +++++++++++++++++
 templates/settings.docker.php.twig |  24 ++++
 tests/PluginTest.php               |  24 ++++
 8 files changed, 487 insertions(+)
 create mode 100644 .editorconfig
 create mode 100644 .gitignore
 create mode 100644 composer.json
 create mode 100644 src/Handler.php
 create mode 100644 src/Plugin.php
 create mode 100644 templates/docker-compose.yml.twig
 create mode 100644 templates/settings.docker.php.twig
 create mode 100644 tests/PluginTest.php

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..12bcb27
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# Drupal editor configuration normalization
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[{composer.json,composer.lock}]
+indent_size = 4
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7579f74
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+vendor
+composer.lock
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..80b92c3
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,43 @@
+{
+    "name": "lakedrops/docker4drupal",
+    "description": "Composer Plugin to prepare local Drupal development environment for Docker.",
+    "type": "composer-plugin",
+    "keywords": ["Drupal", "Development", "Docker"],
+    "homepage": "https://gitlab.lakedrops.com/lakedrops/docker4drupal",
+    "license": "GPL-2.0+",
+    "authors": [
+        {
+            "name": "Jürgen Haas",
+            "email": "juergen@paragon-es.de",
+            "homepage": "https://www.paragon-es.de",
+            "role": "Drupal Expert"
+        },
+        {
+            "name": "Richard Papp",
+            "email": "richard.papp@boromino.com",
+            "homepage": "http://www.boromino.com",
+            "role": "Drupal Expert"
+        }
+    ],
+    "support": {
+        "issues": "https://gitlab.lakedrops.com/lakedrops/docker4drupal/issues",
+        "source": "https://gitlab.lakedrops.com/lakedrops/docker4drupal/tree/master"
+    },
+    "require": {
+        "php": ">=5.4.5",
+        "composer-plugin-api": "^1.0.0",
+        "twig/twig": "^1.23.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "LakeDrops\\Docker4Drupal\\": "src/"
+        }
+    },
+    "extra": {
+        "class": "LakeDrops\\Docker4Drupal\\Plugin"
+    },
+    "require-dev": {
+        "composer/composer": "dev-master",
+        "phpunit/phpunit": "^4.4.0"
+    }
+}
diff --git a/src/Handler.php b/src/Handler.php
new file mode 100644
index 0000000..e17013b
--- /dev/null
+++ b/src/Handler.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * @file
+ * Contains \LakeDrops\Docker4Drupal\Handler.
+ */
+
+namespace LakeDrops\Docker4Drupal;
+
+use Composer\Package\PackageInterface;
+use Composer\Composer;
+use Composer\IO\IOInterface;
+use Composer\Script\Event as ScriptEvent;
+use Symfony\Component\Filesystem\Filesystem;
+use Rych\Random\Random;
+use Rych\Random\Encoder\Base64Encoder;
+
+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 $force
+   */
+  public function configureProject($event, $force = FALSE) {
+
+    // We only do the fancy stuff for developers
+    if (!$event->isDevMode()) {
+      return;
+    }
+
+    // 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.
+    if ($this->getPackage('lakedrops/d8-project-scaffold') && !$force) {
+      return;
+    }
+
+    $options = $this->getOptions();
+    $fs = new Filesystem();
+    $installationManager = $this->composer->getInstallationManager();
+
+    $drupalCorePackage = $this->getDrupalCorePackage();
+    $corePath = $installationManager->getInstallPath($drupalCorePackage);
+
+    // Directory where the root project is being created
+    $projectRoot = getcwd();
+    // Directory where Drupal's index.php is located
+    $webRoot = dirname($corePath);
+    // Directory where this plugin is being installed
+    $pluginRoot = $installationManager->getInstallPath($this->getPackage('lakedrops/docker4drupal'));
+
+    // Link Drupal site's settings files
+    $twig_loader = new \Twig_Loader_Array([]);
+    $twig = new \Twig_Environment($twig_loader);
+    foreach ($this->getFiles($projectRoot, $webRoot) as $template => $def) {
+      $file = $def['dest'] . '/' . $template;
+      if (!$fs->exists($file)) {
+        $twig_loader->setTemplate($template, file_get_contents($pluginRoot . '/templates/' . $template . '.twig'));
+        $rendered = $twig->render($template, $options);
+        file_put_contents($file, $rendered);
+      }
+      if (isset($def['link'])) {
+        $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);
+    }
+
+    // Initialize local git working copy
+    try {
+      $this->git('ignore docker-compose.yml');
+      $this->git('ignore settings.docker.php');
+    }
+    catch (\Exception $ex) {
+      // We're ignoring this for now
+    }
+
+  }
+
+  protected function getFiles($projectRoot, $webRoot) {
+    return [
+      'settings.docker.php' => [
+        'dest' => $projectRoot . '/settings/default',
+        'link' => $webRoot . '/sites/default',
+      ],
+      'docker-compose.yml.twig' => [
+        'dest' => $projectRoot,
+      ],
+    ];
+  }
+
+  /**
+   * Retrieve excludes from optional "extra" configuration.
+   *
+   * @return array
+   */
+  protected function getOptions() {
+    $extra = $this->composer->getPackage()->getExtra() + ['docker4drupal' => []];
+    $options = $extra['docker4drupal'] + [
+      'port' => 8000,
+      'drupal' => [
+        'version' => '8',
+      ],
+      'php' => [
+        'version' => '7.0',# 5.3|5.6|7.1
+        'xdebug' => 1,
+      ],
+      'nginx' => [
+        'version' => '1.10',
+      ],
+      'varnish' => [
+        'enable' => 0,
+      ],
+      'solr' => [
+        'enable' => 0,
+      ],
+      'node' => [
+        'enable' => 0,
+      ],
+    ];
+    return $options;
+  }
+
+  /**
+   * Wrapper for git command in the root directory.
+   *
+   * @param $command
+   *   Git command name, arguments and/or options.
+   * @throws \Exception
+   */
+  protected function git($command) {
+    passthru(escapeshellcmd('git -c "user.email=d8-project@lakedrops.com" ' . $command), $exit_code);
+    if ($exit_code !== 0) {
+      throw new \Exception('Git returned a non-zero exit code');
+    }
+  }
+
+}
diff --git a/src/Plugin.php b/src/Plugin.php
new file mode 100644
index 0000000..858eb53
--- /dev/null
+++ b/src/Plugin.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @file
+ * Contains LakeDrops\Docker4Drupal\Plugin.
+ */
+
+namespace LakeDrops\Docker4Drupal;
+
+use Composer\Composer;
+use Composer\EventDispatcher\EventSubscriberInterface;
+use Composer\IO\IOInterface;
+use Composer\Plugin\PluginInterface;
+use Composer\Script\ScriptEvents;
+
+/**
+ * Composer plugin for handling docker4drupal setup.
+ */
+class Plugin implements PluginInterface, EventSubscriberInterface {
+
+  /**
+   * @var \LakeDrops\Docker4Drupal\Handler
+   */
+  protected $handler;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function activate(Composer $composer, IOInterface $io) {
+    $this->handler = new Handler($composer, $io);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return array(
+      ScriptEvents::POST_CREATE_PROJECT_CMD => 'configureProject',
+      ScriptEvents::POST_INSTALL_CMD => 'configureProjectForce',
+      ScriptEvents::POST_UPDATE_CMD => 'configureProjectForce',
+    );
+  }
+
+  /**
+   * Configure project event callback.
+   *
+   * @param \Composer\Script\Event $event
+   */
+  public function configureProject($event) {
+    $this->handler->configureProject($event);
+  }
+
+  /**
+   * Configure project event callback.
+   *
+   * @param \Composer\Script\Event $event
+   */
+  public function configureProjectForce($event) {
+    $this->handler->configureProject($event, TRUE);
+  }
+
+  /**
+   * Script callback for putting in composer scripts to configure the project.
+   *
+   * @param \Composer\Script\Event $event
+   */
+  public static function config($event) {
+    $handler = new Handler($event->getComposer(), $event->getIO());
+    $handler->configureProject($event, TRUE);
+  }
+
+}
diff --git a/templates/docker-compose.yml.twig b/templates/docker-compose.yml.twig
new file mode 100644
index 0000000..28249ab
--- /dev/null
+++ b/templates/docker-compose.yml.twig
@@ -0,0 +1,112 @@
+version: "2"
+
+services:
+  mariadb:
+    image: wodby/mariadb:10.1-2.0.0
+    environment:
+      MYSQL_ROOT_PASSWORD: password
+      MYSQL_DATABASE: drupal
+      MYSQL_USER: drupal
+      MYSQL_PASSWORD: drupal
+    ports:
+      - '8306:3306'
+
+  php:
+    image: wodby/drupal-php:{{ php.version }}-2.0.0
+    environment:
+      PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S mailhog:1025
+{% if php.debug %}
+      PHP_XDEBUG: 1
+      PHP_XDEBUG_DEFAULT_ENABLE: 1
+{% endif %}
+    volumes:
+      - ./:/var/www/html
+
+  nginx:
+    image: wodby/drupal-nginx:{{ drupal.version }}-{{ nginx.version }}-2.0.0
+    restart: unless-stopped
+    depends_on:
+      - php
+    environment:
+      NGINX_BACKEND_HOST: php
+      NGINX_SERVER_ROOT: /var/www/html/web/
+    volumes:
+      - ./:/var/www/html
+    labels:
+      - 'traefik.backend=nginx'
+      - 'traefik.port=80'
+      - 'traefik.frontend.rule=Host:drupal.docker.localhost'
+
+{% if varnish.enable %}
+  varnish:
+    image: wodby/drupal-varnish:4.1-2.0.0
+    depends_on:
+      - nginx
+    environment:
+      VARNISH_SECRET: secret
+      VARNISH_BACKEND_HOST: nginx
+      VARNISH_BACKEND_PORT: 80
+    labels:
+      - 'traefik.backend=varnish'
+      - 'traefik.port=6081'
+      - 'traefik.frontend.rule=Host:varnish.drupal.docker.localhost'
+{% endif %}
+
+  redis:
+    image: wodby/redis:3.2-2.0.1
+
+  pma:
+    image: phpmyadmin/phpmyadmin
+    environment:
+      PMA_HOST: mariadb
+      PMA_USER: drupal
+      PMA_PASSWORD: drupal
+      PHP_UPLOAD_MAX_FILESIZE: 1G
+      PHP_MAX_INPUT_VARS: 1G
+    labels:
+      - 'traefik.backend=pma'
+      - 'traefik.port=80'
+      - 'traefik.frontend.rule=Host:pma.drupal.docker.localhost'
+
+{% if solr.enable %}
+  solr:
+    image: wodby/drupal-solr:8-6.4-2.0.0
+    environment:
+      SOLR_HEAP: 1024m
+    labels:
+      - 'traefik.backend=solr'
+      - 'traefik.port=8983'
+      - 'traefik.frontend.rule=Host:solr.drupal.docker.localhost'
+{% endif %}
+
+  mailhog:
+    image: mailhog/mailhog
+    labels:
+      - 'traefik.backend=mailhog'
+      - 'traefik.port=8025'
+      - 'traefik.frontend.rule=Host:mailhog.drupal.docker.localhost'
+
+{% if node.enable %}
+  node:
+    image: node:7-alpine
+    working_dir: /app
+    labels:
+      - 'traefik.backend=node'
+      - 'traefik.port=3000'
+      - 'traefik.frontend.rule=Host:front.drupal.docker.localhost'
+    expose:
+      - "3000"
+    volumes:
+      - ./path/to/your/single-page-app:/app
+    command: sh -c 'npm install && npm run start'
+{% endif %}
+
+  traefik:
+    image: traefik
+    restart: unless-stopped
+    command: -c /dev/null --web --docker --logLevel=INFO
+    ports:
+      - '{{ port }}:80'
+      - '8080:8080'
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
diff --git a/templates/settings.docker.php.twig b/templates/settings.docker.php.twig
new file mode 100644
index 0000000..7445786
--- /dev/null
+++ b/templates/settings.docker.php.twig
@@ -0,0 +1,24 @@
+<?php
+
+$settings['trusted_host_patterns'] = array(
+  '^drupal\.docker\.localhost$',
+);
+$databases = array (
+  'default' => array (
+    'default' => array (
+      'driver' => 'mysql',
+      'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
+      'database' => 'drupal',
+      'username' => 'drupal',
+      'password' => 'drupal',
+      'host' => 'mariadb',
+      'port' => '3306',
+      'prefix' => '',
+    ),
+  ),
+);
+
+if (PHP_SAPI === 'cli') {
+  $databases['default']['default']['host'] = '127.0.0.1';
+  $databases['default']['default']['port'] = '8306';
+}
diff --git a/tests/PluginTest.php b/tests/PluginTest.php
new file mode 100644
index 0000000..bae2fde
--- /dev/null
+++ b/tests/PluginTest.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \LakeDrops\Docker4Drupal\Tests\PluginTest.
+ */
+
+namespace LakeDrops\Docker4Drupal\Tests;
+
+/**
+ * Tests composer plugin functionality.
+ */
+class PluginTest extends \PHPUnit_Framework_TestCase {
+
+  static $name = 'docker4drupal';
+
+  /**
+   * SetUp test
+   */
+  public function setUp() {
+    $this->rootDir = realpath(realpath(__DIR__ . '/..'));
+  }
+
+}
-- 
GitLab