Add a readonly mode to the cache, fixes #9150
parent
875a4784ed
commit
90332f1dbd
|
@ -204,6 +204,10 @@ downloads. When the garbage collection is periodically ran, this is the maximum
|
||||||
size the cache will be able to use. Older (less used) files will be removed
|
size the cache will be able to use. Older (less used) files will be removed
|
||||||
first until the cache fits.
|
first until the cache fits.
|
||||||
|
|
||||||
|
## cache-read-only
|
||||||
|
|
||||||
|
Defaults to `false`. Whether to use the Composer cache in read-only mode.
|
||||||
|
|
||||||
## bin-compat
|
## bin-compat
|
||||||
|
|
||||||
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
|
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
|
||||||
|
|
|
@ -237,6 +237,10 @@
|
||||||
"type": ["string", "integer"],
|
"type": ["string", "integer"],
|
||||||
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
||||||
},
|
},
|
||||||
|
"cache-read-only": {
|
||||||
|
"type": ["boolean"],
|
||||||
|
"description": "Whether to use the Composer cache in read-only mode."
|
||||||
|
},
|
||||||
"bin-compat": {
|
"bin-compat": {
|
||||||
"enum": ["auto", "full"],
|
"enum": ["auto", "full"],
|
||||||
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
|
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
|
||||||
|
|
|
@ -30,19 +30,22 @@ class Cache
|
||||||
private $enabled = true;
|
private $enabled = true;
|
||||||
private $allowlist;
|
private $allowlist;
|
||||||
private $filesystem;
|
private $filesystem;
|
||||||
|
private $readOnly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IOInterface $io
|
* @param IOInterface $io
|
||||||
* @param string $cacheDir location of the cache
|
* @param string $cacheDir location of the cache
|
||||||
* @param string $allowlist List of characters that are allowed in path names (used in a regex character class)
|
* @param string $allowlist List of characters that are allowed in path names (used in a regex character class)
|
||||||
* @param Filesystem $filesystem optional filesystem instance
|
* @param Filesystem $filesystem optional filesystem instance
|
||||||
|
* @param bool $readOnly whether the cache is in readOnly mode
|
||||||
*/
|
*/
|
||||||
public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null)
|
public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null, $readOnly = false)
|
||||||
{
|
{
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->root = rtrim($cacheDir, '/\\') . '/';
|
$this->root = rtrim($cacheDir, '/\\') . '/';
|
||||||
$this->allowlist = $allowlist;
|
$this->allowlist = $allowlist;
|
||||||
$this->filesystem = $filesystem ?: new Filesystem();
|
$this->filesystem = $filesystem ?: new Filesystem();
|
||||||
|
$this->readOnly = (bool) $readOnly;
|
||||||
|
|
||||||
if (!self::isUsable($cacheDir)) {
|
if (!self::isUsable($cacheDir)) {
|
||||||
$this->enabled = false;
|
$this->enabled = false;
|
||||||
|
@ -59,6 +62,22 @@ class Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $readOnly
|
||||||
|
*/
|
||||||
|
public function setReadOnly($readOnly)
|
||||||
|
{
|
||||||
|
$this->readOnly = (bool) $readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isReadOnly()
|
||||||
|
{
|
||||||
|
return $this->readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
public static function isUsable($path)
|
public static function isUsable($path)
|
||||||
{
|
{
|
||||||
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
|
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
|
||||||
|
@ -90,7 +109,7 @@ class Cache
|
||||||
|
|
||||||
public function write($file, $contents)
|
public function write($file, $contents)
|
||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled && !$this->readOnly) {
|
||||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||||
|
|
||||||
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
|
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
|
||||||
|
@ -128,7 +147,7 @@ class Cache
|
||||||
*/
|
*/
|
||||||
public function copyFrom($file, $source)
|
public function copyFrom($file, $source)
|
||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled && !$this->readOnly) {
|
||||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||||
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ EOT
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$cache = new Cache($io, $cachePath);
|
$cache = new Cache($io, $cachePath);
|
||||||
|
$cache->setReadOnly($config->get('cache-read-only'));
|
||||||
if (!$cache->isEnabled()) {
|
if (!$cache->isEnabled()) {
|
||||||
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
|
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Config
|
||||||
'cache-ttl' => 15552000, // 6 months
|
'cache-ttl' => 15552000, // 6 months
|
||||||
'cache-files-ttl' => null, // fallback to cache-ttl
|
'cache-files-ttl' => null, // fallback to cache-ttl
|
||||||
'cache-files-maxsize' => '300MiB',
|
'cache-files-maxsize' => '300MiB',
|
||||||
|
'cache-read-only' => false,
|
||||||
'bin-compat' => 'auto',
|
'bin-compat' => 'auto',
|
||||||
'discard-changes' => false,
|
'discard-changes' => false,
|
||||||
'autoloader-suffix' => null,
|
'autoloader-suffix' => null,
|
||||||
|
@ -236,6 +237,7 @@ class Config
|
||||||
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
||||||
|
|
||||||
// booleans with env var support
|
// booleans with env var support
|
||||||
|
case 'cache-read-only':
|
||||||
case 'htaccess-protect':
|
case 'htaccess-protect':
|
||||||
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
|
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
|
||||||
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
|
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
|
||||||
|
|
|
@ -187,7 +187,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
||||||
$url = reset($urls);
|
$url = reset($urls);
|
||||||
$cacheKey = $url['cacheKey'];
|
$cacheKey = $url['cacheKey'];
|
||||||
|
|
||||||
if ($cache) {
|
if ($cache && !$cache->isReadOnly()) {
|
||||||
$self->lastCacheWrites[$package->getName()] = $cacheKey;
|
$self->lastCacheWrites[$package->getName()] = $cacheKey;
|
||||||
$cache->copyFrom($cacheKey, $fileName);
|
$cache->copyFrom($cacheKey, $fileName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,6 +477,7 @@ class Factory
|
||||||
$cache = null;
|
$cache = null;
|
||||||
if ($config->get('cache-files-ttl') > 0) {
|
if ($config->get('cache-files-ttl') > 0) {
|
||||||
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
|
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
|
||||||
|
$cache->setReadOnly($config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$fs = new Filesystem($process);
|
$fs = new Filesystem($process);
|
||||||
|
|
|
@ -130,6 +130,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$~');
|
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$~');
|
||||||
|
$this->cache->setReadOnly($config->get('cache-read-only'));
|
||||||
$this->versionParser = new VersionParser();
|
$this->versionParser = new VersionParser();
|
||||||
$this->loader = new ArrayLoader($this->versionParser);
|
$this->loader = new ArrayLoader($this->versionParser);
|
||||||
$this->httpDownloader = $httpDownloader;
|
$this->httpDownloader = $httpDownloader;
|
||||||
|
@ -1071,7 +1072,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data = $response->decodeJson();
|
$data = $response->decodeJson();
|
||||||
HttpDownloader::outputWarnings($this->io, $this->url, $data);
|
HttpDownloader::outputWarnings($this->io, $this->url, $data);
|
||||||
|
|
||||||
if ($cacheKey) {
|
if ($cacheKey && !$this->cache->isReadOnly()) {
|
||||||
if ($storeLastModifiedTime) {
|
if ($storeLastModifiedTime) {
|
||||||
$lastModifiedDate = $response->getHeader('last-modified');
|
$lastModifiedDate = $response->getHeader('last-modified');
|
||||||
if ($lastModifiedDate) {
|
if ($lastModifiedDate) {
|
||||||
|
@ -1155,7 +1156,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data['last-modified'] = $lastModifiedDate;
|
$data['last-modified'] = $lastModifiedDate;
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
}
|
}
|
||||||
$this->cache->write($cacheKey, $json);
|
if (!$this->cache->isReadOnly()) {
|
||||||
|
$this->cache->write($cacheKey, $json);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -1238,7 +1241,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$data['last-modified'] = $lastModifiedDate;
|
$data['last-modified'] = $lastModifiedDate;
|
||||||
$json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE);
|
$json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
$cache->write($cacheKey, $json);
|
if (!$cache->isReadOnly()) {
|
||||||
|
$cache->write($cacheKey, $json);
|
||||||
|
}
|
||||||
$repo->freshMetadataUrls[$filename] = true;
|
$repo->freshMetadataUrls[$filename] = true;
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -60,6 +60,7 @@ abstract class BitbucketDriver extends VcsDriver
|
||||||
$this->repository,
|
$this->repository,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -75,6 +75,7 @@ class GitDriver extends VcsDriver
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,6 +58,7 @@ class GitHubDriver extends VcsDriver
|
||||||
$this->originUrl = 'github.com';
|
$this->originUrl = 'github.com';
|
||||||
}
|
}
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
if ( $this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'] ) ){
|
if ( $this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'] ) ){
|
||||||
$this->setupGitDriver($this->url);
|
$this->setupGitDriver($this->url);
|
||||||
|
|
|
@ -105,6 +105,7 @@ class GitLabDriver extends VcsDriver
|
||||||
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
$this->repository = preg_replace('#(\.git)$#', '', $match['repo']);
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository);
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository);
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
$this->fetchProject();
|
$this->fetchProject();
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ class SvnDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
|
||||||
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
$this->getTags();
|
$this->getTags();
|
||||||
|
|
Loading…
Reference in New Issue