ddev / ddev

Docker-based local PHP+Node.js web development environments
https://ddev.com
Apache License 2.0
2.61k stars 589 forks source link

Add global phpcs and drupal/coder to ddev in custom Dockerfile #2173

Closed amitaibu closed 4 years ago

amitaibu commented 4 years ago

Describe the bug

Following the examples in https://www.ddev.com/ddev-local/customizing-ddev-local-images-with-a-custom-dockerfile/ and the example Docker file:

# You can copy this Dockerfile.example to Dockerfile to add configuration
# or packages or anything else to your webimage
ARG BASE_IMAGE=drud/ddev-webserver:v1.13.0
FROM $BASE_IMAGE
RUN npm install --global gulp-cli

I tried to composer global require drupal/coder.

This didn't work as expected, as it installs for root

image

However the user that will later try to invoke it, isn't root.

To Reproduce

Add Dockerfile under .ddev/web-build with

ARG BASE_IMAGE=drud
FROM $BASE_IMAGE
RUN composer global require drupal/coder:8.3.5 --verbose
RUN composer global require dealerdirect/phpcodesniffer-composer-installer --verbose
RUN export PATH="$PATH:$HOME/.composer/vendor/bin"
RUN phpcs --config-set installed_paths ~/.composer/vendor/drupal/coder/coder_sniffer

It will error out on phpcs

image

If Instead I install it manually after the container is up with ddev composer global require... it works as-expected. So I suspect it has to do with the USER used to run the commands.

rfay commented 4 years ago

Absolutely everything in Dockerfile build time (for all Dockerfiles everywhere) happens as root. composer global require should just be using root's home directory as a temporary storage place.

In your case, phpcs isn't really being installed globally, it's being installed in root's composer cache.

I see you're not the first person to encounter this, see https://unix.stackexchange.com/questions/280846/find-composer-global-install-path-as-root

I think the first thing to try is to symlink into /usr/local/bin

rfay commented 4 years ago

I'll take a look at this in the morning. But the basic issue is that composer's "install global" means "not site-install". It doesn't mean "make available for the machine", like it would everywhere else. But all that should be required is a symlink IIRC.

amitaibu commented 4 years ago

Thanks. Not sure how to create the symlink (as also the user doesn't have access to root's files), so I'd be happy for some help here :)

Once we figure this out, I'll create a PR to add that to the Dockerfile.example

amitaibu commented 4 years ago

(btw, in that example I should probably use cgr instead)

rfay commented 4 years ago

Here's what we used to do when we used cgr to install drush (I DO NOT recommend this)

RUN composer global require consolidation/cgr
RUN /home/.composer/vendor/bin/cgr drush/drush:$DRUSH_VERSION && ln -s /home/.composer/vendor/bin/drush /usr/local/bin/drush

So that approach will probably work. But the bottom line is that composer isn't a very good tool for installing things globally.

What I recommend is just using the instructions at https://github.com/squizlabs/PHP_CodeSniffer#installation to install with phar file. So much easier. Install it in /usr/local/bin. This is how we install drush8 these days:

RUN curl -sSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8 && chmod +x /usr/local/bin/drush8

BTW, I don't think the Dockerfile.example is a very good place for a complex item like this. Stack Overflow, ddev-contrib, or even adding it to that article are good places.

amitaibu commented 4 years ago

@rfay I see. But one more thing I didn't figure out yet - what about getting the specific coder sniffer for Drupal?

So we could do for example:

RUN composer global require consolidation/cgr
RUN ~/.composer/vendor/bin/cgr drupal/coder:^8.3.1
RUN ~/.composer/vendor/bin/cgr dealerdirect/phpcodesniffer-composer-installer

Then would we symlink them? If so what would be the correct place to do that?

amitaibu commented 4 years ago

BTW, I don't think the Dockerfile.example is a very good place for a complex item like this

:+1:

rfay commented 4 years ago

The install doc provides a lot of options, https://www.drupal.org/docs/8/modules/code-review-module/installing-coder-sniffer - It seems you can manually set the paths, phpcs --config-set installed_paths ~/.composer/vendor/drupal/coder/coder_sniffer, so that means you can composer-install drupal/coder as you wish and then point to it.

amitaibu commented 4 years ago

Sorry, I've actually tried it - and it doesn't work:

ARG BASE_IMAGE=drud
FROM $BASE_IMAGE

# We try to avoid when possible relying on composer to download global, so in PHPCS case we can use the phar.
RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o /usr/local/bin/phpcs.phar
RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar -o /usr/local/bin/phpcbf.phar

# If however we need to download a package, we use `cgr` for that.
RUN composer global require consolidation/cgr
RUN ~/.composer/vendor/bin/cgr drupal/coder:^8.3.1
RUN ~/.composer/vendor/bin/cgr dealerdirect/phpcodesniffer-composer-installer

# Register Drupal's code sniffer rules.
RUN php /usr/local/bin/phpcs.phar --config-set installed_paths ~/.composer/global/drupal/coder/coder_sniffer --verbose

I believe the reason it doesn't work, is that when I use it as a non-root, it no longer sees those files. It just sees the default ones:

$ php /usr/local/bin/phpcs.phar -i

The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz and Zend
rfay commented 4 years ago

Sorry, the way to work on this is not to do it in a Dockerfile. It's to do it in the running container. Or a running Debian container might be easiest. It's way to hard to work in a Dockerfile when you can't tell whether what is going to happen. But each of these steps can be taken one at a time inside the container, and it's much more satisfying. I'd be happy to do a zoom or whatever with you and we can work through this together.

Where I'd start is docker run -it drud/ddev-webserver:v1.13.1 bash and proceed from there. It's a much more experimental setup and lets you figure out what's going on and solve the problems one at a time.

rfay commented 4 years ago

If you want to zoom right now we could. Or whenever it works for you. Ping me in slack either to give it a try or to set an appointment.

rfay commented 4 years ago

You were almost there. The path given for the phpcs --config-set installed_paths in the drupal.org article was incorrect, but there was still the problem of permissions in ~root. There are many ways to solve this, but here's what I did, using COMPOSER_HOME as /usr/local/composer during the build stage.

What I did was to work interactively in docker run -it --rm drud/ddev-webserver:v1.13.1 bash (or docker run -it --rm drud/ddev-webserver:v1.13.1-<projectname>-built bash which has everything already working in your add-on Dockerfile working in it.

ARG BASE_IMAGE
FROM $BASE_IMAGE

ENV COMPOSER_HOME=/usr/local/composer

# We try to avoid when possible relying on composer to download global, so in PHPCS case we can use the phar.
RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o /usr/local/bin/phpcs && chmod +x /usr/local/bin/phpcs
RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar -o /usr/local/bin/phpcbf && chmod +x /usr/local/bin/phpcbf

# If however we need to download a package, we use `cgr` for that.
RUN composer global require consolidation/cgr
RUN $COMPOSER_HOME/vendor/bin/cgr drupal/coder:^8.3.1
RUN $COMPOSER_HOME/vendor/bin/cgr dealerdirect/phpcodesniffer-composer-installer

# Register Drupal's code sniffer rules.
RUN phpcs --config-set installed_paths $COMPOSER_HOME/global/drupal/coder/vendor/drupal/coder/coder_sniffer --verbose
# Make Codesniffer config file writable for ordinary users in container.
RUN chmod 666 /usr/local/bin/CodeSniffer.conf
# Make COMPOSER_HOME writable if regular users need to use it.
RUN chmod -R ugo+rw $COMPOSER_HOME
# Now turn it off, because ordinary users will want to be using the default
ENV COMPOSER_HOME=""
$ ddev ssh
rfay@d8composer-web:/var/www/html/web$ phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz, Zend, Drupal and DrupalPractice
amitaibu commented 4 years ago

@rfay this works great, thank you. Would you like me to add it in Stackoverflow or ddev-contrib?

rfay commented 4 years ago

Yay! Yes, please. ddev-contrib has an empty section for this kind of thing, https://github.com/drud/ddev-contrib#ddevweb-builddockerfile-examples-to-customize-web-container

Stack Overflow is fine and a little easier for a quick thing. Either is fine. Thanks!

rfay commented 4 years ago

Glad it's working! Closing this for now.

amitaibu commented 4 years ago

btw, here's a PR on drupal-starter that now uses this -- https://github.com/Gizra/drupal-starter/pull/7

amitaibu commented 4 years ago

Finally got to add it to StackOverflow -- https://stackoverflow.com/questions/61870801/add-global-phpcs-and-drupal-coder-to-ddev-in-custom-dockerfile/61870802#61870802

rfay commented 4 years ago

Thanks!

cballenar commented 4 months ago

I recently had to set this up and a few things have changed, mainly:

Here's the latest config that worked for me:

## Install PHPCS Globally
ENV COMPOSER_HOME="/usr/local/composer"

# We try to avoid relying on Composer to download global, so in `phpcs` case we can use the PHAR.
RUN curl -L https://phars.phpcodesniffer.com/phpcs.phar -o /usr/local/bin/phpcs && chmod +x /usr/local/bin/phpcs
RUN curl -L https://phars.phpcodesniffer.com/phpcbf.phar -o /usr/local/bin/phpcbf && chmod +x /usr/local/bin/phpcbf

# Allow installation of known package plugins
RUN composer global config --no-plugins allow-plugins.drupal/coder true
RUN composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true

# Require composer packages globally
RUN composer global require drupal/coder
RUN composer global require dealerdirect/phpcodesniffer-composer-installer

## Register Drupal's code sniffer rules for phpcs.
RUN phpcs --config-set installed_paths $COMPOSER_HOME/vendor/drupal/coder/coder_sniffer,$COMPOSER_HOME/vendor/slevomat/coding-standard/SlevomatCodingStandard/,$COMPOSER_HOME/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ --verbose
## Make Codesniffer config file writable for ordinary users in container.
RUN chmod 666 /usr/local/bin/CodeSniffer.conf
## Make COMPOSER_HOME writable if regular users need to use it.
RUN chmod -R ugo+rw $COMPOSER_HOME
## Now turn it off, because ordinary users will want to be using the default
ENV COMPOSER_HOME=""

This was tested on ddev v1.23.0-2-g5cc331835 with PHPStorm and VSCode.

rfay commented 4 months ago

@cballenar thanks for this!

Could you please edit your post to say what version of DDEV you changed for? (v1.23.0?)

Could you also add a comment with a link on https://stackoverflow.com/questions/61870801/add-global-phpcs-and-drupal-coder-to-ddev-in-custom-dockerfile/61870802#61870802 ? (Or edit it if you have permissions)

emircanerkul commented 3 months ago

@cballenar your snipped not working

PHP Fatal error:  Uncaught Error: Class "Symfony\Component\Yaml\Yaml" not found in /usr/local/composer/vendor/drupal/coder/coder_sniffer/Drupal/Sniffs/InfoFiles/AutoAddedKeysSniff.php:59
sergiirepin commented 1 month ago

@rfay Are where general way to install composer package globally ? For example how can I utilize composer autocomplete for magento ?

rfay commented 1 month ago

Hi @sergiirepin - Please open a new issue when you have a new issue. But the approach would be the same as here, a custom Dockerfile that does

RUN composer global require bamarni/symfony-console-autocomplete

https://ddev.readthedocs.io/en/stable/users/extend/customizing-images/#adding-extra-dockerfiles-for-webimage-and-dbimage

sergiirepin commented 1 month ago

@rfay thanks! I will add an issue, but I am not sure if it is a ddev issue :) Need some guide probablly, it is not clear how to configure the autocomplete: shell itself in WSL but PHP in docker.

rfay commented 1 month ago

You'll be using it inside ddev ssh, inside the web container, not on the WSL2 shell.

sergiirepin commented 1 month ago

@rfay Thanks! The question in SO now. :)