diff --git a/system/defines.php b/system/defines.php
index a56126901..7d1ce424a 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '1.0.10');
+define('GRAV_VERSION', '1.1.0-beta');
define('DS', '/');
define('GRAV_PHP_MIN', '5.5.9');
diff --git a/system/src/Grav/Common/GPM/Common/Package.php b/system/src/Grav/Common/GPM/Common/Package.php
index ff9a087c9..b6dd3febd 100644
--- a/system/src/Grav/Common/GPM/Common/Package.php
+++ b/system/src/Grav/Common/GPM/Common/Package.php
@@ -3,6 +3,17 @@ namespace Grav\Common\GPM\Common;
use Grav\Common\Data\Data;
+/**
+ * @property string name
+ * @property string version
+ * @property string available
+ * @property string package_type
+ * @property string description_plain
+ * @property string slug
+ * @property array author
+ * @property mixed changelog
+ */
+
class Package {
protected $data;
diff --git a/system/src/Grav/Common/GPM/GPM.php b/system/src/Grav/Common/GPM/GPM.php
index b18075a2a..246d40a28 100644
--- a/system/src/Grav/Common/GPM/GPM.php
+++ b/system/src/Grav/Common/GPM/GPM.php
@@ -194,6 +194,28 @@ class GPM extends Iterator
return $items;
}
+ /**
+ * Get the latest release of a package from the GPM
+ *
+ * @param $package_name
+ *
+ * @return string
+ */
+ public function getLatestVersionOfPackage($package_name)
+ {
+ $repository = $this->repository['plugins'];
+
+ if (isset($repository[$package_name])) {
+ return $repository[$package_name]->version;
+ }
+
+ //Not a plugin, it's a theme?
+ $repository = $this->repository['themes'];
+ if (isset($repository[$package_name])) {
+ return $repository[$package_name]->version;
+ }
+ }
+
/**
* Check if a Plugin or Theme is updatable
* @param string $slug The slug of the package
@@ -349,11 +371,6 @@ class GPM extends Iterator
return false;
}
- /**
- * Returns the list of Plugins and Themes available in the repository
- * @return array Array of available Plugins and Themes
- * Format: ['plugins' => array, 'themes' => array]
- */
/**
* Searches for a list of Packages in the repository
* @param array $searches An array of either slugs or names
diff --git a/system/src/Grav/Common/GPM/Remote/Plugins.php b/system/src/Grav/Common/GPM/Remote/Plugins.php
index 036fc0c2a..17b6a7f35 100644
--- a/system/src/Grav/Common/GPM/Remote/Plugins.php
+++ b/system/src/Grav/Common/GPM/Remote/Plugins.php
@@ -12,7 +12,7 @@ class Plugins extends AbstractPackageCollection
*/
protected $type = 'plugins';
- protected $repository = 'https://getgrav.org/downloads/plugins.json';
+ protected $repository = 'https://getgrav.org/downloads/plugins.json?v=' . GRAV_VERSION;
/**
* Local Plugins Constructor
diff --git a/system/src/Grav/Common/GPM/Remote/Themes.php b/system/src/Grav/Common/GPM/Remote/Themes.php
index 43a5af54d..e5008dc87 100644
--- a/system/src/Grav/Common/GPM/Remote/Themes.php
+++ b/system/src/Grav/Common/GPM/Remote/Themes.php
@@ -12,7 +12,7 @@ class Themes extends AbstractPackageCollection
*/
protected $type = 'themes';
- protected $repository = 'https://getgrav.org/downloads/themes.json';
+ protected $repository = 'https://getgrav.org/downloads/themes.json?v=' . GRAV_VERSION;
/**
* Local Themes Constructor
diff --git a/system/src/Grav/Console/Cli/BackupCommand.php b/system/src/Grav/Console/Cli/BackupCommand.php
index cc55b224d..235e402cf 100644
--- a/system/src/Grav/Console/Cli/BackupCommand.php
+++ b/system/src/Grav/Console/Cli/BackupCommand.php
@@ -14,7 +14,10 @@ use Symfony\Component\Console\Input\InputArgument;
*/
class BackupCommand extends ConsoleCommand
{
+ /** @var string $source */
protected $source;
+
+ /** @var ProgressBar $progress */
protected $progress;
/**
diff --git a/system/src/Grav/Console/Cli/CleanCommand.php b/system/src/Grav/Console/Cli/CleanCommand.php
index ff6d28614..d664942bc 100644
--- a/system/src/Grav/Console/Cli/CleanCommand.php
+++ b/system/src/Grav/Console/Cli/CleanCommand.php
@@ -4,7 +4,6 @@ namespace Grav\Console\Cli;
use Grav\Common\Filesystem\Folder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
@@ -184,6 +183,9 @@ class CleanCommand extends Command
}
/**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ *
* @return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
@@ -214,7 +216,7 @@ class CleanCommand extends Command
}
}
- /**
+ /**
* Set colors style definition for the formatter.
*
* @param InputInterface $input
@@ -222,16 +224,16 @@ class CleanCommand extends Command
*/
public function setupConsole(InputInterface $input, OutputInterface $output)
{
- $this->input = $input;
+ $this->input = $input;
$this->output = $output;
$this->output->getFormatter()->setStyle('normal', new OutputFormatterStyle('white'));
- $this->output->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, array('bold')));
- $this->output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
- $this->output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, array('bold')));
- $this->output->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, array('bold')));
- $this->output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, array('bold')));
- $this->output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
+ $this->output->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, ['bold']));
+ $this->output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, ['bold']));
+ $this->output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, ['bold']));
+ $this->output->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, ['bold']));
+ $this->output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, ['bold']));
+ $this->output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, ['bold']));
}
}
diff --git a/system/src/Grav/Console/Cli/ComposerCommand.php b/system/src/Grav/Console/Cli/ComposerCommand.php
index 729b425a8..259bf23c6 100644
--- a/system/src/Grav/Console/Cli/ComposerCommand.php
+++ b/system/src/Grav/Console/Cli/ComposerCommand.php
@@ -2,7 +2,6 @@
namespace Grav\Console\Cli;
use Grav\Console\ConsoleCommand;
-use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
diff --git a/system/src/Grav/Console/Cli/NewProjectCommand.php b/system/src/Grav/Console/Cli/NewProjectCommand.php
index 2d63f04dd..e0854f776 100644
--- a/system/src/Grav/Console/Cli/NewProjectCommand.php
+++ b/system/src/Grav/Console/Cli/NewProjectCommand.php
@@ -43,17 +43,17 @@ class NewProjectCommand extends ConsoleCommand
$sandboxCommand = $this->getApplication()->find('sandbox');
$installCommand = $this->getApplication()->find('install');
- $sandboxArguments = new ArrayInput(array(
+ $sandboxArguments = new ArrayInput([
'command' => 'sandbox',
'destination' => $this->input->getArgument('destination'),
'-s' => $this->input->getOption('symlink')
- ));
+ ]);
- $installArguments = new ArrayInput(array(
+ $installArguments = new ArrayInput([
'command' => 'install',
'destination' => $this->input->getArgument('destination'),
'-s' => $this->input->getOption('symlink')
- ));
+ ]);
$sandboxCommand->run($sandboxArguments, $this->output);
$installCommand->run($installArguments, $this->output);
diff --git a/system/src/Grav/Console/Cli/SandboxCommand.php b/system/src/Grav/Console/Cli/SandboxCommand.php
index be692dc02..5fa320b67 100644
--- a/system/src/Grav/Console/Cli/SandboxCommand.php
+++ b/system/src/Grav/Console/Cli/SandboxCommand.php
@@ -15,7 +15,7 @@ class SandboxCommand extends ConsoleCommand
/**
* @var array
*/
- protected $directories = array(
+ protected $directories = [
'/backup',
'/cache',
'/logs',
@@ -27,22 +27,22 @@ class SandboxCommand extends ConsoleCommand
'/user/data',
'/user/plugins',
'/user/themes',
- );
+ ];
/**
* @var array
*/
- protected $files = array(
+ protected $files = [
'/.dependencies',
'/.htaccess',
'/user/config/site.yaml',
'/user/config/system.yaml',
- );
+ ];
/**
* @var array
*/
- protected $mappings = array(
+ protected $mappings = [
'/.editorconfig' => '/.editorconfig',
'/.gitignore' => '/.gitignore',
'/CHANGELOG.md' => '/CHANGELOG.md',
@@ -56,7 +56,7 @@ class SandboxCommand extends ConsoleCommand
'/vendor' => '/vendor',
'/webserver-configs' => '/webserver-configs',
'/codeception.yml' => '/codeception.yml',
- );
+ ];
/**
* @var string
@@ -200,7 +200,7 @@ class SandboxCommand extends ConsoleCommand
*/
private function initFiles()
{
- $this->check($this->output);
+ $this->check();
$this->output->writeln('');
$this->output->writeln('File Initializing');
@@ -225,8 +225,6 @@ class SandboxCommand extends ConsoleCommand
if (!$files_init) {
$this->output->writeln(' Files already exist');
}
-
-
}
/**
@@ -239,7 +237,7 @@ class SandboxCommand extends ConsoleCommand
// get pages files and initialize if no pages exist
$pages_dir = $this->destination . '/user/pages';
- $pages_files = array_diff(scandir($pages_dir), array('..', '.'));
+ $pages_files = array_diff(scandir($pages_dir), ['..', '.']);
if (count($pages_files) == 0) {
$destination = $this->source . '/user/pages';
@@ -269,7 +267,6 @@ class SandboxCommand extends ConsoleCommand
$this->output->writeln("");
}
-
/**
*
*/
@@ -295,6 +292,7 @@ class SandboxCommand extends ConsoleCommand
$success = false;
}
}
+
if (!$success) {
$this->output->writeln('');
$this->output->writeln('install should be run with --symlink|--s to symlink first');
diff --git a/system/src/Grav/Console/ConsoleCommand.php b/system/src/Grav/Console/ConsoleCommand.php
index 349c77ac8..e75bc3537 100644
--- a/system/src/Grav/Console/ConsoleCommand.php
+++ b/system/src/Grav/Console/ConsoleCommand.php
@@ -3,7 +3,6 @@ namespace Grav\Console;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
diff --git a/system/src/Grav/Console/Gpm/InfoCommand.php b/system/src/Grav/Console/Gpm/InfoCommand.php
index 99f8bd2e2..f52e66595 100644
--- a/system/src/Grav/Console/Gpm/InfoCommand.php
+++ b/system/src/Grav/Console/Gpm/InfoCommand.php
@@ -83,7 +83,7 @@ class InfoCommand extends ConsoleCommand
$this->output->writeln("" . str_pad("Author",
12) . ": " . $foundPackage->author['name'] . ' <' . $foundPackage->author['email'] . '> ' . $packageURL);
- foreach (array(
+ foreach ([
'version',
'keywords',
'date',
@@ -95,7 +95,7 @@ class InfoCommand extends ConsoleCommand
'bugs',
'zipball_url',
'license'
- ) as $info) {
+ ] as $info) {
if (isset($foundPackage->$info)) {
$name = ucfirst($info);
$data = $foundPackage->$info;
diff --git a/system/src/Grav/Console/Gpm/InstallCommand.php b/system/src/Grav/Console/Gpm/InstallCommand.php
index dca4fe77b..f8d93ae4d 100644
--- a/system/src/Grav/Console/Gpm/InstallCommand.php
+++ b/system/src/Grav/Console/Gpm/InstallCommand.php
@@ -5,12 +5,12 @@ use Grav\Common\Filesystem\Folder;
use Grav\Common\GPM\GPM;
use Grav\Common\GPM\Installer;
use Grav\Common\GPM\Response;
+use Grav\Common\Grav;
use Grav\Common\Utils;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\ConfirmationQuestion;
-use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Yaml\Yaml;
define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/(.*)/');
@@ -21,29 +21,29 @@ define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/
*/
class InstallCommand extends ConsoleCommand
{
- /**
- * @var
- */
+ /** @var */
protected $data;
- /**
- * @var
- */
+
+ /** @var GPM */
protected $gpm;
- /**
- * @var
- */
+
+ /** @var */
protected $destination;
- /**
- * @var
- */
+
+ /** @var */
protected $file;
- /**
- * @var
- */
+
+ /** @var */
protected $tmp;
+ /** @var */
protected $local_config;
+ /** @var bool */
+ protected $use_symlinks;
+
+ /** @var array */
+ protected $demo_processing = [];
/**
*
@@ -81,7 +81,17 @@ class InstallCommand extends ConsoleCommand
}
/**
- * @return int|null|void
+ * Allows to set the GPM object, used for testing the class
+ *
+ * @param $gpm
+ */
+ public function setGpm($gpm)
+ {
+ $this->gpm = $gpm;
+ }
+
+ /**
+ * @return int|null|void|bool
*/
protected function serve()
{
@@ -91,7 +101,7 @@ class InstallCommand extends ConsoleCommand
$packages = array_map('strtolower', $this->input->getArgument('package'));
$this->data = $this->gpm->findPackages($packages);
- if (false === $this->isWindows() && @is_file(getenv("HOME").'/.grav/config')) {
+ if (false === $this->isWindows() && @is_file(getenv("HOME") . '/.grav/config')) {
$local_config_file = exec('eval echo ~/.grav/config');
if (file_exists($local_config_file)) {
$this->local_config = Yaml::parse($local_config_file);
@@ -122,36 +132,344 @@ class InstallCommand extends ConsoleCommand
unset($this->data['not_found']);
unset($this->data['total']);
- foreach ($this->data as $data) {
- foreach ($data as $package) {
- //Check for dependencies
- if (isset($package->dependencies)) {
- $this->output->writeln("Package " . $package->name . " has ". count($package->dependencies) . " required dependencies that must be installed first...");
- $this->output->writeln('');
+ if (isset($this->local_config)) {
+ // Symlinks available, ask if Grav should use them
- $dependency_data = $this->gpm->findPackages($package->dependencies);
+ $this->use_symlinks = false;
+ $helper = $this->getHelper('question');
+ $question = new ConfirmationQuestion('Should Grav use the symlinks if available? [y|N] ', false);
- if (!$dependency_data['total']) {
- $this->output->writeln("No dependencies found...");
- $this->output->writeln('');
- } else {
- unset($dependency_data['total']);
+ if ($helper->ask($this->input, $this->output, $question)) {
+ $this->use_symlinks = true;
+ }
+ }
- foreach($dependency_data as $type => $dep_data) {
- foreach($dep_data as $name => $dep_package) {
+ $this->output->writeln('');
- $this->processPackage($dep_package);
- }
- }
- }
+ try {
+ $dependencies = $this->processDependencies($packages);
+ } catch (\Exception $e) {
+ //Error out if there are incompatible packages requirements and tell which ones, and what to do
+ //Error out if there is any error in parsing the dependencies and their versions, and tell which one is broken
+ $this->output->writeln("" . $e->getMessage() . "");
+ return false;
+ }
+
+ if ($dependencies) {
+ //First, check for Grav dependency. If a dependency requires Grav > the current version, abort and tell.
+ if (isset($dependencies['grav'])) {
+ if (version_compare($this->calculateVersionNumberFromDependencyVersion($dependencies['grav']), GRAV_VERSION) === 1) {
+ //Needs a Grav update first
+ $this->output->writeln("One of the package dependencies requires Grav " . $dependencies['grav'] . ". Please update Grav first with `bin/gpm selfupgrade`");
+ return false;
}
+ unset($dependencies['grav']);
+ }
- $this->processPackage($package);
+ try {
+ $this->installDependencies($dependencies, 'install', "The following dependencies need to be installed...");
+ $this->installDependencies($dependencies, 'update', "The following dependencies need to be updated...");
+ $this->installDependencies($dependencies, 'ignore', "The following dependencies can be updated as there is a newer version, but it's not mandatory...");
+ } catch (\Exception $e) {
+ $this->output->writeln("Installation aborted");
+ return false;
+ }
+ }
+
+ //We're done installing dependencies. Install the actual packages
+ foreach ($this->data as $data) {
+ foreach ($data as $packageName => $package) {
+ if (in_array($packageName, array_keys($dependencies))) {
+ $this->output->writeln("Package " . $packageName . " already installed as dependency");
+ } else {
+ $this->processPackage($package);
+ }
+ }
+ }
+
+ if (count($this->demo_processing) > 0) {
+ foreach ($this->demo_processing as $package) {
+ $this->installDemoContent($package);
}
}
// clear cache after successful upgrade
$this->clearCache();
+
+ return true;
+ }
+
+ /**
+ * Given a $dependencies list, filters their type according to $type and
+ * shows $message prior to listing them to the user. Then asks the user a confirmation prior
+ * to installing them.
+ *
+ * @param array $dependencies The dependencies array
+ * @param string $type The type of dependency to show: install, update, ignore
+ * @param string $message A message to be shown prior to listing the dependencies
+ *
+ * @throws \Exception
+ */
+ public function installDependencies($dependencies, $type, $message) {
+ $packages = array_filter($dependencies, function ($action) use ($type) { return $action === $type; });
+ if (count($packages) > 0) {
+ $this->output->writeln($message);
+
+ foreach ($packages as $dependencyName => $dependencyVersion) {
+ $this->output->writeln(" |- Package " . $dependencyName . " requires a newer version");
+ }
+
+ $this->output->writeln("");
+
+ $helper = $this->getHelper('question');
+ $question = new ConfirmationQuestion('Update these packages? [y|N] ', false);
+
+ if ($helper->ask($this->input, $this->output, $question)) {
+ foreach ($packages as $dependencyName => $dependencyVersion) {
+ $this->processPackage($dependencyName);
+ }
+ $this->output->writeln('');
+ } else {
+ throw new \Exception();
+ }
+ }
+ }
+
+ /**
+ * Fetch the dependencies, check the installed packages and return an array with
+ * the list of packages with associated an information on what to do: install, update or ignore.
+ *
+ * `ignore` means the package is already installed and can be safely left as-is.
+ * `install` means the package is not installed and must be installed.
+ * `update` means the package is already installed and must be updated as a dependency needs a higher version.
+ *
+ * @param array $packages
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ public function processDependencies($packages) {
+ $dependencies = $this->calculateMergedDependenciesOfPackages($packages);
+
+ foreach ($dependencies as $dependencySlug => $dependencyVersion) {
+ if ($this->gpm->isPluginInstalled($dependencySlug)) {
+ $dependencyVersion = $this->calculateVersionNumberFromDependencyVersion($dependencyVersion);
+
+ // check the version, if an update is not strictly required mark as 'ignore'
+ $locator = Grav::instance()['locator'];
+ $blueprints_path = $locator->findResource('plugins://' . $dependencySlug . DS . 'blueprints.yaml');
+ $package_yaml = Yaml::parse(file_get_contents($blueprints_path));
+ $currentlyInstalledVersion = $package_yaml['version'];
+
+ //if I already have the latest release, remove the dependency
+ $latestRelease = $this->gpm->getLatestVersionOfPackage($dependencySlug);
+
+ if (version_compare($latestRelease, $dependencyVersion) == -1) {
+ //throw an exception if a required version cannot be found in the GPM yet
+ throw new \Exception('Dependency ' . $package_yaml['name'] . ' is required in a version higher than the latest release. Try running `bin/gpm -f index` to force a refresh of the GPM cache', 1);
+ }
+
+ if (version_compare($currentlyInstalledVersion, $dependencyVersion) == -1) {
+ $dependencies[$dependencySlug] = 'update';
+ } else {
+ if ($currentlyInstalledVersion == $latestRelease) {
+ unset($dependencies[$dependencySlug]);
+ } else {
+ $dependencies[$dependencySlug] = 'ignore';
+ }
+ }
+ } else {
+ $dependencies[$dependencySlug] = 'install';
+ }
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ * Calculates and merges the dependencies of a package
+ *
+ * @param string $packageName The package information
+ *
+ * @param array $dependencies The dependencies array
+ *
+ * @return array
+ * @throws \Exception
+ */
+ private function calculateMergedDependenciesOfPackage($packageName, $dependencies)
+ {
+ $packageData = $this->gpm->findPackage($packageName);
+
+ //Check for dependencies
+ if (isset($packageData->dependencies)) {
+ foreach ($packageData->dependencies as $dependency) {
+ $current_package_name = $dependency['name'];
+ if (isset($dependency['version'])) {
+ $current_package_version_information = $dependency['version'];
+ }
+
+ if (!isset($dependencies[$current_package_name])) {
+ // Dependency added for the first time
+
+ if (!isset($current_package_version_information)) {
+ $dependencies[$current_package_name] = '*';
+ } else {
+ $dependencies[$current_package_name] = $current_package_version_information;
+ }
+
+ //Factor in the package dependencies too
+ $dependencies = $this->calculateMergedDependenciesOfPackage($current_package_name, $dependencies);
+ }
+ else {
+ // Dependency already added by another package
+ //if this package requires a version higher than the currently stored one, store this requirement instead
+ if (isset($current_package_version_information) && $current_package_version_information !== '*') {
+
+ $currently_stored_version_information = $dependencies[$current_package_name];
+ $currently_stored_version_number = $this->calculateVersionNumberFromDependencyVersion($currently_stored_version_information);
+
+ $currently_stored_version_is_in_next_significant_release_format = false;
+ if ($this->versionFormatIsNextSignificantRelease($currently_stored_version_information)) {
+ $currently_stored_version_is_in_next_significant_release_format = true;
+ }
+
+ if (!$currently_stored_version_number) {
+ $currently_stored_version_number = '*';
+ }
+
+ $current_package_version_number = $this->calculateVersionNumberFromDependencyVersion($current_package_version_information);
+ if (!$current_package_version_number) {
+ throw new \Exception('Bad format for version of dependency ' . $current_package_name . ' for package ' . $packageName, 1);
+ }
+
+ $current_package_version_is_in_next_significant_release_format = false;
+ if ($this->versionFormatIsNextSignificantRelease($current_package_version_information)) {
+ $current_package_version_is_in_next_significant_release_format = true;
+ }
+
+ //If I had stored '*', change right away with the more specific version required
+ if ($currently_stored_version_number === '*') {
+ $dependencies[$current_package_name] = $current_package_version_information;
+ } else {
+ if (!$currently_stored_version_is_in_next_significant_release_format && !$current_package_version_is_in_next_significant_release_format) {
+ //Comparing versions equals or higher, a simple version_compare is enough
+ if (version_compare($currently_stored_version_number, $current_package_version_number) == -1) { //Current package version is higher
+ $dependencies[$current_package_name] = $current_package_version_information;
+ }
+ } else {
+ $compatible = $this->checkNextSignificantReleasesAreCompatible($currently_stored_version_number, $current_package_version_number);
+ if (!$compatible) {
+ throw new \Exception('Dependency ' . $current_package_name . ' is required in two incompatible versions', 2);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ * Calculates and merges the dependencies of the passed packages
+ *
+ * @param array $packages
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ public function calculateMergedDependenciesOfPackages($packages)
+ {
+ $dependencies = [];
+
+ foreach ($packages as $package) {
+ $dependencies = $this->calculateMergedDependenciesOfPackage($package, $dependencies);
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ * Returns the actual version from a dependency version string.
+ * Examples:
+ * $versionInformation == '~2.0' => returns '2.0'
+ * $versionInformation == '>=2.0.2' => returns '2.0.2'
+ * $versionInformation == '*' => returns null
+ * $versionInformation == '' => returns null
+ *
+ * @param $versionInformation
+ *
+ * @return null|string
+ */
+ public function calculateVersionNumberFromDependencyVersion($versionInformation)
+ {
+ if ($this->versionFormatIsNextSignificantRelease($versionInformation)) {
+ return substr($versionInformation, 1);
+ } elseif ($this->versionFormatIsEqualOrHigher($versionInformation)) {
+ return substr($versionInformation, 2);
+ }
+
+ return null;
+ }
+
+ /**
+ * Check if the passed version information contains next significant release (tilde) operator
+ *
+ * Example: returns true for $version: '~2.0'
+ *
+ * @param $version
+ *
+ * @return bool
+ */
+ public function versionFormatIsNextSignificantRelease($version) {
+ return substr($version, 0, 1) == '~';
+ }
+
+ /**
+ * Check if the passed version information contains equal or higher operator
+ *
+ * Example: returns true for $version: '>=2.0'
+ *
+ * @param $version
+ *
+ * @return bool
+ */
+ public function versionFormatIsEqualOrHigher($version) {
+ return substr($version, 0, 2) == '>=';
+ }
+
+ /**
+ * Check if two releases are compatible by next significant release
+ *
+ * ~1.2 is equivalent to >=1.2 <2.0.0
+ * ~1.2.3 is equivalent to >=1.2.3 <1.3.0
+ *
+ * In short, allows the last digit specified to go up
+ *
+ * @param string $version1 the version string (e.g. '2.0.0' or '1.0')
+ * @param string $version2 the version string (e.g. '2.0.0' or '1.0')
+ *
+ * @return bool
+ */
+ public function checkNextSignificantReleasesAreCompatible($version1, $version2)
+ {
+ $version1array = explode('.', $version1);
+ $version2array = explode('.', $version2);
+
+ if (count($version1array) > count($version2array)) {
+ list($version1array, $version2array) = [$version2array, $version1array];
+ }
+
+ $i = 0;
+ while ($i < count($version1array) - 1) {
+ if ($version1array[$i] != $version2array[$i]) {
+ return false;
+ }
+ $i++;
+ }
+
+ return true;
}
/**
@@ -159,78 +477,65 @@ class InstallCommand extends ConsoleCommand
*/
private function processPackage($package)
{
- $install_options = ['GPM'];
-
- // if no name, not found in GPM
- if (!isset($package->version)) {
- unset($install_options[0]);
- }
- // if local config found symlink is a valid option
- if (isset($this->local_config) && $this->getSymlinkSource($package)) {
- $install_options[] = 'Symlink';
- }
- // if override set, can install via git
- if (isset($package->override_repository)) {
- $install_options[] = 'Git';
+ $symlink = false;
+ if ($this->use_symlinks) {
+ if ($this->getSymlinkSource($package) || !isset($package->version)) {
+ $symlink = true;
+ }
}
- // reindex list
- $install_options = array_values($install_options);
+ $symlink ? $this->processSymlink($package) : $this->processGpm($package);
- if (count($install_options) == 0) {
- // no valid install options - error and return
- $this->output->writeln("not valid installation methods found!");
- return;
- } elseif (count($install_options) == 1) {
- // only one option, use it...
- $method = $install_options[0];
- } else {
- $helper = $this->getHelper('question');
- $question = new ChoiceQuestion(
- 'Please select installation method for ' . $package->name . ' ('.$install_options[0].' is default)', array_values($install_options), 0
- );
- $question->setErrorMessage('Method %s is invalid');
- $method = $helper->ask($this->input, $this->output, $question);
- }
-
- $this->output->writeln('');
-
- $method_name = 'process'.$method;
- $this->$method_name($package);
-
- $this->installDemoContent($package);
+ $this->processDemo($package);
}
+ /**
+ * Add package to the queue to process the demo content, if demo content exists
+ *
+ * @param $package
+ */
+ private function processDemo($package)
+ {
+ $demo_dir = $this->destination . DS . $package->install_path . DS . '_demo';
+ if (file_exists($demo_dir)) {
+ $this->demo_processing[] = $package;
+ }
+ }
/**
+ * Prompt to install the demo content of a package
+ *
* @param $package
*/
private function installDemoContent($package)
{
$demo_dir = $this->destination . DS . $package->install_path . DS . '_demo';
- $dest_dir = $this->destination . DS . 'user';
- $pages_dir = $dest_dir . DS . 'pages';
if (file_exists($demo_dir)) {
+ $dest_dir = $this->destination . DS . 'user';
+ $pages_dir = $dest_dir . DS . 'pages';
+
// Demo content exists, prompt to install it.
- $this->output->writeln("Attention: ".$package->name . " contains demo content");
+ $this->output->writeln("Attention: " . $package->name . " contains demo content");
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Do you wish to install this demo content? [y|N] ', false);
if (!$helper->ask($this->input, $this->output, $question)) {
$this->output->writeln(" '- Skipped! ");
$this->output->writeln('');
+
return;
}
// if pages folder exists in demo
if (file_exists($demo_dir . DS . 'pages')) {
$pages_backup = 'pages.' . date('m-d-Y-H-i-s');
- $question = new ConfirmationQuestion('This will backup your current `user/pages` folder to `user/'. $pages_backup. '`, continue? [y|N]', false);
+ $question = new ConfirmationQuestion('This will backup your current `user/pages` folder to `user/' . $pages_backup . '`, continue? [y|N]', false);
if (!$helper->ask($this->input, $this->output, $question)) {
$this->output->writeln(" '- Skipped! ");
$this->output->writeln('');
+
return;
}
@@ -259,9 +564,7 @@ class InstallCommand extends ConsoleCommand
*/
private function getGitRegexMatches($package)
{
- if (isset($package->override_repository)) {
- $repository = $package->override_repository;
- } elseif (isset($package->repository)) {
+ if (isset($package->repository)) {
$repository = $package->repository;
} else {
return false;
@@ -294,6 +597,7 @@ class InstallCommand extends ConsoleCommand
return $from;
}
}
+
return false;
}
@@ -336,6 +640,7 @@ class InstallCommand extends ConsoleCommand
}
+
return;
}
@@ -346,34 +651,7 @@ class InstallCommand extends ConsoleCommand
/**
* @param $package
*/
- private function processGit($package)
- {
- $matches = $this->getGitRegexMatches($package);
-
- $this->output->writeln("Preparing to Git clone " . $package->name . " from " . $matches[0]);
-
- $this->output->write(" |- Checking destination... ");
- $checks = $this->checkDestination($package);
-
- if (!$checks) {
- $this->output->writeln(" '- Installation failed or aborted.");
- $this->output->writeln('');
- } else {
- $cmd = 'cd ' . $this->destination . ' && git clone ' . $matches[0] . ' ' . $package->install_path;
- exec($cmd);
-
- // extra white spaces to clear out the buffer properly
- $this->output->writeln(" |- Cloning package... ok ");
-
- $this->output->writeln(" '- Success! ");
- $this->output->writeln('');
- }
- }
-
- /**
- * @param $package
- */
- private function processGPM($package)
+ private function processGpm($package)
{
$version = isset($package->available) ? $package->available : $package->version;
diff --git a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
index 542878380..6178fb5ec 100644
--- a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
+++ b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
@@ -35,7 +35,7 @@ class SelfupgradeCommand extends ConsoleCommand
/**
* @var array
*/
- protected $types = array('plugins', 'themes');
+ protected $types = ['plugins', 'themes'];
/**
* @var
*/
diff --git a/system/src/Grav/Console/Gpm/UninstallCommand.php b/system/src/Grav/Console/Gpm/UninstallCommand.php
index 63555aa9e..c03a30d25 100644
--- a/system/src/Grav/Console/Gpm/UninstallCommand.php
+++ b/system/src/Grav/Console/Gpm/UninstallCommand.php
@@ -58,6 +58,38 @@ class UninstallCommand extends ConsoleCommand
->setHelp('The uninstall command allows to uninstall plugins and themes');
}
+ /**
+ * Return the list of packages that have the passed one as dependency
+ *
+ * @param $package_slug The slug name of the package
+ *
+ * @return bool
+ */
+ protected function getPackagesThatDependOnPackage($package_slug)
+ {
+ $plugins = $this->gpm->getInstalledPlugins();
+ $themes = $this->gpm->getInstalledThemes();
+ $packages = array_merge($plugins->toArray(), $themes->toArray());
+
+ $dependent_packages = [];
+
+ foreach($packages as $package_name => $package) {
+ if (isset($package['dependencies'])) {
+ foreach($package['dependencies'] as $dependency) {
+ if (is_array($dependency)) {
+ $dependency = array_keys($dependency)[0];
+ }
+
+ if ($dependency == $package_slug) {
+ $dependent_packages[] = $package_name;
+ }
+ }
+ }
+ }
+
+ return $dependent_packages;
+ }
+
/**
* @return int|null|void
*/
@@ -98,7 +130,6 @@ class UninstallCommand extends ConsoleCommand
foreach ($this->data as $slug => $package) {
$this->output->writeln("Preparing to uninstall " . $package->name . " [v" . $package->version . "]");
-
$this->output->write(" |- Checking destination... ");
$checks = $this->checkDestination($slug, $package);
@@ -117,6 +148,7 @@ class UninstallCommand extends ConsoleCommand
$this->output->writeln('');
}
}
+
}
// clear cache after successful upgrade
@@ -132,6 +164,23 @@ class UninstallCommand extends ConsoleCommand
*/
private function uninstallPackage($slug, $package)
{
+ //check if there are packages that have this as a dependency. Abort and show list
+ $dependency_packages = $this->getPackagesThatDependOnPackage($slug);
+ if ($dependency_packages) {
+ $this->output->writeln('');
+ $this->output->writeln('');
+ $this->output->writeln("Uninstallation failed.");
+ $this->output->writeln('');
+ if (count($dependency_packages) > 1) {
+ $this->output->writeln("The installed packages " . implode(', ', $dependency_packages) . " depend on this package. Please remove those first.");
+ } else {
+ $this->output->writeln("The installed package " . implode(', ', $dependency_packages) . " depend on this package. Please remove it first.");
+ }
+
+ $this->output->writeln('');
+ exit;
+ }
+
$path = Grav::instance()['locator']->findResource($package->package_type . '://' .$slug);
Installer::uninstall($path);
$errorCode = Installer::lastErrorCode();
@@ -149,6 +198,35 @@ class UninstallCommand extends ConsoleCommand
// extra white spaces to clear out the buffer properly
$this->output->writeln(" |- Uninstalling package... ok ");
+
+ if (isset($package->dependencies)) {
+ $questionHelper = $this->getHelper('question');
+
+ foreach($package->dependencies as $dependency) {
+ if (is_array($dependency)) {
+ $dependency = array_keys($dependency)[0];
+ }
+
+ $dependencyPackage = $this->gpm->findPackage($dependency);
+ $question = new ConfirmationQuestion(" | '- Delete dependency " . $dependency . " too? [y|N] ", false);
+ $answer = $questionHelper->ask($this->input, $this->output, $question);
+
+ if ($answer) {
+ $this->output->writeln(" | '- You decided to delete " . $dependency . ".");
+
+ $uninstall = $this->uninstallPackage($dependency, $dependencyPackage);
+
+ if (!$uninstall) {
+ $this->output->writeln(" '- Uninstallation failed or aborted.");
+ $this->output->writeln('');
+ } else {
+ $this->output->writeln(" '- Success! ");
+ $this->output->writeln('');
+ }
+ }
+ }
+ }
+
return true;
}
diff --git a/system/src/Grav/Console/Gpm/UpdateCommand.php b/system/src/Grav/Console/Gpm/UpdateCommand.php
index 5ca795ea8..74bcd1be2 100644
--- a/system/src/Grav/Console/Gpm/UpdateCommand.php
+++ b/system/src/Grav/Console/Gpm/UpdateCommand.php
@@ -38,7 +38,7 @@ class UpdateCommand extends ConsoleCommand
/**
* @var array
*/
- protected $types = array('plugins', 'themes');
+ protected $types = ['plugins', 'themes'];
/**
* @var GPM $gpm
*/
@@ -149,13 +149,13 @@ class UpdateCommand extends ConsoleCommand
// finally update
$install_command = $this->getApplication()->find('install');
- $args = new ArrayInput(array(
+ $args = new ArrayInput([
'command' => 'install',
'package' => $slugs,
'-f' => $this->input->getOption('force'),
'-d' => $this->destination,
'-y' => true
- ));
+ ]);
$command_exec = $install_command->run($args, $this->output);
if ($command_exec != 0) {
diff --git a/system/src/Grav/Console/Gpm/VersionCommand.php b/system/src/Grav/Console/Gpm/VersionCommand.php
index 231645dcc..4b64586d0 100644
--- a/system/src/Grav/Console/Gpm/VersionCommand.php
+++ b/system/src/Grav/Console/Gpm/VersionCommand.php
@@ -70,7 +70,8 @@ class VersionCommand extends ConsoleCommand
}
} else {
- if ($installed = $this->gpm->findPackage($package)) {
+ $installed = $this->gpm->findPackage($package);
+ if ($installed) {
$name = $installed->name;
$version = $installed->version;
diff --git a/tests/unit/Grav/Common/InflectorTest.php b/tests/unit/Grav/Common/InflectorTest.php
index be6f68386..9caa3e810 100644
--- a/tests/unit/Grav/Common/InflectorTest.php
+++ b/tests/unit/Grav/Common/InflectorTest.php
@@ -138,9 +138,9 @@ class InflectorTest extends \Codeception\TestCase\Test
public function testMonthize()
{
$this->assertSame(0, $this->inflector->monthize(10));
- $this->assertSame(1, $this->inflector->monthize(30));
+ $this->assertSame(1, $this->inflector->monthize(33));
$this->assertSame(1, $this->inflector->monthize(41));
- $this->assertSame(11, $this->inflector->monthize(365));
+ $this->assertSame(11, $this->inflector->monthize(364));
}
}
diff --git a/tests/unit/Grav/Console/Gpm/InstallCommandTest.php b/tests/unit/Grav/Console/Gpm/InstallCommandTest.php
new file mode 100644
index 000000000..a9088ca11
--- /dev/null
+++ b/tests/unit/Grav/Console/Gpm/InstallCommandTest.php
@@ -0,0 +1,326 @@
+data[$packageName])) {
+ return $this->data[$packageName];
+ }
+
+ }
+
+ public function findPackages()
+ {
+ return $this->data;
+ }
+}
+
+/**
+ * Class InstallCommandTest
+ */
+class InstallCommandTest extends \Codeception\TestCase\Test
+{
+ /** @var Grav $grav */
+ protected $grav;
+
+ /** @var InstallCommand */
+ protected $installCommand;
+
+ /** @var GpmStub */
+ protected $gpm;
+
+ protected function _before()
+ {
+ $this->grav = Fixtures::get('grav');
+ $this->installCommand = new InstallCommand();
+
+ $this->gpm = new GpmStub();
+ }
+
+ protected function _after()
+ {
+ }
+
+ public function testCalculateMergedDependenciesOfPackages()
+ {
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // First working example
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "grav", "version" => ">=1.0.10"],
+ ["name" => "form", "version" => "~2.0"],
+ ["name" => "login", "version" => ">=2.0"],
+ ["name" => "errors", "version" => "*"],
+ ["name" => "problems"],
+ ]
+ ],
+ 'test' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=1.0"]
+ ]
+ ],
+ 'grav',
+ 'form' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=3.2"]
+ ]
+ ]
+
+
+ ];
+ $this->installCommand->setGpm($this->gpm);
+
+ $packages = ['admin', 'test'];
+
+ $dependencies = $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+
+ $this->assertTrue(is_array($dependencies));
+ $this->assertSame(5, count($dependencies));
+
+ $this->assertTrue($dependencies['grav'] == '>=1.0.10');
+ $this->assertTrue(isset($dependencies['errors']));
+ $this->assertTrue(isset($dependencies['problems']));
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Second working example
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $packages = ['admin', 'form'];
+
+ $dependencies = $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+ $this->assertTrue(is_array($dependencies));
+ $this->assertSame(5, count($dependencies));
+ $this->assertTrue($dependencies['errors'] == '>=3.2');
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Third working example
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=4.0"],
+ ]
+ ],
+ 'test' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=1.0"]
+ ]
+ ],
+ 'another' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=3.2"]
+ ]
+ ]
+
+ ];
+ $this->installCommand->setGpm($this->gpm);
+
+ $packages = ['admin', 'test', 'another'];
+
+
+ $dependencies = $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+ $this->assertTrue(is_array($dependencies));
+ $this->assertSame(1, count($dependencies));
+ $this->assertTrue($dependencies['errors'] == '>=4.0');
+
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Test alpha / beta / rc
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "package1", "version" => ">=4.0.0-rc1"],
+ ["name" => "package4", "version" => ">=3.2.0"],
+ ]
+ ],
+ 'test' => (object)[
+ 'dependencies' => [
+ ["name" => "package1", "version" => ">=4.0.0-rc2"],
+ ["name" => "package2", "version" => ">=3.2.0-alpha"],
+ ["name" => "package3", "version" => ">=3.2.0-alpha.2"],
+ ["name" => "package4", "version" => ">=3.2.0-alpha"],
+ ]
+ ],
+ 'another' => (object)[
+ 'dependencies' => [
+ ["name" => "package2", "version" => ">=3.2.0-beta.11"],
+ ["name" => "package3", "version" => ">=3.2.0-alpha.1"],
+ ["name" => "package4", "version" => ">=3.2.0-beta"],
+ ]
+ ]
+ ];
+ $this->installCommand->setGpm($this->gpm);
+
+ $packages = ['admin', 'test', 'another'];
+
+
+ $dependencies = $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+ $this->assertTrue($dependencies['package1'] == '>=4.0.0-rc2');
+ $this->assertTrue($dependencies['package2'] == '>=3.2.0-beta.11');
+ $this->assertTrue($dependencies['package3'] == '>=3.2.0-alpha.2');
+ $this->assertTrue($dependencies['package4'] == '>=3.2.0');
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Raise exception if no version is specified
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">=4.0"],
+ ]
+ ],
+ 'test' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => ">="]
+ ]
+ ],
+
+ ];
+ $this->installCommand->setGpm($this->gpm);
+ $packages = ['admin', 'test'];
+
+ try {
+ $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+ $this->fail("Expected Exception not thrown");
+ } catch (Exception $e) {
+ $this->assertEquals(EXCEPTION_BAD_FORMAT, $e->getCode());
+ $this->assertStringStartsWith("Bad format for version of dependency", $e->getMessage());
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Raise exception if incompatible versions are specified
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => "~4.0"],
+ ]
+ ],
+ 'test' => (object)[
+ 'dependencies' => [
+ ["name" => "errors", "version" => "~3.0"]
+ ]
+ ],
+ ];
+ $this->installCommand->setGpm($this->gpm);
+ $packages = ['admin', 'test'];
+
+ try {
+ $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+ $this->fail("Expected Exception not thrown");
+ } catch (Exception $e) {
+ $this->assertEquals(EXCEPTION_INCOMPATIBLE_VERSIONS, $e->getCode());
+ $this->assertStringEndsWith("required in two incompatible versions", $e->getMessage());
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Test dependencies of dependencies
+ //////////////////////////////////////////////////////////////////////////////////////////
+ $this->gpm->data = [
+ 'admin' => (object)[
+ 'dependencies' => [
+ ["name" => "grav", "version" => ">=1.0.10"],
+ ["name" => "form", "version" => "~2.0"],
+ ["name" => "login", "version" => ">=2.0"],
+ ["name" => "errors", "version" => "*"],
+ ["name" => "problems"],
+ ]
+ ],
+ 'login' => (object)[
+ 'dependencies' => [
+ ["name" => "antimatter", "version" => ">=1.0"]
+ ]
+ ],
+ 'grav',
+ 'antimatter' => (object)[
+ 'dependencies' => [
+ ["name" => "something", "version" => ">=3.2"]
+ ]
+ ]
+
+
+ ];
+ $this->installCommand->setGpm($this->gpm);
+
+ $packages = ['admin'];
+
+ $dependencies = $this->installCommand->calculateMergedDependenciesOfPackages($packages);
+
+ $this->assertTrue(is_array($dependencies));
+ $this->assertSame(7, count($dependencies));
+
+ $this->assertSame('>=1.0.10', $dependencies['grav']);
+ $this->assertTrue(isset($dependencies['errors']));
+ $this->assertTrue(isset($dependencies['problems']));
+ $this->assertTrue(isset($dependencies['antimatter']));
+ $this->assertTrue(isset($dependencies['something']));
+ $this->assertSame('>=3.2', $dependencies['something']);
+ }
+
+ public function testVersionFormatIsNextSignificantRelease()
+ {
+ $this->assertFalse($this->installCommand->versionFormatIsNextSignificantRelease('>=1.0'));
+ $this->assertFalse($this->installCommand->versionFormatIsNextSignificantRelease('>=2.3.4'));
+ $this->assertFalse($this->installCommand->versionFormatIsNextSignificantRelease('>=2.3.x'));
+ $this->assertFalse($this->installCommand->versionFormatIsNextSignificantRelease('1.0'));
+ $this->assertTrue($this->installCommand->versionFormatIsNextSignificantRelease('~2.3.x'));
+ $this->assertTrue($this->installCommand->versionFormatIsNextSignificantRelease('~2.0'));
+ }
+
+ public function testVersionFormatIsEqualOrHigher()
+ {
+ $this->assertTrue($this->installCommand->versionFormatIsEqualOrHigher('>=1.0'));
+ $this->assertTrue($this->installCommand->versionFormatIsEqualOrHigher('>=2.3.4'));
+ $this->assertTrue($this->installCommand->versionFormatIsEqualOrHigher('>=2.3.x'));
+ $this->assertFalse($this->installCommand->versionFormatIsEqualOrHigher('~2.3.x'));
+ $this->assertFalse($this->installCommand->versionFormatIsEqualOrHigher('1.0'));
+ }
+
+ public function testCheckNextSignificantReleasesAreCompatible()
+ {
+ /*
+ * ~1.0 is equivalent to >=1.0 < 2.0.0
+ * ~1.2 is equivalent to >=1.2 <2.0.0
+ * ~1.2.3 is equivalent to >=1.2.3 <1.3.0
+ */
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0', '1.2'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.2', '1.0'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0', '1.0.10'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.1', '1.1.10'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('30.0', '30.10'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0', '1.1.10'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0', '1.8'));
+ $this->assertTrue($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0.1', '1.1'));
+
+ $this->assertFalse($this->installCommand->checkNextSignificantReleasesAreCompatible('1.0', '2.2'));
+ $this->assertFalse($this->installCommand->checkNextSignificantReleasesAreCompatible('0.9.99', '1.0.0'));
+ $this->assertFalse($this->installCommand->checkNextSignificantReleasesAreCompatible('0.9.99', '1.0.10'));
+ $this->assertFalse($this->installCommand->checkNextSignificantReleasesAreCompatible('0.9.99', '1.0.10.2'));
+ }
+
+
+ public function testCalculateVersionNumberFromDependencyVersion()
+ {
+ $this->assertSame('2.0', $this->installCommand->calculateVersionNumberFromDependencyVersion('>=2.0'));
+ $this->assertSame('2.0.2', $this->installCommand->calculateVersionNumberFromDependencyVersion('>=2.0.2'));
+ $this->assertSame('2.0.2', $this->installCommand->calculateVersionNumberFromDependencyVersion('~2.0.2'));
+ $this->assertSame('1', $this->installCommand->calculateVersionNumberFromDependencyVersion('~1'));
+ $this->assertSame(null, $this->installCommand->calculateVersionNumberFromDependencyVersion(''));
+ $this->assertSame(null, $this->installCommand->calculateVersionNumberFromDependencyVersion('*'));
+ $this->assertSame(null, $this->installCommand->calculateVersionNumberFromDependencyVersion('2.0.2'));
+ }
+}
\ No newline at end of file