浏览代码

Merge remote-tracking branch 'burci/feature/path_downloader_option_to_force_copy'

Jordi Boggiano 9 年之前
父节点
当前提交
4df7ade801
共有 3 个文件被更改,包括 92 次插入13 次删除
  1. 20 0
      doc/05-repositories.md
  2. 40 10
      src/Composer/Downloader/PathDownloader.php
  3. 32 3
      src/Composer/Repository/PathRepository.php

+ 20 - 0
doc/05-repositories.md

@@ -649,6 +649,26 @@ the console will read `Symlinked from ../../packages/my-package`. If symlinking
 is _not_ possible the package will be copied. In that case, the console will
 output `Mirrored from ../../packages/my-package`.
 
+Instead of default fallback strategy you can force to use symlink with `"symlink": true` or
+mirroring with `"symlink": false` option.
+Forcing mirroring can be useful when deploying or generating package from a monolithic repository.
+
+```json
+{
+    "repositories": [
+        {
+            "type": "path",
+            "url": "../../packages/my-package",
+            "options": {
+                "symlink": false
+            }
+        }
+    ]
+}
+```
+
+
+
 Instead of using a relative path, an absolute path can also be used.
 
 > **Note:** Repository paths can also contain wildcards like ``*`` and ``?``.

+ 40 - 10
src/Composer/Downloader/PathDownloader.php

@@ -25,6 +25,9 @@ use Symfony\Component\Filesystem\Filesystem;
  */
 class PathDownloader extends FileDownloader
 {
+    const STRATEGY_SYMLINK = 10;
+    const STRATEGY_MIRROR  = 20;
+
     /**
      * {@inheritdoc}
      */
@@ -45,6 +48,21 @@ class PathDownloader extends FileDownloader
             ));
         }
 
+        // Get the transport options with default values
+        $transportOptions = $package->getTransportOptions() + array('symlink'=>null);
+
+        // When symlink transport option is null, both symlink and mirror are allowed
+        $currentStrategy = self::STRATEGY_SYMLINK;
+        $allowedStrategies = array(self::STRATEGY_SYMLINK, self::STRATEGY_MIRROR);
+
+        if (true === $transportOptions['symlink']) {
+            $currentStrategy = self::STRATEGY_SYMLINK;
+            $allowedStrategies = array(self::STRATEGY_SYMLINK);
+        } elseif(false === $transportOptions['symlink']) {
+            $currentStrategy = self::STRATEGY_MIRROR;
+            $allowedStrategies = array(self::STRATEGY_MIRROR);
+        }
+
         $fileSystem = new Filesystem();
         $this->filesystem->removeDirectory($path);
 
@@ -54,17 +72,29 @@ class PathDownloader extends FileDownloader
             $package->getFullPrettyVersion()
         ));
 
-        try {
-            if (Platform::isWindows()) {
-                // Implement symlinks as NTFS junctions on Windows
-                $this->filesystem->junction($realUrl, $path);
-                $this->io->writeError(sprintf('    Junctioned from %s', $url));
-            } else {
-                $shortestPath = $this->filesystem->findShortestPath($path, $realUrl);
-                $fileSystem->symlink($shortestPath, $path);
-                $this->io->writeError(sprintf('    Symlinked from %s', $url));
+        if (self::STRATEGY_SYMLINK == $currentStrategy) {
+            try {
+                if (Platform::isWindows()) {
+                    // Implement symlinks as NTFS junctions on Windows
+                    $this->filesystem->junction($realUrl, $path);
+                    $this->io->writeError(sprintf('    Junctioned from %s', $url));
+                } else {
+                    $shortestPath = $this->filesystem->findShortestPath($path, $realUrl);
+                    $fileSystem->symlink($shortestPath, $path);
+                    $this->io->writeError(sprintf('    Symlinked from %s', $url));
+                }
+            } catch (IOException $e) {
+                if (in_array(self::STRATEGY_MIRROR, $allowedStrategies)) {
+                    $this->io->writeError('    <error>Symlink failed, fallback to use mirroring!</error>');
+                    $currentStrategy = self::STRATEGY_MIRROR;
+                } else {
+                    throw new \RuntimeException(sprintf('Symlink from "%s" to "%s" failed!', $realUrl, $path));
+                }
             }
-        } catch (IOException $e) {
+        }
+
+        // Fallback if symlink failed or if symlink is not allowed for the package
+        if (self::STRATEGY_MIRROR == $currentStrategy) {
             $fileSystem->mirror($realUrl, $path);
             $this->io->writeError(sprintf('    Mirrored from %s', $url));
         }

+ 32 - 3
src/Composer/Repository/PathRepository.php

@@ -41,7 +41,14 @@ use Composer\Util\ProcessExecutor;
  *     {
  *         "type": "path",
  *         "url": "/absolute/path/to/several/packages/*"
- *     }
+ *     },
+ *     {
+ *         "type": "path",
+ *         "url": "../../relative/path/to/package/",
+ *         "options": {
+ *             "symlink": false
+ *         }
+ *     },
  * ]
  * @endcode
  *
@@ -75,6 +82,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
      */
     private $process;
 
+    /**
+     * @var array
+     */
+    private $options;
+
     /**
      * Initializes path repository.
      *
@@ -88,11 +100,12 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
             throw new \RuntimeException('You must specify the `url` configuration for the path repository');
         }
 
-        $this->loader = new ArrayLoader();
+        $this->loader = new ArrayLoader(null, true);
         $this->url = $repoConfig['url'];
         $this->process = new ProcessExecutor($io);
         $this->versionGuesser = new VersionGuesser($config, $this->process, new VersionParser());
         $this->repoConfig = $repoConfig;
+        $this->options = isset($repoConfig['options']) ? $repoConfig['options'] : array();
 
         parent::__construct();
     }
@@ -126,6 +139,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
                 'url' => $url,
                 'reference' => sha1($json),
             );
+            $package['transport-options'] = $this->getOptions();
 
             if (!isset($package['version'])) {
                 $package['version'] = $this->versionGuesser->guessVersion($package, $path) ?: 'dev-master';
@@ -135,7 +149,6 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
             if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
                 $package['dist']['reference'] = trim($output);
             }
-
             $package = $this->loader->load($package);
             $this->addPackage($package);
         }
@@ -153,4 +166,20 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
             return str_replace(DIRECTORY_SEPARATOR, '/', $val);
         }, glob($this->url, GLOB_MARK | GLOB_ONLYDIR));
     }
+
+    /**
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * @param array $options
+     */
+    public function setOptions($options)
+    {
+        $this->options = $options;
+    }
 }