keep pull: fill saved template with real values by default
All checks were successful
Docker / build-and-push (push) Successful in 1m54s
All checks were successful
Docker / build-and-push (push) Successful in 1m54s
A bare key/value dump throws away the comments/structure a template preserves; now a plain pull reconstitutes the real .env from the template when the vault has one, falling back to the flat dump otherwise. --template still returns the redacted structure alone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
11
README.md
11
README.md
@@ -124,14 +124,19 @@ Comments explaining *where a value comes from* or *why it exists* are
|
||||
exactly the kind of context a bare key/value pair throws away, so this
|
||||
keeps it without keeping the secret itself around unencrypted anywhere.
|
||||
|
||||
A plain `keep pull` uses that template by default: if the vault has one,
|
||||
you get real values filled back into it, comments and all — not just a
|
||||
bare key/value dump. Ask for the redacted structure itself with:
|
||||
|
||||
```bash
|
||||
keep pull myapp/production --template > .env.example
|
||||
```
|
||||
|
||||
Vaults pushed before this existed just don't have a saved template —
|
||||
`keep pull --template` on one of those fails with a clear error instead
|
||||
of silently returning nothing. No migration needed; push again and the
|
||||
template gets backfilled from whatever `.env` you push next.
|
||||
`keep pull` on one of those falls back to a plain key/value dump, and
|
||||
`keep pull --template` fails with a clear error instead of silently
|
||||
returning nothing. No migration needed; push again and the template
|
||||
gets backfilled from whatever `.env` you push next.
|
||||
|
||||
## Granting, revoking, rotating
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'node:fs';
|
||||
import { requireIdentityAndRecipientId } from '../identityStore.js';
|
||||
import { signedFetch, adminFetch, expectOk } from '../signedFetch.js';
|
||||
import { parseEnv, serializeEnv, buildEnvTemplate } from '../envFormat.js';
|
||||
import { parseEnv, serializeEnv, buildEnvTemplate, fillEnvTemplate } from '../envFormat.js';
|
||||
import {
|
||||
ready,
|
||||
Identity,
|
||||
@@ -89,7 +89,9 @@ export async function vaultPull(vaultKey: string, format: 'env' | 'json', outFil
|
||||
return;
|
||||
}
|
||||
|
||||
const out = format === 'json' ? JSON.stringify(values, null, 2) : serializeEnv(values);
|
||||
const out = format === 'json'
|
||||
? JSON.stringify(values, null, 2)
|
||||
: template !== undefined ? fillEnvTemplate(template, values) : serializeEnv(values);
|
||||
if (outFile) {
|
||||
fs.writeFileSync(outFile, out);
|
||||
console.error(`wrote ${Object.keys(values).length} key(s) to ${outFile}${previous ? ' (previous version)' : ''}`);
|
||||
|
||||
@@ -25,6 +25,23 @@ export function serializeEnv(values: Record<string, string>): string {
|
||||
// — the whole point is to preserve the "why"/"where to get this" context
|
||||
// that plain parseEnv() throws away, without keeping the actual secret
|
||||
// around in the template.
|
||||
// Inverse of buildEnvTemplate: substitutes real values back into a
|
||||
// template's `KEY=` lines while leaving comments/blank lines/order intact.
|
||||
export function fillEnvTemplate(template: string, values: Record<string, string>): string {
|
||||
return template
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith('#')) return line;
|
||||
const eq = line.indexOf('=');
|
||||
if (eq === -1) return line;
|
||||
const key = line.slice(0, eq);
|
||||
const value = values[key.trim()] ?? '';
|
||||
return `${key}=${/[\s#"']/.test(value) ? JSON.stringify(value) : value}`;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export function buildEnvTemplate(text: string): string {
|
||||
return text
|
||||
.split('\n')
|
||||
|
||||
@@ -55,7 +55,8 @@ function printUsage(): void {
|
||||
|
||||
keep push <vault> [--file .env] [--purge] (--purge: don't retain the outgoing version as rollback)
|
||||
keep pull <vault> [--format env|json] [--out <path>] [--previous] [--template]
|
||||
(--template: comments/structure with values redacted, not the secrets)
|
||||
(fills the saved template with real values by default, if one exists;
|
||||
--template: comments/structure with values redacted, not the secrets)
|
||||
keep grant <vault> <recipient-id> [--read-only]
|
||||
keep revoke <vault> <recipient-id> (admin — needs KEEP_ADMIN_PASSWORD)
|
||||
keep log <vault>
|
||||
|
||||
Reference in New Issue
Block a user