Self-Updating Composer in Production using Laravel Envoy

I was deploying a site today with Laravel Envoy and noticed that Composer was complaining about using a build older than 30 days old. Logging in manually and running the self-update is totally doable, but why not use Envoy to make the update? I added the following to my deployment script:

@task('deploy', ['on' => 'web'])
    composer self-update
    git reset --hard
    git checkout master
    git pull origin master
    composer install --no-dev
@endtask

However, I quickly found that the web user didn’t have the appropriate permissions to update Composer when installed globally to /usr/local/bin/composer.

[[email protected]]: [Composer\Downloader\FilesystemException]                                        
  Filesystem exception:                                                            
  Composer update failed: the "/usr/local/bin/composer" file could not be written

The user does have sudo ability, though, so I tried prefixing the command with sudo. Unfortunately the command then prompted for a password which Envoy can’t handle. To get around this issue I dug in deeper and modified the sudoers file:

$ sudo visudo
[sudo] password for <user>:

Inside the file I added a line which allows any user in the ‘sudo’ group to run composer as root without it requesting a password. That line looks something like this:

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL
%sudo   ALL=(ALL) NOPASSWD: /usr/local/bin/composer

This new line is pretty cryptic, so let’s break it down.

%sudo Group name, must be prefixed with % to signify this rule is for a group. Without the % it will be considered a user name.
ALL= Host list. This gives access to all hosts and can be restricted down to only certain specified IPs or hosts.
(ALL) Operator list. This is a list of users the sudoer must be running the command as.
NOPASSWD: Tag list. In this case, we specify to not request a password on execution.
/usr/local/bin/composer Command list. The command(s) that are available to be executed with root privileges.

This can be set to “ALL” to allow access to all commands (dangerous!)

Set to “/usr/local/bin/composer self-update” to only allow the user to run the self-update command.

You can set the parameter to “/usr/local/bin/composer self-update,/usr/local/bin/composer install” to allow the user to self-update or install with root privileges.

Lastly, “/usr/local/bin/composer s*” would allow the user to run search, self-update, show, and status with root privileges. Pretty cool!

I saved the file and then re-ran my Envoy script…voila!

$ envoy run deploy
[[email protected]]: Updating to version 0c85ca426d6e8235366340f29ad0e6aab1747b83.
    Downloading: 100%         
Use composer self-update --rollback to return to version 1efa02a7ab43b7fc555dccb7d31294acbc62bbeb
...