Execute "npm" from Laravel application in Laravel Forge-Setup

Viewed 829

I developed a tool, which generates a scss-file, runs npm run prod and uploads the generated app.css to a server.

Locally this was no problem. I simply ran shell_exec('npm run production') and it worked successfully.

Now, today, I rolled out the tool to a DigitalOcean-Server managed by Laravel Forge. Now, the execution of the NPM-command doesn't work anymore. Instead I see this error when running npm run production:

ERR! file sh
npm ERR! path sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm 

When I now change the script to npm -v I see this:

6.9.0

Anybody know, how to get NPM running here?

(production is an alias for this command: cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js)

Update:

Now, I tried to use yarn. Running yarn install worked fine. The result was expected. But running yarn run development this following error occurs:

2020-11-07 20:03:55 STDOUT: yarn run v1.17.3
2020-11-07 20:03:55 STDOUT: $ npm run development
2020-11-07 20:03:55 STDERR: /bin/sh: 1: 
2020-11-07 20:03:55 STDERR: npm: not found
2020-11-07 20:03:55 STDERR: error Command failed with exit code 127.
2020-11-07 20:03:55 STDOUT: info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

If I run yarn run development manually through ssh in the exact same directory, everything works fine.

Update 2: I thought, maybe the problem is, that Laravel is executed by another user than me in SSH. But both are the user forge.

1 Answers

I've found two possible solutions for that.

One of them in a question about shell_exec and npm from a few years ago with the solution that $PATH doesn't include /bin when called as an npm script so it fails to find /bin/sh per error file sh (not my words, see the original question). Their solution was to symlink /bin/sh to /usr/bin/sh:

ln -s /bin/sh /usr/bin/sh

The other solution might be by using Symony's Process:

use Symfony\Component\Process\Process;

(new Process(['npm', 'run', 'production'], base_path()))
    ->setTimeout(null)
    ->run(function ($type, $output) {
        $this->output->write($output);
    });

Both solutions aren't tested since I currently don't have access to a DigitalOcean server or Laravel Forge account.

Related