bight
Patches .env files automatically on git checkout — keeping local environments in sync with the current branch.
git checkout feat-login
# bight: .env → DB_NAME=myapp_feat-login
# bight: .env → JWT_SECRET=***
Only the listed vars are touched. The rest of your .env is left untouched.
Installation
With Homebrew:
brew install andrewadev/tap/bight
With Go:
go install github.com/AndrewADev/bight@latest
From a release binary:
Download the binary for your platform from the releases page:
| Platform |
File |
| macOS (Apple Silicon) |
bight-darwin-arm64 |
| macOS (Intel) |
bight-darwin-amd64 |
| Linux (x86-64) |
bight-linux-amd64 |
| Linux (ARM64) |
bight-linux-arm64 |
| Windows |
bight-windows-amd64.exe |
Then make it executable and put it on your PATH:
chmod +x bight-darwin-arm64
mv bight-darwin-arm64 /usr/local/bin/bight
Each release includes a checksums.txt for verification:
sha256sum -c checksums.txt --ignore-missing
Trying a PR preview:
Once a maintainer has triggered the Preview workflow on a PR (see DEVELOPING.md), per-platform binaries plus a checksums.txt are attached to the run as artifacts, downloadable by anyone with read access. Preview binaries report a version like v0.0.0-preview-pr<N>-<sha> so they can't be mistaken for a release.
If you have the Task runner and gh CLI installed, you can grab the right one for your platform with:
task preview-fetch -- 42 # downloads the artifact for your OS/arch into dist/preview-pr42/
It prints the install command you can run to drop the binary onto your PATH.
Getting started
1. Install
Run once per repo, after cloning:
bight install
This writes the git hook and walks you through creating a .bight.yml config:
bight: hook installed
bight: no config file found. Create .bight.yml? [Y/n]
Project name [myapp]:
Env file path [.env]:
Add env vars to track? [Y/n]
(blank name to finish)
Var name: DB_NAME
Strategy:
1) template - interpolate branch/project name (default)
2) random - fresh random value on each checkout
Choice [1]: 1
Var name:
bight: created .bight.yml
2. Confirm everything is wired up
bight doctor
bight doctor:
[ok] git repo detected
[ok] config: .bight.yml loaded
[ok] config: project = "myapp", 1 env file(s)
[ok] hook: installed
[ok] env file: .env
[ok] vars: all strategies valid
[ok] vars: all triggers valid
3. Preview before it fires automatically
bight run --dry-run
# bight (dry-run): .env → DB_NAME=myapp_main
No files are touched. When you're happy with what you see, you're done — the hook fires on every checkout from here on.
4. Switch branches
git checkout -b feat-login
# bight: .env → DB_NAME=myapp_feat-login
Reference
Commands
Full per-command reference (flags, usage, subcommand tree) is auto-generated from the CLI itself and lives in docs/commands/bight.md. You can also run bight help <command> locally.
Config file
bight install generates a starter config, but you can hand-edit .bight.yml at any time:
project: myapp
defaults:
branch_template: "{{.Project}}_{{.Branch}}" # used by the template strategy
env_files:
- path: .env
backup: true # write .env.bak before patching (optional, default false)
vars:
- name: DB_NAME
strategy: template # renders to e.g. myapp_feat-login
on: checkout
- name: JWT_SECRET
strategy: random # fresh 64-char hex string on every branch switch
on: checkout
sensitive: true # mask value in console output
Using a non-default config file
If your config isn't named .bight.yml or lives at a non-standard path, use --config:
bight run --config path/to/custom.bight.yml
bight doctor --config path/to/custom.bight.yml
--config is a global flag — it works with any subcommand that reads config.
Alternatively, set the BIGHT_CONFIG environment variable. This is convenient for the post-checkout hook itself or CI jobs where passing a flag is awkward:
export BIGHT_CONFIG=path/to/custom.bight.yml
bight doctor # picks up BIGHT_CONFIG automatically
Precedence: --config > BIGHT_CONFIG > auto-discovery of .bight.yml in the current directory. If BIGHT_CONFIG points to a missing or unreadable file, bight will error rather than silently falling back to auto-discovery. bight doctor reports which source was used.
Manual patching
To apply env patching for the current branch without switching:
bight run
Tip: to test how another branch would be patched, suppress the hook when switching so bight doesn't fire automatically, then use --dry-run:
git -c core.hooksPath=/dev/null checkout other-branch
bight run --dry-run
Strategies
| Strategy |
Output |
Typical use |
template |
Rendered from {{.Project}} / {{.Branch}} |
DB_NAME |
random |
Fresh 32-byte hex string |
JWT_SECRET, tokens |
deterministic |
Stable 64-char hex derived from project + branch |
DB_NAME (same value across machines) |
Sensitive vars (sensitive)
Mark a var sensitive: true to prevent its value from appearing in console output. The value is still written to the .env file normally — only the terminal display is affected.
- name: JWT_SECRET
strategy: random
on: checkout
sensitive: true
Output with sensitive: true:
bight: .env → JWT_SECRET=***
Backup files (backup)
Set backup: true on an env file entry to write a copy of the file to {path}.bak before each patch is applied. Useful for inspecting what changed or recovering a previous value.
env_files:
- path: .env
backup: true
vars:
- name: DB_NAME
strategy: template
on: checkout
The backup is a verbatim copy of the file as it was immediately before patching. It is overwritten on each checkout — only the most recent pre-patch state is kept.
Full comment preservation is not supported, as the package we use, godotenv, strips comments on rewrite. As a partial workaround, defaults.collect-comments re-appends comments collected before the patch was applied:
Note: This is a best-effort feature. Comments are collected from the file before patching and re-appended at the end afterwards — their original positions are not restored, and inline comments (KEY=val # note) are lost entirely.
| Value |
Behavior |
all |
Re-appends every full-line comment |
blocks-only |
Re-appends only contiguous comment blocks (≥ 2 lines) — skips isolated # notes |
unset / none |
Comments are not preserved (default) |
defaults:
collect-comments: blocks-only
Comments are always written after the key=value pairs.
Triggers (on)
| Value |
When |
checkout |
Every branch switch |
Seeding env files (copy)
When you git worktree add a new working tree, you land in a clean directory with no .env. Set copy on an env file to seed it from somewhere — typically the same file in the main worktree:
env_files:
- path: .env
copy: ../main/.env # short form: just the source path
vars: # Optional. Can be omitted when no branch-dependent variable updates desired.
- name: JWT_SECRET
strategy: random
on: checkout
Source paths may be:
- absolute (
/path/to/.env)
~-prefixed (~/envs/myapp.env)
- relative — resolved against the main worktree root, not the current working directory. This is what makes
../main/.env work the same from every linked worktree.
If you need to control overwrite behavior, use the mapping form:
env_files:
- path: .env
copy:
source: ../main/.env
overwrite: true # clobber an existing .env on init
overwrite behavior:
false (default) — if .env already exists, the copy is silently skipped. copy: is a "seed if absent" declaration, not a per-checkout request — once the file exists, bight leaves it alone. To see whether bight thinks it would copy, use bight doctor or bight run --dry-run.
true — if .env already exists it is replaced (after the backup step, if backup: true).
overwrite controls only the file copy. Var patching always rewrites the keys it targets regardless of this setting.
Global config (~/.bight.yml)
Settings in ~/.bight.yml apply across all repos and are overridden field-by-field by the repo's .bight.yml. Only defaults fields are supported globally — env_files and vars must be defined in the repo config. If a repo has no .bight.yml, bight does nothing — the global config alone is not enough to trigger patching.
defaults:
branch_template: "{{.Project}}_{{.Branch}}"
collect-comments: blocks-only
Developing
See DEVELOPING.md