mani, a CLI Tool to Manage Multiple Repositories
TL;DR - Mani is a many-repo tool that helps you manage multiple repositories. It's useful when you are working with microservices, multi-project systems, or just a bunch of personal repositories and want a central place for pulling all repositories and running commands over them.
In mani, you specify repositories and commands in a config file and then run the commands over all or a subset of the projects.
Don't worry, this post isn't going to be about the old age debate of whether you should be using a mono- or a many-repo setup. I think there's a place for both, and the two approaches have their pros and cons.
Anyway, even if you're using a mono-repo in your current workplace, you're probably using a many-repo setup for your projects (work, personal projects, etc.), so you still might find Mani useful (or some of the alternatives).
Mani came about because I needed a CLI tool to manage multiple repositories, both at work and for my projects. The premise is, you have a bunch of repositories and want the following:
- a central place for your repositories, containing name, URL, and a small description of the repository
- ability to clone all repositories in 1 command
- ability to run ad-hoc and custom commands (perhaps
git status
to see working tree status) on 1, a subset, or all of the repositories - ability to get an overview of 1, a subset, or all of the repositories and commands
Mani also standardizes one of the recurring patterns that I've seen in the workplace: on your first day, you're given access to an organization on Github, and perhaps if you're lucky, there's an outdated bash script which pulls some of the repositories you're supposed to work on. Other times, you're simply given a README document with links to all the projects you're supposed to visit manually and clone.
Usage
Let's install Mani, binaries are available on Github release page, or you can install it via cURL (only Linux & MacOS):
curl -sfL https://raw.githubusercontent.com/alajmo/mani/main/install.sh | sh
There's also additional install methods in manicli.com.
Now, let's say you have a bunch git repositories:
$ tree -L 2
.
├── frontend
│ ├── dashgrid
│ └── pinto
└── template-generator
We'll start by initializing Mani in this directory:
$ mani init
Initialized mani repository in /home/samir/tmp/init
- Created mani.yaml
- Created .gitignore
Following projects were added to mani.yaml
Project | Path
--------------------+--------------------
init | .
dashgrid | frontend/dashgrid
pinto | frontend/pinto
template-generator | template-generator
Let's see the content of the two files generated:
$ cat mani.yaml
projects:
init:
path: .
dashgrid:
path: frontend/dashgrid
url: https://github.com/alajmo/dashgrid.git
pinto:
path: frontend/pinto
url: https://github.com/alajmo/pinto.git
template-generator:
url: https://github.com/alajmo/template-generator.git
tasks:
hello:
desc: Print Hello World
cmd: echo "Hello World"
$ cat .gitignore
# mani #
template-generator
frontend/pinto
frontend/dashgrid
# mani #
the mani init
command created a mani.yaml
file that contains our repositories as well as an example task named hello-world
and a .gitignore
file that contains all the projects. This is because when we initialize this directory as a git repository (which is recommended, as other users can simply clone this repository and then run mani sync
to clone all repositories), we want to prevent the directories to be added to our Mani repository.
Mani has a bunch of different sub-commands that helps us view and manage our repositories:
# Open mani.yaml in your preferred editor
$ mani edit
# List projects
$ mani list projects
Project
-------------------
init
dashgrid
pinto
template-generator
# List repositories in a tree-like format
$ mani list projects --tree
┌─ frontend
│ ├─ dashgrid
│ └─ pinto
└─ template-generator
# Describe all tasks
$ mani describe tasks
Name: hello
Description: Print Hello World
Theme: default
Target:
All: false
Cwd: false
Projects:
Paths:
Tags:
Spec:
Output: text
Parallel: false
IgnoreError: false
OmitEmpty: false
Cmd:
echo "Hello World"
Now, let's adds some tags and descriptions to our projects, and some new tasks:
projects:
example:
path: .
desc: A mani example
pinto:
path: frontend/pinto
url: https://github.com/alajmo/pinto.git
desc: A vim theme editor
tags: [frontend, node]
template-generator:
url: https://github.com/alajmo/template-generator.git
desc: A simple bash script used to manage boilerplates
tags: [cli, bash]
env:
branch: master
themes:
custom:
table:
options:
draw_border: true
separate_columns: true
separate_header: true
separate_rows: true
tasks:
git-status:
desc: show working tree status
cmd: git status
git-last-commit-msg:
desc: show last commit
cmd: git log -1 --pretty=%B
git-last-commit-date:
desc: show last commit date
cmd: |
git log -1 --format="%cd (%cr)" -n 1 --date=format:"%d %b %y" \
| sed 's/ //'
git-branch:
desc: show current git branch
cmd: git rev-parse --abbrev-ref HEAD
npm-install:
desc: run npm install in node repos
target:
tags: [node]
cmd: npm install
git-overview:
desc: show branch, local and remote diffs, last commit and date
theme: custom
commands:
- task: git-branch
- task: git-last-commit-msg
- task: git-last-commit-date
Run git-status
task and target projects with the tag bash
:
$ mani run git-status --tags bash
TASK [git-status: show working tree status] **************************
template-generator | On branch master
template-generator | Your branch is up to date with 'origin/master'.
template-generator |
template-generator | nothing to commit, working tree clean
Name: git-status
Description: Show git status
Shell: sh -c
Env:
Command: git status
Run task git-status
for repositories under the frontend
directory:
TASK [git-status: show working tree status] *************
pinto | On branch main
pinto | Your branch is up to date with 'origin/main'.
pinto |
pinto | nothing to commit, working tree clean
Run another task that has multiple commands and display the output in a table:
$ mani run git-overview -t bash -d frontend/ -o table
+--------------------+------------+-------------------------------------------+-----------------------------------+
| Project | Git-Branch | Git-Last-Commit-Msg | Git-Last-Commit-Date |
+--------------------+------------+-------------------------------------------+-----------------------------------+
| pinto | main | Update readme | 22 Mar 22 (6 weeks ago) |
| | | | |
+--------------------+------------+-------------------------------------------+-----------------------------------+
| template-generator | master | Edit command should work without argument | 24 Jan 20 (2 years, 3 months ago) |
| | | | |
+--------------------+------------+-------------------------------------------+-----------------------------------+
Now if we want to execute an ad-hoc command, for instance, count the number of files in all projects, we can use the mani exec
sub-command:
$ mani exec --all --output table --parallel 'find . -type f | wc -l'
Project | Output
--------------------+--------
example | 486
pinto | 361
template-generator | 42
Conclusion
I hope you find this intro to Mani useful, and if you want to learn more, head to github.com/alajmovic/mani.
Alternatives
Mani isn't the first of its kind, check out the following alternatives!