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
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 ``?``.

@ -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,18 +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));
}

@ -42,7 +42,14 @@ use Composer\Util\ProcessExecutor;
* {
* "type": "path",
* "url": "/absolute/path/to/several/packages/*"
* }
* },
* {
* "type": "path",
* "url": "../../relative/path/to/package/",
* "options": {
* "symlink": false
* }
* },
* ]
* @endcode
*
@ -76,6 +83,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
*/
private $process;
/**
* @var array
*/
private $options;
/**
* 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');
}
$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();
}
@ -127,6 +140,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';
@ -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)) {
$package['dist']['reference'] = trim($output);
}
$package = $this->loader->load($package);
$this->addPackage($package);
}
@ -154,4 +167,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;
}
}

Loading…
Cancel
Save