Signals end every command, not only ssh to and ssh install (closes #48)
check / check (push) Successful in 2m20s

SIGINT, SIGTERM and SIGHUP were caught for the whole run, but only the
ssh and sftp children acted on them: the mnemonic prompt waited for
Enter, and an interrupted `age encrypt -o` went on to put the
encryption of the cut-off input in place. Now `ssh to` and
`ssh install` catch them from once the mnemonic is read until their
cleanup has run, and `age encrypt -o` and `age decrypt -o` catch them
while they write, to remove the unfinished file and exit with status 1;
everywhere else they end the tool at once. Tests cover an interrupted
`age encrypt -o` and `age decrypt -o` and the install working directory
on a signal.

Model: opus-5-5
This commit is contained in:
2026-10-04 05:32:51 +00:00
parent d4fbcbc83d
commit a31a03b2c3
7 changed files with 210 additions and 23 deletions
+8 -14
View File
@@ -2,13 +2,10 @@
package cli
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"runtime/debug"
"syscall"
"github.com/spf13/cobra"
"sneak.berlin/go/keyfunc/internal/cli/age"
@@ -70,18 +67,15 @@ func Root() *cobra.Command {
// ended with. ssh has already said whatever it had to say in that
// case, so nothing more is printed.
//
// SIGINT, SIGTERM and SIGHUP cancel the command's context instead of
// killing the process outright, so the child ssh or sftp ends and the
// deferred cleanup that removes the agent socket and the install
// working directory still runs.
// SIGINT, SIGTERM and SIGHUP end the tool at once, as they end any Go
// program, so a command waiting at the mnemonic prompt or reading what
// it encrypts or decrypts goes no further. The exceptions catch the
// signals to clean up first: "ssh to" and "ssh install" while they
// have ssh or sftp running, so the child ends and their own cleanup
// still runs, and "age encrypt -o" and "age decrypt -o" while they
// write, so the unfinished file is removed.
func Main() int {
ctx, stop := signal.NotifyContext(
context.Background(),
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP,
)
defer stop()
err := Root().ExecuteContext(ctx)
err := Root().Execute()
if err == nil {
return 0
}