Skip to content

✨ Add migration utilities for unified card framework (Phase 5)#684

Merged
clubanderson merged 1 commit intomainfrom
feat/unified-migration-utilities
Feb 7, 2026
Merged

✨ Add migration utilities for unified card framework (Phase 5)#684
clubanderson merged 1 commit intomainfrom
feat/unified-migration-utilities

Conversation

@clubanderson
Copy link
Contributor

Summary

Adds migration utilities to help plan and execute the migration of legacy card components to the UnifiedCard framework.

New Files

  • web/src/lib/unified/migration/analyzer.ts - Card complexity analysis
  • web/src/lib/unified/migration/validator.ts - Migration validation
  • web/src/lib/unified/migration/report.ts - Report generation
  • web/src/lib/unified/migration/types.ts - TypeScript types

Card Analysis Summary

Category Count Effort
Simple (list cards) 46 ~0.5h each
Moderate (charts) 32 ~2h each
Complex (maps/topologies) 15 ~4h each
Non-candidates (games/embeds) 40+ Skip

Migration Batches

  1. Batch 1: Simple list cards (36 cards, ~18h)
  2. Batch 2: Status grid cards
  3. Batch 3: Chart & gauge cards
  4. Batch 4: Table cards
  5. Batch 5: Complex visualizations

Test plan

  • npm run build passes
  • Import and use generateMigrationReport() to verify analysis
  • Validate report output matches expectations

🤖 Generated with Claude Code

Adds utilities to analyze, validate, and plan card migrations:

- analyzer.ts: Categorizes cards by complexity and visualization type
  - Simple (46 cards): Use CardWrapper pattern, ~0.5h effort each
  - Moderate (32 cards): Charts/visualizations, ~2h effort each
  - Complex (15 cards): Maps/topologies, ~4h effort each
  - Non-candidates (40+ cards): Games, embeds - skip these

- validator.ts: Validates migration correctness
  - Data parity checking between legacy and unified
  - Config validation for UnifiedCardConfig
  - Feature compatibility checking

- report.ts: Generates migration reports and batch recommendations
  - Batch 1: Simple list cards (36 cards, ~18h)
  - Batch 2: Status grid cards
  - Batch 3: Chart & gauge cards
  - Batch 4: Table cards
  - Batch 5: Complex visualizations

- types.ts: TypeScript types for migration analysis

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings February 7, 2026 03:39
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Feb 7, 2026
@kubestellar-prow
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 92ac463
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/6986b405ef72e00008ad4a81
😎 Deploy Preview https://deploy-preview-684.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@clubanderson clubanderson merged commit fed6a18 into main Feb 7, 2026
10 of 11 checks passed
@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

Welcome to KubeStellar! 🚀 Thank you for submitting this Pull Request.

Before your PR can be merged, please ensure:

DCO Sign-off - All commits must be signed off with git commit -s to certify the Developer Certificate of Origin

PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), ⚠️ (breaking change)

Getting Started with KubeStellar:

Contributor Resources:


🌟 Help KubeStellar Grow - We Need Adopters!

Our roadmap is driven entirely by adopter feedback. Whether you're using KubeStellar yourself or know someone who could benefit from multi-cluster Kubernetes:

📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction!


A maintainer will review your PR soon. Feel free to ask questions in the comments or on Slack!

@kubestellar-prow kubestellar-prow bot deleted the feat/unified-migration-utilities branch February 7, 2026 03:39
@kubestellar-prow kubestellar-prow bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Feb 7, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

🎉 Thank you for your contribution! Your PR has been successfully merged.


🌟 Help KubeStellar Grow - We Need Adopters!

Our roadmap is driven entirely by adopter feedback - nothing else. Whether you're using KubeStellar yourself or know organizations that could benefit from multi-cluster Kubernetes, we need your help:

📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction!

🗣️ Spread the word - Tell colleagues, write blog posts, present at meetups

💬 Share feedback on Slack #kubestellar-dev

Every adopter story helps us prioritize what matters most. Thank you for being part of the KubeStellar community!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new web/src/lib/unified/migration/ utility package intended to help plan and validate migration of legacy card components into the UnifiedCard framework by analyzing card types, generating reports/batches, and running basic validations.

Changes:

  • Introduces migration analysis (analyzer.ts) with complexity/visualization classification and hook registration heuristics.
  • Adds report generation and formatting utilities (report.ts) including batch recommendations.
  • Adds validation utilities (validator.ts) and shared migration-focused types (types.ts), exported via a new barrel (index.ts).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
web/src/lib/unified/migration/analyzer.ts Hardcoded card/hook classification logic and “known card types” enumeration for migration analysis.
web/src/lib/unified/migration/report.ts Generates migration reports, batch recommendations, and quick stats.
web/src/lib/unified/migration/validator.ts Provides runtime-ish migration validation (data parity) and config/feature validation helpers.
web/src/lib/unified/migration/types.ts Defines migration-specific analysis/validation/report types.
web/src/lib/unified/migration/index.ts Re-exports migration utilities from a single entrypoint.

Comment on lines +75 to +91
*/
const REGISTERED_HOOKS: Record<string, boolean> = {
useCachedPodIssues: true,
useCachedDeploymentIssues: true,
useCachedSecurityIssues: true,
useCachedEvents: true,
useCachedPods: true,
useCachedServices: true,
useCachedDeployments: true,
useClusters: true,
useGPUNodes: true,
useAlerts: true,
useOperators: true,
useHelmReleases: true,
useNamespaces: true,
// Add more as they get registered
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REGISTERED_HOOKS duplicates the hook registry but is already out of sync with the actual hook registrations (see web/src/lib/unified/registerHooks.ts, which registers many more hooks like usePVCs, useServices, etc.). This will cause isHookRegistered() / dataSource.isRegistered to be wrong for most cards. Suggest deriving this from the real registry (or exporting a hasDataHook() helper from the unified hook registry) instead of maintaining a separate hardcoded map.

Suggested change
*/
const REGISTERED_HOOKS: Record<string, boolean> = {
useCachedPodIssues: true,
useCachedDeploymentIssues: true,
useCachedSecurityIssues: true,
useCachedEvents: true,
useCachedPods: true,
useCachedServices: true,
useCachedDeployments: true,
useClusters: true,
useGPUNodes: true,
useAlerts: true,
useOperators: true,
useHelmReleases: true,
useNamespaces: true,
// Add more as they get registered
}
*
* NOTE: This is implemented as a Proxy that treats any hook name as registered.
* The actual source of truth is the unified hook registry (see registerHooks.ts);
* this avoids having to maintain a duplicated, hardcoded list here that can fall
* out of sync and incorrectly report valid hooks as unregistered.
*/
const REGISTERED_HOOKS: Record<string, boolean> = new Proxy(
{},
{
get: (
_target: Record<string, boolean>,
_prop: string | symbol,
): boolean => {
return true
},
has: (): boolean => {
return true
},
},
)

Copilot uses AI. Check for mistakes.
Comment on lines +131 to +142
* Get list of all known card types
*/
export function getAllKnownCardTypes(): string[] {
const allCards = new Set<string>()

NON_MIGRATION_CARDS.forEach(c => allCards.add(c))
UNIFIED_PATTERN_CARDS.forEach(c => allCards.add(c))
CHART_CARDS.forEach(c => allCards.add(c))
COMPLEX_CARDS.forEach(c => allCards.add(c))

return Array.from(allCards).sort()
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAllKnownCardTypes() is built from several hardcoded sets, but the repo already has an auto-generated unified card config registry (web/src/config/cards/index.ts) that enumerates all available card types. Using the hardcoded sets means generateMigrationReport() will silently omit many existing cards (e.g. alert_rules, cert_manager, etc.). Consider sourcing card types from the config registry (or from the card component registry) to keep reports complete and automatically up-to-date.

Copilot uses AI. Check for mistakes.
Comment on lines +249 to +263
export function getQuickStats(): {
totalCards: number
migrationCandidates: number
simpleCards: number
estimatedHours: number
} {
const candidates = getMigrationCandidates()
const analyses = candidates.map(analyzeCard)

return {
totalCards: getAllKnownCardTypes().length,
migrationCandidates: candidates.length,
simpleCards: analyses.filter(a => a.complexity === 'simple').length,
estimatedHours: analyses.reduce((sum, a) => sum + a.estimatedEffort, 0),
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getQuickStats() uses getMigrationCandidates(), but getMigrationCandidates() currently only includes UNIFIED_PATTERN_CARDS and CHART_CARDS and excludes COMPLEX_CARDS, even though createComplexAnalysis() marks complex cards as isMigrationCandidate: true. This makes quick stats inconsistent with generateMigrationReport() (which counts candidates via isMigrationCandidate). Consider computing candidates from analyzeCard() results (filter by isMigrationCandidate) or updating getMigrationCandidates() to include complex candidates.

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +210
// Required fields for all configs
const requiredFields = ['type', 'title', 'dataSource', 'content']

for (const field of requiredFields) {
if (!(field in config)) {
issues.push({
severity: 'error',
category: 'feature',
message: `Missing required config field: ${field}`,
})
}
}

// Validate dataSource
const dataSource = config.dataSource as Record<string, unknown> | undefined
if (dataSource) {
if (!dataSource.type) {
issues.push({
severity: 'error',
category: 'feature',
message: 'dataSource missing type field',
})
}
if (dataSource.type === 'hook' && !dataSource.hook) {
issues.push({
severity: 'error',
category: 'feature',
message: 'dataSource type is "hook" but hook name is missing',
})
}
}

// Validate content
const content = config.content as Record<string, unknown> | undefined
if (content) {
if (!content.type) {
issues.push({
severity: 'error',
category: 'feature',
message: 'content missing type field',
})
}
if (content.type === 'list' && !content.columns) {
issues.push({
severity: 'warning',
category: 'feature',
message: 'list content should have columns defined',
})
}
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateConfig()'s required fields don't match the actual UnifiedCardConfig shape: category is required by the type but is not validated here, so invalid configs can pass validation. Also, for content.type === 'list', columns are required by the unified types, so treating missing columns as a warning will under-report real config errors. Consider aligning validation rules with UnifiedCardConfig / CardDataSource / CardContent discriminated unions (e.g., validate api.endpoint, context.contextKey, custom.component, etc.) and promoting missing required fields to error.

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +164
/** Card category for grouping */
export type CardCategory =
| 'health'
| 'events'
| 'resources'
| 'ai-ml'
| 'charts'
| 'security'
| 'gitops'
| 'games'
| 'utilities'
| 'specialized'
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file introduces a new CardCategory type, but CardCategory is already a widely used exported type in web/src/lib/cards/types.ts (and re-exported via web/src/lib/unified/types.ts) with a different set of allowed values. Exporting another CardCategory from unified/migration is likely to cause confusing import/name collisions. Consider renaming this to something migration-specific (e.g. MigrationCardCategory) or reusing the existing CardCategory type if that’s what you intend.

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +120
return createNonCandidateAnalysis(cardType, 'game_or_embed')
}

// Check if uses unified pattern (simple migration)
if (UNIFIED_PATTERN_CARDS.has(normalizedType)) {
return createSimpleAnalysis(cardType)
}

// Check if chart card (moderate effort)
if (CHART_CARDS.has(normalizedType)) {
return createChartAnalysis(cardType)
}

// Check if complex card
if (COMPLEX_CARDS.has(normalizedType)) {
return createComplexAnalysis(cardType)
}

// Unknown card - assume moderate complexity
return createUnknownAnalysis(cardType)
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

analyzeCard() normalizes the input (normalizedType) for set membership checks, but then passes the unnormalized cardType into create*Analysis(). Downstream helpers like detectPatterns(), getDataSourceInfo(), pascalCase(), and configFile construction expect snake_case keys, so calling analyzeCard('pod-issues') (or any hyphenated/cased variant) will produce incorrect patterns/dataSource/componentFile values even though the card is correctly classified. Consider consistently using normalizedType for internal lookups and derived fields (and optionally store the original input separately if you want it for display).

Suggested change
return createNonCandidateAnalysis(cardType, 'game_or_embed')
}
// Check if uses unified pattern (simple migration)
if (UNIFIED_PATTERN_CARDS.has(normalizedType)) {
return createSimpleAnalysis(cardType)
}
// Check if chart card (moderate effort)
if (CHART_CARDS.has(normalizedType)) {
return createChartAnalysis(cardType)
}
// Check if complex card
if (COMPLEX_CARDS.has(normalizedType)) {
return createComplexAnalysis(cardType)
}
// Unknown card - assume moderate complexity
return createUnknownAnalysis(cardType)
return createNonCandidateAnalysis(normalizedType, 'game_or_embed')
}
// Check if uses unified pattern (simple migration)
if (UNIFIED_PATTERN_CARDS.has(normalizedType)) {
return createSimpleAnalysis(normalizedType)
}
// Check if chart card (moderate effort)
if (CHART_CARDS.has(normalizedType)) {
return createChartAnalysis(normalizedType)
}
// Check if complex card
if (COMPLEX_CARDS.has(normalizedType)) {
return createComplexAnalysis(normalizedType)
}
// Unknown card - assume moderate complexity
return createUnknownAnalysis(normalizedType)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant