DEV Community

Daniel Rotter
Daniel Rotter

Posted on • Originally published at danielrotter.at

Automatic command execution on file changes with entr

One of the best features of Jest (the JavaScript testing library) is its
watch mode
, which can be activated by adding the --watch flag to the call
of the jest command. So if the jest executable is in your current path, you can call it like that:

jest --watch
Enter fullscreen mode Exit fullscreen mode

And what this simple flag does is absolutely amazing. Instead of just executing all the tests and shut down, Jest
will execute only the tests being affected by the currently not staged changes of your git repository.
That means you
don't have to execute all the tests all the time, which will result in much less load on your machine. At the same time
only the necessary tests run and finish much sooner, which will result in a much better developer experience.
Additionally, Jest will watch all your files, and rerun your tests as soon as a file within your codebase changes.

A watch mode for PHPUnit

Actually I was so fascinated by this feature, that I wanted to have something similar for
PHPUnit, my PHP testing framework of choice. Unfortunately I did not find any built-in solution,
but I found another interesting generic-purpose tool called entr. Based on this
I could build something I would call a poor man's watch mode. Just recently I had to resolve a merge conflict in a
PHP file; luckily the error was also caught by a unit test. Since the unit test failed, and I knew which file was
causing the error, it was easily possible to rerun the unit test everytime the content of that PHP file changed.

ls path/to/source-file.php | entr phpunit --filter=path/to/test-file.php
Enter fullscreen mode Exit fullscreen mode

ls in the above example will only return the path to source-file.php and pass that value as input to the entr
command. The entr command will add a file watch to this file, and execute the command it gets passed everytime
source-file.php changes its content. That's already a poor man's watch mode! And it was really useful in that case,
because I didn't have to change to another terminal everytime I wanted to run the tests. I just had two terminals open,
and when I changed the content of source-file.php in order to resolve the conflict in one terminal, the other
terminal automatically ran the tests without any manual instruction from my side.

But the ls command is only the easiest way to get a list of files entr should watch. A more powerful alternative is
the find command, that is also distributed with almost every linux installation. So we can use the following command
if we want to run the entire phpunit testsuite when any file ending with Test.php is updated:

find . -name "*Test.php" | entr phpunit
Enter fullscreen mode Exit fullscreen mode

Mind that this might not make a lot of sense if you have a huge test suite, since waiting an hour after everytime you
touch a PHP file would be a waste of time (so is waiting too long for your tests, so you might refactor that suite
anyway).

You can even use other powerful tools like ack to get the list of files entr should
start watching. If you e.g. want to run your entire test suite whenever a file is changed, which instantiates a Media
object (again, not sure this is a very good use case, but I still want to show how it would work, so that you
understand it better) your command would look like this:

ack "new Media()" -l | entr phpunit
Enter fullscreen mode Exit fullscreen mode

The -l flag of the ack command will only list the files containing this content, not the content itself. It is
necessary, because otherwise entr get more than just a list of entries, and it wouldn't be able to handle that.

Of course you can also combine this new knowledge with any of your previous knowledge of your shell. But keep in mind
that this is still not as powerful as Jest's watch feature. That is because Jest can analyze the JavaScript code, and
will even be able to tell which files are importing a changed file, and include that information when finding the tests
that are affected by this change.

Reuse that watch mode with other commands

Since we have found now a more generic solution to this problem (which comes with the just mentioned downsides) we
are able to also reuse that command in other situations
. Something that I found very helpful is to automatically
create new HTML output when I am building one of my
markdown presentations. All
I have to do to make this work is to write a command like this, which will then run and create a new HTML output
whenever my slides.md file changes:

ls slides.md | entr make html
Enter fullscreen mode Exit fullscreen mode

So while this approach is not as powerful as having a watch mode fully integrated in some other tools, I still like to
have it in my toolbelt, since I can apply it to many different situations in which a watch mode might not be
available
.

If you are curious now and you want to play with entr, you can easily install it in ArchLinux using pacman:

pacman -S entr
Enter fullscreen mode Exit fullscreen mode

I am sure that other linux distributions also have an entr package, so check it out and happy entring!

Discussion (0)