Introduce option to force copy instead of symlinking in PathDownloader

main
Peter Buri 8 years ago
parent 9e623f50e7
commit 861b2bc8e8

@ -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 is _not_ possible the package will be copied. In that case, the console will
output `Mirrored from ../../packages/my-package`. 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. Instead of using a relative path, an absolute path can also be used.
> **Note:** Repository paths can also contain wildcards like ``*`` and ``?``. > **Note:** Repository paths can also contain wildcards like ``*`` and ``?``.

@ -25,6 +25,9 @@ use Symfony\Component\Filesystem\Filesystem;
*/ */
class PathDownloader extends FileDownloader class PathDownloader extends FileDownloader
{ {
const STRATEGY_SYMLINK = 10;
const STRATEGY_MIRROR = 20;
/** /**
* {@inheritdoc} * {@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(); $fileSystem = new Filesystem();
$this->filesystem->removeDirectory($path); $this->filesystem->removeDirectory($path);
@ -54,18 +72,29 @@ class PathDownloader extends FileDownloader
$package->getFullPrettyVersion() $package->getFullPrettyVersion()
)); ));
if (self::STRATEGY_SYMLINK == $currentStrategy) {
try { try {
if (Platform::isWindows()) { if (Platform::isWindows()) {
// Implement symlinks as NTFS junctions on Windows // Implement symlinks as NTFS junctions on Windows
$this->filesystem->junction($realUrl, $path); $this->filesystem->junction($realUrl, $path);
$this->io->writeError(sprintf(' Junctioned from %s', $url)); $this->io->writeError(sprintf(' Junctioned from %s', $url));
} else { } else {
$shortestPath = $this->filesystem->findShortestPath($path, $realUrl); $shortestPath = $this->filesystem->findShortestPath($path, $realUrl);
$fileSystem->symlink($shortestPath, $path); $fileSystem->symlink($shortestPath, $path);
$this->io->writeError(sprintf(' Symlinked from %s', $url)); $this->io->writeError(sprintf(' Symlinked from %s', $url));
} }
} catch (IOException $e) { } 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));
}
}
}
// Fallback if symlink failed or if symlink is not allowed for the package
if (self::STRATEGY_MIRROR == $currentStrategy) {
$fileSystem->mirror($realUrl, $path); $fileSystem->mirror($realUrl, $path);
$this->io->writeError(sprintf(' Mirrored from %s', $url)); $this->io->writeError(sprintf(' Mirrored from %s', $url));
} }

@ -42,7 +42,14 @@ use Composer\Util\ProcessExecutor;
* { * {
* "type": "path", * "type": "path",
* "url": "/absolute/path/to/several/packages/*" * "url": "/absolute/path/to/several/packages/*"
* },
* {
* "type": "path",
* "url": "../../relative/path/to/package/",
* "options": {
* "symlink": false
* } * }
* },
* ] * ]
* @endcode * @endcode
* *
@ -76,6 +83,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
*/ */
private $process; private $process;
/**
* @var array
*/
private $options;
/** /**
* Initializes path repository. * Initializes path repository.
* *
@ -89,11 +101,12 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
throw new \RuntimeException('You must specify the `url` configuration for the path repository'); 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->url = $repoConfig['url'];
$this->process = new ProcessExecutor($io); $this->process = new ProcessExecutor($io);
$this->versionGuesser = new VersionGuesser($config, $this->process, new VersionParser()); $this->versionGuesser = new VersionGuesser($config, $this->process, new VersionParser());
$this->repoConfig = $repoConfig; $this->repoConfig = $repoConfig;
$this->options = isset($repoConfig['options']) ? $repoConfig['options'] : array();
parent::__construct(); parent::__construct();
} }
@ -127,6 +140,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
'url' => $url, 'url' => $url,
'reference' => sha1($json), 'reference' => sha1($json),
); );
$package['transport-options'] = $this->getOptions();
if (!isset($package['version'])) { if (!isset($package['version'])) {
$package['version'] = $this->versionGuesser->guessVersion($package, $path) ?: 'dev-master'; $package['version'] = $this->versionGuesser->guessVersion($package, $path) ?: 'dev-master';
@ -136,7 +150,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)) { if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
$package['dist']['reference'] = trim($output); $package['dist']['reference'] = trim($output);
} }
$package = $this->loader->load($package); $package = $this->loader->load($package);
$this->addPackage($package); $this->addPackage($package);
} }
@ -154,4 +167,20 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
return str_replace(DIRECTORY_SEPARATOR, '/', $val); return str_replace(DIRECTORY_SEPARATOR, '/', $val);
}, glob($this->url, GLOB_MARK | GLOB_ONLYDIR)); }, 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;
}
} }

Loading…
Cancel
Save