Enhance secrets CLI to support additional secret file types and clear command
- Updated file tracking to include `.dev.vars` alongside `.env` and `.env.*`. - Improved pre-commit hook to block plaintext secret files, including `.dev.vars`. - Added `clear` command to remove plaintext secret files from the current directory and workspaces. - Enhanced tests to cover new functionality for `.dev.vars` and the `clear` command. - Updated documentation to reflect changes in tracked files and command usage.
This commit is contained in:
parent
585367b9a6
commit
e347e73976
5 changed files with 503 additions and 74 deletions
131
secrets
131
secrets
|
|
@ -46,12 +46,13 @@ derive_project_name() {
|
|||
basename "$PWD"
|
||||
}
|
||||
|
||||
# Collect .env and .env.* files from a directory (excluding .envrc, .environment-*)
|
||||
# Collect secret files from a directory:
|
||||
# .env, .env.*, .dev.vars (excluding .envrc, .environment-*)
|
||||
# Sets the COLLECTED_FILES array. Returns 1 if no files found.
|
||||
collect_env_files() {
|
||||
local dir="$1"
|
||||
COLLECTED_FILES=()
|
||||
for f in "$dir"/.env "$dir"/.env.*; do
|
||||
for f in "$dir"/.env "$dir"/.env.* "$dir"/.dev.vars; do
|
||||
[ -f "$f" ] || continue
|
||||
local basename_f
|
||||
basename_f=$(basename "$f")
|
||||
|
|
@ -97,9 +98,9 @@ install_hook() {
|
|||
# Inline hook if template not found (e.g. secrets installed standalone)
|
||||
cat > "$hook_dst" << 'HOOKEOF'
|
||||
#!/usr/bin/env bash
|
||||
BLOCKED=$(git diff --cached --name-only | grep -E '\.env' | grep -v '\.age$' || true)
|
||||
BLOCKED=$(git diff --cached --name-only | grep -E '\.(env|dev\.vars)' | grep -v '\.age$' || true)
|
||||
if [ -n "$BLOCKED" ]; then
|
||||
echo "ERROR: Plaintext env files staged for commit:"
|
||||
echo "ERROR: Plaintext secret files staged for commit:"
|
||||
echo "$BLOCKED"
|
||||
echo "Only .age (encrypted) files should be committed."
|
||||
exit 1
|
||||
|
|
@ -132,13 +133,15 @@ cmd_init() {
|
|||
# Never commit the private key
|
||||
key.txt
|
||||
|
||||
# Block plaintext env files
|
||||
# Block plaintext secret files
|
||||
**/.env
|
||||
**/.env.*
|
||||
**/.dev.vars
|
||||
|
||||
# Allow encrypted env files
|
||||
# Allow encrypted files
|
||||
!**/.env.age
|
||||
!**/.env.*.age
|
||||
!**/.dev.vars.age
|
||||
EOF
|
||||
|
||||
# Install pre-commit hook
|
||||
|
|
@ -220,7 +223,7 @@ cmd_push() {
|
|||
pubkey=$(get_pubkey)
|
||||
|
||||
if ! push_dir_to_project "$PWD" "$project" "$pubkey"; then
|
||||
die "No .env or .env.* files found in $PWD"
|
||||
die "No secret files (.env, .env.*, .dev.vars) found in $PWD"
|
||||
fi
|
||||
|
||||
commit_and_push_secrets "update $project"
|
||||
|
|
@ -260,7 +263,7 @@ cmd_push_workspaces() {
|
|||
done <<< "$workspaces"
|
||||
|
||||
if [ "$total" -eq 0 ]; then
|
||||
die "No .env files found in any workspace"
|
||||
die "No secret files found in any workspace"
|
||||
fi
|
||||
|
||||
commit_and_push_secrets "update $monorepo_name workspaces"
|
||||
|
|
@ -513,27 +516,114 @@ cmd_rekey() {
|
|||
echo "For full rotation, create a fresh repo."
|
||||
}
|
||||
|
||||
cmd_clear() {
|
||||
local dir="$PWD"
|
||||
if ! collect_env_files "$dir"; then
|
||||
info "No secret files to clear in $dir"
|
||||
return
|
||||
fi
|
||||
|
||||
local count=0
|
||||
for f in "${COLLECTED_FILES[@]}"; do
|
||||
rm "$f"
|
||||
count=$((count + 1))
|
||||
done
|
||||
info "Cleared $count secret file(s) from $dir"
|
||||
}
|
||||
|
||||
cmd_clear_workspaces() {
|
||||
check_cmd jq
|
||||
|
||||
local root="$PWD"
|
||||
local total=0
|
||||
|
||||
# Clear root
|
||||
if collect_env_files "$root"; then
|
||||
for f in "${COLLECTED_FILES[@]}"; do
|
||||
rm "$f"
|
||||
total=$((total + 1))
|
||||
done
|
||||
fi
|
||||
|
||||
# Clear each workspace
|
||||
local workspaces
|
||||
workspaces=$(get_workspaces "$root")
|
||||
while IFS= read -r ws; do
|
||||
[ -n "$ws" ] || continue
|
||||
local ws_dir="$root/$ws"
|
||||
if collect_env_files "$ws_dir"; then
|
||||
for f in "${COLLECTED_FILES[@]}"; do
|
||||
rm "$f"
|
||||
total=$((total + 1))
|
||||
done
|
||||
fi
|
||||
done <<< "$workspaces"
|
||||
|
||||
info "Cleared $total secret file(s) from workspace"
|
||||
}
|
||||
|
||||
cmd_run() {
|
||||
local workspace_mode=false
|
||||
local project=""
|
||||
|
||||
# Parse flags before the command
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-w|--workspaces) workspace_mode=true; shift ;;
|
||||
--) shift; break ;;
|
||||
-*) die "Unknown flag: $1. Usage: secrets run [-w] [--] <command...>" ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ $# -gt 0 ] || die "Usage: secrets run [-w] [--] <command...>"
|
||||
|
||||
# Pull secrets
|
||||
if [ "$workspace_mode" = true ]; then
|
||||
cmd_pull_workspaces
|
||||
else
|
||||
cmd_pull "$project"
|
||||
fi
|
||||
|
||||
# Set trap to clear secrets on exit (normal, error, interrupt, terminate)
|
||||
if [ "$workspace_mode" = true ]; then
|
||||
trap 'cmd_clear_workspaces' EXIT
|
||||
else
|
||||
trap 'cmd_clear' EXIT
|
||||
fi
|
||||
|
||||
# Execute the command, capturing exit code (don't let set -e kill us)
|
||||
local rc=0
|
||||
"$@" || rc=$?
|
||||
exit "$rc"
|
||||
}
|
||||
|
||||
cmd_help() {
|
||||
cat << 'EOF'
|
||||
secrets — encrypted env file sync between machines
|
||||
secrets — encrypted secret file sync between machines
|
||||
|
||||
Usage:
|
||||
secrets init Initialize the secrets repo and generate an age key
|
||||
secrets push [project] Encrypt .env* files and push to the secrets repo
|
||||
secrets push -w|--workspaces Push .env* from all workspaces in package.json
|
||||
secrets pull [project] Pull and decrypt .env* files into current directory
|
||||
secrets pull -w|--workspaces Pull .env* into all workspaces from package.json
|
||||
secrets push [project] Encrypt secret files and push to the secrets repo
|
||||
secrets push -w|--workspaces Push secrets from all workspaces in package.json
|
||||
secrets pull [project] Pull and decrypt secret files into current directory
|
||||
secrets pull -w|--workspaces Pull secrets into all workspaces from package.json
|
||||
secrets clear Remove plaintext secret files from current directory
|
||||
secrets clear -w|--workspaces Clear secrets from all workspaces in package.json
|
||||
secrets run [-w] <command> Pull secrets, run command, clear secrets on exit
|
||||
secrets list List all projects and their secret files
|
||||
secrets rm <project> Remove a project's secrets from the repo
|
||||
secrets rekey Re-encrypt all secrets with a new key
|
||||
|
||||
Tracked files: .env, .env.*, .dev.vars
|
||||
|
||||
If [project] is omitted, it is derived from the current directory's
|
||||
git remote (if available) or the directory name.
|
||||
|
||||
Workspaces:
|
||||
With -w/--workspaces, reads package.json "workspaces" field to find
|
||||
workspace directories. Each workspace's .env* files are stored under
|
||||
<monorepo>/<workspace-path>/ in the secrets repo. Root .env* files
|
||||
workspace directories. Each workspace's secret files are stored under
|
||||
<monorepo>/<workspace-path>/ in the secrets repo. Root secret files
|
||||
are stored under <monorepo>/ directly. Requires jq.
|
||||
|
||||
Environment:
|
||||
|
|
@ -559,6 +649,17 @@ case "${1:-help}" in
|
|||
cmd_pull "${2:-}"
|
||||
fi
|
||||
;;
|
||||
clear)
|
||||
if [ "${2:-}" = "-w" ] || [ "${2:-}" = "--workspaces" ]; then
|
||||
cmd_clear_workspaces
|
||||
else
|
||||
cmd_clear
|
||||
fi
|
||||
;;
|
||||
run)
|
||||
shift
|
||||
cmd_run "$@"
|
||||
;;
|
||||
list) cmd_list ;;
|
||||
rm) cmd_rm "${2:-}" ;;
|
||||
rekey) cmd_rekey ;;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue