Make Capistrano use alias on sever when running scripts - php

I have the following problem using Capistrano with laravel:
My hosting provider does not provide a cli php version via php but only via a usr/bin/local/.../PHP-CLI command
I did create an alias for it in my .bash_profile so running composer install from the cli is no problem.
However, Capistrano (as far as I understand due to it starting in a very basic shell http://capistranorb.com/documentation/faq/why-does-something-work-in-my-ssh-session-but-not-in-capistrano/) does not load this alias, so I get an error from the composer scripts e.g. php artisan.
However, on my dev machine I need to keep it as php, since this is where php is here.
How can I solve this problem best? Any more info you need? Thanks.
Just in case it helps, this is how I call the script:
desc 'Composer install'
task :composer_install do
on roles(:app), in: :groups, limit:1 do
execute "/usr/local/bin/php5-56STABLE-CLI composer.phar install --working-dir #{fetch(:release_path)}"
execute "cp #{fetch(:deploy_to)}/shared/.env #{fetch(:release_path)}/.env"
end
end

It sounds like your scenario is the perfect fit for Capistrano's "command map" feature, as documented here: https://github.com/capistrano/sshkit#the-command-map.
Here are the two main takeaways:
Write your Capistrano execute commands so that the binary name (php) is a separate argument. This will allow it to be substituted using the command map. For example:
execute :php, "composer.phar install --working-dir #{fetch(:release_path)}"
In your Capistrano deployment config, tell the command map how to substitute the :php command, like this:
SSHKit.config.command_map[:php] = "/usr/local/bin/php5-56STABLE-CLI"
If you want this substitution to affect all deployment environments, place it in deploy.rb. If it only applies to your production environment, then put it in production.rb.

Okay, my current workaround is the following:
in your capistrano deploy.rb in the script that you execute at deploy update.
desc 'Composer install'
task :composer_install do
on roles(:app), in: :groups, limit:1 do
execute "/usr/local/bin/php5-56STABLE-CLI /path/to/composer.phar install --working-dir #{fetch(:release_path)} --no-scripts"
execute "cd #{fetch(:release_path)} && /usr/local/bin/php5-56STABLE-CLI artisan clear-compiled"
execute "cd #{fetch(:release_path)} && /usr/local/bin/php5-56STABLE-CLI artisan optimize"
end
end
end
after "deploy:updated", "deploy:composer_install"
I am not 100% sure if the artisan clear-compiled is needed. Anyway, those 2 are composer scripts that would normally be called via composer, but the --no-scripts flag keeps them from being called, so that it does not fail on install. When calling them from capistrano, I can easily change which php to use, as you can see.
However if anyone has a better solution, please let me know.

Related

Running composer dump-autoload in a controller does nothing

I'm attempting to run composer dump-autoload -o -d C:\laragon\www\site inside a Laravel controller using the following (or similar - see below):
$command = 'composer dump-autoload -o -d ' . base_path();
exec($command, $output);
But when doing this, output is always null/empty, and the status code is always 1. No errors in either the Laravel or PHP logs. The composer files in vendor/composer do not get updated. I have also triple checked that the directory exists, even removing the -d parameter, which made no difference. Running composer dump-autoload in the terminal manually, works as expected, and with no errors.
I have also tried system(), passthru(), shell_exec, but all exhibit similar behaviour.
Additionally, since I'm running the latest Laravel, I have access to Illuminate\Support\Composer. I've tried the below code, but again it behaves exactly as the above code:
use Illuminate\Support\Composer;
// Attempt 1
app(Composer::class)->dumpAutoloads();
// Attempt 2
app(Composer::class)->dumpOptimized();
I've been playing with this for a couple hours now, and have been using these sources as reference:
https://laracasts.com/discuss/channels/laravel/how-to-composer-dump-autload-in-php
Run composer dump-autoload from controller in laravel 5
EDIT:
After manually adding logging to the $callback parameter inside the Composer::dumpAutoloads() helper in laravel, I've found that I'm getting the following error:
local.DEBUG: 'composer' is not recognized as an internal or external command, operable program or batch file.
Composer works in all my other terminals (Windows Terminal, and the embedded PHPStorm terminal). I've also confirmed that Composer is in my environment variables, and that the path for that variable is valid and is the composer bin folder: C:\ProgramData\ComposerSetup\bin, Composer is also installed for all users.
Right now I'm wondering if this is some "weird" behaviour on Windows and how it determines what user runs the process when initiated by PHP (or what environment variables the process can see). Unfortunately, I don't have a ready-to-go Linux environment to test this against. I'm aware the Composer helper in Laravel uses the Symfony process classes under the hood, perhaps I'm missing something here? Not sure why the PHP exec functions, etc, also didn't work though.

Setting up PHPunit - PHP CLI is not executing the shell script

I'm in a multi developer program. The other developer installed PHPUnit via composer and successfully is running tests. I took the composer files, ran composer update, but when I try to run the tests, I am receiving this error:
C:\Repos\Project\TDD>php vendor/bin/phpunit
Output:
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../phpunit/phpunit" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/phpunit" "$#"
This is the content of the phpunit shell script. The shebang first line #!/usr/bin/env sh is not being output, but this is the rest of the file. The problem appears to be that the PHP CLI is not executing the script, just reading it. I can't figure out how to fix that.
I am on a windows environment, and I am not running apache locally.
Things I have tried:
I have tried using full paths (eg, C:/php/php.exe) for both the php and the shell script, and that didn't matter.
I have tried reversing the slashes vendor\bin\phpunit
I can run php -v successfully, but php -f vendor/bin/phpunit still just outputs the file contents.
I have confirmed the PATH for php is accurate and restarted.
All expected vendor files do appear to be present.
Composer update is not throwing errors of any kind.
I've changed my terminal / CLI processor to use powershell instead of the default cmd.exe (same result as above)
I've confirmed encoding of the phpunit file is UTF-8 without BOM
I've tried making the php command execute the phpunit script in powershell and git bash directly with no luck --- it outputs the shell file. It seems to be that php is just not executing it, which makes me think there is a problem with my php.ini or something.
I CAN run the phpunit shell directly, such as phpunit --version, but I can't get it to run my tests.
I set the composer platform to match my cli php -v
"config": {
"platform": {
"php": "7.4.20"
}
}
Edit: Interestingly, I uploaded it all to my LAMP webserver and ran the same script there, and it is not executing there either. It's outputting the files...
There is a lot of stuff you're fiddling with while trouble-shooting your issue, so there can be only giving extended comments on it. Take it with a grain of salt and pick the pieces that make sense to you, it looks to me you're clever and should be close to the point where you connect the dots more soon than later (don't give up):
C:\Repos\Project\TDD>php vendor/bin/phpunit
This on your windows shell prompt command the php binary to execute the (php) script vendor/bin/phpunit.
And PHP does exactly this (everything else would be a surprise, right?), it executes that script.
The output you see confirms it. As php is the PHP CLI SAPI (PHP Command Line Binary), the first line starting with # is not output (you can find this behaviour described in the PHP documentation as well: https://www.php.net/manual/en/features.commandline.usage.php and on antoher place that I can't find right now that exactly describes this difference to the common mode https://www.php.net/manual/en/language.basic-syntax.phpmode.php).
You then correctly analyze:
This is the content of the phpunit shell script.
and make the note on the shebang line:
#!/usr/bin/env sh
So this looks to me that this shell script vendor/bin/phpunit is not to be executed with the given commandline:
php vendor/bin/phpunit
but instead just by:
C:\Repos\Project\TDD>vendor/bin/phpunit
Which won't (or might not) work on your windows system.
Some more background information (this applies to phpunit bin-stubs as well as other when installed as composer(1) dependencies):
composer manages these scripts (for you)
composer handles them for POSIX compatible systems as well as for others (compare the /usr/bin/env sh which is stupid, it's /bin/sh, always, may need a report on the composer project) incl. windows or windows with cygwin or WSL running on Windows, Vagrant box setups etc. pp. (yes they really care since years).
composer handles this at the time of install/update.
for windows (which I can't compare are not using it but from what I remember) composer is adding .bat or .cmd files next to the commands (as that is how Windows handles executables).
So mabye using:
C:\Repos\Project\TDD>composer exec phpunit
already solves your invocation problem.
Or
C:\Repos\Project\TDD>vendor\bin\phpunit.bat
Or
C:\Repos\Project\TDD>vendor\bin\phpunit.cmd
Or
C:\Repos\Project\TDD>vendor\bin\phpunit
does. Point in case is this: https://getcomposer.org/doc/articles/vendor-binaries.md
All in all this looks to me you got instructions from one developer working on a unixoide sytem (Mac, Linux) and you're running on Windows. This should not pose a problem on that level, however the onboarding in your team might be low or it's just the knowledge management (unfortunately after a decade or more of Stackoverflow this got worse) and you've been left alone with the trouble shooting.
It's not a programming problem, but merely getting the tooling to run.
Perhaps there is some regime about composer and running it which prevents you from looking there first. But this is hard to say on Stackoverflow.
I'd start fresh with the project, remove it from disk and install it anew. This must work, always. To not sink the current project you can do this a-new in a second directory (the project might support composer create-project to give it a quick start - that is cloning new and doing the composer install).
Similar it is that you could do a composer update in the existing project (which is intended to modify it and depending on how well the project you work with is integrated with composer and your development platform will enable or break things).
At the end of the day the common workflow is:
> rmdir vendor
(remove the vendor directory, that is the blank slate)
> composer install
(install the vendor directory, that is all PHP dependencies of the project)
> composer exec phpunit
(execute the phpunit test-runner)
Edit: Interestingly, I uploaded it all to my LAMP webserver and ran the same script there, and it is not executing there either. It's outputting the files...
Never install phpunit on a webserver system. Run it in development or CI, but not on the webserver. Period. (this has security implications, and it won't help you to execute it there, you need to run it where you develop when you're doing TDD - keep this focus for troubleshooting)
The true path to the PHP script that is represented by vendor/bin/phpunit is:
vendor/sebastianbergmann/phpunit/phpunit
(you can find it in the composer.json of phpunit https://github.com/sebastianbergmann/phpunit/blob/master/composer.json#L66)
Given your original command-line and from the various other information you give, this should work:
php vendor/sebastianbergmann/phpunit/phpunit

Composer windows compatible scripts

I'm facing a small issue here. I'm trying to use composer's script to lint my PHP project. So far what I've done is have a composer file, and enter some scripts in them. My snippet of composer file looks something like this:
"scripts": {
"document": "vendor/bin/apigen generate -s ./src -d ./docs",
"codecept": "vendor/bin/codecept",
"test": "vendor/bin/codecept run",
"lint": "vendor/bin/phpcs --standard=PSR2 src",
"bootstrap": "composer install && composer codecept bootstrap"
}
I've already defined dependencies and it works perfectly on POSIX compliant machines (MAC/Linux based OS), but when someone who is using windows wants to use any of the scripts, it simply doesn't work.
Problem I'm facing is, I've got / as directory separator but windows understands \ I thought windows was smart enough to automatically convert this.
Dirty solution what I'm thinking of would be to have windows-lint command which is really really annoying. I just wanted to know what other people are doing when they want to work on multiple OS.
Thanks in advance!
Just use scripts without specifying vendor/bin path, it will be done by composer automatically, see the Note section in https://getcomposer.org/doc/articles/scripts.md#writing-custom-commands
"scripts": {
"document": "apigen generate -s ./src -d ./docs",
"codecept": "codecept",
"test": "codecept run",
"lint": "phpcs --standard=PSR2 src",
"bootstrap": "composer install && composer codecept bootstrap"
}
I was thinking at the same problem, there may be multiple solutions.
Option 1, no shell script, pure PHP only
The shell script interpreter differs between your platforms, and it's quite hard to make something "universal" enough.
But you may use only pure PHP. Composer documents this quite well. Your PHP should be able to adapt easily things for the Windows case or for the POSIX case.
However you may not like wrapping external programs call and shell commands inside a PHP.
Option 2, run the scripts from a Linux VM
You would for example launch the Linux VM defined by a Vagrant file, and then run composer trough "vagrant ssh".
Pros:
Same shell for everyone
Defining correctly a Vagrant environment may simplify a lot of local tests by developers, removing the well-known "this works on my machine". Every developer has the "same" local environment.
Cons:
Some initial setup (including the two-side sync of the project folder with appropriate permissions), lot of examples online may help.
Running a VM locally requires enough RAM.
Option 3, use another shell than cmd on Windows
Don't use the standard shell to launch composer. There are plenty of other options, including:
cygwin (I wouldn't use it if I may avoid however)
git bash (something like MINGW32 that may be installed easily with the git client, using git is not mandatory)
I've created a composer package, which converts slashes to backslashes, if executed on Windows OS.
In your composer.json you just need to add:
{
"require": {
"instituteweb/composer-scripts": "^1.0",
},
"scripts": {
"test": [
"\\InstituteWeb\\ComposerScripts\\ImprovedScriptExecution::apply",
"vendor/bin/whatever"
]
}
}
So when you perform composer run test, it will work on Unix and Windows OS.
How it works:
The apply method in script queue, stops propagation of the original "test" command. Instead, it creates and executes a new command called "_test" (prepends underscore) which contains the original commands, just with converted slashes.
Just commands after the apply method call are affected.
Link to package: https://packagist.org/packages/instituteweb/composer-scripts

run composer and laravel (artisan) commands without ssh access

I want to test some Laravel applications on my basic shared host.
Currently I just upload my complete application including the vendor files, however this takes quite long.
Since I do not have ssh access to my host I'd like to know whether there's an option to run composer / artisan commands without this.
I found this link: Use Composer without ssh access to server (Second Answer) which describes how to run composer using http://phpshell.sourceforge.net/
However, I can change folders in the console etc. But I cannot run php commands - I always get internal server error.
Check if your shared hosting provider has console feature in their CP which allows to run shell commands. Maybe you'll be able to run commands from there.
As alternative, you could right your own artisan runner and call artisan commands from the code:
Artisan::call('migrate');
To run composer command from PHP code, use shell_exec:
shell_exec('composer update');

Run PHPUnit tests on change

I'd like to run my PHPUnit tests (or at least a subset of them) whenever a file changes on disk. Very similar to what you can do with "grunt watch". I have a project in which I have both JS and PHP, and am using Grunt. There I shell out to PHPUnit to have it run on top of my JS tests using grunt watch. While that works just fine, it seems like an awful lot of hassle to do this in a PHP only project. I'd need to introduce grunt, and add a dependency on node. Plus I have a lot of such PHP projects. A more simple solution is thus in order.
You can use node watch which is quite sweet:
Run npm init to set up a package (takes a minute), then
npm install --save-dev watch
Then edit your package.json file to include these two entries:
"scripts": {
"test": "bin/phpunit tests --color",
"test:watch": "watch 'npm run --silent test' ."
},
Then trigger the watching with:
npm run test:watch
(credit due to this interesting article)
I've wrote a blog post on this a while back: http://edorian.github.io/2010-03-11-running-your-unit-tests-everytime-you-save-a-file/
The most basic command would be:
pywatch phpunit .
which would mean "monitor all files in this directory and below for changes and if one of them changes run PHPUnit".
If you have a lot of files in there (assets etc) that can get slow so a better/faster version is:
find -name '*.php' | xargs pywatch "./runSuite.sh"
which only monitors changes to .php files
Maybe you can try phpunit-watcher. https://packagist.org/packages/spatie/phpunit-watcher
First, to install phpunit-watcher with composer require spatie/phpunit-watcher --dev, and then, modify the composer.json to add an item in scripts property:
{
"scripts" : {
"test" : "./vendor/bin/phpunit-watcher watch --bootstrap vendor/autoload.php tests/"
}
}
Then, you can run test cases with: composer test.
Sometimes, you should run composer run test --timeout=0, otherwise the process would be killed after 300 seconds.
In PHPStorm you can configure File Watcher that will execute shell commands for you at file modifications. For example you can have one File Watcher that will trigger on php file modification and will run phpunit command with all necessary commands.
More info about File Watchers can be found on JetBrains web help page: https://www.jetbrains.com/phpstorm/webhelp/using-file-watchers.html
A dead simple way to do this, using inotifywait and a bash script:
#!/bin/bash
while true; do
FILE=$(inotifywait --exclude=".*.*sw*" --exclude="4913" ./ --format "%w%f" -e close_write) &&
clear &&
phpunit --color $FILE
done
Then you just run that in a terminal (or however you want) and you're good. I'm running tmux, so I just have a stacked pane set up, with a little window to see my test results. Just run this script in the directory where your tests live.
Works well for me.
Also, this script is Vim friendly (Vim creates a file, 4913, during saves, this script ignores it).
It was totally inspired from various places around the web -- probably even from some of these answers.
Install grunt and use https://github.com/SaschaGalley/grunt-phpunit
Then you'd setup the phpunit task under with the watch task. This is how I do it right now.
I've created a solution to deal with this very problem: "What's Changed?" In short, there are cases where you have some really large test suites and you only want to run the tests relevant to the classes which have changed.
You can pull this in via composer by running composer require --dev icyapril/whats-changed
With this in place just run ./vendor/bin/whatschanged and only the file changed in your last commit or in your working directory will be run. Magic!
You can use inotifywait, see https://speakerdeck.com/rowan_m/building-better-developers-2?slide=31 for a PHPUnit example.
I found the phpunit-testrunner node package, which is of use for my problem. One config json file per PHP project, which can then be used by anyone that has the node package installed. Also cross platform. Still requires node though, so not an ideal solution.
i found this solution use node+gulp
0. install node
1. npm init
2. npm install gulp --save-dev
create a gulpfile.js and put this
var gulp = require('gulp'),
util = require('util'),
exec = require('child_process').exec;
gulp.task('phpunit', function() {
exec('phpunit -c my_project_name/App', function(error, stdout) {
util.puts(stdout);
});
});
gulp.task('default',['phpunit'], function() {
gulp.watch('my_project_name/**/*.php', ['phpunit']);
});
my_project_name/App is the path of all source code
if add extension add this line on default task
gulp.watch('my_project_name//*.otherext', ['phpunit']);
after edit php file run the phpunit test

Categories