From 57d42b15e5edf76f73c74fad13ab914a7debbb07 Mon Sep 17 00:00:00 2001
From: Oleh Posyniak <oposyniak@magento.com>
Date: Thu, 29 Dec 2016 17:21:08 +0200
Subject: [PATCH] MAGETWO-62655: [GitHub] CLI won't work without write
 permissions #7933

---
 .../Magento/Framework/Console/Cli.php         | 104 ++++++++++++++----
 1 file changed, 80 insertions(+), 24 deletions(-)

diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php
index c42d7a2ea72..f803690c322 100644
--- a/lib/internal/Magento/Framework/Console/Cli.php
+++ b/lib/internal/Magento/Framework/Console/Cli.php
@@ -5,18 +5,19 @@
  */
 namespace Magento\Framework\Console;
 
+use Magento\Framework\App\DeploymentConfig;
 use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\State;
 use Magento\Framework\Composer\ComposerJsonFinder;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Input\ArgvInput;
-use Symfony\Component\Console\Application as SymfonyApplication;
+use Magento\Setup\Model\ObjectManagerProvider;
+use Symfony\Component\Console;
 use Magento\Framework\App\Bootstrap;
 use Magento\Framework\Filesystem\Driver\File;
 use Magento\Framework\Shell\ComplexParameter;
 use Magento\Setup\Console\CompilerPreparation;
 use Magento\Framework\App\ProductMetadata;
 use Magento\Framework\ObjectManagerInterface;
+use Zend\ServiceManager\ServiceManager;
 
 /**
  * Magento 2 CLI Application.
@@ -25,7 +26,7 @@ use Magento\Framework\ObjectManagerInterface;
  * {@inheritdoc}
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class Cli extends SymfonyApplication
+class Cli extends Console\Application
 {
     /**
      * Name of input option.
@@ -42,7 +43,7 @@ class Cli extends SymfonyApplication
     /**
      * Service Manager.
      *
-     * @var \Zend\ServiceManager\ServiceManager
+     * @var ServiceManager
      */
     private $serviceManager;
 
@@ -53,6 +54,11 @@ class Cli extends SymfonyApplication
      */
     private $initException;
 
+    /**
+     * @var ObjectManagerInterface
+     */
+    private $objectManager;
+
     /**
      * Constructor.
      *
@@ -66,6 +72,9 @@ class Cli extends SymfonyApplication
         $this->serviceManager = \Zend\Mvc\Application::init(require BP . '/setup/config/application.config.php')
             ->getServiceManager();
 
+        $this->initObjectManager();
+        $this->assertGenerationPermissions();
+
         /**
          * Temporary workaround until the compiler is able to clear the generation directory
          * @todo remove after MAGETWO-44493 resolved
@@ -73,7 +82,7 @@ class Cli extends SymfonyApplication
         if (class_exists(CompilerPreparation::class)) {
             $compilerPreparation = new CompilerPreparation(
                 $this->serviceManager,
-                new ArgvInput(),
+                new Console\Input\ArgvInput(),
                 new File()
             );
 
@@ -81,9 +90,9 @@ class Cli extends SymfonyApplication
         }
 
         if ($version == 'UNKNOWN') {
-            $directoryList      = new DirectoryList(BP);
+            $directoryList = new DirectoryList(BP);
             $composerJsonFinder = new ComposerJsonFinder($directoryList);
-            $productMetadata    = new ProductMetadata($composerJsonFinder);
+            $productMetadata = new ProductMetadata($composerJsonFinder);
             $version = $productMetadata->getVersion();
         }
 
@@ -93,18 +102,21 @@ class Cli extends SymfonyApplication
     /**
      * {@inheritdoc}
      *
-     * @throws \Exception
+     * @throws \Exception the exception in case of unexpected error
      */
-    public function doRun(InputInterface $input, OutputInterface $output)
+    public function doRun(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
     {
         $exitCode = parent::doRun($input, $output);
+
         if ($this->initException) {
             $output->writeln(
                 "<error>We're sorry, an error occurred. Try clearing the cache and code generation directories. "
                 . "By default, they are: var/cache, var/di, var/generation, and var/page_cache.</error>"
             );
+
             throw $this->initException;
         }
+
         return $exitCode;
     }
 
@@ -119,39 +131,83 @@ class Cli extends SymfonyApplication
     /**
      * Gets application commands
      *
-     * @return array
+     * @return array a list of available application commands
      */
     protected function getApplicationCommands()
     {
         $commands = [];
         try {
-            $bootstrapParam = new ComplexParameter(self::INPUT_KEY_BOOTSTRAP);
-            $params = $bootstrapParam->mergeFromArgv($_SERVER, $_SERVER);
-            $params[Bootstrap::PARAM_REQUIRE_MAINTENANCE] = null;
-            $bootstrap = Bootstrap::create(BP, $params);
-            $objectManager = $bootstrap->getObjectManager();
-            /** @var \Magento\Setup\Model\ObjectManagerProvider $omProvider */
-            $omProvider = $this->serviceManager->get(\Magento\Setup\Model\ObjectManagerProvider::class);
-            $omProvider->setObjectManager($objectManager);
-
             if (class_exists(\Magento\Setup\Console\CommandList::class)) {
                 $setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager);
                 $commands = array_merge($commands, $setupCommandList->getCommands());
             }
 
-            if ($objectManager->get(\Magento\Framework\App\DeploymentConfig::class)->isAvailable()) {
+            if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) {
                 /** @var \Magento\Framework\Console\CommandListInterface */
-                $commandList = $objectManager->create(\Magento\Framework\Console\CommandListInterface::class);
+                $commandList = $this->objectManager->create(CommandListInterface::class);
                 $commands = array_merge($commands, $commandList->getCommands());
             }
 
-            $commands = array_merge($commands, $this->getVendorCommands($objectManager));
+            $commands = array_merge(
+                $commands,
+                $this->getVendorCommands($this->objectManager)
+            );
         } catch (\Exception $e) {
             $this->initException = $e;
         }
+
         return $commands;
     }
 
+    /**
+     * Object Manager initialization.
+     *
+     * @return void
+     */
+    private function initObjectManager()
+    {
+        $params = (new ComplexParameter(self::INPUT_KEY_BOOTSTRAP))->mergeFromArgv($_SERVER, $_SERVER);
+        $params[Bootstrap::PARAM_REQUIRE_MAINTENANCE] = null;
+
+        $this->objectManager = Bootstrap::create(BP, $params)->getObjectManager();
+
+        /** @var ObjectManagerProvider $omProvider */
+        $omProvider = $this->serviceManager->get(ObjectManagerProvider::class);
+        $omProvider->setObjectManager($this->objectManager);
+    }
+
+    /**
+     * Checks whether generation directory is read-only.
+     * Depends on the current mode:
+     *      production - application will proceed
+     *      default - application will be terminated
+     *      developer - application will be terminated
+     *
+     * @return void
+     */
+    private function assertGenerationPermissions()
+    {
+        $generationDirectoryAccess = new GenerationDirectoryAccess($this->serviceManager);
+        /** @var DeploymentConfig $deploymentConfig */
+        $deploymentConfig = $this->objectManager->get(DeploymentConfig::class);
+        /** @var State $state */
+        $state = $this->objectManager->get(State::class);
+
+        if (
+            $deploymentConfig->isAvailable()
+            && $state->getMode() !== State::MODE_PRODUCTION
+            && !$generationDirectoryAccess->check()
+        ) {
+            $output = new Console\Output\ConsoleOutput();
+            $output->writeln(
+                '<error>Command line user does not have read and write permissions on var/generation directory.  Please'
+                . ' address this issue before using Magento command line.</error>'
+            );
+
+            exit(static::RETURN_FAILURE);
+        }
+    }
+
     /**
      * Retrieves vendor commands.
      *
-- 
GitLab