Files
ahab/README.md

132 lines
4.6 KiB
Markdown

# ahab
A wrapper around `docker compose` for our dockerized django projects, so the
same commands work in every repository.
## installing
You will need rust installed. Clone repo and run:
```bash
cargo install --path .
```
## seeing what it runs
`-v` prints each docker command as it runs, and `--dry-run` prints the ones it
would run without running them. Both work before or after the subcommand.
```bash
ahab -v django test
ahab --dry-run postgres import ./dump
```
## shell completion
`ahab completions <shell>` writes a completion script to stdout, for bash,
elvish, fish, powershell or zsh:
```bash
ahab completions zsh > ~/.local/share/zsh/completions/_ahab
eval "$(ahab completions zsh)" # or one line in .zshrc, never goes stale
```
Completions are also generated during the build. They land in
`target/*/build/*/out/` by default, and `SHELL_COMPLETIONS_DIR_<SHELL>`
installs a single shell's file straight into place:
```bash
SHELL_COMPLETIONS_DIR_ZSH=~/.local/share/zsh/completions \
SHELL_COMPLETIONS_DIR_FISH=~/.config/fish/completions \
cargo install --path .
```
`SHELL_COMPLETIONS_DIR` writes every shell into one directory instead.
## the compose file
`ahab` does not pass `-f`. docker compose finds the file itself, so set
docker's own `COMPOSE_FILE` when it is not in the working directory, including
its `base.yaml:override.yaml` form. A project's `.env` is a good place for it,
since docker reads that too:
```
COMPOSE_FILE=docker/docker-compose.yaml
```
## service detection
`ahab` finds the services it needs in `docker compose config`, so they can be
named anything:
- **postgres**: the service whose image is a postgres flavour, matching
`postg`, `timescale`, `pgvector` or `citus`
- **django**: the service that both builds an image and sets
`DJANGO_SETTINGS_MODULE`. A worker sharing the same build and `env_file`
matches too, so the one publishing ports wins
If nothing matches, or two candidates cannot be told apart, `ahab` says so and
lists the services it looked at rather than guessing.
`POSTGRES_USER` and `POSTGRES_DB` are read off the detected postgres service, so
`dropdb`, `createdb`, `pg_restore` and `pg_dump` use the role and database the
project declares, falling back to `db` when it declares neither.
## django
```bash
ahab django run <cmd> # in a fresh container, through the entrypoint
ahab django bash # shell in a fresh container
ahab django manage <args> # manage.py
ahab django makemigrations
ahab django migrate <args>
ahab django shell
ahab django test
ahab django make-command <app> <name>
```
## postgres
```bash
ahab postgres dump <path> # pg_dump, custom format
ahab postgres dump -F plain <path> # pg_dump, plain sql
ahab postgres dump -F tar <path> # pg_dump, tar
ahab postgres dump -F directory <d> # pg_dump, a directory of files
ahab postgres dump -F cluster <path> # pg_dumpall, roles and all databases
ahab postgres dump -F plain -z <path> # any of them, gzipped
ahab postgres import <path> # drop, create, then restore
```
The format of a dump being imported is read from the file rather than its name.
A custom format dump starts with `PGDMP` and a tar one with `toc.dat`, both of
which go to `pg_restore`, as does a directory produced by `pg_dump -Fd`.
Anything else is treated as sql and fed to `psql` with `ON_ERROR_STOP` and
`--single-transaction`, so a bad file rolls back instead of half applying.
A whole cluster dump from `pg_dumpall` is recognised by its header and handled
differently again: it creates its own databases and carries role statements, so
the database is dropped but not recreated, the dump goes to `psql` connected to
`postgres`, and it runs without `ON_ERROR_STOP` because roles that already exist
report errors that are expected. Gzipped dumps are decompressed on the way in,
whichever of the three they hold.
## link
`ahab link` moves untracked paths out of the repository into an out-of-repo
store and symlinks them back, so a sandbox that mounts the repository sees a
dangling symlink instead of the contents, while the host resolves it as before.
```bash
ahab link add .env secrets/ # move out, leave symlinks behind
ahab link check # what a sandbox can still read
ahab link check --porcelain # `<code> <path>`, for scripts
```
The store lives under
`${XDG_DATA_HOME:-$HOME/.local/share}/ahab/<host>/<owner>/<repo>/`, derived
from the git `origin` remote.
## configuration
Currently `ahab` respects the following environment variables.
- `AHAB_LINK_ROOT`: root of the out-of-repo store `ahab link` moves paths into - defaults to `${XDG_DATA_HOME:-$HOME/.local/share}/ahab`