Skip to content

Commit

Permalink
Fix executable node/mjml-cli path checks
Browse files Browse the repository at this point in the history
A regression in 00db9a9 broke setups
that depended on PATH environment variables to execute the given node
and MJML CLI binaries (`MJML_NODE_PATH` and `MJML_CLI_PATH` environment
configurations).

Various combinations with `npx`, `nvm`, `pnpm dlx` broke.

This commit introduces a more intelligent check using `is_executable`
and all PATH environment prefixes that should guarantee that an
executable will still still be found. A convenient wrapper has been
introduced via `MJMLService::isExecutable`.

Fixes #26
  • Loading branch information
soulseekah committed Jun 26, 2024
1 parent 259a1ae commit 343fbd2
Showing 1 changed file with 43 additions and 4 deletions.
47 changes: 43 additions & 4 deletions src/services/MJMLService.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ public function parseCli(?string $html = null): ?MJMLModel

$view->setTemplateMode($oldTemplateMode);

// Check if Node.js exists
if (!file_exists($nodePath)) {
// Check if Node.js exists and is executable
if (!$this->isExecutable($nodePath)) {
throw new InvalidConfigException("Node.js executable not found at path: {$nodePath}");
}

// Check if MJML CLI exists
if (!file_exists($mjmlCliPath)) {
// Check if MJML CLI exists and is executable
if (!$this->isExecutable($mjmlCliPath)) {
throw new InvalidConfigException("MJML CLI executable not found at path: {$mjmlCliPath}");
}

Expand Down Expand Up @@ -211,4 +211,43 @@ protected function executeShellCommand(string $command): string

return $shellCommand->getOutput();
}

/**
* Checks whether a give path is executable.
*
* Looks through the PATH environment variables to find
* the binary unless an absolute path has been supplied.
*
* Makes sure the binary is executable.
*
* Caches a positive result to prevent redundant stat calls
* when sending large amounts of emails in one go.
*
* @param $path The executable path
*
* @return bool
*/
protected function isExecutable(string $path): bool
{
static $executables = [];

if ($executables[$path] ?? false) {
return true; // Cached from previous checks
}

if (is_executable($path)) {
$executables[$path] = true; // Cache for next checks
return true;
}

// Look through PATH
foreach (explode(PATH_SEPARATOR, getenv("PATH")) as $prefix) {
if (is_executable($prefix.DIRECTORY_SEPARATOR.$path)) {
$executables[$path] = true; // Cache for next checks
return true;
}
}

return false; // Not found or not executable
}
}

0 comments on commit 343fbd2

Please sign in to comment.