2

I am developing a PHP web application inside of a Docker container. Using volumes: inside of my docker-compose.yml file, I have specified a local directory so that any files generated are dumped and persist after the container is destroyed.

volumes:
  - ./docroot:/var/www/html

Inside my Dockerfile, I RUN a command that installs a command line management tool:

RUN curl -sS https://getcomposer.org/installer | php && \
    mv composer.phar /usr/local/bin/composer && \
    ln -s /root/.composer/vendor/bin/drush /usr/local/bin/drush

RUN composer global require drush/drush:8.3.3 && \
    composer global update

When the container comes up, I can use docker-compose exec -it <container> bash to get inside the container, and everything works fine. drush is in my path, and I can use it globally throughout the container to manage the app.

Now here is the strange part. Part of my application is that I have to run that command from a PHP script inside the container to help automatically manage some of the build process.

Using php, I run exec('drush dbupdate', $output, $retval); $retval returns a exit status of 127, or command not found and $output is empty. If I switch up the exec to use the full path I get an exit status 126.

If I go back into the container, I can run that command just fine. Note all other cli commands work as expected with exec (ls, whoami, etc but which drush returns exist status 1)

What am I missing? Why can I use it with no problems manually, but PHP exec() can't find it? passthru(), shell_exec(), and others have the same behavior.

yivi
  • 42,438
  • 18
  • 116
  • 138
user658182
  • 2,148
  • 5
  • 21
  • 36
  • Have you tried `chmod +x /usr/local/bin/drush`? Maybe some permissions or different users (you login as root, but php runs from different user) – Justinas Jun 01 '21 at 11:56
  • ah, we're getting somewhere. whoami shows the user www-data, but I thought how I had it installed from above, it could be used by all users. – user658182 Jun 01 '21 at 12:01
  • Yup, but it's not. Refer to the answer below on why it is. :) – yivi Jun 01 '21 at 12:02

1 Answers1

3

composer global install will not install the command "globally" for all users, but "globally" as in "for all projects".

Generally, these packages are installed in the home directory for the user executing the command (e.g. ~/.composer), and if they are available in your path is because ~/.composer/vendor/bin is added to the session path.

But when you run composer global require (while building the image) or when you "log in" to the running container (using exec [...] bash) the user involved is root. But when your PHP script runs, it's being executed by another user (presumably www-data). And for that user, ~/.composer does not contain anything.

Maybe do not install drush using composer, but rather download the PHAR file directly or something like that while you are building the image, and put it in /usr/local/bin.

If you are using Drupal >= 8, the recommended way of installing Drush is not as a "global" dependency, but as "project" dependency, so that the appropriate drush version is installed. This comes straight from the docs:

It is recommended that Drupal 8 sites be built using Composer, with Drush listed as a dependency. That project already includes Drush in its composer.json. If your Composer project doesn't yet depend on Drush, run composer require drush/drush to add it. After this step, you may call Drush via vendor/bin/drush

yivi
  • 42,438
  • 18
  • 116
  • 138
  • curious to know more why the ln -s ... /usr/local/bin/drush isn't working as expected. I'm indeed a fan of dl'ing the .phar file, but Drupal is SO composer dependent these days I was hesitant to go down that route. If I look at the permissions on the file they are owned by root:root 777. How does what I wrote not achieve the same as mv drush.phar /usr/local/bin/drush? – user658182 Jun 01 '21 at 12:17
  • I don't know if the composer installation gets you a self-contained phar archive you can move around. And the recommended way of installing drush is via composer, but not globally but locally to the project (so dependencies match). And if you want to use the same call path in all projects, use something like "drush-ops/drush-launcher" for that. – yivi Jun 01 '21 at 12:25