Skip to content

Commit d50ba7e

Browse files
authored
Merge pull request #14 from DevDavido/development
Bump to v1.0.8
2 parents 05b8014 + f7885ec commit d50ba7e

10 files changed

+231
-226
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 1.0.8
4+
- Improved: Refactored plugin base class, additionally removed now unneeded update classes
5+
- Fixed: Updates could remove Node dependencies which now get reinstalled (regression bug)
6+
- Fixed: Plugin cleanup now removes also symlinked directories
7+
38
## 1.0.7
49
- Improved: Plugin check is now also running pre-checks first
510
- Improved: Plugin check is now independent of regular audit flow

Controller.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@ public function version()
4040
*/
4141
public function pluginCheck()
4242
{
43-
$plugin = new PerformanceAudit();
43+
$installer = new NodeDependencyInstaller();
4444

4545
$error = '';
4646
try {
47-
$plugin->checkInternetAvailability();
48-
$plugin->checkDirectoriesWriteable();
49-
$plugin->checkNpm();
50-
$plugin->checkNpmDependencies();
47+
Helper::checkDirectoriesWriteable(['Audits', 'node_modules']);
48+
49+
$installer->checkInternetAvailability();
50+
$installer->checkNpm();
51+
$installer->checkNpmDependencies();
5152
} catch (Exception $exception) {
5253
$error = $exception->getMessage();
5354
}

Helper.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* Matomo - free/libre analytics platform
4+
*
5+
* @link https://matomo.org
6+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7+
*/
8+
9+
namespace Piwik\Plugins\PerformanceAudit;
10+
11+
require PIWIK_INCLUDE_PATH . '/plugins/PerformanceAudit/vendor/autoload.php';
12+
13+
use Piwik\Plugins\PerformanceAudit\Exceptions\DirectoryNotWriteableException;
14+
15+
class Helper
16+
{
17+
/**
18+
* Check if certain directories are writeable.
19+
*
20+
* @param array $directories
21+
* @return void
22+
* @throws DirectoryNotWriteableException
23+
*/
24+
public static function checkDirectoriesWriteable(array $directories)
25+
{
26+
clearstatcache();
27+
foreach ($directories as $directory) {
28+
$directoryPath = realpath(__DIR__ . DIRECTORY_SEPARATOR . $directory);
29+
if (!is_writable($directoryPath)) {
30+
throw new DirectoryNotWriteableException($directoryPath . ' needs to be a writeable directory.');
31+
}
32+
}
33+
}
34+
}

NodeDependencyInstaller.php

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<?php
2+
/**
3+
* Matomo - free/libre analytics platform
4+
*
5+
* @link https://matomo.org
6+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7+
*/
8+
9+
namespace Piwik\Plugins\PerformanceAudit;
10+
11+
require PIWIK_INCLUDE_PATH . '/plugins/PerformanceAudit/vendor/autoload.php';
12+
13+
use Exception;
14+
use Piwik\Log;
15+
use Piwik\Plugins\PerformanceAudit\Exceptions\DependencyOfChromeMissingException;
16+
use Piwik\Plugins\PerformanceAudit\Exceptions\DependencyUnexpectedResultException;
17+
use Piwik\Plugins\PerformanceAudit\Exceptions\InstallationFailedException;
18+
use Piwik\Plugins\PerformanceAudit\Exceptions\InternetUnavailableException;
19+
use Piwik\SettingsPiwik;
20+
use RecursiveDirectoryIterator;
21+
use RecursiveIteratorIterator;
22+
23+
class NodeDependencyInstaller
24+
{
25+
const MINIMUM_NPM_VERSION = 6.13;
26+
const MINIMUM_CHROME_VERSION = 54.0;
27+
28+
/**
29+
* Run checks and if no error occurs install dependencies.
30+
*
31+
* @return bool
32+
*/
33+
public function install()
34+
{
35+
try {
36+
Helper::checkDirectoriesWriteable(['Audits', 'node_modules']);
37+
38+
$this->checkInternetAvailability();
39+
$this->checkNpm();
40+
$this->installNpmDependencies();
41+
$this->checkNpmDependencies();
42+
} catch (Exception $exception) {
43+
Log::error('Unable to install Node dependencies.', ['exception' => $exception]);
44+
45+
throw new InstallationFailedException('Node.js dependency installation failed due to the following error: ' . PHP_EOL . $exception->getMessage());
46+
}
47+
48+
return true;
49+
}
50+
51+
/**
52+
* Uninstall dependencies.
53+
*
54+
* @return bool
55+
*/
56+
public function uninstall()
57+
{
58+
$filesToDelete =
59+
new RecursiveIteratorIterator(
60+
new RecursiveDirectoryIterator(__DIR__ . DIRECTORY_SEPARATOR . 'node_modules', RecursiveDirectoryIterator::CURRENT_AS_FILEINFO | RecursiveDirectoryIterator::KEY_AS_PATHNAME | RecursiveDirectoryIterator::SKIP_DOTS),
61+
RecursiveIteratorIterator::CHILD_FIRST
62+
);
63+
64+
foreach ($filesToDelete as $file) {
65+
if ($file->getFilename() === '.gitkeep') {
66+
continue;
67+
}
68+
$action = ($file->isFile() || $file->isLink()) ? 'unlink' : 'rmdir';
69+
if (!$action($file->getPathname())) {
70+
return false;
71+
}
72+
}
73+
74+
unlink(__DIR__ . DIRECTORY_SEPARATOR . 'package-lock.json');
75+
76+
return true;
77+
}
78+
79+
/**
80+
* Check if NPM (from Node.js) is installed.
81+
*
82+
* @return void
83+
* @throws DependencyUnexpectedResultException
84+
*/
85+
public function checkNpm()
86+
{
87+
$npmVersion = $this->checkDependency('npm', ['-v']);
88+
if ($npmVersion < self::MINIMUM_NPM_VERSION) {
89+
throw new DependencyUnexpectedResultException('npm needs to be at least v' . self::MINIMUM_NPM_VERSION . ' but v' . $npmVersion . ' is installed instead.');
90+
}
91+
}
92+
93+
/**
94+
* Check if Chrome is properly installed.
95+
*
96+
* @return void
97+
* @throws DependencyUnexpectedResultException
98+
*/
99+
public function checkNpmDependencies()
100+
{
101+
$lighthouse = new Lighthouse();
102+
$chromeVersion = $this->checkDependency($lighthouse->getChromePath(), ['--version']);
103+
if ($chromeVersion < self::MINIMUM_CHROME_VERSION) {
104+
throw new DependencyUnexpectedResultException('Chrome needs to be at least v' . self::MINIMUM_CHROME_VERSION . ' but v' . $chromeVersion . ' is installed instead.');
105+
}
106+
}
107+
108+
/**
109+
* Check if dependency is installed.
110+
*
111+
* @param string $executableName
112+
* @param array $args
113+
* @return float
114+
* @throws DependencyUnexpectedResultException
115+
*/
116+
private function checkDependency($executableName, $args)
117+
{
118+
$executablePath = ExecutableFinder::search($executableName);
119+
$process = new Process(array_merge([$executablePath], $args));
120+
$process->run();
121+
122+
if (!$process->isSuccessful()) {
123+
$errorOutput = $process->getErrorOutput();
124+
throw (stristr($errorOutput, 'libX11-xcb')) ?
125+
new DependencyOfChromeMissingException() :
126+
new DependencyUnexpectedResultException(ucfirst($executableName) . ' has the following unexpected output: ' . PHP_EOL . $errorOutput);
127+
}
128+
129+
return floatval(trim(preg_replace('/[^0-9.]/', '', $process->getOutput())));
130+
}
131+
132+
/**
133+
* Install Puppeteer + Lighthouse and its dependencies.
134+
*
135+
* @return string
136+
* @throws DependencyUnexpectedResultException
137+
*/
138+
private function installNpmDependencies()
139+
{
140+
$npmPath = ExecutableFinder::search('npm');
141+
// Puppeteer + Lighthouse
142+
$process = new Process([$npmPath, 'install', '--quiet', '--no-progress', '--no-audit', '--force', '--only=production', '--prefix=' . __DIR__, 'puppeteer@^3.0', 'lighthouse@^6.0']);
143+
$process->run();
144+
145+
if (!$process->isSuccessful()) {
146+
throw new DependencyUnexpectedResultException('NPM has the following unexpected output: ' . PHP_EOL . $process->getErrorOutput());
147+
}
148+
149+
return trim($process->getOutput());
150+
}
151+
152+
/**
153+
* Check if internet connection is required.
154+
*
155+
* @return void
156+
* @throws InternetUnavailableException
157+
*/
158+
public function checkInternetAvailability()
159+
{
160+
if (!SettingsPiwik::isInternetEnabled()) {
161+
throw new InternetUnavailableException('Internet needs to be enabled in order to install plugin dependencies.');
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)