initial commit
This commit is contained in:
131
vendor/symfony/flex/src/Configurator/AbstractConfigurator.php
vendored
Normal file
131
vendor/symfony/flex/src/Configurator/AbstractConfigurator.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Options;
|
||||
use Symfony\Flex\Path;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractConfigurator
|
||||
{
|
||||
protected $composer;
|
||||
protected $io;
|
||||
protected $options;
|
||||
protected $path;
|
||||
|
||||
public function __construct(Composer $composer, IOInterface $io, Options $options)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
$this->options = $options;
|
||||
$this->path = new Path($options->get('root-dir'));
|
||||
}
|
||||
|
||||
abstract public function configure(Recipe $recipe, $config, Lock $lock, array $options = []);
|
||||
|
||||
abstract public function unconfigure(Recipe $recipe, $config, Lock $lock);
|
||||
|
||||
abstract public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void;
|
||||
|
||||
protected function write($messages, $verbosity = IOInterface::VERBOSE)
|
||||
{
|
||||
if (!\is_array($messages)) {
|
||||
$messages = [$messages];
|
||||
}
|
||||
foreach ($messages as $i => $message) {
|
||||
$messages[$i] = ' '.$message;
|
||||
}
|
||||
$this->io->writeError($messages, true, $verbosity);
|
||||
}
|
||||
|
||||
protected function isFileMarked(Recipe $recipe, string $file): bool
|
||||
{
|
||||
return is_file($file) && str_contains(file_get_contents($file), \sprintf('###> %s ###', $recipe->getName()));
|
||||
}
|
||||
|
||||
protected function markData(Recipe $recipe, string $data): string
|
||||
{
|
||||
return "\n".\sprintf('###> %s ###%s%s%s###< %s ###%s', $recipe->getName(), "\n", rtrim($data, "\r\n"), "\n", $recipe->getName(), "\n");
|
||||
}
|
||||
|
||||
protected function isFileXmlMarked(Recipe $recipe, string $file): bool
|
||||
{
|
||||
return is_file($file) && str_contains(file_get_contents($file), \sprintf('###+ %s ###', $recipe->getName()));
|
||||
}
|
||||
|
||||
protected function markXmlData(Recipe $recipe, string $data): string
|
||||
{
|
||||
return "\n".\sprintf(' <!-- ###+ %s ### -->%s%s%s <!-- ###- %s ### -->%s', $recipe->getName(), "\n", rtrim($data, "\r\n"), "\n", $recipe->getName(), "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if section was found and replaced
|
||||
*/
|
||||
protected function updateData(string $file, string $data): bool
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
$newContents = $this->updateDataString($contents, $data);
|
||||
if (null === $newContents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_put_contents($file, $newContents);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null returns the updated content if the section was found, null if not found
|
||||
*/
|
||||
protected function updateDataString(string $contents, string $data): ?string
|
||||
{
|
||||
$pieces = explode("\n", trim($data));
|
||||
$startMark = trim(reset($pieces));
|
||||
$endMark = trim(end($pieces));
|
||||
|
||||
if (!str_contains($contents, $startMark) || !str_contains($contents, $endMark)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pattern = '/'.preg_quote($startMark, '/').'.*?'.preg_quote($endMark, '/').'/s';
|
||||
|
||||
return preg_replace($pattern, trim($data), $contents);
|
||||
}
|
||||
|
||||
protected function extractSection(Recipe $recipe, string $contents): ?string
|
||||
{
|
||||
$section = $this->markData($recipe, '----');
|
||||
|
||||
$pieces = explode("\n", trim($section));
|
||||
$startMark = trim(reset($pieces));
|
||||
$endMark = trim(end($pieces));
|
||||
|
||||
$pattern = '/'.preg_quote($startMark, '/').'.*?'.preg_quote($endMark, '/').'/s';
|
||||
|
||||
$matches = [];
|
||||
preg_match($pattern, $contents, $matches);
|
||||
|
||||
return $matches[0] ?? null;
|
||||
}
|
||||
}
|
||||
274
vendor/symfony/flex/src/Configurator/AddLinesConfigurator.php
vendored
Normal file
274
vendor/symfony/flex/src/Configurator/AddLinesConfigurator.php
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
*/
|
||||
class AddLinesConfigurator extends AbstractConfigurator
|
||||
{
|
||||
private const POSITION_TOP = 'top';
|
||||
private const POSITION_BOTTOM = 'bottom';
|
||||
private const POSITION_AFTER_TARGET = 'after_target';
|
||||
|
||||
private const VALID_POSITIONS = [
|
||||
self::POSITION_TOP,
|
||||
self::POSITION_BOTTOM,
|
||||
self::POSITION_AFTER_TARGET,
|
||||
];
|
||||
|
||||
/**
|
||||
* Holds file contents for files that have been loaded.
|
||||
* This allows us to "change" the contents of a file multiple
|
||||
* times before we actually write it out.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $fileContents = [];
|
||||
|
||||
public function configure(Recipe $recipe, $config, Lock $lock, array $options = []): void
|
||||
{
|
||||
$this->fileContents = [];
|
||||
$this->executeConfigure($recipe, $config);
|
||||
|
||||
foreach ($this->fileContents as $file => $contents) {
|
||||
$this->write(\sprintf('[add-lines] Patching file "%s"', $this->relativize($file)));
|
||||
file_put_contents($file, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $config, Lock $lock): void
|
||||
{
|
||||
$this->fileContents = [];
|
||||
$this->executeUnconfigure($recipe, $config);
|
||||
|
||||
foreach ($this->fileContents as $file => $change) {
|
||||
$this->write(\sprintf('[add-lines] Reverting file "%s"', $this->relativize($file)));
|
||||
file_put_contents($file, $change);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
// manually check for "requires", as unconfigure ignores it
|
||||
$originalConfig = array_filter($originalConfig, function ($item) {
|
||||
return !isset($item['requires']) || $this->isPackageInstalled($item['requires']);
|
||||
});
|
||||
|
||||
// reset the file content cache
|
||||
$this->fileContents = [];
|
||||
$this->executeUnconfigure($recipeUpdate->getOriginalRecipe(), $originalConfig);
|
||||
$this->executeConfigure($recipeUpdate->getNewRecipe(), $newConfig);
|
||||
$newFiles = [];
|
||||
$originalFiles = [];
|
||||
foreach ($this->fileContents as $file => $contents) {
|
||||
// set the original file to the current contents
|
||||
$originalFiles[$this->relativize($file)] = file_get_contents($file);
|
||||
// and the new file where the old recipe was unconfigured, and the new configured
|
||||
$newFiles[$this->relativize($file)] = $contents;
|
||||
}
|
||||
$recipeUpdate->addOriginalFiles($originalFiles);
|
||||
$recipeUpdate->addNewFiles($newFiles);
|
||||
}
|
||||
|
||||
public function executeConfigure(Recipe $recipe, $config): void
|
||||
{
|
||||
foreach ($config as $patch) {
|
||||
if (!isset($patch['file'])) {
|
||||
$this->write(\sprintf('The "file" key is required for the "add-lines" configurator for recipe "%s". Skipping', $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($patch['requires']) && !$this->isPackageInstalled($patch['requires'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($patch['content'])) {
|
||||
$this->write(\sprintf('The "content" key is required for the "add-lines" configurator for recipe "%s". Skipping', $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
$content = $patch['content'];
|
||||
|
||||
$file = $this->path->concatenate([$this->options->get('root-dir'), $this->options->expandTargetDir($patch['file'])]);
|
||||
$warnIfMissing = isset($patch['warn_if_missing']) && $patch['warn_if_missing'];
|
||||
if (!is_file($file)) {
|
||||
$this->write([
|
||||
\sprintf('Could not add lines to file <info>%s</info> as it does not exist. Missing lines:', $patch['file']),
|
||||
'<comment>"""</comment>',
|
||||
$content,
|
||||
'<comment>"""</comment>',
|
||||
'',
|
||||
], $warnIfMissing ? IOInterface::NORMAL : IOInterface::VERBOSE);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($patch['position'])) {
|
||||
$this->write(\sprintf('The "position" key is required for the "add-lines" configurator for recipe "%s". Skipping', $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
$position = $patch['position'];
|
||||
if (!\in_array($position, self::VALID_POSITIONS, true)) {
|
||||
$this->write(\sprintf('The "position" key must be one of "%s" for the "add-lines" configurator for recipe "%s". Skipping', implode('", "', self::VALID_POSITIONS), $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (self::POSITION_AFTER_TARGET === $position && !isset($patch['target'])) {
|
||||
$this->write(\sprintf('The "target" key is required when "position" is "%s" for the "add-lines" configurator for recipe "%s". Skipping', self::POSITION_AFTER_TARGET, $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
$target = isset($patch['target']) ? $patch['target'] : null;
|
||||
|
||||
$newContents = $this->getPatchedContents($file, $content, $position, $target, $warnIfMissing);
|
||||
$this->fileContents[$file] = $newContents;
|
||||
}
|
||||
}
|
||||
|
||||
public function executeUnconfigure(Recipe $recipe, $config): void
|
||||
{
|
||||
foreach ($config as $patch) {
|
||||
if (!isset($patch['file'])) {
|
||||
$this->write(\sprintf('The "file" key is required for the "add-lines" configurator for recipe "%s". Skipping', $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore "requires": the target packages may have just become uninstalled.
|
||||
// Checking for a "content" match is enough.
|
||||
|
||||
$file = $this->path->concatenate([$this->options->get('root-dir'), $this->options->expandTargetDir($patch['file'])]);
|
||||
if (!is_file($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($patch['content'])) {
|
||||
$this->write(\sprintf('The "content" key is required for the "add-lines" configurator for recipe "%s". Skipping', $recipe->getName()));
|
||||
|
||||
continue;
|
||||
}
|
||||
$value = $patch['content'];
|
||||
|
||||
$newContents = $this->getUnPatchedContents($file, $value);
|
||||
$this->fileContents[$file] = $newContents;
|
||||
}
|
||||
}
|
||||
|
||||
private function getPatchedContents(string $file, string $value, string $position, ?string $target, bool $warnIfMissing): string
|
||||
{
|
||||
$fileContents = $this->readFile($file);
|
||||
|
||||
if (str_contains($fileContents, $value)) {
|
||||
return $fileContents; // already includes value, skip
|
||||
}
|
||||
|
||||
switch ($position) {
|
||||
case self::POSITION_BOTTOM:
|
||||
$fileContents .= "\n".$value;
|
||||
|
||||
break;
|
||||
case self::POSITION_TOP:
|
||||
$fileContents = $value."\n".$fileContents;
|
||||
|
||||
break;
|
||||
case self::POSITION_AFTER_TARGET:
|
||||
$lines = explode("\n", $fileContents);
|
||||
$targetFound = false;
|
||||
foreach ($lines as $key => $line) {
|
||||
if (str_contains($line, $target)) {
|
||||
array_splice($lines, $key + 1, 0, $value);
|
||||
$targetFound = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$fileContents = implode("\n", $lines);
|
||||
|
||||
if (!$targetFound) {
|
||||
$this->write([
|
||||
\sprintf('Could not add lines after "%s" as no such string was found in "%s". Missing lines:', $target, $file),
|
||||
'<comment>"""</comment>',
|
||||
$value,
|
||||
'<comment>"""</comment>',
|
||||
'',
|
||||
], $warnIfMissing ? IOInterface::NORMAL : IOInterface::VERBOSE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $fileContents;
|
||||
}
|
||||
|
||||
private function getUnPatchedContents(string $file, $value): string
|
||||
{
|
||||
$fileContents = $this->readFile($file);
|
||||
|
||||
if (!str_contains($fileContents, $value)) {
|
||||
return $fileContents; // value already gone!
|
||||
}
|
||||
|
||||
if (str_contains($fileContents, "\n".$value)) {
|
||||
$value = "\n".$value;
|
||||
} elseif (str_contains($fileContents, $value."\n")) {
|
||||
$value .= "\n";
|
||||
}
|
||||
|
||||
$position = strpos($fileContents, $value);
|
||||
|
||||
return substr_replace($fileContents, '', $position, \strlen($value));
|
||||
}
|
||||
|
||||
private function isPackageInstalled($packages): bool
|
||||
{
|
||||
if (\is_string($packages)) {
|
||||
$packages = [$packages];
|
||||
}
|
||||
|
||||
$installedRepo = $this->composer->getRepositoryManager()->getLocalRepository();
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$package = explode(':', $package, 2);
|
||||
$packageName = $package[0];
|
||||
$constraint = $package[1] ?? '*';
|
||||
|
||||
if (null === $installedRepo->findPackage($packageName, $constraint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function relativize(string $path): string
|
||||
{
|
||||
$rootDir = $this->options->get('root-dir');
|
||||
if (str_starts_with($path, $rootDir)) {
|
||||
$path = substr($path, \strlen($rootDir) + 1);
|
||||
}
|
||||
|
||||
return ltrim($path, '/\\');
|
||||
}
|
||||
|
||||
private function readFile(string $file): string
|
||||
{
|
||||
if (isset($this->fileContents[$file])) {
|
||||
return $this->fileContents[$file];
|
||||
}
|
||||
|
||||
$fileContents = file_get_contents($file);
|
||||
$this->fileContents[$file] = $fileContents;
|
||||
|
||||
return $fileContents;
|
||||
}
|
||||
}
|
||||
150
vendor/symfony/flex/src/Configurator/BundlesConfigurator.php
vendored
Normal file
150
vendor/symfony/flex/src/Configurator/BundlesConfigurator.php
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class BundlesConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $bundles, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Enabling the package as a Symfony bundle');
|
||||
$registered = $this->configureBundles($bundles);
|
||||
$this->dump($this->getConfFile(), $registered);
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $bundles, Lock $lock)
|
||||
{
|
||||
$this->write('Disabling the Symfony bundle');
|
||||
$file = $this->getConfFile();
|
||||
if (!file_exists($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$registered = $this->load($file);
|
||||
foreach (array_keys($this->prepareBundles($bundles)) as $class) {
|
||||
unset($registered[$class]);
|
||||
}
|
||||
$this->dump($file, $registered);
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$originalBundles = $this->configureBundles($originalConfig, true);
|
||||
$recipeUpdate->setOriginalFile(
|
||||
$this->getLocalConfFile(),
|
||||
$this->buildContents($originalBundles)
|
||||
);
|
||||
|
||||
$newBundles = $this->configureBundles($newConfig, true);
|
||||
$recipeUpdate->setNewFile(
|
||||
$this->getLocalConfFile(),
|
||||
$this->buildContents($newBundles)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureBundles(array $bundles, bool $resetEnvironments = false): array
|
||||
{
|
||||
$file = $this->getConfFile();
|
||||
$registered = $this->load($file);
|
||||
$classes = $this->prepareBundles($bundles);
|
||||
if (isset($classes[$fwb = 'Symfony\Bundle\FrameworkBundle\FrameworkBundle'])) {
|
||||
foreach ($classes[$fwb] as $env) {
|
||||
$registered[$fwb][$env] = true;
|
||||
}
|
||||
unset($classes[$fwb]);
|
||||
}
|
||||
foreach ($classes as $class => $envs) {
|
||||
// do not override existing configured envs for a bundle
|
||||
if (!isset($registered[$class]) || $resetEnvironments) {
|
||||
if ($resetEnvironments) {
|
||||
// used during calculating an "upgrade"
|
||||
// here, we want to "undo" the bundle's configuration entirely
|
||||
// then re-add it fresh, in case some environments have been
|
||||
// removed in an updated version of the recipe
|
||||
$registered[$class] = [];
|
||||
}
|
||||
|
||||
foreach ($envs as $env) {
|
||||
$registered[$class][$env] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $registered;
|
||||
}
|
||||
|
||||
private function prepareBundles(array $bundles): array
|
||||
{
|
||||
foreach ($bundles as $class => $envs) {
|
||||
$bundles[ltrim($class, '\\')] = $envs;
|
||||
}
|
||||
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
private function load(string $file): array
|
||||
{
|
||||
$bundles = file_exists($file) ? (require $file) : [];
|
||||
if (!\is_array($bundles)) {
|
||||
$bundles = [];
|
||||
}
|
||||
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
private function dump(string $file, array $bundles)
|
||||
{
|
||||
$contents = $this->buildContents($bundles);
|
||||
|
||||
if (!is_dir(\dirname($file))) {
|
||||
mkdir(\dirname($file), 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($file, $contents);
|
||||
|
||||
if (\function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate($file);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildContents(array $bundles): string
|
||||
{
|
||||
$contents = "<?php\n\nreturn [\n";
|
||||
foreach ($bundles as $class => $envs) {
|
||||
$contents .= " $class::class => [";
|
||||
foreach ($envs as $env => $value) {
|
||||
$booleanValue = var_export($value, true);
|
||||
$contents .= "'$env' => $booleanValue, ";
|
||||
}
|
||||
$contents = substr($contents, 0, -2)."],\n";
|
||||
}
|
||||
$contents .= "];\n";
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
private function getConfFile(): string
|
||||
{
|
||||
return $this->options->get('root-dir').'/'.$this->getLocalConfFile();
|
||||
}
|
||||
|
||||
private function getLocalConfFile(): string
|
||||
{
|
||||
return $this->options->expandTargetDir('%CONFIG_DIR%/bundles.php');
|
||||
}
|
||||
}
|
||||
73
vendor/symfony/flex/src/Configurator/ComposerCommandsConfigurator.php
vendored
Normal file
73
vendor/symfony/flex/src/Configurator/ComposerCommandsConfigurator.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\Factory;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Json\JsonManipulator;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Marcin Morawski <marcin@morawskim.pl>
|
||||
*/
|
||||
class ComposerCommandsConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $scripts, Lock $lock, array $options = [])
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
|
||||
file_put_contents($json->getPath(), $this->configureScripts($scripts, $json));
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $scripts, Lock $lock)
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
|
||||
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
|
||||
foreach ($scripts as $key => $command) {
|
||||
$manipulator->removeSubNode('scripts', $key);
|
||||
}
|
||||
|
||||
file_put_contents($json->getPath(), $manipulator->getContents());
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
$jsonPath = $json->getPath();
|
||||
if (str_starts_with($jsonPath, $recipeUpdate->getRootDir())) {
|
||||
$jsonPath = substr($jsonPath, \strlen($recipeUpdate->getRootDir()));
|
||||
}
|
||||
$jsonPath = ltrim($jsonPath, '/\\');
|
||||
|
||||
$recipeUpdate->setOriginalFile(
|
||||
$jsonPath,
|
||||
$this->configureScripts($originalConfig, $json)
|
||||
);
|
||||
$recipeUpdate->setNewFile(
|
||||
$jsonPath,
|
||||
$this->configureScripts($newConfig, $json)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureScripts(array $scripts, JsonFile $json): string
|
||||
{
|
||||
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
|
||||
foreach ($scripts as $cmdName => $script) {
|
||||
$manipulator->addSubNode('scripts', $cmdName, $script);
|
||||
}
|
||||
|
||||
return $manipulator->getContents();
|
||||
}
|
||||
}
|
||||
79
vendor/symfony/flex/src/Configurator/ComposerScriptsConfigurator.php
vendored
Normal file
79
vendor/symfony/flex/src/Configurator/ComposerScriptsConfigurator.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\Factory;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Json\JsonManipulator;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ComposerScriptsConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $scripts, Lock $lock, array $options = [])
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
|
||||
file_put_contents($json->getPath(), $this->configureScripts($scripts, $json));
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $scripts, Lock $lock)
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
|
||||
$jsonContents = $json->read();
|
||||
$autoScripts = $jsonContents['scripts']['auto-scripts'] ?? [];
|
||||
foreach (array_keys($scripts) as $cmd) {
|
||||
unset($autoScripts[$cmd]);
|
||||
}
|
||||
|
||||
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
|
||||
$manipulator->addSubNode('scripts', 'auto-scripts', $autoScripts);
|
||||
|
||||
file_put_contents($json->getPath(), $manipulator->getContents());
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
$jsonPath = $json->getPath();
|
||||
if (str_starts_with($jsonPath, $recipeUpdate->getRootDir())) {
|
||||
$jsonPath = substr($jsonPath, \strlen($recipeUpdate->getRootDir()));
|
||||
}
|
||||
$jsonPath = ltrim($jsonPath, '/\\');
|
||||
|
||||
$recipeUpdate->setOriginalFile(
|
||||
$jsonPath,
|
||||
$this->configureScripts($originalConfig, $json)
|
||||
);
|
||||
$recipeUpdate->setNewFile(
|
||||
$jsonPath,
|
||||
$this->configureScripts($newConfig, $json)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureScripts(array $scripts, JsonFile $json): string
|
||||
{
|
||||
$jsonContents = $json->read();
|
||||
$autoScripts = $jsonContents['scripts']['auto-scripts'] ?? [];
|
||||
$autoScripts = array_merge($autoScripts, $scripts);
|
||||
|
||||
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
|
||||
$manipulator->addSubNode('scripts', 'auto-scripts', $autoScripts);
|
||||
|
||||
return $manipulator->getContents();
|
||||
}
|
||||
}
|
||||
164
vendor/symfony/flex/src/Configurator/ContainerConfigurator.php
vendored
Normal file
164
vendor/symfony/flex/src/Configurator/ContainerConfigurator.php
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ContainerConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $parameters, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Setting parameters');
|
||||
$contents = $this->configureParameters($parameters);
|
||||
|
||||
if (null !== $contents) {
|
||||
file_put_contents($this->options->get('root-dir').'/'.$this->getServicesPath(), $contents);
|
||||
}
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $parameters, Lock $lock)
|
||||
{
|
||||
$this->write('Unsetting parameters');
|
||||
$target = $this->options->get('root-dir').'/'.$this->getServicesPath();
|
||||
$lines = $this->removeParametersFromLines(file($target), $parameters);
|
||||
file_put_contents($target, implode('', $lines));
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$recipeUpdate->setOriginalFile(
|
||||
$this->getServicesPath(),
|
||||
$this->configureParameters($originalConfig, true)
|
||||
);
|
||||
|
||||
// for the new file, we need to update any values *and* remove any removed values
|
||||
$removedParameters = [];
|
||||
foreach ($originalConfig as $name => $value) {
|
||||
if (!isset($newConfig[$name])) {
|
||||
$removedParameters[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$updatedFile = $this->configureParameters($newConfig, true);
|
||||
$lines = $this->removeParametersFromLines(explode("\n", $updatedFile), $removedParameters);
|
||||
|
||||
$recipeUpdate->setNewFile(
|
||||
$this->getServicesPath(),
|
||||
implode("\n", $lines)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureParameters(array $parameters, bool $update = false): string
|
||||
{
|
||||
$target = $this->options->get('root-dir').'/'.$this->getServicesPath();
|
||||
$endAt = 0;
|
||||
$isParameters = false;
|
||||
$lines = [];
|
||||
foreach (file($target) as $i => $line) {
|
||||
$lines[] = $line;
|
||||
if (!$isParameters && !preg_match('/^parameters:/', $line)) {
|
||||
continue;
|
||||
}
|
||||
if (!$isParameters) {
|
||||
$isParameters = true;
|
||||
continue;
|
||||
}
|
||||
if (!preg_match('/^\s+.*/', $line) && '' !== trim($line)) {
|
||||
$endAt = $i - 1;
|
||||
$isParameters = false;
|
||||
continue;
|
||||
}
|
||||
foreach ($parameters as $key => $value) {
|
||||
$matches = [];
|
||||
if (preg_match(\sprintf('/^\s+%s\:/', preg_quote($key, '/')), $line, $matches)) {
|
||||
if ($update) {
|
||||
$lines[$i] = substr($line, 0, \strlen($matches[0])).' '.str_replace("'", "''", $value)."\n";
|
||||
}
|
||||
|
||||
unset($parameters[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($parameters) {
|
||||
$parametersLines = [];
|
||||
if (!$endAt) {
|
||||
$parametersLines[] = "parameters:\n";
|
||||
}
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (\is_array($value)) {
|
||||
$parametersLines[] = \sprintf(" %s:\n%s", $key, $this->dumpYaml(2, $value));
|
||||
continue;
|
||||
}
|
||||
$parametersLines[] = \sprintf(" %s: '%s'%s", $key, str_replace("'", "''", $value), "\n");
|
||||
}
|
||||
if (!$endAt) {
|
||||
$parametersLines[] = "\n";
|
||||
}
|
||||
array_splice($lines, $endAt, 0, $parametersLines);
|
||||
}
|
||||
|
||||
return implode('', $lines);
|
||||
}
|
||||
|
||||
private function removeParametersFromLines(array $sourceLines, array $parameters): array
|
||||
{
|
||||
$lines = [];
|
||||
foreach ($sourceLines as $line) {
|
||||
if ($this->removeParameters(1, $parameters, $line)) {
|
||||
continue;
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
private function removeParameters($level, $params, $line)
|
||||
{
|
||||
foreach ($params as $key => $value) {
|
||||
if (\is_array($value) && $this->removeParameters($level + 1, $value, $line)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match(\sprintf('/^(\s{%d}|\t{%d})+%s\:/', 4 * $level, $level, preg_quote($key, '/')), $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function dumpYaml($level, $array): string
|
||||
{
|
||||
$line = '';
|
||||
foreach ($array as $key => $value) {
|
||||
$line .= str_repeat(' ', $level);
|
||||
if (!\is_array($value)) {
|
||||
$line .= \sprintf("%s: '%s'\n", $key, str_replace("'", "''", $value));
|
||||
continue;
|
||||
}
|
||||
$line .= \sprintf("%s:\n", $key).$this->dumpYaml($level + 1, $value);
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
private function getServicesPath(): string
|
||||
{
|
||||
return $this->options->expandTargetDir('%CONFIG_DIR%/services.yaml');
|
||||
}
|
||||
}
|
||||
168
vendor/symfony/flex/src/Configurator/CopyFromPackageConfigurator.php
vendored
Normal file
168
vendor/symfony/flex/src/Configurator/CopyFromPackageConfigurator.php
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CopyFromPackageConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $config, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Copying files from package');
|
||||
$packageDir = $this->composer->getInstallationManager()->getInstallPath($recipe->getPackage());
|
||||
$options = array_merge($this->options->toArray(), $options);
|
||||
|
||||
$files = $this->getFilesToCopy($config, $packageDir);
|
||||
foreach ($files as $source => $target) {
|
||||
$this->copyFile($source, $target, $options);
|
||||
}
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $config, Lock $lock)
|
||||
{
|
||||
$this->write('Removing files from package');
|
||||
$packageDir = $this->composer->getInstallationManager()->getInstallPath($recipe->getPackage());
|
||||
$this->removeFiles($config, $packageDir, $this->options->get('root-dir'));
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$packageDir = $this->composer->getInstallationManager()->getInstallPath($recipeUpdate->getNewRecipe()->getPackage());
|
||||
foreach ($originalConfig as $source => $target) {
|
||||
if (isset($newConfig[$source])) {
|
||||
// path is in both, we cannot update
|
||||
$recipeUpdate->addCopyFromPackagePath(
|
||||
$packageDir.'/'.$source,
|
||||
$this->options->expandTargetDir($target)
|
||||
);
|
||||
|
||||
unset($newConfig[$source]);
|
||||
}
|
||||
|
||||
// if any paths were removed from the recipe, we'll keep them
|
||||
}
|
||||
|
||||
// any remaining files are new, and we can copy them
|
||||
foreach ($this->getFilesToCopy($newConfig, $packageDir) as $source => $target) {
|
||||
if (!file_exists($source)) {
|
||||
throw new \LogicException(\sprintf('File "%s" does not exist!', $source));
|
||||
}
|
||||
|
||||
$recipeUpdate->setNewFile($target, file_get_contents($source));
|
||||
}
|
||||
}
|
||||
|
||||
private function getFilesToCopy(array $manifest, string $from): array
|
||||
{
|
||||
$files = [];
|
||||
foreach ($manifest as $source => $target) {
|
||||
$target = $this->options->expandTargetDir($target);
|
||||
if ('/' === substr($source, -1)) {
|
||||
$files = array_merge($files, $this->getFilesForDir($this->path->concatenate([$from, $source]), $target));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$files[$this->path->concatenate([$from, $source])] = $target;
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
private function removeFiles(array $manifest, string $from, string $to)
|
||||
{
|
||||
foreach ($manifest as $source => $target) {
|
||||
$target = $this->options->expandTargetDir($target);
|
||||
if ('/' === substr($source, -1)) {
|
||||
$this->removeFilesFromDir($this->path->concatenate([$from, $source]), $this->path->concatenate([$to, $target]));
|
||||
} else {
|
||||
$targetPath = $this->path->concatenate([$to, $target]);
|
||||
if (file_exists($targetPath)) {
|
||||
@unlink($targetPath);
|
||||
$this->write(\sprintf(' Removed <fg=green>"%s"</>', $this->path->relativize($targetPath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getFilesForDir(string $source, string $target): array
|
||||
{
|
||||
$iterator = $this->createSourceIterator($source, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$files = [];
|
||||
foreach ($iterator as $item) {
|
||||
$targetPath = $this->path->concatenate([$target, $iterator->getSubPathName()]);
|
||||
|
||||
$files[(string) $item] = $targetPath;
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source The absolute path to the source file
|
||||
* @param string $target The relative (to root dir) path to the target
|
||||
*/
|
||||
public function copyFile(string $source, string $target, array $options)
|
||||
{
|
||||
$target = $this->options->get('root-dir').'/'.$this->options->expandTargetDir($target);
|
||||
if (is_dir($source)) {
|
||||
// directory will be created when a file is copied to it
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->options->shouldWriteFile($target, $options['force'] ?? false, $options['assumeYesForPrompts'] ?? false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists($source)) {
|
||||
throw new \LogicException(\sprintf('File "%s" does not exist!', $source));
|
||||
}
|
||||
|
||||
if (!file_exists(\dirname($target))) {
|
||||
mkdir(\dirname($target), 0777, true);
|
||||
$this->write(\sprintf(' Created <fg=green>"%s"</>', $this->path->relativize(\dirname($target))));
|
||||
}
|
||||
|
||||
file_put_contents($target, $this->options->expandTargetDir(file_get_contents($source)));
|
||||
@chmod($target, fileperms($target) | (fileperms($source) & 0111));
|
||||
$this->write(\sprintf(' Created <fg=green>"%s"</>', $this->path->relativize($target)));
|
||||
}
|
||||
|
||||
private function removeFilesFromDir(string $source, string $target)
|
||||
{
|
||||
if (!is_dir($source)) {
|
||||
return;
|
||||
}
|
||||
$iterator = $this->createSourceIterator($source, \RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
$targetPath = $this->path->concatenate([$target, $iterator->getSubPathName()]);
|
||||
if ($item->isDir()) {
|
||||
// that removes the dir only if it is empty
|
||||
@rmdir($targetPath);
|
||||
$this->write(\sprintf(' Removed directory <fg=green>"%s"</>', $this->path->relativize($targetPath)));
|
||||
} else {
|
||||
@unlink($targetPath);
|
||||
$this->write(\sprintf(' Removed <fg=green>"%s"</>', $this->path->relativize($targetPath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function createSourceIterator(string $source, int $mode): \RecursiveIteratorIterator
|
||||
{
|
||||
return new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), $mode);
|
||||
}
|
||||
}
|
||||
149
vendor/symfony/flex/src/Configurator/CopyFromRecipeConfigurator.php
vendored
Normal file
149
vendor/symfony/flex/src/Configurator/CopyFromRecipeConfigurator.php
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CopyFromRecipeConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $config, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Copying files from recipe');
|
||||
$options = array_merge($this->options->toArray(), $options);
|
||||
|
||||
$lock->add($recipe->getName(), ['files' => $this->copyFiles($config, $recipe->getFiles(), $options)]);
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $config, Lock $lock)
|
||||
{
|
||||
$this->write('Removing files from recipe');
|
||||
$rootDir = $this->options->get('root-dir');
|
||||
|
||||
foreach ($this->options->getRemovableFiles($recipe, $lock) as $file) {
|
||||
if ('.git' !== $file) { // never remove the main Git directory, even if it was created by a recipe
|
||||
$this->removeFile($this->path->concatenate([$rootDir, $file]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
foreach ($recipeUpdate->getOriginalRecipe()->getFiles() as $filename => $data) {
|
||||
$filename = $this->resolveTargetFolder($filename, $originalConfig);
|
||||
$recipeUpdate->setOriginalFile($filename, $data['contents']);
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($recipeUpdate->getNewRecipe()->getFiles() as $filename => $data) {
|
||||
$filename = $this->resolveTargetFolder($filename, $newConfig);
|
||||
$recipeUpdate->setNewFile($filename, $data['contents']);
|
||||
|
||||
$files[] = $this->getLocalFilePath($recipeUpdate->getRootDir(), $filename);
|
||||
}
|
||||
|
||||
$recipeUpdate->getLock()->add($recipeUpdate->getPackageName(), ['files' => $files]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $config
|
||||
*/
|
||||
private function resolveTargetFolder(string $path, array $config): string
|
||||
{
|
||||
foreach ($config as $key => $target) {
|
||||
if (str_starts_with($path, $key)) {
|
||||
return $this->options->expandTargetDir($target).substr($path, \strlen($key));
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
private function copyFiles(array $manifest, array $files, array $options): array
|
||||
{
|
||||
$copiedFiles = [];
|
||||
$to = $options['root-dir'] ?? '.';
|
||||
|
||||
foreach ($manifest as $source => $target) {
|
||||
$target = $this->options->expandTargetDir($target);
|
||||
if ('/' === substr($source, -1)) {
|
||||
$copiedFiles = array_merge(
|
||||
$copiedFiles,
|
||||
$this->copyDir($source, $this->path->concatenate([$to, $target]), $files, $options)
|
||||
);
|
||||
} else {
|
||||
$copiedFiles[] = $this->copyFile($this->path->concatenate([$to, $target]), $files[$source]['contents'], $files[$source]['executable'], $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $copiedFiles;
|
||||
}
|
||||
|
||||
private function copyDir(string $source, string $target, array $files, array $options): array
|
||||
{
|
||||
$copiedFiles = [];
|
||||
foreach ($files as $file => $data) {
|
||||
if (str_starts_with($file, $source)) {
|
||||
$file = $this->path->concatenate([$target, substr($file, \strlen($source))]);
|
||||
$copiedFiles[] = $this->copyFile($file, $data['contents'], $data['executable'], $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $copiedFiles;
|
||||
}
|
||||
|
||||
private function copyFile(string $to, string $contents, bool $executable, array $options): string
|
||||
{
|
||||
$basePath = $options['root-dir'] ?? '.';
|
||||
$copiedFile = $this->getLocalFilePath($basePath, $to);
|
||||
|
||||
if (!$this->options->shouldWriteFile($to, $options['force'] ?? false, $options['assumeYesForPrompts'] ?? false)) {
|
||||
return $copiedFile;
|
||||
}
|
||||
|
||||
if (!is_dir(\dirname($to))) {
|
||||
mkdir(\dirname($to), 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($to, $this->options->expandTargetDir($contents));
|
||||
if ($executable) {
|
||||
@chmod($to, fileperms($to) | 0111);
|
||||
}
|
||||
|
||||
$this->write(\sprintf(' Created <fg=green>"%s"</>', $this->path->relativize($to)));
|
||||
|
||||
return $copiedFile;
|
||||
}
|
||||
|
||||
private function removeFile(string $to)
|
||||
{
|
||||
if (!file_exists($to)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@unlink($to);
|
||||
$this->write(\sprintf(' Removed <fg=green>"%s"</>', $this->path->relativize($to)));
|
||||
|
||||
if (0 === \count(glob(\dirname($to).'/*', \GLOB_NOSORT))) {
|
||||
@rmdir(\dirname($to));
|
||||
}
|
||||
}
|
||||
|
||||
private function getLocalFilePath(string $basePath, $destination): string
|
||||
{
|
||||
return str_replace($basePath.\DIRECTORY_SEPARATOR, '', $destination);
|
||||
}
|
||||
}
|
||||
404
vendor/symfony/flex/src/Configurator/DockerComposeConfigurator.php
vendored
Normal file
404
vendor/symfony/flex/src/Configurator/DockerComposeConfigurator.php
vendored
Normal file
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Json\JsonManipulator;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Options;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* Adds services and volumes to compose.yaml file.
|
||||
*
|
||||
* @author Kévin Dunglas <kevin@dunglas.dev>
|
||||
*/
|
||||
class DockerComposeConfigurator extends AbstractConfigurator
|
||||
{
|
||||
private $filesystem;
|
||||
|
||||
public static $configureDockerRecipes;
|
||||
|
||||
public function __construct(Composer $composer, IOInterface $io, Options $options)
|
||||
{
|
||||
parent::__construct($composer, $io, $options);
|
||||
|
||||
$this->filesystem = new Filesystem();
|
||||
}
|
||||
|
||||
public function configure(Recipe $recipe, $config, Lock $lock, array $options = [])
|
||||
{
|
||||
if (!self::shouldConfigureDockerRecipe($this->composer, $this->io, $recipe)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->configureDockerCompose($recipe, $config, $options['force'] ?? false);
|
||||
|
||||
$this->write('Docker Compose definitions have been modified. Please run "docker compose up --build" again to apply the changes.');
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $config, Lock $lock)
|
||||
{
|
||||
$rootDir = $this->options->get('root-dir');
|
||||
foreach ($this->normalizeConfig($config) as $file => $extra) {
|
||||
if (null === $dockerComposeFile = $this->findDockerComposeFile($rootDir, $file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $recipe->getName();
|
||||
// Remove recipe and add break line
|
||||
$contents = preg_replace(\sprintf('{%s+###> %s ###.*?###< %s ###%s+}s', "\n", $name, $name, "\n"), \PHP_EOL.\PHP_EOL, file_get_contents($dockerComposeFile), -1, $count);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($extra as $key => $value) {
|
||||
if (0 === preg_match(\sprintf('{^%s:[ \t\r\n]*([ \t]+\w|#)}m', $key), $contents, $matches)) {
|
||||
$contents = preg_replace(\sprintf('{\n?^%s:[ \t\r\n]*}sm', $key), '', $contents, -1, $count);
|
||||
}
|
||||
}
|
||||
|
||||
$this->write(\sprintf('Removing Docker Compose entries from "%s"', $dockerComposeFile));
|
||||
file_put_contents($dockerComposeFile, ltrim($contents, "\n"));
|
||||
}
|
||||
|
||||
$this->write('Docker Compose definitions have been modified. Please run "docker compose up" again to apply the changes.');
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
if (!self::shouldConfigureDockerRecipe($this->composer, $this->io, $recipeUpdate->getNewRecipe())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$recipeUpdate->addOriginalFiles(
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getOriginalRecipe(), $originalConfig)
|
||||
);
|
||||
|
||||
$recipeUpdate->addNewFiles(
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getNewRecipe(), $newConfig)
|
||||
);
|
||||
}
|
||||
|
||||
public static function shouldConfigureDockerRecipe(Composer $composer, IOInterface $io, Recipe $recipe): bool
|
||||
{
|
||||
if (null !== self::$configureDockerRecipes) {
|
||||
return self::$configureDockerRecipes;
|
||||
}
|
||||
|
||||
if (null !== $dockerPreference = $composer->getPackage()->getExtra()['symfony']['docker'] ?? null) {
|
||||
self::$configureDockerRecipes = filter_var($dockerPreference, \FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
return self::$configureDockerRecipes;
|
||||
}
|
||||
|
||||
if ('install' !== $recipe->getJob()) {
|
||||
// default to not configuring
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($_SERVER['SYMFONY_DOCKER'])) {
|
||||
$answer = self::askDockerSupport($io, $recipe);
|
||||
} elseif (filter_var($_SERVER['SYMFONY_DOCKER'], \FILTER_VALIDATE_BOOLEAN)) {
|
||||
$answer = 'p';
|
||||
} else {
|
||||
$answer = 'x';
|
||||
}
|
||||
|
||||
if ('n' === $answer) {
|
||||
self::$configureDockerRecipes = false;
|
||||
|
||||
return self::$configureDockerRecipes;
|
||||
}
|
||||
if ('y' === $answer) {
|
||||
self::$configureDockerRecipes = true;
|
||||
|
||||
return self::$configureDockerRecipes;
|
||||
}
|
||||
|
||||
// yes or no permanently
|
||||
self::$configureDockerRecipes = 'p' === $answer;
|
||||
$json = new JsonFile(Factory::getComposerFile());
|
||||
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
|
||||
$manipulator->addSubNode('extra', 'symfony.docker', self::$configureDockerRecipes);
|
||||
file_put_contents($json->getPath(), $manipulator->getContents());
|
||||
|
||||
return self::$configureDockerRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the config and return the name of the main Docker Compose file if applicable.
|
||||
*/
|
||||
private function normalizeConfig(array $config): array
|
||||
{
|
||||
foreach ($config as $key => $val) {
|
||||
// Support for the short recipe syntax that modifies compose.yaml only
|
||||
if (isset($val[0])) {
|
||||
return ['compose.yaml' => $config];
|
||||
}
|
||||
|
||||
if (!str_starts_with($key, 'docker-')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the recipe still use the legacy "docker-compose.yml" names, remove the "docker-" prefix and change the extension
|
||||
$newKey = pathinfo(substr($key, 7), \PATHINFO_FILENAME).'.yaml';
|
||||
$config[$newKey] = $val;
|
||||
unset($config[$key]);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Docker Compose file according to these rules: https://docs.docker.com/compose/reference/envvars/#compose_file.
|
||||
*/
|
||||
private function findDockerComposeFile(string $rootDir, string $file): ?string
|
||||
{
|
||||
if (isset($_SERVER['COMPOSE_FILE'])) {
|
||||
$filenameToFind = pathinfo($file, \PATHINFO_FILENAME);
|
||||
$separator = $_SERVER['COMPOSE_PATH_SEPARATOR'] ?? ('\\' === \DIRECTORY_SEPARATOR ? ';' : ':');
|
||||
|
||||
$files = explode($separator, $_SERVER['COMPOSE_FILE']);
|
||||
foreach ($files as $f) {
|
||||
$filename = pathinfo($f, \PATHINFO_FILENAME);
|
||||
if ($filename !== $filenameToFind && "docker-$filenameToFind" !== $filename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->filesystem->isAbsolutePath($f)) {
|
||||
$f = realpath(\sprintf('%s/%s', $rootDir, $f));
|
||||
}
|
||||
|
||||
if ($this->filesystem->exists($f)) {
|
||||
return $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// COMPOSE_FILE not set, or doesn't contain the file we're looking for
|
||||
$dir = $rootDir;
|
||||
do {
|
||||
if (
|
||||
$this->filesystem->exists($dockerComposeFile = \sprintf('%s/%s', $dir, $file))
|
||||
// Test with the ".yml" extension if the file doesn't end up with ".yaml"
|
||||
|| $this->filesystem->exists($dockerComposeFile = substr($dockerComposeFile, 0, -3).'ml')
|
||||
// Test with the legacy "docker-" suffix if "compose.ya?ml" doesn't exist
|
||||
|| $this->filesystem->exists($dockerComposeFile = \sprintf('%s/docker-%s', $dir, $file))
|
||||
|| $this->filesystem->exists($dockerComposeFile = substr($dockerComposeFile, 0, -3).'ml')
|
||||
) {
|
||||
return $dockerComposeFile;
|
||||
}
|
||||
|
||||
$previousDir = $dir;
|
||||
$dir = \dirname($dir);
|
||||
} while ($dir !== $previousDir);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function parse($level, $indent, $services): string
|
||||
{
|
||||
$line = '';
|
||||
foreach ($services as $key => $value) {
|
||||
$line .= str_repeat(' ', $indent * $level);
|
||||
if (!\is_array($value)) {
|
||||
if (\is_string($key)) {
|
||||
$line .= \sprintf('%s:', $key);
|
||||
}
|
||||
$line .= \sprintf("%s\n", $value);
|
||||
continue;
|
||||
}
|
||||
$line .= \sprintf("%s:\n", $key).$this->parse($level + 1, $indent, $value);
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
private function configureDockerCompose(Recipe $recipe, array $config, bool $update): void
|
||||
{
|
||||
$rootDir = $this->options->get('root-dir');
|
||||
foreach ($this->normalizeConfig($config) as $file => $extra) {
|
||||
$dockerComposeFile = $this->findDockerComposeFile($rootDir, $file);
|
||||
if (null === $dockerComposeFile) {
|
||||
$dockerComposeFile = $rootDir.'/'.$file;
|
||||
file_put_contents($dockerComposeFile, '');
|
||||
$this->write(\sprintf(' Created <fg=green>"%s"</>', $file));
|
||||
}
|
||||
|
||||
if (!$update && $this->isFileMarked($recipe, $dockerComposeFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->write(\sprintf('Adding Docker Compose definitions to "%s"', $dockerComposeFile));
|
||||
|
||||
$offset = 2;
|
||||
$node = null;
|
||||
$endAt = [];
|
||||
$startAt = [];
|
||||
$lines = [];
|
||||
$nodesLines = [];
|
||||
foreach (file($dockerComposeFile) as $i => $line) {
|
||||
$lines[] = $line;
|
||||
$ltrimedLine = ltrim($line, ' ');
|
||||
if (null !== $node) {
|
||||
$nodesLines[$node][$i] = $line;
|
||||
}
|
||||
|
||||
// Skip blank lines and comments
|
||||
if (('' !== $ltrimedLine && str_starts_with($ltrimedLine, '#')) || '' === trim($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract Docker Compose keys (usually "services" and "volumes")
|
||||
if (!preg_match('/^[\'"]?([a-zA-Z0-9]+)[\'"]?:\s*$/', $line, $matches)) {
|
||||
// Detect indentation to use
|
||||
$offestLine = \strlen($line) - \strlen($ltrimedLine);
|
||||
if ($offset > $offestLine && 0 !== $offestLine) {
|
||||
$offset = $offestLine;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep end in memory (check break line on previous line)
|
||||
$endAt[$node] = !$i || '' !== trim($lines[$i - 1]) ? $i : $i - 1;
|
||||
$node = $matches[1];
|
||||
if (!isset($nodesLines[$node])) {
|
||||
$nodesLines[$node] = [];
|
||||
}
|
||||
if (!isset($startAt[$node])) {
|
||||
// the section contents starts at the next line
|
||||
$startAt[$node] = $i + 1;
|
||||
}
|
||||
}
|
||||
$endAt[$node] = \count($lines) + 1;
|
||||
|
||||
foreach ($extra as $key => $value) {
|
||||
if (isset($endAt[$key])) {
|
||||
$data = $this->markData($recipe, $this->parse(1, $offset, $value));
|
||||
$updatedContents = $this->updateDataString(implode('', $nodesLines[$key]), $data);
|
||||
if (null === $updatedContents) {
|
||||
// not an update: just add to section
|
||||
array_splice($lines, $endAt[$key], 0, $data);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$originalEndAt = $endAt[$key];
|
||||
$length = $endAt[$key] - $startAt[$key];
|
||||
array_splice($lines, $startAt[$key], $length, ltrim($updatedContents, "\n"));
|
||||
|
||||
// reset any start/end positions after this to the new positions
|
||||
foreach ($startAt as $sectionKey => $at) {
|
||||
if ($at > $originalEndAt) {
|
||||
$startAt[$sectionKey] = $at - $length - 1;
|
||||
}
|
||||
}
|
||||
foreach ($endAt as $sectionKey => $at) {
|
||||
if ($at > $originalEndAt) {
|
||||
$endAt[$sectionKey] = $at - $length;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$lines[] = \sprintf("\n%s:", $key);
|
||||
$lines[] = $this->markData($recipe, $this->parse(1, $offset, $value));
|
||||
}
|
||||
|
||||
file_put_contents($dockerComposeFile, implode('', $lines));
|
||||
}
|
||||
}
|
||||
|
||||
private function getContentsAfterApplyingRecipe(string $rootDir, Recipe $recipe, array $config): array
|
||||
{
|
||||
if (0 === \count($config)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = array_filter(array_map(function ($file) use ($rootDir) {
|
||||
return $this->findDockerComposeFile($rootDir, $file);
|
||||
}, array_keys($config)));
|
||||
|
||||
$originalContents = [];
|
||||
foreach ($files as $file) {
|
||||
$originalContents[$file] = file_exists($file) ? file_get_contents($file) : null;
|
||||
}
|
||||
|
||||
$this->configureDockerCompose(
|
||||
$recipe,
|
||||
$config,
|
||||
true
|
||||
);
|
||||
|
||||
$updatedContents = [];
|
||||
foreach ($files as $file) {
|
||||
$localPath = $file;
|
||||
if (str_starts_with($file, $rootDir)) {
|
||||
$localPath = substr($file, \strlen($rootDir) + 1);
|
||||
}
|
||||
$localPath = ltrim($localPath, '/\\');
|
||||
$updatedContents[$localPath] = file_exists($file) ? file_get_contents($file) : null;
|
||||
}
|
||||
|
||||
foreach ($originalContents as $file => $contents) {
|
||||
if (null === $contents) {
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
} else {
|
||||
file_put_contents($file, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
return $updatedContents;
|
||||
}
|
||||
|
||||
private static function askDockerSupport(IOInterface $io, Recipe $recipe): string
|
||||
{
|
||||
$warning = $io->isInteractive() ? 'WARNING' : 'IGNORING';
|
||||
$io->writeError(\sprintf(' - <warning> %s </> %s', $warning, $recipe->getFormattedOrigin()));
|
||||
$question = ' The recipe for this package contains some Docker configuration.
|
||||
|
||||
This may create/update <comment>compose.yaml</comment> or update <comment>Dockerfile</comment> (if it exists).
|
||||
|
||||
Do you want to include Docker configuration from recipes?
|
||||
[<comment>y</>] Yes
|
||||
[<comment>n</>] No
|
||||
[<comment>p</>] Yes permanently, never ask again for this project
|
||||
[<comment>x</>] No permanently, never ask again for this project
|
||||
(defaults to <comment>y</>): ';
|
||||
|
||||
return $io->askAndValidate(
|
||||
$question,
|
||||
function ($value) {
|
||||
if (null === $value) {
|
||||
return 'y';
|
||||
}
|
||||
$value = strtolower($value[0]);
|
||||
if (!\in_array($value, ['y', 'n', 'p', 'x'], true)) {
|
||||
throw new \InvalidArgumentException('Invalid choice.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
null,
|
||||
'y'
|
||||
);
|
||||
}
|
||||
}
|
||||
125
vendor/symfony/flex/src/Configurator/DockerfileConfigurator.php
vendored
Normal file
125
vendor/symfony/flex/src/Configurator/DockerfileConfigurator.php
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* Adds commands to a Dockerfile.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DockerfileConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $config, Lock $lock, array $options = [])
|
||||
{
|
||||
if (!DockerComposeConfigurator::shouldConfigureDockerRecipe($this->composer, $this->io, $recipe)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->configureDockerfile($recipe, $config, $options['force'] ?? false);
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $config, Lock $lock)
|
||||
{
|
||||
if (!file_exists($dockerfile = $this->options->get('root-dir').'/Dockerfile')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = $recipe->getName();
|
||||
$contents = preg_replace(\sprintf('{%s+###> %s ###.*?###< %s ###%s+}s', "\n", $name, $name, "\n"), "\n", file_get_contents($dockerfile), -1, $count);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->write('Removing Dockerfile entries');
|
||||
file_put_contents($dockerfile, ltrim($contents, "\n"));
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
if (!DockerComposeConfigurator::shouldConfigureDockerRecipe($this->composer, $this->io, $recipeUpdate->getNewRecipe())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$recipeUpdate->setOriginalFile(
|
||||
'Dockerfile',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getOriginalRecipe(), $originalConfig)
|
||||
);
|
||||
|
||||
$recipeUpdate->setNewFile(
|
||||
'Dockerfile',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getNewRecipe(), $newConfig)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureDockerfile(Recipe $recipe, array $config, bool $update, bool $writeOutput = true): void
|
||||
{
|
||||
$dockerfile = $this->options->get('root-dir').'/Dockerfile';
|
||||
if (!file_exists($dockerfile) || (!$update && $this->isFileMarked($recipe, $dockerfile))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($writeOutput) {
|
||||
$this->write('Adding Dockerfile entries');
|
||||
}
|
||||
|
||||
$data = ltrim($this->markData($recipe, implode("\n", $config)), "\n");
|
||||
if ($this->updateData($dockerfile, $data)) {
|
||||
// done! Existing spot updated
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = [];
|
||||
foreach (file($dockerfile) as $line) {
|
||||
$lines[] = $line;
|
||||
if (!preg_match('/^###> recipes ###$/', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lines[] = $data;
|
||||
}
|
||||
|
||||
file_put_contents($dockerfile, implode('', $lines));
|
||||
}
|
||||
|
||||
private function getContentsAfterApplyingRecipe(Recipe $recipe, array $config): ?string
|
||||
{
|
||||
if (0 === \count($config)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$dockerfile = $this->options->get('root-dir').'/Dockerfile';
|
||||
$originalContents = file_exists($dockerfile) ? file_get_contents($dockerfile) : null;
|
||||
|
||||
$this->configureDockerfile(
|
||||
$recipe,
|
||||
$config,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
$updatedContents = file_exists($dockerfile) ? file_get_contents($dockerfile) : null;
|
||||
|
||||
if (null === $originalContents) {
|
||||
if (file_exists($dockerfile)) {
|
||||
unlink($dockerfile);
|
||||
}
|
||||
} else {
|
||||
file_put_contents($dockerfile, $originalContents);
|
||||
}
|
||||
|
||||
return $updatedContents;
|
||||
}
|
||||
}
|
||||
50
vendor/symfony/flex/src/Configurator/DotenvConfigurator.php
vendored
Normal file
50
vendor/symfony/flex/src/Configurator/DotenvConfigurator.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
class DotenvConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $vars, Lock $lock, array $options = [])
|
||||
{
|
||||
foreach ($vars as $suffix => $vars) {
|
||||
$configurator = new EnvConfigurator($this->composer, $this->io, $this->options, $suffix);
|
||||
$configurator->configure($recipe, $vars, $lock, $options);
|
||||
}
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $vars, Lock $lock)
|
||||
{
|
||||
foreach ($vars as $suffix => $vars) {
|
||||
$configurator = new EnvConfigurator($this->composer, $this->io, $this->options, $suffix);
|
||||
$configurator->unconfigure($recipe, $vars, $lock);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
foreach ($originalConfig as $suffix => $vars) {
|
||||
$configurator = new EnvConfigurator($this->composer, $this->io, $this->options, $suffix);
|
||||
$configurator->update($recipeUpdate, $vars, $newConfig[$suffix] ?? []);
|
||||
}
|
||||
|
||||
foreach ($newConfig as $suffix => $vars) {
|
||||
if (!isset($originalConfig[$suffix])) {
|
||||
$configurator = new EnvConfigurator($this->composer, $this->io, $this->options, $suffix);
|
||||
$configurator->update($recipeUpdate, [], $vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
295
vendor/symfony/flex/src/Configurator/EnvConfigurator.php
vendored
Normal file
295
vendor/symfony/flex/src/Configurator/EnvConfigurator.php
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Options;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EnvConfigurator extends AbstractConfigurator
|
||||
{
|
||||
private string $suffix;
|
||||
|
||||
public function __construct(Composer $composer, IOInterface $io, Options $options, string $suffix = '')
|
||||
{
|
||||
parent::__construct($composer, $io, $options);
|
||||
$this->suffix = $suffix;
|
||||
}
|
||||
|
||||
public function configure(Recipe $recipe, $vars, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Adding environment variable defaults'.('' === $this->suffix ? '' : ' ('.$this->suffix.')'));
|
||||
|
||||
$this->configureEnvDist($recipe, $vars, $options['force'] ?? false);
|
||||
|
||||
if ('' !== $this->suffix) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists($this->options->get('root-dir').'/'.($this->options->get('runtime')['dotenv_path'] ?? '.env').'.test')) {
|
||||
$this->configurePhpUnit($recipe, $vars, $options['force'] ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $vars, Lock $lock)
|
||||
{
|
||||
$this->unconfigureEnvFiles($recipe, $vars);
|
||||
$this->unconfigurePhpUnit($recipe, $vars);
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$recipeUpdate->addOriginalFiles(
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getOriginalRecipe(), $originalConfig)
|
||||
);
|
||||
|
||||
$recipeUpdate->addNewFiles(
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getNewRecipe(), $newConfig)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureEnvDist(Recipe $recipe, $vars, bool $update)
|
||||
{
|
||||
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
|
||||
$files = '' === $this->suffix ? [$dotenvPath.'.dist', $dotenvPath] : [$dotenvPath.'.'.$this->suffix];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$env = $this->options->get('root-dir').'/'.$file;
|
||||
if (!is_file($env)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$update && $this->isFileMarked($recipe, $env)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = '';
|
||||
foreach ($vars as $key => $value) {
|
||||
$existingValue = $update ? $this->findExistingValue($key, $env, $recipe) : null;
|
||||
$value = $this->evaluateValue($value, $existingValue);
|
||||
if ('#' === $key[0] && is_numeric(substr($key, 1))) {
|
||||
if ('' === $value) {
|
||||
$data .= "#\n";
|
||||
} else {
|
||||
$data .= '# '.$value."\n";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $this->options->expandTargetDir($value);
|
||||
if (false !== strpbrk($value, " \t\n&!\"")) {
|
||||
$value = '"'.str_replace(['\\', '"', "\t", "\n"], ['\\\\', '\\"', '\t', '\n'], $value).'"';
|
||||
}
|
||||
$data .= "$key=$value\n";
|
||||
}
|
||||
$data = $this->markData($recipe, $data);
|
||||
|
||||
if (!$this->updateData($env, $data)) {
|
||||
file_put_contents($env, $data, \FILE_APPEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function configurePhpUnit(Recipe $recipe, $vars, bool $update)
|
||||
{
|
||||
foreach (['phpunit.xml.dist', 'phpunit.dist.xml', 'phpunit.xml'] as $file) {
|
||||
$phpunit = $this->options->get('root-dir').'/'.$file;
|
||||
if (!is_file($phpunit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$update && $this->isFileXmlMarked($recipe, $phpunit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = '';
|
||||
foreach ($vars as $key => $value) {
|
||||
$value = $this->evaluateValue($value);
|
||||
if ('#' === $key[0]) {
|
||||
if (is_numeric(substr($key, 1))) {
|
||||
$doc = new \DOMDocument();
|
||||
$data .= ' '.$doc->saveXML($doc->createComment(' '.$value.' '))."\n";
|
||||
} else {
|
||||
$value = $this->options->expandTargetDir($value);
|
||||
$doc = new \DOMDocument();
|
||||
$fragment = $doc->createElement('env');
|
||||
$fragment->setAttribute('name', substr($key, 1));
|
||||
$fragment->setAttribute('value', $value);
|
||||
$data .= ' '.str_replace(['<', '/>'], ['<!-- ', ' -->'], $doc->saveXML($fragment))."\n";
|
||||
}
|
||||
} else {
|
||||
$value = $this->options->expandTargetDir($value);
|
||||
$doc = new \DOMDocument();
|
||||
$fragment = $doc->createElement('env');
|
||||
$fragment->setAttribute('name', $key);
|
||||
$fragment->setAttribute('value', $value);
|
||||
$data .= ' '.$doc->saveXML($fragment)."\n";
|
||||
}
|
||||
}
|
||||
$data = $this->markXmlData($recipe, $data);
|
||||
|
||||
if (!$this->updateData($phpunit, $data)) {
|
||||
file_put_contents($phpunit, preg_replace('{^(\s+</php>)}m', $data.'$1', file_get_contents($phpunit)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function unconfigureEnvFiles(Recipe $recipe, $vars)
|
||||
{
|
||||
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
|
||||
$files = '' === $this->suffix ? [$dotenvPath, $dotenvPath.'.dist'] : [$dotenvPath.'.'.$this->suffix];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$env = $this->options->get('root-dir').'/'.$file;
|
||||
if (!file_exists($env)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = preg_replace(\sprintf('{%s*###> %s ###.*###< %s ###%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($env), -1, $count);
|
||||
if (!$count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->write(\sprintf('Removing environment variables from %s', $file));
|
||||
file_put_contents($env, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
private function unconfigurePhpUnit(Recipe $recipe, $vars)
|
||||
{
|
||||
foreach (['phpunit.dist.xml', 'phpunit.xml.dist', 'phpunit.xml'] as $file) {
|
||||
$phpunit = $this->options->get('root-dir').'/'.$file;
|
||||
if (!is_file($phpunit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = preg_replace(\sprintf('{%s*\s+<!-- ###\+ %s ### -->.*<!-- ###- %s ### -->%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($phpunit), -1, $count);
|
||||
if (!$count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->write(\sprintf('Removing environment variables from %s', $file));
|
||||
file_put_contents($phpunit, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates expressions like %generate(secret)%.
|
||||
*
|
||||
* If $originalValue is passed, and the value contains an expression.
|
||||
* the $originalValue is used.
|
||||
*/
|
||||
private function evaluateValue($value, ?string $originalValue = null)
|
||||
{
|
||||
if ('%generate(secret)%' === $value) {
|
||||
if (null !== $originalValue) {
|
||||
return $originalValue;
|
||||
}
|
||||
|
||||
return $this->generateRandomBytes();
|
||||
}
|
||||
if (preg_match('~^%generate\(secret,\s*([0-9]+)\)%$~', $value, $matches)) {
|
||||
if (null !== $originalValue) {
|
||||
return $originalValue;
|
||||
}
|
||||
|
||||
return $this->generateRandomBytes($matches[1]);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function generateRandomBytes($length = 16)
|
||||
{
|
||||
return bin2hex(random_bytes($length));
|
||||
}
|
||||
|
||||
private function getContentsAfterApplyingRecipe(string $rootDir, Recipe $recipe, array $vars): array
|
||||
{
|
||||
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
|
||||
$files = '' === $this->suffix ? [$dotenvPath, $dotenvPath.'.dist', 'phpunit.dist.xml', 'phpunit.xml.dist', 'phpunit.xml'] : [$dotenvPath.'.'.$this->suffix];
|
||||
|
||||
if (0 === \count($vars)) {
|
||||
return array_fill_keys($files, null);
|
||||
}
|
||||
|
||||
$originalContents = [];
|
||||
foreach ($files as $file) {
|
||||
$originalContents[$file] = file_exists($rootDir.'/'.$file) ? file_get_contents($rootDir.'/'.$file) : null;
|
||||
}
|
||||
|
||||
$this->configureEnvDist(
|
||||
$recipe,
|
||||
$vars,
|
||||
true
|
||||
);
|
||||
|
||||
if ('' === $this->suffix && !file_exists($rootDir.'/'.$dotenvPath.'.test')) {
|
||||
$this->configurePhpUnit(
|
||||
$recipe,
|
||||
$vars,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$updatedContents = [];
|
||||
foreach ($files as $file) {
|
||||
$updatedContents[$file] = file_exists($rootDir.'/'.$file) ? file_get_contents($rootDir.'/'.$file) : null;
|
||||
}
|
||||
|
||||
foreach ($originalContents as $file => $contents) {
|
||||
if (null === $contents) {
|
||||
if (file_exists($rootDir.'/'.$file)) {
|
||||
unlink($rootDir.'/'.$file);
|
||||
}
|
||||
} else {
|
||||
file_put_contents($rootDir.'/'.$file, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
return $updatedContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find the existing value of an environment variable.
|
||||
*/
|
||||
private function findExistingValue(string $var, string $filename, Recipe $recipe): ?string
|
||||
{
|
||||
if (!file_exists($filename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($filename);
|
||||
$section = $this->extractSection($recipe, $contents);
|
||||
if (!$section) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lines = explode("\n", $section);
|
||||
foreach ($lines as $line) {
|
||||
if (!str_starts_with($line, \sprintf('%s=', $var))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return trim(substr($line, \strlen($var) + 1));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
105
vendor/symfony/flex/src/Configurator/GitignoreConfigurator.php
vendored
Normal file
105
vendor/symfony/flex/src/Configurator/GitignoreConfigurator.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class GitignoreConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $vars, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Adding entries to .gitignore');
|
||||
|
||||
$this->configureGitignore($recipe, $vars, $options['force'] ?? false);
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $vars, Lock $lock)
|
||||
{
|
||||
$file = $this->options->get('root-dir').'/.gitignore';
|
||||
if (!file_exists($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = preg_replace(\sprintf('{%s*###> %s ###.*###< %s ###%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($file), -1, $count);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->write('Removing entries in .gitignore');
|
||||
file_put_contents($file, ltrim($contents, "\r\n"));
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$recipeUpdate->setOriginalFile(
|
||||
'.gitignore',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getOriginalRecipe(), $originalConfig)
|
||||
);
|
||||
|
||||
$recipeUpdate->setNewFile(
|
||||
'.gitignore',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getNewRecipe(), $newConfig)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureGitignore(Recipe $recipe, array $vars, bool $update)
|
||||
{
|
||||
$gitignore = $this->options->get('root-dir').'/.gitignore';
|
||||
if (!$update && $this->isFileMarked($recipe, $gitignore)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = '';
|
||||
foreach ($vars as $value) {
|
||||
$value = $this->options->expandTargetDir($value);
|
||||
$data .= "$value\n";
|
||||
}
|
||||
$data = "\n".ltrim($this->markData($recipe, $data), "\r\n");
|
||||
|
||||
if (!$this->updateData($gitignore, $data)) {
|
||||
file_put_contents($gitignore, $data, \FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
private function getContentsAfterApplyingRecipe(string $rootDir, Recipe $recipe, $vars): ?string
|
||||
{
|
||||
if (0 === \count($vars)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $rootDir.'/.gitignore';
|
||||
$originalContents = file_exists($file) ? file_get_contents($file) : null;
|
||||
|
||||
$this->configureGitignore(
|
||||
$recipe,
|
||||
$vars,
|
||||
true
|
||||
);
|
||||
|
||||
$updatedContents = file_exists($file) ? file_get_contents($file) : null;
|
||||
|
||||
if (null === $originalContents) {
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
} else {
|
||||
file_put_contents($file, $originalContents);
|
||||
}
|
||||
|
||||
return $updatedContents;
|
||||
}
|
||||
}
|
||||
124
vendor/symfony/flex/src/Configurator/MakefileConfigurator.php
vendored
Normal file
124
vendor/symfony/flex/src/Configurator/MakefileConfigurator.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Flex\Configurator;
|
||||
|
||||
use Symfony\Flex\Lock;
|
||||
use Symfony\Flex\Recipe;
|
||||
use Symfony\Flex\Update\RecipeUpdate;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MakefileConfigurator extends AbstractConfigurator
|
||||
{
|
||||
public function configure(Recipe $recipe, $definitions, Lock $lock, array $options = [])
|
||||
{
|
||||
$this->write('Adding Makefile entries');
|
||||
|
||||
$this->configureMakefile($recipe, $definitions, $options['force'] ?? false);
|
||||
}
|
||||
|
||||
public function unconfigure(Recipe $recipe, $vars, Lock $lock)
|
||||
{
|
||||
if (!file_exists($makefile = $this->options->get('root-dir').'/Makefile')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = preg_replace(\sprintf('{%s*###> %s ###.*###< %s ###%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($makefile), -1, $count);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->write(\sprintf('Removing Makefile entries from %s', $makefile));
|
||||
if (!trim($contents)) {
|
||||
@unlink($makefile);
|
||||
} else {
|
||||
file_put_contents($makefile, ltrim($contents, "\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
|
||||
{
|
||||
$recipeUpdate->setOriginalFile(
|
||||
'Makefile',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getOriginalRecipe(), $originalConfig)
|
||||
);
|
||||
|
||||
$recipeUpdate->setNewFile(
|
||||
'Makefile',
|
||||
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getNewRecipe(), $newConfig)
|
||||
);
|
||||
}
|
||||
|
||||
private function configureMakefile(Recipe $recipe, array $definitions, bool $update)
|
||||
{
|
||||
$makefile = $this->options->get('root-dir').'/Makefile';
|
||||
if (!$update && $this->isFileMarked($recipe, $makefile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->options->expandTargetDir(implode("\n", $definitions));
|
||||
$data = $this->markData($recipe, $data);
|
||||
$data = "\n".ltrim($data, "\r\n");
|
||||
|
||||
if (!file_exists($makefile)) {
|
||||
$envKey = $this->options->get('runtime')['env_var_name'] ?? 'APP_ENV';
|
||||
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
|
||||
file_put_contents(
|
||||
$this->options->get('root-dir').'/Makefile',
|
||||
<<<EOF
|
||||
ifndef {$envKey}
|
||||
include {$dotenvPath}
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: help
|
||||
help:
|
||||
@awk 'BEGIN {FS = ":.*?## "}; /^[a-zA-Z-]+:.*?## .*$$/ {printf "\033[32m%-15s\033[0m %s\\n", $$1, $$2}' Makefile | sort
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->updateData($makefile, $data)) {
|
||||
file_put_contents($makefile, $data, \FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
private function getContentsAfterApplyingRecipe(string $rootDir, Recipe $recipe, array $definitions): ?string
|
||||
{
|
||||
if (0 === \count($definitions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $rootDir.'/Makefile';
|
||||
$originalContents = file_exists($file) ? file_get_contents($file) : null;
|
||||
|
||||
$this->configureMakefile(
|
||||
$recipe,
|
||||
$definitions,
|
||||
true
|
||||
);
|
||||
|
||||
$updatedContents = file_exists($file) ? file_get_contents($file) : null;
|
||||
|
||||
if (null === $originalContents) {
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
} else {
|
||||
file_put_contents($file, $originalContents);
|
||||
}
|
||||
|
||||
return $updatedContents;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user