Yake — Yet Another maKe clonE
Let us invite you to the CLI (command-line interface) world. How often do you need an alias for some long commands? How often do you need to duplicate some (sometimes the same) bash commands from one project to another? How often do you copy & paste long command lines and edit only one word before execution?
Do these questions sound familiar? It was a huge challenge for us too!
The background
When we have started using Docker as a stack for our project, we ran into some CLI issues. Let’s say we have a Docker Container called webapp
. It’s a simple web server application, with some Backend stuff. To execute basic commands on the Backend, like database migration or create a new user, we have to reach the Backend CLI first.
Step 1: Log in to the container
$ docker-compose exec webapp bash
Step 2: Execute a CLI command
$ docker-compose exec webapp bash
root@1802asdf6d:/# ./manage.py migrate
Step 3: We can improve it a bit by inserting a full command directly to the “exec”
docker-compose exec webapp ./manage.py migrate
Not bad, right?
Step 4: We can also have a CLI tool inside the Container with some extra params required to make it work
docker-compose exec webapp php composer.phar show monolog/monolog
In this case, it would be great to have at least a webapp
alias, to use:
webapp php composer.phar show monolog/monolog
But how about getting one step further and create one more alias called composer
, to be able to use:
composer show monolog/monolog
Instead of the full command?
There is a nice solution for that: creating a Bash “aliases” and add them globally. This way you can reuse them for each project. But what happens if the container has a different name? For example app
instead of webapp
?
There is an opportunity to add a Bash alias locally inside the project. In this case, you have to be sure you “import” such aliases each time you want to use them. But as we know, It’s not something that developers like to do.
Here you might have said that there are some dedicated tools already exist. For example, Make?
Make — see daylight
Make is a dedicated CLI tool to compile applications from the source code. It’s usually used for the C language projects. It has a local (per project) text file called “Makefile” with all the commands required to build the app (aliases). This is actually all that we need!
Let’s have a look at Makefile sample (please don’t focus on not so clear code lines like “%: ; @:”):
We can execute a short command using:
make composer CMD=”show monolog/monolog”
And it will work.
What are the issues with Make?
First of all, Make is a tool to build a low-level language code. It has some features and limitations for this job like tasks auto execution feature (next command on the list is executed when the previous one succeeded) or lack of inserting extra command to the task during the execution (see a “CMD
” hack), and a few more drawbacks.
Moreover, it’s not always installed by default in the system. You have no extra advantage to using it because it requires installing additional software.
And the final disadvantage: the Makefile structure is not intuitive. Using it isn’t a pleasure, as you need to overwrite a CMD
each time.
Are there more ways to work with this challenge? — Yes.
NodeJS way
NodeJS becomes more and more popular and the JS developers run into the same “aliases” issues like us. How do they solve it?
packages.json
file has a “scripts” section where we can define a custom “aliases” to the longer commands. Moreover, it supports console auto-completion, extra params injection and task preview feature. It’s actually all we want to have.
But, there are some difficulties. You can’t just handle a NodeJS tool to manage Docker-based projects. One of the reasons could be no NodeJS environment in some projects. Furthermore, “scripts” was developed to help with NodeJS environment, not the Docker one. Mixing both of them in the same file may be hard to maintain.
Do we have a solution for this challenge? — and Yes again!
It’s time for Yake
As we didn’t find any worth solution through the existing ones, we decided to create a separate tool — Yake. First of all to manage commands for Docker-based projects, but also for all projects where any “aliases” are needed. Yake is a free to use, open source Perl & Bash script, developed by Amsterdam Standard.
We’ve combined Make
features, package.json
flexibility and implemented it into the one bash-based CLI environment.
Yake
is a shell script, installed globally, which loads a local directory’s Yakefile
YAML file, where we can define all the aliases. The flow is the same like on Make
.
Once the Yake
is installed, you can use a yake
command to execute local-defined aliases.
The most important features:
- Recursive support (one command can run the other one inside)
- Full native bash support, including CLI interactive and I/O (tasks are executed directly on the bash environment, no proxy or capsulation)
- Variables support (commands can be modified dynamically using pre-defined ENVs)
- Auto-completion for Bash and Zsh
- Able to debug (show) command before execution
Let’s have a look at the Docker example.
We have a webapp
command defined with $NAME
ENV (webapp
value by default). To enter the Container’s bash shell, you can run:
yake webapp bash
You can also overwrite the “$NAME” ENV by custom Container name:
yake NAME=webapp-dev webapp bash
Thanks to recursive commands and native Bash support, you can run the CLI Backend tool with all necessary params:
yake manage user:create — role=”admin” — force
All the bash error response codes and I/O operations are supported.
Yake — behind the scenes
Yake is actually 2 separate scripts: Perl Core and Bash runner.
Perl Core is responsible for loading a local YAML file and create a full bash command. It reads all the aliases and processes them. Array commands are merged, all the internal VARs are injecting. It has also some HOWTO knowledge included in the source code.
Bash runner passes the user’s command to the Core, and run it or display the result.
Why only Bash or only Perl is not used? Unfortunately, Perl doesn’t support a native Bash script execution (e.g. I/O interaction). But Bash doesn’t go well with YAML files and processing the array of chars.
We also tried to use Go-lang, but it doesn’t support a native Bash command execution, along with all other non-Bash languages.
Installation
The only requirement to install Yake is to have Perl
installed. For all popular OS Perl is installed by default. In some cases (you will be notified by Yake installer), cpanm
(cpanminus) package could be required to install:
curl -L https://cpanmin.us | perl — App::cpanminus
Then you can install Yake:
curl -L https://yake.amsdard.io/install.sh | sudo -E bash
To summarize
It was a pleasure for us to develop Yake. We are using it now for all our projects and we enjoy it. Managing project aliases isn’t a nightmare anymore. What are we going to do next? We’ve made the Yake public and currently, we are collecting feedback from users to improve it as much as possible. Please, feel free to leave yours in the comment field below! Further, we are planning to deploy Yake into the Linux repository and Brew package to provide users the simplest way of installing it.
To find out some extra examples please check the Yake official website:
Enjoy Yake along with us!