Skip to content

Commit fbf8ec0

Browse files
authored
Merge branch 'main' into fix/govet-store-import
2 parents 0318b4b + cb7e528 commit fbf8ec0

33 files changed

Lines changed: 287 additions & 78 deletions

.github/workflows/pr-verifier.yml

Lines changed: 0 additions & 16 deletions
This file was deleted.

.github/workflows/pre-merge-gate.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
build-gate:
1313
name: build-gate
1414
runs-on: ubuntu-latest
15-
timeout-minutes: 10
15+
timeout-minutes: 15
1616
steps:
1717
- uses: actions/checkout@v4
1818

pkg/mcp/bridge.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,21 @@ import (
1313
"github.com/kubestellar/console/pkg/safego"
1414
)
1515

16+
// bridgeClient is the interface that MCP clients must satisfy for use in Bridge.
17+
// It is defined here (not in client.go) to keep Bridge testable: tests inject
18+
// mock implementations without spinning up real stdio processes.
19+
type bridgeClient interface {
20+
CallTool(ctx context.Context, name string, args map[string]interface{}) (*CallToolResult, error)
21+
Tools() []Tool
22+
Stop() error
23+
IsReady() bool
24+
}
25+
1626
// Bridge manages MCP client connections and provides a unified interface
1727
type Bridge struct {
18-
opsClient *Client
19-
deployClient *Client
20-
gadgetClient *Client
28+
opsClient bridgeClient
29+
deployClient bridgeClient
30+
gadgetClient bridgeClient
2131
mu sync.RWMutex
2232
config BridgeConfig
2333
}
@@ -489,8 +499,6 @@ func (b *Bridge) CallDeployTool(ctx context.Context, name string, args map[strin
489499
return client.CallTool(ctx, name, args)
490500
}
491501

492-
493-
494502
// Status returns the current status of the MCP bridge
495503
func (b *Bridge) Status() map[string]interface{} {
496504
b.mu.RLock()

pkg/mcp/bridge_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ func TestBridge_GetPods(t *testing.T) {
555555
}
556556
return tc.mockResponse, nil
557557
})
558-
bridge.opsClient = mockOps.Client
558+
bridge.opsClient = mockOps
559559

560560
pods, err := bridge.GetPods(context.Background(), tc.cluster, tc.namespace, tc.labelSelector)
561561

@@ -648,7 +648,7 @@ func TestBridge_FindPodIssues(t *testing.T) {
648648
}
649649
return tc.mockResponse, nil
650650
})
651-
bridge.opsClient = mockOps.Client
651+
bridge.opsClient = mockOps
652652

653653
issues, err := bridge.FindPodIssues(context.Background(), tc.cluster, tc.namespace)
654654

@@ -734,7 +734,7 @@ func TestBridge_GetEvents(t *testing.T) {
734734
}
735735
return tc.mockResponse, nil
736736
})
737-
bridge.opsClient = mockOps.Client
737+
bridge.opsClient = mockOps
738738

739739
events, err := bridge.GetEvents(context.Background(), tc.cluster, tc.namespace, tc.limit)
740740

@@ -819,7 +819,7 @@ func TestBridge_GetWarningEvents(t *testing.T) {
819819
}
820820
return tc.mockResponse, nil
821821
})
822-
bridge.opsClient = mockOps.Client
822+
bridge.opsClient = mockOps
823823

824824
events, err := bridge.GetWarningEvents(context.Background(), tc.cluster, tc.namespace, tc.limit)
825825

@@ -905,7 +905,7 @@ func TestBridge_GetClusterHealth(t *testing.T) {
905905
}
906906
return tc.mockResponse, nil
907907
})
908-
bridge.opsClient = mockOps.Client
908+
bridge.opsClient = mockOps
909909

910910
health, err := bridge.GetClusterHealth(context.Background(), tc.cluster)
911911

@@ -974,7 +974,7 @@ func TestBridge_ListClusters(t *testing.T) {
974974
}
975975
return tc.mockResponse, nil
976976
})
977-
bridge.opsClient = mockOps.Client
977+
bridge.opsClient = mockOps
978978

979979
clusters, err := bridge.ListClusters(context.Background())
980980

pkg/mcp/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ func (c *Client) CallTool(ctx context.Context, name string, args map[string]inte
333333
return nil, err
334334
}
335335

336+
if result == nil {
337+
return nil, fmt.Errorf("nil result from tools/call")
338+
}
336339
var toolResult CallToolResult
337340
if err := json.Unmarshal(result, &toolResult); err != nil {
338341
return nil, fmt.Errorf("failed to parse tool result: %w", err)
@@ -355,6 +358,9 @@ func (c *Client) initialize(ctx context.Context) error {
355358
return err
356359
}
357360

361+
if result == nil {
362+
return fmt.Errorf("nil result from initialize")
363+
}
358364
var initResult InitializeResult
359365
if err := json.Unmarshal(result, &initResult); err != nil {
360366
return fmt.Errorf("failed to parse initialize result: %w", err)
@@ -375,6 +381,9 @@ func (c *Client) listTools(ctx context.Context) error {
375381
return err
376382
}
377383

384+
if result == nil {
385+
return fmt.Errorf("nil result from tools/list")
386+
}
378387
var toolsResult ToolsListResult
379388
if err := json.Unmarshal(result, &toolsResult); err != nil {
380389
return fmt.Errorf("failed to parse tools list: %w", err)

web/src/components/auth/Login.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ describe('Login Component', () => {
6868
expect(screen.getByText('KubeStellar')).toBeInTheDocument()
6969
})
7070

71+
it('does not render a terms of service footer', () => {
72+
renderLogin()
73+
expect(screen.queryByText('login.termsOfServicePrefix')).not.toBeInTheDocument()
74+
expect(screen.queryByText('login.termsOfServiceLink')).not.toBeInTheDocument()
75+
})
76+
7177
describe('OAuth setup wizard (backendUp && !oauthConfigured)', () => {
7278
beforeEach(() => {
7379
oauthProbeResult = { backendUp: true, oauthConfigured: false }

web/src/components/auth/Login.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ import { sanitizeUrl } from '@/lib/utils/sanitizeUrl'
1919
// The Suspense fallback (spinner) shows while loading.
2020
const GlobeAnimation = safeLazy(() => import('../animations/globe'), 'GlobeAnimation')
2121

22-
// Apache 2.0 license is the project's effective terms; link opens in a new tab (#8376).
23-
const TERMS_OF_SERVICE_URL = 'https://github.com/kubestellar/console/blob/main/LICENSE'
24-
2522
// GitHub Developer Settings URL for creating OAuth Apps.
2623
const GITHUB_DEVELOPER_SETTINGS_URL = 'https://github.com/settings/developers'
2724

@@ -583,18 +580,6 @@ export function Login() {
583580
</div>
584581
)}
585582

586-
{/* Footer */}
587-
<div className="text-center text-sm text-muted-foreground mt-8">
588-
{t('login.termsOfServicePrefix')}{' '}
589-
<a
590-
href={TERMS_OF_SERVICE_URL}
591-
target="_blank"
592-
rel="noopener noreferrer"
593-
className="underline hover:text-foreground transition-colors"
594-
>
595-
{t('login.termsOfServiceLink')}
596-
</a>
597-
</div>
598583
</div>
599584
</div>
600585

web/src/components/clusters/components/ClusterGrid.common.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,21 @@ export function ActionTooltipWrapper({
3838
tooltip: string
3939
children: ReactNode
4040
}) {
41+
const handleKeyDown = (event: KeyboardEvent) => {
42+
if (event.key === 'Enter' || event.key === ' ') {
43+
event.preventDefault()
44+
event.stopPropagation()
45+
}
46+
}
47+
4148
return (
4249
<span
4350
className="inline-flex"
51+
role="button"
52+
tabIndex={0}
4453
onClick={(event) => event.stopPropagation()}
4554
onMouseDown={(event) => event.stopPropagation()}
55+
onKeyDown={handleKeyDown}
4656
>
4757
<Tooltip content={tooltip}>{children}</Tooltip>
4858
</span>

web/src/components/drilldown/views/HelmReleaseDrillDown.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export function HelmReleaseDrillDown({ data }: Props) {
169169
if (result.success) {
170170
// Refresh data after rollback
171171
fetchReleaseInfo()
172-
fetchHistory()
172+
fetchHistory(true)
173173
}
174174
}
175175

@@ -228,8 +228,8 @@ export function HelmReleaseDrillDown({ data }: Props) {
228228
}
229229

230230
// Fetch release history
231-
const fetchHistory = async () => {
232-
if (!agentConnected || releaseHistory) return
231+
const fetchHistory = async (force = false) => {
232+
if (!agentConnected || (releaseHistory && !force)) return
233233
setHistoryLoading(true)
234234
try {
235235
const output = await runHelm(['history', releaseName, '-n', namespace, '-o', 'json'])
@@ -252,8 +252,9 @@ export function HelmReleaseDrillDown({ data }: Props) {
252252
}
253253
} catch {
254254
setReleaseHistory([])
255+
} finally {
256+
setHistoryLoading(false)
255257
}
256-
setHistoryLoading(false)
257258
}
258259

259260
// Fetch release resources (manifest)

web/src/components/drilldown/views/PodDrillDown.actions.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface UsePodActionsProps {
2222
annotations: Record<string, string> | null
2323
ownerChain: RelatedResource[]
2424
openTrackedWs: () => Promise<WebSocket>
25+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- WS messages are untyped JSON
2526
parseWsMessage: (event: MessageEvent, context: string) => any
2627
}
2728

0 commit comments

Comments
 (0)