Streamlining Development with Custom Shell Functions: Reusing a command with a custom config for each project

| 3 min read

As a freelance technical agile coach, working across multiple projects means dealing with a variety of tech stacks. Every client has their own preferred tools, some with Docker, some without, and often there are custom setups or hacks. Over time, managing these different workflows can become mentally exhausting. You don’t want to waste time remembering specific commands for each environment or tool every time you start a project.

To solve this, I’ve created custom shell functions to simplify and streamline my workflow. These helpers sit on top of the usual tools, allowing me to invoke commands without remembering intricate details or worrying about specific configurations for each project.

Here’s how I did it.

Example: Custom Shell Function Command for PHPUnit

Here’s an example of a custom bash function I’ve written for PHPUnit. This function is placed in my shell alias file.

phpunit() {
if [ -f "./my-conf" ]; then
cmd=$(grep ^PHPUNIT ./my-conf | awk -F"PHPUNIT=" '{print $2}')
fi

if [ ! -z "$cmd" ]; then
eval $cmd $@
elif [ -f "my-phpunit.xml" ]; then
vendor/bin/phpunit -c my-phpunit.xml $@
else
vendor/bin/phpunit $@
fi
}

Breaking it down:

  1. Check for a custom configuration file: The function first looks for a file called my-conf in the current project directory. This file can override the default behavior by specifying custom commands.
  2. Extract custom PHPUnit command: If my-conf contains a line starting with PHPUNIT=, the script grabs that line and uses awk to extract the command that follows. This allows me to define a custom PHPUnit command per project if necessary. For instance, some projects require running PHPUnit inside a Docker container; I just don’t want to have to think about that.
  3. Execute the custom command if found: If a custom PHPUnit command is found, it is executed using eval.
  4. Fallbacks to default behavior:
    • If no custom command is found, the function checks for a my-phpunit.xml configuration file and runs PHPUnit with that. I don’t remember why I put that in, as it supposed to be the default PHPUnit behavior. No code is perfect, and at least you get the idea if you want to include some configuration in your command by default.
    • If neither is available, it runs the default PHPUnit command from the vendor folder.

This setup allows me to run PHPUnit across multiple projects without having to remember specific configurations for each one.

I’ve created such function for every tool that is reused accross project

The my-conf File: Customizing Behavior for Each Project

In each project, I can create a my-conf file that overrides the default behavior for specific tools. This file is included in my global .gitignore, so it doesn’t get committed to version control.

Here’s an example of what a my-conf file might look like for a Laravel project using Sail and Docker:

PHPSTAN=sail php ./vendor/bin/phpstan --memory-limit=-1  
RECTOR=docker compose -f my-docker-compose.yml run --rm -it rector ./bin/rector
COMPOSER=sail composer
PHPUNIT=./bin/phpunit

In this example, I have custom commands for various tools like PHPStan, Rector, Composer, and PHPUnit. This file allows me to run these commands seamlessly without having to think about whether I need to use Sail or Docker in a specific project.

Why Use Custom Shell Functions?

Using custom shell functions gives me several key advantages:

  • Consistency: I can run the same command across different projects without worrying about project-specific details.
  • Efficiency: With these helpers in place, I don’t have to spend mental energy remembering how each tool is configured in each project.
  • Flexibility: I can easily adapt to new client setups by modifying the my-conf file without touching the global setup.
  • Collaboration: Teams can have their own configurations, while I keep my personalized workflow without impacting theirs.

Conclusion

By creating custom shell functions for tools like PHPUnit, Composer, PHPStan, and others, I’ve streamlined my workflow significantly. These helpers give me the flexibility to work across multiple projects with different setups, while maintaining a consistent and efficient workflow.

If you’re working across multiple projects and stacks like me, this method can save you valuable time and mental energy. And if your team isn’t quite ready to adopt these shortcuts, no worries — you can keep them in your local environment without disrupting the broader team workflow.

Want to streamline your team’s workflow or improve your technical agility? Book a session with me here and let’s discuss how we can make your development process smoother.