diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 934c05bb..e1a8c706 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch] jobs: selftest: name: CI test (make validate) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check out repository code @@ -58,7 +58,7 @@ jobs: citest: name: Integration tests needs: selftest - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 services: postgres: @@ -181,7 +181,7 @@ jobs: phartest: name: Integration tests (PHAR) needs: buildphar - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 services: postgres: diff --git a/docs/CLI.md b/docs/CLI.md index d11fcd7d..cf155124 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -859,7 +859,7 @@ Install everything required for CI testing ### Usage -* `install [--moodle MOODLE] [--data DATA] [--repo REPO] [--branch BRANCH] [--plugin PLUGIN] [--db-type DB-TYPE] [--db-user DB-USER] [--db-pass DB-PASS] [--db-name DB-NAME] [--db-host DB-HOST] [--db-port DB-PORT] [--not-paths NOT-PATHS] [--not-names NOT-NAMES] [--extra-plugins EXTRA-PLUGINS] [--no-init] [--no-plugin-node] [--node-version NODE-VERSION]` +* `install [--moodle MOODLE] [--data DATA] [--repo REPO] [--branch BRANCH] [--plugin PLUGIN] [--db-type DB-TYPE] [--db-user DB-USER] [--db-pass DB-PASS] [--db-name DB-NAME] [--db-host DB-HOST] [--db-port DB-PORT] [--not-paths NOT-PATHS] [--not-names NOT-NAMES] [--extra-plugins EXTRA-PLUGINS] [--no-init] [--no-nvm] [--no-plugin-node] [--node-version NODE-VERSION]` Install everything required for CI testing @@ -1015,6 +1015,16 @@ Prevent PHPUnit and Behat initialization * Is negatable: no * Default: `false` +#### `--no-nvm` + +Prevent nvm installation + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--no-plugin-node` Prevent Node.js plugin dependencies installation diff --git a/src/Command/InstallCommand.php b/src/Command/InstallCommand.php index 827bf0ed..5d182166 100644 --- a/src/Command/InstallCommand.php +++ b/src/Command/InstallCommand.php @@ -102,6 +102,7 @@ protected function configure(): void ->addOption('not-names', null, InputOption::VALUE_REQUIRED, 'CSV of file names to exclude', $names) ->addOption('extra-plugins', null, InputOption::VALUE_REQUIRED, 'Directory of extra plugins to install', $extra) ->addOption('no-init', null, InputOption::VALUE_NONE, 'Prevent PHPUnit and Behat initialization') + ->addOption('no-nvm', null, InputOption::VALUE_NONE, 'Prevent nvm installation') ->addOption('no-plugin-node', null, InputOption::VALUE_NONE, 'Prevent Node.js plugin dependencies installation') ->addOption('node-version', null, InputOption::VALUE_REQUIRED, 'Node.js version to use for nvm install (this will override one defined in .nvmrc)', $node); } @@ -175,6 +176,7 @@ public function initializeInstallerFactory(InputInterface $input): InstallerFact $factory->dumper = $this->initializePluginConfigDumper($input); $factory->pluginsDir = $pluginsDir; $factory->noInit = $input->getOption('no-init'); + $factory->noNvm = $input->getOption('no-nvm'); $factory->noPluginNode = $input->getOption('no-plugin-node'); $factory->nodeVer = $input->getOption('node-version'); $factory->database = $resolver->resolveDatabase( diff --git a/src/Installer/InstallerFactory.php b/src/Installer/InstallerFactory.php index 5421a356..2d2d8b20 100644 --- a/src/Installer/InstallerFactory.php +++ b/src/Installer/InstallerFactory.php @@ -33,6 +33,7 @@ class InstallerFactory public ConfigDumper $dumper; public ?string $pluginsDir; public bool $noInit; + public bool $noNvm; public bool $noPluginNode; public ?string $nodeVer; @@ -52,7 +53,7 @@ public function addInstallers(InstallerCollection $installers): void } $installers->add(new PluginInstaller($this->moodle, $this->plugin, $this->pluginsDir, $this->dumper)); - $installers->add(new VendorInstaller($this->moodle, $this->plugin, $this->execute, $this->noPluginNode, $this->nodeVer)); + $installers->add(new VendorInstaller($this->moodle, $this->plugin, $this->execute, $this->noPluginNode, $this->nodeVer, $this->noNvm)); if ($this->noInit) { return; diff --git a/src/Installer/VendorInstaller.php b/src/Installer/VendorInstaller.php index 3f1fb3bc..dc89d994 100644 --- a/src/Installer/VendorInstaller.php +++ b/src/Installer/VendorInstaller.php @@ -15,6 +15,7 @@ use MoodlePluginCI\Bridge\Moodle; use MoodlePluginCI\Bridge\MoodlePlugin; use MoodlePluginCI\Process\Execute; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Process\Process; /** @@ -25,6 +26,7 @@ class VendorInstaller extends AbstractInstaller private Moodle $moodle; private MoodlePlugin $plugin; private Execute $execute; + private bool $noNvm; private bool $noPluginNode; public ?string $nodeVer; /** @@ -38,17 +40,22 @@ class VendorInstaller extends AbstractInstaller * @param Execute $execute * @param string|null $nodeVer */ - public function __construct(Moodle $moodle, MoodlePlugin $plugin, Execute $execute, bool $noPluginNode, ?string $nodeVer) + public function __construct(Moodle $moodle, MoodlePlugin $plugin, Execute $execute, bool $noPluginNode, ?string $nodeVer, bool $noNvm) { $this->moodle = $moodle; $this->plugin = $plugin; $this->execute = $execute; $this->nodeVer = $nodeVer; $this->noPluginNode = $noPluginNode; + $this->noNvm = $noNvm; } public function install(): void { + if ($this->canInstallNvm()) { + $this->getOutput()->step('Installing nvm'); + $this->installNvm(); + } if ($this->canInstallNode()) { $this->getOutput()->step('Installing Node.js'); $this->installNode(); @@ -85,10 +92,38 @@ public function install(): void public function stepCount(): int { return 2 + // Normally 2 steps: global dependencies and Moodle npm dependencies. + ($this->canInstallNvm() ? 1 : 0) + // Plus nvm installation. ($this->canInstallNode() ? 1 : 0) + // Plus Node.js installation. ((!$this->noPluginNode && $this->plugin->hasNodeDependencies()) ? 1 : 0); // Plus plugin npm dependencies step. } + /** + * Check if we have to install nvm. + * + * @return bool + */ + public function canInstallNvm(): bool + { + return !$this->noNvm; + } + + /** + * Install nvm. + */ + public function installNvm(): void + { + $nvmDir = "{$this->moodle->directory}/.nvm"; + (new Filesystem())->mkdir($nvmDir); + $cmd = "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | NVM_DIR=\"{$nvmDir}\" bash"; + $process = $this->execute->passThroughProcess( + Process::fromShellCommandline($cmd, $this->moodle->directory, null, null, null) + ); + if (!$process->isSuccessful()) { + throw new \RuntimeException('nvm installation failed.'); + } + putenv('NVM_DIR=' . $nvmDir); + } + /** * Check if we have nvm to proceed with Node.js installation step. * diff --git a/tests/Installer/VendorInstallerTest.php b/tests/Installer/VendorInstallerTest.php index 432715da..bfd41901 100644 --- a/tests/Installer/VendorInstallerTest.php +++ b/tests/Installer/VendorInstallerTest.php @@ -27,7 +27,8 @@ public function testInstall() new MoodlePlugin($this->pluginDir), new DummyExecute(), false, - null + null, + false, ); $installer->install(); @@ -41,7 +42,8 @@ public function testInstallNodeNoNvmrc() new MoodlePlugin($this->pluginDir), new DummyExecute(), false, - null + null, + false, ); // Remove .nvmrc @@ -53,6 +55,22 @@ public function testInstallNodeNoNvmrc() $this->assertSame('lts/carbon', file_get_contents($this->moodleDir . '/.nvmrc')); } + public function testInstallNoNvm() + { + $installer = new VendorInstaller( + new DummyMoodle($this->moodleDir), + new MoodlePlugin($this->pluginDir), + new DummyExecute(), + false, + null, + true, + ); + + $installer->install(); + + $this->assertSame(3, $installer->getOutput()->getStepCount()); + } + public function testInstallNodeUserVersion() { $userVersion = '8.9'; @@ -61,8 +79,10 @@ public function testInstallNodeUserVersion() new MoodlePlugin($this->pluginDir), new DummyExecute(), false, - $userVersion + $userVersion, + false, ); + $installer->installNode(); // Expect .nvmrc containing user specified version. @@ -77,14 +97,15 @@ public function testInstallNodePluginDependencies() new MoodlePlugin($this->pluginDir), new DummyExecute(), false, - null + null, + false, ); $this->fs->copy(__DIR__ . '/../Fixture/plugin/package.json', $this->pluginDir . '/package.json'); $installer->install(); - $this->assertSame(4, $installer->getOutput()->getStepCount()); + $this->assertSame(5, $installer->getOutput()->getStepCount()); } public function testSkipNodePluginDependencies() @@ -94,13 +115,14 @@ public function testSkipNodePluginDependencies() new MoodlePlugin($this->pluginDir), new DummyExecute(), true, - null + null, + false, ); $this->fs->copy(__DIR__ . '/../Fixture/plugin/package.json', $this->pluginDir . '/package.json'); $installer->install(); - $this->assertSame(3, $installer->getOutput()->getStepCount()); + $this->assertSame(4, $installer->getOutput()->getStepCount()); } }