✨ Add migration utilities for unified card framework (Phase 5)#684
✨ Add migration utilities for unified card framework (Phase 5)#684clubanderson merged 1 commit intomainfrom
Conversation
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>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
✅ Deploy Preview for kubestellarconsole ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
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 ✅ PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), 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! |
|
🎉 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! |
There was a problem hiding this comment.
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. |
| */ | ||
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| */ | |
| 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 | |
| }, | |
| }, | |
| ) |
| * 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() | ||
| } |
There was a problem hiding this comment.
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.
| 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), | ||
| } |
There was a problem hiding this comment.
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.
| // 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', | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| /** Card category for grouping */ | ||
| export type CardCategory = | ||
| | 'health' | ||
| | 'events' | ||
| | 'resources' | ||
| | 'ai-ml' | ||
| | 'charts' | ||
| | 'security' | ||
| | 'gitops' | ||
| | 'games' | ||
| | 'utilities' | ||
| | 'specialized' |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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).
| 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) |
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 analysisweb/src/lib/unified/migration/validator.ts- Migration validationweb/src/lib/unified/migration/report.ts- Report generationweb/src/lib/unified/migration/types.ts- TypeScript typesCard Analysis Summary
Migration Batches
Test plan
npm run buildpassesgenerateMigrationReport()to verify analysis🤖 Generated with Claude Code