Lock the state directory and write vault files atomically (closes #34)
check / check (push) Failing after 17s

Each command that changes the state directory holds one lock: flock(2)
on `lock` in the state directory, dropped by the kernel if the process
dies, or a process-wide mutex on the in-memory test filesystem. It
covers the state directory, not each vault, because `currentvault`,
`vault create` and cross-vault moves span vaults, and a lock file in a
vault would be deleted by `vault remove` under a waiting command.

Files go through `secret.WriteFileAtomic`; versions, new secrets and
cross-vault copies are built in a temporary directory and renamed into
place; removals rename out of the way first. Left for later: replacing
an unlocker (#71) and deleting
what an interrupted command leaves under a `.tmp-` name
(#75).

Model: opus-5-5
This commit is contained in:
2026-10-03 16:18:38 +00:00
committed by sneak
parent 7c6531eaf7
commit 60afd590c0
24 changed files with 1832 additions and 187 deletions
+13 -1
View File
@@ -239,6 +239,12 @@ func formatVersionTime(t *time.Time) string {
func (cli *Instance) PromoteVersion(
cmd *cobra.Command, secretName string, version string,
) error {
release, err := vault.LockStateDir(cli.fs, cli.stateDir)
if err != nil {
return err
}
defer release()
// Get current vault
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
@@ -282,6 +288,12 @@ func (cli *Instance) PromoteVersion(
func (cli *Instance) RemoveVersion(
cmd *cobra.Command, secretName string, version string,
) error {
release, err := vault.LockStateDir(cli.fs, cli.stateDir)
if err != nil {
return err
}
defer release()
// Get current vault
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
@@ -333,7 +345,7 @@ func (cli *Instance) RemoveVersion(
}
// Remove the version directory
err = cli.fs.RemoveAll(versionDir)
err = secret.RemoveDirAtomic(cli.fs, versionDir)
if err != nil {
return fmt.Errorf("failed to remove version: %w", err)
}