Logo Projects Blog

Managing multiple dev projects

Spinup logo

At work I often have to have two or even more projects open at the same time while developing because one for example calls the an API of another. Since we moved from full stack javascript frameworks over to Laravel and React using Inertia this means having to run two commands to start each project. I think you can imagine this becoming a bit of a hassle.

Initial solutions

Initially I used tmux (a terminal multiplexer that lets you manage multiple sessions within a single window, awesome tool btw) to avoid having multiple terminal windows open, which I had to navigate through to restart or stop a specific development server. This approach simplified things slightly, but I still had to manually start each server. Then I discovered the concurrently package for node, which allowed me to run multiple commands simultaneously in one terminal window. This was an improvement, but I still needed to change directories for each project and run the correct concurrently command from my history. Running multiple Laravel instances also had it's challenge as each instance defaults to port 8000, with others launched at the same time running on ports 8001, 8002 and so on. The port for each Laravel instance depends on the start order, making it hard to remember which one to use in the browser for which project. Additionally, if one project needed to call another project's API, the port had to match the one defined in the .env file. This was far from ideal.

The goal

I wanted an easier way to start multiple projects and not have to remember which port each project ran on. Having some experience with nginx I set up a few config files that had a .test domain and had a reverse proxy to a certain port. This improved the part of trying to guess which port belonged to which project but the issue of starting the projects in the correct order or with the correct command to specify the port was still present.

Creating a CLI

The solution I came up with was to create a CLI that could start any project with a simple command. Certain commands were the same across projects, such as npm run dev or php artisan serve. I started with writing a simple CLI in Go to read these reusable commands from a configuration file. Next I added functionality to read configuration for projects from a separate file, where each project had a list of command names that should be executed when the project would be started. This allowed me to type something like spinup run project1 to execute the commands defined in the configuration file for project1. Additionally I added a port property to each project that could be used in a command by templating in the following way:

php artisan serve --port={{port}}

This way I could define the port in the configuration file and use it in the command. This made it possible to start multiple projects with a single command and have them run on the same port each time.

Adding configuration and automation

Next I added the ability to create, edit and delete commands and projects via the command line instead of having to go to the config file and editing it manually. This made it a lot easier to manage projects and commands. Having to update entries in the hosts file and all the nginx config files was annoying so I automated updating the hosts file and relevant nginx configuration file when changing anything about any project.

Removing concurrently dependency

The CLI was still calling concurrently under the hood to run multiple commands simultaneously. This meant is was required to be installed globally, which was not that great so I switched to using sync.WaitGroup to run the commands concurrently. sync.WaitGroup is a built in feature of Go that makes it possible to wait for a collection of asynchronous tasks to finish executing. This means the program stops when all commands that are being run are done. In the case of processes that continue until stopped using Ctrl+c or any other way we have to send a signal to each process in the wait group to terminate.

Removing concurrently from the program meant I could build the CLI into a single binary that could be run from anywhere when placed in a bin directory. Running a project is now as simple as this:

Spinup CLI output

Conclusion

This CLI has made it much easier to manage multiple projects and has saved me a considerable amount of time. I can now start any project with a single command and have it run in the right location and have it available on a local test domain.

If you are interested in the project you can find it on GitHub.