From cb845e88c267b15651ef27c5bdf6868b28f076ca Mon Sep 17 00:00:00 2001 From: root Date: Sun, 1 Feb 2026 01:48:39 +0200 Subject: [PATCH] Fix npm build perms during upgrade --- VERSION | 2 +- .../Commands/Jabali/UpgradeCommand.php | 82 +++++++++++++++---- tests/Unit/UpgradeCommandTest.php | 4 + 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/VERSION b/VERSION index 36fd23f..7ed0dac 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -VERSION=0.9-rc35 +VERSION=0.9-rc36 diff --git a/app/Console/Commands/Jabali/UpgradeCommand.php b/app/Console/Commands/Jabali/UpgradeCommand.php index 8d062b4..200cc49 100644 --- a/app/Console/Commands/Jabali/UpgradeCommand.php +++ b/app/Console/Commands/Jabali/UpgradeCommand.php @@ -185,21 +185,29 @@ class UpgradeCommand extends Command try { $this->ensureCommandAvailable('npm'); $this->ensureNpmCacheDirectory(); - $npmInstall = File::exists($this->basePath.'/package-lock.json') ? 'npm ci' : 'npm install'; - $installResult = $this->executeCommand($npmInstall, 1200); - if ($installResult['exitCode'] !== 0) { - throw new Exception($installResult['output'] ?: 'npm install failed.'); - } - if ($installResult['output'] !== '') { - $this->line($installResult['output']); - } + $this->ensureNodeModulesPermissions(); + if (! $this->isNodeModulesWritable()) { + $this->warn('Skipping frontend build because node_modules is not writable by the current user.'); + $this->warn('Run: sudo chown -R www-data:www-data '.$this->getNodeModulesPath()); + } else { + $npmInstall = File::exists($this->basePath.'/package-lock.json') ? 'npm ci' : 'npm install'; + $installResult = $this->executeCommand($npmInstall, 1200); + if ($installResult['exitCode'] !== 0) { + throw new Exception($installResult['output'] ?: 'npm install failed.'); + } + if ($installResult['output'] !== '') { + $this->line($installResult['output']); + } - $buildResult = $this->executeCommand('npm run build', 1200); - if ($buildResult['exitCode'] !== 0) { - throw new Exception($buildResult['output'] ?: 'npm build failed.'); - } - if ($buildResult['output'] !== '') { - $this->line($buildResult['output']); + $buildResult = $this->executeCommand('npm run build', 1200); + if ($buildResult['exitCode'] !== 0) { + throw new Exception($buildResult['output'] ?: 'npm build failed.'); + } + if ($buildResult['output'] !== '') { + $this->line($buildResult['output']); + } + + $this->ensureNodeModulesPermissions(); } } catch (Exception $e) { $this->error('Asset build failed: '.$e->getMessage()); @@ -428,6 +436,7 @@ class UpgradeCommand extends Command $paths = [ $this->basePath.'/database', $this->basePath.'/storage', + $this->getNodeModulesPath(), $this->getNpmCacheDir(), $this->getPuppeteerCacheDir(), $this->getXdgCacheDir(), @@ -465,11 +474,56 @@ class UpgradeCommand extends Command } + protected function ensureNodeModulesPermissions(): void + { + $nodeModules = $this->getNodeModulesPath(); + + if (! File::isDirectory($nodeModules)) { + return; + } + + if ($this->isRunningAsRoot() && $this->userExists('www-data')) { + $escaped = escapeshellarg($nodeModules); + $this->executeCommand("chgrp -R www-data {$escaped}"); + $this->executeCommand("chmod -R g+rwX {$escaped}"); + $this->executeCommand("find {$escaped} -type d -exec chmod g+s {} +"); + + return; + } + + $this->executeCommand('chmod -R u+rwX '.escapeshellarg($nodeModules)); + } + + protected function isNodeModulesWritable(): bool + { + $nodeModules = $this->getNodeModulesPath(); + + if (! File::isDirectory($nodeModules)) { + return true; + } + + $binPath = $nodeModules.'/.bin'; + if (! is_writable($nodeModules)) { + return false; + } + + if (File::isDirectory($binPath) && ! is_writable($binPath)) { + return false; + } + + return true; + } + protected function getNpmCacheDir(): string { return $this->basePath.'/storage/npm-cache'; } + protected function getNodeModulesPath(): string + { + return $this->basePath.'/node_modules'; + } + protected function getPuppeteerCacheDir(): string { return $this->basePath.'/storage/puppeteer-cache'; diff --git a/tests/Unit/UpgradeCommandTest.php b/tests/Unit/UpgradeCommandTest.php index 2c7884c..4622887 100644 --- a/tests/Unit/UpgradeCommandTest.php +++ b/tests/Unit/UpgradeCommandTest.php @@ -132,6 +132,10 @@ class UpgradeCommandTest extends TestCase $paths = [ base_path().'/database', base_path().'/storage', + base_path().'/node_modules', + base_path().'/storage/npm-cache', + base_path().'/storage/puppeteer-cache', + base_path().'/storage/.cache', base_path().'/bootstrap/cache', ];