Repository: alecthomas/kong (go)

Issue:
panic on Run with no selected nodes
If there's an application with no nodes selected, instead of returning an error, `Context.Run` panics.
Reproduction:

```go
func TestRegression(t *testing.T) {
        var emptyCLI struct{}
        parser, err := kong.New(&emptyCLI)
        assert.NoError(t, err)

        kctx, err := parser.Parse([]string{})
        assert.NoError(t, err)

        err = kctx.Run()
        assert.Error(t, err)
        assert.Contains(t, err.Error(), "no command selected")
}
```

This test passes against v1.2.1 but not against 1.6.0.
`git bisect` indicates that the issue was introduced in 2544d3f00855f6857cf121ce04608296b5858e4a.

Pull request description:
fix(Context.Run): Don't panic on unselected root node
Fixes bug introduced in 2544d3f00855f6857cf121ce04608296b5858e4a.

That commit refactored control flow slightly and missed a case
where the selected node is an application node but it does not
have a Run method.

The fix was straightforward:
Treat the "if application node" code path as a possible way to fill
`node`, and check it again afterwards.

Resolves #483


Patch:
diff --git a/context.go b/context.go
index 4fd2302..b6a56e3 100644
--- a/context.go
+++ b/context.go
@@ -827,7 +827,9 @@ func (c *Context) Run(binds ...any) (err error) {
 			if method.IsValid() {
 				node = selected
 			}
-		} else {
+		}
+
+		if node == nil {
 			return fmt.Errorf("no command selected")
 		}
 	}


Test patch:
diff --git a/kong_test.go b/kong_test.go
index 2f3ab47..a7c03b2 100644
--- a/kong_test.go
+++ b/kong_test.go
@@ -2495,3 +2495,16 @@ func TestPrefixXorIssue343(t *testing.T) {
 	_, err = kctx.Parse([]string{"--source-password-file=foo", "--source-password=bar"})
 	assert.Error(t, err)
 }
+
+func TestIssue483EmptyRootNodeNoRun(t *testing.T) {
+	var emptyCLI struct{}
+	parser, err := kong.New(&emptyCLI)
+	assert.NoError(t, err)
+
+	kctx, err := parser.Parse([]string{})
+	assert.NoError(t, err)
+
+	err = kctx.Run()
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "no command selected")
+}