Skip to content

Commit f888fb9

Browse files
committed
feat: block pack when completeness check fails
Pack now runs Check() before packing. If check has errors, pack is blocked with a clear error message. Use --force to bypass the gate.
1 parent 97c4b41 commit f888fb9

1 file changed

Lines changed: 27 additions & 3 deletions

File tree

cmd/nutshell/main.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ func usage() {
3434
fmt.Print(shellArt)
3535
fmt.Println("Usage:")
3636
fmt.Println(" nutshell init [--dir <path>] Initialize a new bundle directory")
37-
fmt.Println(" nutshell pack [--dir <path>] [-o <file>] Pack directory into .nut bundle")
37+
fmt.Println(" nutshell pack [--dir <path>] [-o <file>] Pack directory into .nut bundle (check must pass)")
3838
fmt.Println(" nutshell pack --encrypt --peer <pubkey> Pack & encrypt for a specific peer")
39+
fmt.Println(" nutshell pack --force Pack even if check fails")
3940
fmt.Println(" nutshell unpack <file> [-o <path>] Unpack a .nut bundle (auto-decrypts)")
4041
fmt.Println(" nutshell inspect <file|-> [--json] Inspect bundle without unpacking")
4142
fmt.Println(" nutshell validate <file|dir> [--json] Validate bundle against spec")
@@ -135,17 +136,20 @@ var subHelpText = map[string]string{
135136
Flags:
136137
--dir, -d <path> Target directory (default: .)`,
137138

138-
"pack": `pack [--dir <path>] [-o <file>] [--encrypt --peer <pubkey>]
139+
"pack": `pack [--dir <path>] [-o <file>] [--encrypt --peer <pubkey>] [--force]
139140
140141
Pack a directory into a .nut bundle.
142+
Runs a completeness check first — if check fails, pack is blocked.
143+
Use --force to pack despite check errors.
141144
With --encrypt --peer, the bundle is encrypted for the given peer's Ed25519
142145
public key. Only that peer can unpack it.
143146
144147
Flags:
145148
--dir, -d <path> Source directory (default: .)
146149
-o <file> Output filename (default: <slug>.nut)
147150
--encrypt Encrypt the bundle for a specific peer
148-
--peer <hex-pubkey> Recipient's Ed25519 public key (64 hex chars)`,
151+
--peer <hex-pubkey> Recipient's Ed25519 public key (64 hex chars)
152+
--force Pack even if completeness check fails`,
149153

150154
"unpack": `unpack <file> [-o <path>] [--identity <keyfile>]
151155
@@ -362,13 +366,33 @@ func cmdPack(args []string) {
362366
}
363367
output, args := getFlag(args, "--output", "-o")
364368
encrypt, args := hasFlag(args, "--encrypt")
369+
force, args := hasFlag(args, "--force")
365370
peerHex, _ := getFlag(args, "--peer")
366371

367372
if encrypt && peerHex == "" {
368373
fmt.Fprintf(os.Stderr, "%s✗%s --encrypt requires --peer <hex-pubkey>\n", red, reset)
369374
os.Exit(1)
370375
}
371376

377+
// Run completeness check before packing
378+
_, checkResult, checkErr := nutshell.Check(dir)
379+
if checkErr != nil {
380+
fmt.Fprintf(os.Stderr, "%s✗%s Pre-pack check failed: %s\n", red, reset, checkErr)
381+
os.Exit(1)
382+
}
383+
if !checkResult.IsValid() {
384+
fmt.Fprintf(os.Stderr, "\n %s✗ Pack blocked — completeness check failed:%s\n\n", red, reset)
385+
for _, e := range checkResult.Errors {
386+
fmt.Fprintf(os.Stderr, " %s✗%s %s\n", red, reset, e)
387+
}
388+
fmt.Fprintln(os.Stderr)
389+
if !force {
390+
fmt.Fprintf(os.Stderr, " Fix the errors above, or use %s--force%s to pack anyway.\n\n", bold, reset)
391+
os.Exit(1)
392+
}
393+
fmt.Fprintf(os.Stderr, " %s⚠%s --force specified, packing despite errors.\n\n", yellow, reset)
394+
}
395+
372396
// Determine output path
373397
if output == "" {
374398
data, err := os.ReadFile(filepath.Join(dir, "nutshell.json"))

0 commit comments

Comments
 (0)