feat: secrets list --json machine-readable output (EGB-699)
Add a --json flag to `secrets list` that emits a structured object for
tooling/CI instead of the human table — feeds the EGB-671 install scripts,
which need to enumerate a cloned store programmatically.
Contract: {"store", "projects":[{"name","entries":[...]}]}, each entry
self-describing via a type discriminator — {type:dotenv,path} or
{type:external,subtype:properties|file,path}. cmd_list_json mirrors the same
recursive store walk as the human list (nested <project>/<relpath>.age +
external/<slug>.age); jq assembles the JSON so paths escape correctly and
stdout stays pure JSON (the non-default-store hint is suppressed; jq is a
hard dep only in --json mode).
Tests: 7 new bats cases (dotenv, nested relpath, external properties + file
subtypes, empty store, pure-stdout-under-notice, store path). Full suite
261 pass / 0 fail.
VERSION 0.7.1.0 -> 0.7.2.0; CHANGELOG/README/CLAUDE.md updated.
This commit is contained in:
parent
b8fe20f9bf
commit
446256caf1
6 changed files with 172 additions and 4 deletions
73
secrets
73
secrets
|
|
@ -1744,9 +1744,21 @@ cmd_pull_workspaces() {
|
|||
}
|
||||
|
||||
cmd_list() {
|
||||
local json=0
|
||||
case "${1:-}" in
|
||||
--json) json=1 ;;
|
||||
"") ;;
|
||||
*) die "Usage: secrets list [--json]" ;;
|
||||
esac
|
||||
|
||||
resolve_store
|
||||
check_initialized
|
||||
|
||||
if [ "$json" -eq 1 ]; then
|
||||
cmd_list_json
|
||||
return
|
||||
fi
|
||||
|
||||
local found=0
|
||||
for dir in "$SECRETS_DIR"/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
|
|
@ -1782,6 +1794,64 @@ cmd_list() {
|
|||
fi
|
||||
}
|
||||
|
||||
# EGB-699: machine-readable listing for tooling/CI (feeds EGB-671 install
|
||||
# scripts). Contract: a single JSON object on stdout —
|
||||
# {"store": "<dir>", "projects": [{"name", "entries": [...]}]}
|
||||
# where each entry is {"type":"dotenv","path":<relpath>} or
|
||||
# {"type":"external","subtype":"properties"|"file","path":<slug>}. Mirrors the
|
||||
# recursive store walk the human `list` uses (nested <project>/<relpath>.age +
|
||||
# external/<slug>.age). jq does the assembly so paths are escaped correctly;
|
||||
# stdout stays pure JSON (the human store hint is suppressed in this mode).
|
||||
cmd_list_json() {
|
||||
check_cmd jq
|
||||
|
||||
{
|
||||
for dir in "$SECRETS_DIR"/*/; do
|
||||
[ -d "$dir" ] || continue
|
||||
local project
|
||||
project=$(basename "$dir")
|
||||
[[ "$project" == .* ]] && continue
|
||||
# Marker line so a project with zero blobs still appears (mirrors the
|
||||
# human header), grouped via jq below.
|
||||
printf 'project\t%s\n' "$project"
|
||||
local f rel name
|
||||
while IFS= read -r f; do
|
||||
[ -f "$f" ] || continue
|
||||
rel=${f#"$dir"}
|
||||
rel=${rel%.age}
|
||||
case "$rel" in
|
||||
external/*)
|
||||
name=${rel#external/}
|
||||
case "$name" in
|
||||
*.file) printf 'entry\t%s\texternal\tfile\t%s\n' "$project" "${name%.file}" ;;
|
||||
*.properties) printf 'entry\t%s\texternal\tproperties\t%s\n' "$project" "${name%.properties}" ;;
|
||||
*.gradle-properties) printf 'entry\t%s\texternal\tproperties\t%s\n' "$project" "${name%.gradle-properties}" ;;
|
||||
*) printf 'entry\t%s\texternal\tunknown\t%s\n' "$project" "$name" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
printf 'entry\t%s\tdotenv\t\t%s\n' "$project" "$rel"
|
||||
;;
|
||||
esac
|
||||
done < <(find "$dir" -type f -name '*.age' | sort)
|
||||
done
|
||||
} | jq -R -n --arg store "$SECRETS_DIR" '
|
||||
[inputs | split("\t")] as $lines
|
||||
| ($lines | map(select(.[0] == "project") | .[1]) | unique) as $names
|
||||
| {
|
||||
store: $store,
|
||||
projects: ($names | map(. as $p | {
|
||||
name: $p,
|
||||
entries: [ $lines[]
|
||||
| select(.[0] == "entry" and .[1] == $p)
|
||||
| if .[2] == "external"
|
||||
then { type: "external", subtype: .[3], path: .[4] }
|
||||
else { type: "dotenv", path: .[4] }
|
||||
end ]
|
||||
}))
|
||||
}'
|
||||
}
|
||||
|
||||
cmd_rm() {
|
||||
check_cmd git
|
||||
resolve_store
|
||||
|
|
@ -2464,6 +2534,7 @@ Usage:
|
|||
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 list --json Same listing as machine-readable JSON (for tooling/CI)
|
||||
secrets rm <project> Remove a project's secrets from the repo
|
||||
secrets rekey Re-encrypt all secrets with a new key
|
||||
secrets verify [project] Check the manifest against the store + decrypt every blob
|
||||
|
|
@ -2636,7 +2707,7 @@ case "${1:-help}" in
|
|||
cmd_run "$@"
|
||||
;;
|
||||
add) cmd_add "${2:-}" ;;
|
||||
list) cmd_list ;;
|
||||
list) shift; cmd_list "$@" ;;
|
||||
rm) cmd_rm "${2:-}" ;;
|
||||
rekey) cmd_rekey ;;
|
||||
verify) shift; cmd_verify "$@" ;;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue