diff --git a/composer.json b/composer.json index 71fb238..78d2a10 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "require": { "symfony/console": "^3.0", "symfony/yaml": "^3.0", - "noccylabs/downloader": "@dev" + "noccylabs/downloader": "@dev", + "symfony/expression-language": "^3.2" } } diff --git a/docs/EXPRESSIONS.md b/docs/EXPRESSIONS.md new file mode 100644 index 0000000..7f799c0 --- /dev/null +++ b/docs/EXPRESSIONS.md @@ -0,0 +1,29 @@ +Matching Expressions +==================== + +Hotfixes can provide a `for` key with an array of supported systems. The +expressions are evaluated using Symfony's ExpressionLanguage and as such +can handle some pretty complex stuff. + +For a hotfix to be considered supported, at least one of the expressions +must evaluate to true. + + +## Example: + + for: + - lsb.id=='raspbian' + - lsb.id=='ubuntu' and lsb.release=>'16.04' + + +## System facts + +### Distribution + +Information is retrieved using `lsb_release` or the `/etc/os-release` file. + + lsb.id Distribution ID (ex. ubuntu or raspbian) + lsb.description Pretty description + lsb.release Version number (ex. 16.04 or 8) + lsb.codename Release codename (ex. xenial, not always present) + diff --git a/src/Command/ApplyCommand.php b/src/Command/ApplyCommand.php index cc0cfef..2d52e69 100644 --- a/src/Command/ApplyCommand.php +++ b/src/Command/ApplyCommand.php @@ -10,7 +10,10 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use NoccyLabs\Hotfix\Hotfix\Loader; +use NoccyLabs\Hotfix\System\Facts; + class ApplyCommand extends Command { @@ -64,6 +67,28 @@ class ApplyCommand extends Command } else { $output->writeln("Warning: Hotfix is not signed or signature not valid!"); } + + $requires = $hotfix->getRequirements(); + if (count($requires)>0) { + $engine = new ExpressionLanguage(); + $facts = Facts::getSystemFacts()->getFacts(); + while (true) { + $expr = array_shift($requires); + $ret = $engine->evaluate($expr, $facts); + if ($ret) { + //$output->writeln("Hotfix is compatible with the current distribution"); + break; + } + if (count($requires)==0) { + $output->writeln("Error: This hotfix it not compatible with the current distribution"); + return false; + } + } + //} else { + //$output->writeln("Hotfix indicates universal compatibility."); + } + + $output->writeln(""); $output->writeln(" Hotfix: ".$hotfix->getName().""); $output->writeln(" Author: ".$hotfix->getAuthor().""); diff --git a/src/Hotfix/Hotfix.php b/src/Hotfix/Hotfix.php index 5a510fa..1e9497f 100644 --- a/src/Hotfix/Hotfix.php +++ b/src/Hotfix/Hotfix.php @@ -70,6 +70,14 @@ class Hotfix unlink($tmpfile); } + public function getRequirements() + { + if (!array_key_exists('for',$this->header)) { + return []; + } + return $this->header['for']; + } + public function getSignedBy() { if (!$this->signer) { diff --git a/src/System/Facts.php b/src/System/Facts.php new file mode 100644 index 0000000..061d62a --- /dev/null +++ b/src/System/Facts.php @@ -0,0 +1,51 @@ +read(); + } + return $facts; + } + + public function read() + { + $facts = []; + $has_lsb_release = exec("which lsb_release"); + if ($has_lsb_release) { + exec("lsb_release -idrcs", $output); + $facts['lsb'] = (object)[ + 'id' => strtolower($output[0]), + 'description' => $output[1], + 'release' => $output[2], + 'codename' => $output[3] + ]; + } elseif (file_exists("/etc/os-release")) { + $ini = parse_ini_file("/etc/os-release"); + $facts['lsb'] = (object)[ + 'id' => strtolower(@$ini['ID']), + 'description' => @$ini['PRETTY_NAME'], + 'release' => @$ini['VERSION_ID'], + 'codename' => @$ini['VERSION_CODENAME'] + ]; + } + + $this->facts = $facts; + } + + public function getFacts() + { + return $this->facts; + } + +}