feat: adds --readonly and --pin flags, and displays Size column in list by default

This commit is contained in:
Lewis Wynne 2026-02-13 18:52:34 +00:00
parent e5b6dcd187
commit 5bcd3581dd
46 changed files with 711 additions and 177 deletions

View file

@ -2,7 +2,7 @@ $ pda config get display_ascii_art
true
$ pda config get store.default_store_name
default
store
$ pda config get git.auto_commit
false

View file

@ -4,14 +4,14 @@ key.always_prompt_delete = false
key.always_prompt_glob_delete = true
key.always_prompt_overwrite = false
key.always_encrypt = false
store.default_store_name = default
store.default_store_name = store
store.always_prompt_delete = true
store.always_prompt_overwrite = true
list.always_show_all_stores = true
list.default_list_format = table
list.always_show_full_values = false
list.always_hide_header = false
list.default_columns = key,store,value,ttl
list.default_columns = meta,size,ttl,store,key,value
git.auto_fetch = false
git.auto_commit = false
git.auto_push = false

View file

@ -32,7 +32,7 @@ json
# Invalid list columns
$ pda config set list.default_columns foo --> FAIL
FAIL cannot set 'list.default_columns': must be a comma-separated list of 'key', 'store', 'value', 'ttl' (got 'foo')
FAIL cannot set 'list.default_columns': must be a comma-separated list of 'key', 'store', 'value', 'meta', 'size', 'ttl' (got 'foo')
# Duplicate columns
$ pda config set list.default_columns key,key --> FAIL
@ -50,9 +50,9 @@ FAIL unknown config key 'git.auto_comit'
hint did you mean 'git.auto_commit'?
# Reset changed values so subsequent tests see defaults
$ pda config set store.default_store_name default
$ pda config set store.default_store_name store
$ pda config set list.default_list_format table
$ pda config set list.default_columns key,store,value,ttl
ok store.default_store_name set to 'default'
$ pda config set list.default_columns meta,size,ttl,store,key,value
ok store.default_store_name set to 'store'
ok list.default_list_format set to 'table'
ok list.default_columns set to 'key,store,value,ttl'
ok list.default_columns set to 'meta,size,ttl,store,key,value'

20
testdata/help-list.ct vendored
View file

@ -6,10 +6,9 @@ By default, list shows entries from every store. Pass a store name as a
positional argument to narrow to a single store, or use --store/-s with a
glob pattern to filter by store name.
The Store column is always shown so entries can be distinguished across
stores. Use --key/-k and --value/-v to filter by key or value glob, and
--store/-s to filter by store name. All filters are repeatable and OR'd
within the same flag.
Use --key/-k and --value/-v to filter by key or value glob, and --store/-s
to filter by store name. All filters are repeatable and OR'd within the
same flag.
Usage:
pda list [STORE] [flags]
@ -27,6 +26,9 @@ Flags:
-k, --key strings filter keys with glob pattern (repeatable)
--no-header suppress the header row
--no-keys suppress the key column
--no-meta suppress the meta column
--no-size suppress the size column
--no-store suppress the store column
--no-ttl suppress the TTL column
--no-values suppress the value column
-s, --store strings filter stores with glob pattern (repeatable)
@ -37,10 +39,9 @@ By default, list shows entries from every store. Pass a store name as a
positional argument to narrow to a single store, or use --store/-s with a
glob pattern to filter by store name.
The Store column is always shown so entries can be distinguished across
stores. Use --key/-k and --value/-v to filter by key or value glob, and
--store/-s to filter by store name. All filters are repeatable and OR'd
within the same flag.
Use --key/-k and --value/-v to filter by key or value glob, and --store/-s
to filter by store name. All filters are repeatable and OR'd within the
same flag.
Usage:
pda list [STORE] [flags]
@ -58,6 +59,9 @@ Flags:
-k, --key strings filter keys with glob pattern (repeatable)
--no-header suppress the header row
--no-keys suppress the key column
--no-meta suppress the meta column
--no-size suppress the size column
--no-store suppress the store column
--no-ttl suppress the TTL column
--no-values suppress the value column
-s, --store strings filter stores with glob pattern (repeatable)

View file

@ -9,6 +9,7 @@ Aliases:
remove, rm
Flags:
--force bypass read-only protection
-h, --help help for remove
-i, --interactive prompt yes/no for each deletion
-k, --key strings delete keys matching glob pattern (repeatable)
@ -24,6 +25,7 @@ Aliases:
remove, rm
Flags:
--force bypass read-only protection
-h, --help help for remove
-i, --interactive prompt yes/no for each deletion
-k, --key strings delete keys matching glob pattern (repeatable)

View file

@ -23,8 +23,11 @@ Aliases:
Flags:
-e, --encrypt encrypt the value at rest using age
-f, --file string read value from a file
--force bypass read-only protection
-h, --help help for set
-i, --interactive prompt before overwriting an existing key
--pin pin the key (sorts to top in list)
--readonly mark the key as read-only
--safe do not overwrite if the key already exists
-t, --ttl duration expire the key after the provided duration (e.g. 24h, 30m)
Set a key to a given value or stdin. Optionally specify a store.
@ -50,7 +53,10 @@ Aliases:
Flags:
-e, --encrypt encrypt the value at rest using age
-f, --file string read value from a file
--force bypass read-only protection
-h, --help help for set
-i, --interactive prompt before overwriting an existing key
--pin pin the key (sorts to top in list)
--readonly mark the key as read-only
--safe do not overwrite if the key already exists
-t, --ttl duration expire the key after the provided duration (e.g. 24h, 30m)

View file

@ -1,5 +1,5 @@
# Error when all columns are suppressed
$ pda set a@las 1
$ pda ls las --no-keys --no-values --no-ttl --> FAIL
$ pda ls las --no-keys --no-store --no-values --no-meta --no-size --no-ttl --> FAIL
FAIL cannot ls '@las': no columns selected
hint disable --no-keys, --no-values, or --no-ttl
hint disable some --no-* flags

20
testdata/list-all.ct vendored
View file

@ -2,25 +2,25 @@
$ pda set lax@laa 1
$ pda set lax@lab 2
$ pda ls --key "lax" --format tsv
Key Store Value TTL
lax laa 1 none
lax lab 2 none
Meta Size TTL Store Key Value
-w-- 1 - laa lax 1
-w-- 1 - lab lax 2
$ pda ls --key "lax" --count
2
$ pda ls --key "lax" --format json
[{"key":"lax","value":"1","encoding":"text","store":"laa"},{"key":"lax","value":"2","encoding":"text","store":"lab"}]
# Positional arg narrows to one store
$ pda ls laa --key "lax" --format tsv
Key Store Value TTL
lax laa 1 none
Meta Size TTL Store Key Value
-w-- 1 - laa lax 1
# --store glob filter
$ pda ls --store "la?" --key "lax" --format tsv
Key Store Value TTL
lax laa 1 none
lax lab 2 none
Meta Size TTL Store Key Value
-w-- 1 - laa lax 1
-w-- 1 - lab lax 2
$ pda ls --store "laa" --key "lax" --format tsv
Key Store Value TTL
lax laa 1 none
Meta Size TTL Store Key Value
-w-- 1 - laa lax 1
# --store cannot be combined with positional arg
$ pda ls --store "laa" laa --> FAIL
FAIL cannot use --store with a store argument

View file

@ -7,5 +7,5 @@ Key Value
a 1
# Reset
$ pda config set list.default_columns key,store,value,ttl
ok list.default_columns set to 'key,store,value,ttl'
$ pda config set list.default_columns meta,size,ttl,store,key,value
ok list.default_columns set to 'meta,size,ttl,store,key,value'

View file

@ -3,7 +3,7 @@ $ pda config set list.always_hide_header true
$ pda set a@lchh 1
$ pda ls lchh --format tsv
ok list.always_hide_header set to 'true'
a lchh 1 none
-w-- 1 - lchh a 1
# Reset
$ pda config set list.always_hide_header false

View file

@ -2,6 +2,6 @@
$ pda set a@csv 1
$ pda set b@csv 2
$ pda ls csv --format csv
Key,Store,Value,TTL
a,csv,1,none
b,csv,2,none
Meta,Size,TTL,Store,Key,Value
-w--,1,-,csv,a,1
-w--,1,-,csv,b,2

View file

@ -2,7 +2,7 @@
$ pda set a@md 1
$ pda set b@md 2
$ pda ls md --format markdown
| Key | Store | Value | TTL |
| --- | --- | --- | --- |
| a | md | 1 | none |
| b | md | 2 | none |
| Meta | Size | TTL | Store | Key | Value |
| --- | --- | --- | --- | --- | --- |
| -w-- | 1 | - | md | a | 1 |
| -w-- | 1 | - | md | b | 2 |

View file

@ -2,11 +2,11 @@ $ pda set a1@lg 1
$ pda set a2@lg 2
$ pda set b1@lg 3
$ pda ls lg --key "a*" --format tsv
Key Store Value TTL
a1 lg 1 none
a2 lg 2 none
Meta Size TTL Store Key Value
-w-- 1 - lg a1 1
-w-- 1 - lg a2 2
$ pda ls lg --key "b*" --format tsv
Key Store Value TTL
b1 lg 3 none
Meta Size TTL Store Key Value
-w-- 1 - lg b1 3
$ pda ls lg --key "c*" --> FAIL
FAIL cannot ls '@lg': no matches for key pattern 'c*'

View file

@ -2,10 +2,10 @@ $ pda set dburl@kv postgres://localhost:5432
$ pda set apiurl@kv https://api.example.com
$ pda set dbpass@kv s3cret
$ pda ls kv -k "db*" -v "**localhost**" --format tsv
Key Store Value TTL
dburl kv postgres://localhost:5432 none
Meta Size TTL Store Key Value
-w-- 25 - kv dburl postgres://localhost:5432
$ pda ls kv -k "*url*" -v "**example**" --format tsv
Key Store Value TTL
apiurl kv https://api.example.com none
Meta Size TTL Store Key Value
-w-- 23 - kv apiurl https://api.example.com
$ pda ls kv -k "db*" -v "**nomatch**" --> FAIL
FAIL cannot ls '@kv': no matches for key pattern 'db*' and value pattern '**nomatch**'

9
testdata/list-meta-column.ct vendored Normal file
View file

@ -0,0 +1,9 @@
# Meta column shows ewtp flags
$ pda set plain@lmc hello
$ pda set readonly@lmc world --readonly
$ pda set pinned@lmc foo --pin
$ pda ls lmc --format tsv --no-ttl --no-size
Meta Store Key Value
-w-p lmc pinned foo
-w-- lmc plain hello
---- lmc readonly world

View file

@ -1,4 +1,4 @@
# --no-header suppresses the header row
$ pda set a@nh 1
$ pda ls nh --format tsv --no-header
a nh 1 none
-w-- 1 - nh a 1

View file

@ -1,5 +1,5 @@
# --no-keys suppresses the key column
$ pda set a@nk 1
$ pda ls nk --format tsv --no-keys
Store Value TTL
nk 1 none
Meta Size TTL Store Value
-w-- 1 - nk 1

View file

@ -1,5 +1,5 @@
# --no-ttl suppresses the TTL column
$ pda set a@nt 1
$ pda ls nt --format tsv --no-ttl
Key Store Value
a nt 1
Meta Size Store Key Value
-w-- 1 nt a 1

View file

@ -1,5 +1,5 @@
# --no-values suppresses the value column
$ pda set a@nv 1
$ pda ls nv --format tsv --no-values
Key Store TTL
a nv none
Meta Size TTL Store Key
-w-- 1 - nv a

9
testdata/list-pinned-sort.ct vendored Normal file
View file

@ -0,0 +1,9 @@
# Pinned entries sort to the top
$ pda set alpha@lps one
$ pda set beta@lps two
$ pda set gamma@lps three --pin
$ pda ls lps --format tsv --no-meta --no-size
TTL Store Key Value
- lps gamma three
- lps alpha one
- lps beta two

View file

@ -2,8 +2,8 @@
$ pda set a@lsalpha 1
$ pda set b@lsbeta 2
$ pda ls lsalpha --format tsv
Key Store Value TTL
a lsalpha 1 none
Meta Size TTL Store Key Value
-w-- 1 - lsalpha a 1
$ pda ls lsbeta --format tsv
Key Store Value TTL
b lsbeta 2 none
Meta Size TTL Store Key Value
-w-- 1 - lsbeta b 2

View file

@ -3,13 +3,13 @@ $ fecho tmpval hello world
$ pda set greeting@vt < tmpval
$ pda set number@vt 42
$ pda ls vt --value "**world**" --format tsv
Key Store Value TTL
greeting vt hello world (..1 more chars) none
Meta Size TTL Store Key Value
-w-- 12 - vt greeting hello world (..1 more chars)
$ pda ls vt --value "**https**" --format tsv
Key Store Value TTL
url vt https://example.com none
Meta Size TTL Store Key Value
-w-- 19 - vt url https://example.com
$ pda ls vt --value "*" --format tsv
Key Store Value TTL
number vt 42 none
Meta Size TTL Store Key Value
-w-- 2 - vt number 42
$ pda ls vt --value "**nomatch**" --> FAIL
FAIL cannot ls '@vt': no matches for value pattern '**nomatch**'

View file

@ -3,6 +3,6 @@ $ fecho tmpval hello world
$ pda set greeting@vm < tmpval
$ pda set number@vm 42
$ pda ls vm --value "**world**" --value "42" --format tsv
Key Store Value TTL
greeting vm hello world (..1 more chars) none
number vm 42 none
Meta Size TTL Store Key Value
-w-- 12 - vm greeting hello world (..1 more chars)
-w-- 2 - vm number 42

View file

@ -1,9 +1,12 @@
# Decrypt an existing encrypted key
$ pda set --encrypt hello@md world
$ pda meta hello@md --decrypt
ok decrypted hello@md
$ pda meta hello@md
key: hello@md
secret: false
writable: true
pinned: false
expires: never
$ pda get hello@md
world

View file

@ -1,9 +1,12 @@
# Encrypt an existing plaintext key
$ pda set hello@me world
$ pda meta hello@me --encrypt
ok encrypted hello@me
$ pda meta hello@me
key: hello@me
secret: true
writable: true
pinned: false
expires: never
# Value should still be retrievable
$ pda get hello@me

24
testdata/meta-pin.ct vendored Normal file
View file

@ -0,0 +1,24 @@
# --pin marks a key as pinned
$ pda set a@mp hello
$ pda meta a@mp --pin
ok pinned a@mp
$ pda meta a@mp
key: a@mp
secret: false
writable: true
pinned: true
expires: never
# --unpin clears the pinned flag
$ pda meta a@mp --unpin
ok unpinned a@mp
$ pda meta a@mp
key: a@mp
secret: false
writable: true
pinned: false
expires: never
# --pin and --unpin are mutually exclusive
$ pda meta a@mp --pin --unpin --> FAIL
FAIL cannot meta 'a@mp': --pin and --unpin are mutually exclusive

24
testdata/meta-readonly.ct vendored Normal file
View file

@ -0,0 +1,24 @@
# --readonly marks a key as read-only
$ pda set a@mro hello
$ pda meta a@mro --readonly
ok made readonly a@mro
$ pda meta a@mro
key: a@mro
secret: false
writable: false
pinned: false
expires: never
# --writable clears the read-only flag
$ pda meta a@mro --writable
ok made writable a@mro
$ pda meta a@mro
key: a@mro
secret: false
writable: true
pinned: false
expires: never
# --readonly and --writable are mutually exclusive
$ pda meta a@mro --readonly --writable --> FAIL
FAIL cannot meta 'a@mro': --readonly and --writable are mutually exclusive

View file

@ -1,11 +1,15 @@
# Set TTL on a key, then view it (just verify no error, can't match dynamic time)
$ pda set hello@mt world
$ pda meta hello@mt --ttl 1h
ok set ttl to 1h hello@mt
# Clear TTL with --ttl never
$ pda set --ttl 1h expiring@mt val
$ pda meta expiring@mt --ttl never
ok cleared ttl expiring@mt
$ pda meta expiring@mt
key: expiring@mt
secret: false
writable: true
pinned: false
expires: never

4
testdata/meta.ct vendored
View file

@ -3,6 +3,8 @@ $ pda set hello@m world
$ pda meta hello@m
key: hello@m
secret: false
writable: true
pinned: false
expires: never
# View metadata for an encrypted key
@ -10,4 +12,6 @@ $ pda set --encrypt secret@m hunter2
$ pda meta secret@m
key: secret@m
secret: true
writable: true
pinned: false
expires: never

View file

@ -6,5 +6,5 @@ bar
$ pda get x@ms2
y
$ pda ls ms2 --format tsv
Key Store Value TTL
x ms2 y none
Meta Size TTL Store Key Value
-w-- 1 - ms2 x y

23
testdata/mv-readonly.ct vendored Normal file
View file

@ -0,0 +1,23 @@
# Cannot move a read-only key without --force
$ pda set a@mvro hello --readonly
$ pda mv a@mvro b@mvro --> FAIL
FAIL cannot move 'a': key is read-only
# --force bypasses read-only protection
$ pda mv a@mvro b@mvro --force
ok renamed a@mvro to b@mvro
# Copy preserves readonly metadata
$ pda cp b@mvro c@mvro
ok copied b@mvro to c@mvro
$ pda meta c@mvro
key: c@mvro
secret: false
writable: false
pinned: false
expires: never
# Cannot overwrite a read-only destination
$ pda set d@mvro new
$ pda mv d@mvro c@mvro --> FAIL
FAIL cannot overwrite 'c': key is read-only

View file

@ -2,9 +2,9 @@
$ pda set foo@rdd 1
$ pda set bar@rdd 2
$ pda ls rdd --format tsv
Key Store Value TTL
bar rdd 2 none
foo rdd 1 none
Meta Size TTL Store Key Value
-w-- 1 - rdd bar 2
-w-- 1 - rdd foo 1
$ pda rm foo@rdd --key "*@rdd" -y
$ pda get bar@rdd --> FAIL
FAIL cannot get 'bar@rdd': no such key

7
testdata/remove-readonly.ct vendored Normal file
View file

@ -0,0 +1,7 @@
# Cannot remove a read-only key without --force
$ pda set a@rmro hello --readonly
$ pda rm a@rmro --> FAIL
FAIL cannot remove 'a@rmro': key is read-only
# --force bypasses read-only protection
$ pda rm a@rmro --force

8
testdata/set-pin.ct vendored Normal file
View file

@ -0,0 +1,8 @@
# --pin marks a key as pinned
$ pda set b@sp beta --pin
$ pda meta b@sp
key: b@sp
secret: false
writable: true
pinned: true
expires: never

17
testdata/set-readonly.ct vendored Normal file
View file

@ -0,0 +1,17 @@
# --readonly marks a key as read-only
$ pda set a@sro hello --readonly
$ pda meta a@sro
key: a@sro
secret: false
writable: false
pinned: false
expires: never
# Cannot overwrite a read-only key without --force
$ pda set a@sro world --> FAIL
FAIL cannot set 'a@sro': key is read-only
# --force bypasses read-only protection
$ pda set a@sro world --force
$ pda get a@sro
world