Metarepo project and package management.
You’ve heard of monorepos? A metarepo (also referred to as a polyrepo) is similar except that it doesn’t require everything to be in a single repo. (Hat tip to the creators of Meta for both the term metarepo and for influencing Tempo.) Instead, you create a repo that references a collection of existing repos and perform operations against them together. Basically, it’s the best of both worlds: the benefits of a monorepo without the drawbacks.
npm i -g @dashkite/tempo
Usage: tempo [options] [command]
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
add|a [options] <repo> add a project to a metarepo
remove|rm [options] <repo> remove a project from a metarepo
clone [options] <metarepo> clone a metarepo
sync [options] sync a metarepo with remote
import [options] <path> import respositories from a list
exec [options] <command> [arguments...] run a command across repos
run [options] <script> [arguments...] run a saved script
tag [options] <tags...> add tags to a repository
untag [options] <tags...> remove tags from a repository
help [command] display help for command
You can create a new metarepo by simply creating a new repo.
To add another repo to your metarepo, use the add
subcommand with a GitHub repo path:
tempo add dashkite/url-codex
You can similarly remove a repo with remove
.
You can add a bunch of repos all at once with import
:
tempo import repos.yaml
The input file should contain a YAML formatted array of GitHub repo paths.
To work from an existing metarepo, use clone
with the GitHub repo path:
tempo clone vedic-dolphin
This will clone the metarepo and all the metarepos it contains.
You can sync meta repos—which is equivalent to pulling and adding/removing repos—using the sync
command.
You can run arbitrary commands across all the repos in a metarepo with exec
:
tempo exec git pull
You can also define scripts for more complex commands and run them using the run
command:
tempo run pull
Both run
and exec
allow you to use files to target a subset of repos with the targets
option:
tempo run --include core.yaml build
Targeting files should be YAML arrays with only the repo names (not the relative path).
You can also use tags to target repositories:
tempo run --tags module publish
Use the tag
and untag
commands to add and remove tags from repos. You can also target individual repositories with the --repos
option or exclude them with --exclude
which works like --include
.
Run scripts are defined in scripts.yaml
file within the .tempo
directory. Positional arguments can be applied using $
and index of the argument. You can reference all the arguments together with $@
.
For example, to define a commit command that takes a commit message, you might have a tempo.yaml
file that looks something like this:
commit: git add -A . && git commit -m '$0'
You can use jq
and yq
to query your repos and target run
or exec
commands.
For example, to update a specific dependency, we want to target repos that have that dependency. The most direct way to do that is to use exec
and the -e
option of jq
to chain the commands with &&
. However, this gets messy because of the levels of quoting involved. Instead, we would add a script, which also allows us to make it more general:
scripts:
update: jq -e '.dependencies["$0"]' package.json && pnpm add $0@latest
We can thus run:
tempo run update @dashkite/url-codex
However, sometimes the queries are more complex, in which case we can use jq
or yq
to build a list that we can pass to run
(or exec
) instead. Of course, we can use any tool to build our list, or even simply compile it manually.