2026-09-08 11:27:26 +02:00
2023-05-21 22:12:45 +02:00
2026-09-04 11:24:21 +02:00
2026-09-07 13:11:36 +00:00
2026-09-07 13:11:36 +00:00
2026-09-08 11:27:26 +02:00

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:

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.

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:

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:

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

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

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.

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.

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
Description
No description provided
Readme MIT 334 KiB
Languages
Rust 100%