mirror of
https://github.com/juanfont/headscale.git
synced 2026-02-07 20:04:00 +01:00
2.1 KiB
2.1 KiB
CLI Simplification - WithClient Pattern
Problem
Every CLI command has repetitive gRPC client setup boilerplate:
// This pattern appears 25+ times across all commands
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
// ... command logic ...
Solution
Simple closure that handles client lifecycle:
// client.go - 16 lines total
func WithClient(fn func(context.Context, v1.HeadscaleServiceClient) error) error {
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
return fn(ctx, client)
}
Usage Example
Before (users.go listUsersCmd):
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig() // 4 lines
defer cancel()
defer conn.Close()
request := &v1.ListUsersRequest{}
// ... build request ...
response, err := client.ListUsers(ctx, request)
if err != nil {
ErrorOutput(err, "Cannot get users: "+status.Convert(err).Message(), output)
}
// ... handle response ...
}
After:
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
request := &v1.ListUsersRequest{}
// ... build request ...
response, err := client.ListUsers(ctx, request)
if err != nil {
ErrorOutput(err, "Cannot get users: "+status.Convert(err).Message(), output)
return err
}
// ... handle response ...
return nil
})
if err != nil {
return // Error already handled
}
}
Benefits
- Removes 4 lines of boilerplate from every command
- Ensures proper cleanup - no forgetting defer statements
- Simpler error handling - return from closure, handled centrally
- Easy to apply - minimal changes to existing commands
Rollout
This pattern can be applied to all 25+ commands systematically, removing ~100 lines of repetitive boilerplate.