Add a readonly mode to the cache, fixes #9150

main
Jordi Boggiano 4 years ago
parent 875a4784ed
commit 90332f1dbd
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -85,11 +85,11 @@ gitlab.com the domain names must be also specified with the
## gitlab-token
A list of domain names and private tokens. Private token can be either simple
string, or array with username and token. For example using `{"gitlab.com":
A list of domain names and private tokens. Private token can be either simple
string, or array with username and token. For example using `{"gitlab.com":
"privatetoken"}` as the value of this option will use `privatetoken` to access
private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser",
"token": "privatetoken"}}` will use both username and token for gitlab deploy
"token": "privatetoken"}}` will use both username and token for gitlab deploy
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
Please note: If the package is not hosted at
gitlab.com the domain names must be also specified with the
@ -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
first until the cache fits.
## cache-read-only
Defaults to `false`. Whether to use the Composer cache in read-only mode.
## bin-compat
Defaults to `auto`. Determines the compatibility of the binaries to be installed.

@ -237,6 +237,10 @@
"type": ["string", "integer"],
"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": {
"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)."

@ -30,19 +30,22 @@ class Cache
private $enabled = true;
private $allowlist;
private $filesystem;
private $readOnly;
/**
* @param IOInterface $io
* @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 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->root = rtrim($cacheDir, '/\\') . '/';
$this->allowlist = $allowlist;
$this->filesystem = $filesystem ?: new Filesystem();
$this->readOnly = (bool) $readOnly;
if (!self::isUsable($cacheDir)) {
$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)
{
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
@ -90,7 +109,7 @@ class Cache
public function write($file, $contents)
{
if ($this->enabled) {
if ($this->enabled && !$this->readOnly) {
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
@ -128,7 +147,7 @@ class Cache
*/
public function copyFrom($file, $source)
{
if ($this->enabled) {
if ($this->enabled && !$this->readOnly) {
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));

@ -59,6 +59,7 @@ EOT
continue;
}
$cache = new Cache($io, $cachePath);
$cache->setReadOnly($config->get('cache-read-only'));
if (!$cache->isEnabled()) {
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");

@ -41,6 +41,7 @@ class Config
'cache-ttl' => 15552000, // 6 months
'cache-files-ttl' => null, // fallback to cache-ttl
'cache-files-maxsize' => '300MiB',
'cache-read-only' => false,
'bin-compat' => 'auto',
'discard-changes' => false,
'autoloader-suffix' => null,
@ -236,6 +237,7 @@ class Config
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
// booleans with env var support
case 'cache-read-only':
case 'htaccess-protect':
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));

@ -187,7 +187,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
$url = reset($urls);
$cacheKey = $url['cacheKey'];
if ($cache) {
if ($cache && !$cache->isReadOnly()) {
$self->lastCacheWrites[$package->getName()] = $cacheKey;
$cache->copyFrom($cacheKey, $fileName);
}

@ -477,6 +477,7 @@ class Factory
$cache = null;
if ($config->get('cache-files-ttl') > 0) {
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
$cache->setReadOnly($config->get('cache-read-only'));
}
$fs = new Filesystem($process);

@ -130,6 +130,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
$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->setReadOnly($config->get('cache-read-only'));
$this->versionParser = new VersionParser();
$this->loader = new ArrayLoader($this->versionParser);
$this->httpDownloader = $httpDownloader;
@ -1071,7 +1072,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$data = $response->decodeJson();
HttpDownloader::outputWarnings($this->io, $this->url, $data);
if ($cacheKey) {
if ($cacheKey && !$this->cache->isReadOnly()) {
if ($storeLastModifiedTime) {
$lastModifiedDate = $response->getHeader('last-modified');
if ($lastModifiedDate) {
@ -1155,7 +1156,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$data['last-modified'] = $lastModifiedDate;
$json = json_encode($data);
}
$this->cache->write($cacheKey, $json);
if (!$this->cache->isReadOnly()) {
$this->cache->write($cacheKey, $json);
}
return $data;
} catch (\Exception $e) {
@ -1238,7 +1241,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$data['last-modified'] = $lastModifiedDate;
$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;
return $data;

@ -60,6 +60,7 @@ abstract class BitbucketDriver extends VcsDriver
$this->repository,
))
);
$this->cache->setReadOnly($this->config->get('cache-read-only'));
}
/**

@ -75,6 +75,7 @@ class GitDriver extends VcsDriver
$this->getBranches();
$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->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'] ) ){
$this->setupGitDriver($this->url);

@ -105,6 +105,7 @@ class GitLabDriver extends VcsDriver
$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->setReadOnly($this->config->get('cache-read-only'));
$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->setReadOnly($this->config->get('cache-read-only'));
$this->getBranches();
$this->getTags();

Loading…
Cancel
Save