monkeys

eat

monkeys eat [+namespace] [@profile] [--public KEY[,KEY...]]

Moves a project's dotenv files into monkeys and deletes them. It reads .env, .env.local and every .env.<profile> in the current directory, asks one question per key, secret or public, and writes the answers where they belong: a secret's key into .monkeys with the secret in the vault, a public line into .monkeys as it is.

Namespace, +name (Enter for none): foo
Profile for .env [test]:
DATABASE_URL @test,@production  [s]ecret or [p]ublic? s
STRIPE_SECRET_KEY @test  [s]ecret or [p]ublic? s
PORT @test,@production  [s]ecret or [p]ublic? p
  public PORT=3000 for @test, into .monkeys
  public PORT=80 for @production, into .monkeys
SENTRY_DSN @production  [s]ecret or [p]ublic? s
wrote .monkeys: +foo @test,production @test @production, 3 keys
stored @test: DATABASE_URL, STRIPE_SECRET_KEY
stored @production: DATABASE_URL, SENTRY_DSN
public @test: PORT
public @production: PORT
removed .env, .env.production
a .gitignore line for them is dead now and can go; monkeys leaves that file alone

That run started from .env and .env.production and left one file behind:

+foo
@test,production
DATABASE_URL
@test
STRIPE_SECRET_KEY
PORT=3000
@production
SENTRY_DSN
PORT=80

The files map to profiles the way dotenv already names them: .env and .env.local go to the default profile, the first one in .monkeys when the file exists and otherwise the one asked for, and .env.<name> goes to @<name>. A key found in several files gets each file's value under that file's profile, and its line in .monkeys lists those profiles together. .env.example is left alone, since .monkeys is what it was standing in for.

The secret answer is the default; Enter takes it. The value is shown only for a public answer, since that is the moment it becomes a line in a file that gets committed. A key the project already has, as a secret in the vault, a key or a value in .monkeys, asks before it is replaced, and Enter keeps what is there; asked for the other kind, it is kept without asking and named in the summary, since turning one kind into the other is forget and then set.

The grammar is the part of dotenv every library reads the same way: KEY=value, export KEY=value, a value in single or double quotes with the quotes stripped, # comment lines and blank lines. A value that spans lines, one that expands another variable with ${...}, or an unquoted one followed by a # comment is refused with its file and line, and nothing is written until every file parses:

monkeys: .env:2: DATABASE_URL expands another variable; monkeys keeps a value as it is; nothing was written

When no .monkeys exists yet, eat asks for a namespace once, Enter for none, and writes the file at the root of the git checkout, where unpack writes it. When one exists, its + line and its profiles stand, and the keys are added to it the way unpack adds them. The dotenv files are deleted only after every secret is stored and the file is written. The .gitignore lines that kept them out of git are left for you, and the last line says so.

Without the questions

--public names the keys that are not secret, which answers every question before it is asked: those keys become KEY=value lines, every other key becomes a secret in the vault, and an existing entry is kept rather than replaced. Nothing is asked, so no terminal is needed and an agent can run it on a project it finds:

monkeys eat +foo --public PORT
  public PORT=3000 for @test, into .monkeys
  public PORT=80 for @production, into .monkeys
wrote .monkeys: +foo @test,production @test @production, 3 keys
stored @test: DATABASE_URL, STRIPE_SECRET_KEY
stored @production: DATABASE_URL, SENTRY_DSN
public @test: PORT
public @production: PORT
removed .env, .env.production
a .gitignore line for them is dead now and can go; monkeys leaves that file alone

The secrets go from the files into the vault without passing through whatever ran the command. +namespace and @profile stand in for the two questions a first run would otherwise ask; a project that already has a .monkeys file takes both from it. Without --public there are questions to ask, so eat refuses to run with its input piped and says which flag to use.

On this page