-
Notifications
You must be signed in to change notification settings - Fork 136
[cozypkg] Add tool for managing Package and PackageSources #1756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds a new "cozypkg" CLI with subcommands: Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as "cozypkg (add/del/dot/list)"
participant FS as FileSystem
participant K8s as "Kubernetes API"
participant Resolver as "DependencyResolver"
participant Interactive as "VariantSelector"
User->>CLI: invoke command (e.g., add pkgA -f pkgs.yaml)
CLI->>FS: read args & files (YAML docs)
FS-->>CLI: package identifiers / YAML docs
CLI->>K8s: build client & fetch PackageSource/Package resources
K8s-->>CLI: resources
rect rgb(220,235,255)
Note over Resolver: Dependency analysis (buildDependencyTree, topologicalSort)
CLI->>Resolver: build dependency graph & sort
Resolver-->>CLI: install/delete order or cycle error
end
rect rgb(255,245,220)
Note over Interactive: Interactive flow (add command)
loop per item in order
CLI->>Interactive: prompt for variant (if needed)
Interactive-->>CLI: selected variant
CLI->>K8s: create/delete Package resource
K8s-->>CLI: result
CLI-->>User: progress/status
end
end
CLI-->>User: final summary / DOT output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used📓 Path-based instructions (2)cmd/**/*.go📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.go📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (3)📚 Learning: 2025-06-04T06:22:17.306ZApplied to files:
📚 Learning: 2025-12-25T09:45:26.511ZApplied to files:
📚 Learning: 2025-12-25T09:45:26.511ZApplied to files:
🧬 Code graph analysis (4)cmd/cozypkg/main.go (1)
cmd/cozypkg/cmd/dependencies.go (5)
cmd/cozypkg/cmd/add.go (4)
cmd/cozypkg/cmd/list.go (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @kvaps, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces cozypkg, a new CLI tool for managing Cozystack packages. The implementation is well-structured, using cobra for commands and controller-runtime for Kubernetes interactions. The new commands add, del, list, and dot provide essential functionality for package management.
My review focuses on improving efficiency, maintainability, and robustness. Key suggestions include:
- Fixing an N+1 query issue in the
listcommand to improve performance. - Improving the robustness of file parsing and dependency resolution.
- Enhancing code clarity and maintainability by reducing duplication and using more idiomatic Go for Kubernetes error handling.
- Using standard library features like
tabwriterfor better table formatting.
Overall, this is a great addition. The feedback provided should help refine the tool before it's merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
cmd/cozypkg/cmd/del.go (1)
73-96: Consider extracting shared Kubernetes client setup.This kubeconfig loading and scheme registration pattern is duplicated across
add.go,del.go,list.go, anddependencies.go. Consider extracting to a shared helper function.🔎 Example helper function
// In a shared file like client.go func newK8sClient(kubeconfig string) (client.Client, error) { var config *rest.Config var err error if kubeconfig != "" { config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) } else { config, err = ctrl.GetConfig() } if err != nil { return nil, fmt.Errorf("failed to get kubeconfig: %w", err) } scheme := runtime.NewScheme() utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(cozyv1alpha1.AddToScheme(scheme)) return client.New(config, client.Options{Scheme: scheme}) }cmd/cozypkg/cmd/dependencies.go (2)
110-125: Consider consolidating init() functions.Having two
init()functions in the same file is valid Go but can reduce readability. Consider merging them.🔎 Proposed consolidation
func init() { rootCmd.AddCommand(dotCmd) dotCmd.Flags().BoolVarP(&dotCmdFlags.installed, "installed", "i", false, "show dependencies only for installed Package resources") dotCmd.Flags().BoolVar(&dotCmdFlags.components, "components", true, "show component-level dependencies (default: true)") dotCmd.Flags().StringArrayVarP(&dotCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") dotCmd.Flags().StringVar(&dotCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") + + utilruntime.Must(clientgoscheme.AddToScheme(dependenciesScheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(dependenciesScheme)) } var ( dependenciesScheme = runtime.NewScheme() ) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(dependenciesScheme)) - utilruntime.Must(cozyv1alpha1.AddToScheme(dependenciesScheme)) -}
263-275: Avoid redundant string splitting.Line 268 splits the node string twice. Consider splitting once and reusing.
🔎 Proposed fix
// Style nodes based on type if strings.Contains(node, ".") && !strings.HasPrefix(node, "cozystack.") { // Component node + parts := strings.Split(node, ".") n.Attr("shape", "box") n.Attr("style", "rounded,filled") n.Attr("fillcolor", "lightyellow") - n.Attr("label", strings.Split(node, ".")[len(strings.Split(node, "."))-1]) + n.Attr("label", parts[len(parts)-1]) } else {cmd/cozypkg/cmd/add.go (3)
111-118: Silent fallback may hide legitimate errors.When
createPackageFromFilefails, the code silently falls back to interactive installation. This could hide issues like YAML parse errors or permission problems. Consider logging when falling back.🔎 Proposed improvement
// Check if package comes from a file if filePath, fromFile := packagesFromFiles[packageName]; fromFile { // Try to create Package directly from file - if err := createPackageFromFile(ctx, k8sClient, filePath, packageName); err == nil { + err := createPackageFromFile(ctx, k8sClient, filePath, packageName) + if err == nil { fmt.Fprintf(os.Stderr, "✓ Added Package %s\n", packageName) continue } - // If failed, fall back to interactive installation + // If failed, fall back to interactive installation + fmt.Fprintf(os.Stderr, "ℹ Package definition not found in %s, using interactive mode for %s\n", filePath, packageName) }
286-296: Dependency requester tracking overwrites previous requesters.If multiple packages depend on the same package (e.g., A→X and B→X), only the last requester is stored. The error message at line 470 will only show one requester. This is acceptable but could be enhanced to show all requesters.
536-557: Consider adding an exit option for interactive variant selection.The infinite loop requires Ctrl+C to abort. Consider accepting 'q' or empty input (when multiple variants) to cancel gracefully.
🔎 Example enhancement
for { fmt.Fprintf(os.Stderr, prompt) input, err := reader.ReadString('\n') if err != nil { return "", fmt.Errorf("failed to read input: %w", err) } input = strings.TrimSpace(input) + // Allow user to quit + if input == "q" || input == "quit" { + return "", fmt.Errorf("installation cancelled by user") + } + // If input is empty and there's a default variant, use it if input == "" && len(ps.Spec.Variants) == 1 { return defaultVariant, nil }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
cmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/root.gocmd/cozypkg/main.gogo.mod
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Use Controller-runtime patterns and kubebuilder style for Go code
Files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/root.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/list.gocmd/cozypkg/main.gocmd/cozypkg/cmd/dependencies.go
{go.mod,go.sum}
📄 CodeRabbit inference engine (AGENTS.md)
Do not manually modify
go.modandgo.sumfiles; usego getcommand instead
Files:
go.mod
🧠 Learnings (1)
📓 Common learnings
Learnt from: NickVolynkin
Repo: cozystack/cozystack PR: 1117
File: packages/apps/mysql/Makefile:8-8
Timestamp: 2025-06-26T04:29:24.830Z
Learning: The cozystack project uses yq v4+ on their CI runner, so yq v4 syntax (-o json --indent 4) is compatible and version checks are not needed.
🧬 Code graph analysis (3)
cmd/cozypkg/cmd/del.go (2)
pkg/apiserver/apiserver.go (1)
Scheme(53-53)api/v1alpha1/package_types.go (1)
Package(32-38)
cmd/cozypkg/cmd/list.go (2)
api/v1alpha1/packagesource_types.go (3)
PackageSourceList(42-46)Variant(65-82)PackageSource(31-37)api/v1alpha1/package_types.go (1)
PackageList(43-47)
cmd/cozypkg/main.go (1)
cmd/cozypkg/cmd/root.go (1)
Execute(38-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (8)
cmd/cozypkg/main.go (1)
25-29: LGTM!Clean entry point. Error handling is correctly delegated to
cmd.Execute()which prints to stderr, and the exit code 1 signals failure appropriately.cmd/cozypkg/cmd/root.go (1)
26-44: LGTM!Standard Cobra root command setup.
SilenceErrorsandSilenceUsageare correctly paired with manual error printing inExecute()for controlled error output.cmd/cozypkg/cmd/del.go (1)
99-110: LGTM on delete logic.The delete loop correctly handles not-found resources gracefully by logging and continuing, which is appropriate for a batch delete operation.
cmd/cozypkg/cmd/list.go (1)
173-188: Silently ignoring PackageSource fetch errors is acceptable here.The
err == nilcheck silently skips component display when the PackageSource cannot be fetched. This is reasonable since component display is an optional enhancement and the Package list itself is still shown.cmd/cozypkg/cmd/dependencies.go (1)
180-197: Filtering logic is correct but could be simplified.The
packageNameandselectedPackagesfilters are mutually exclusive based on the calling logic (lines 88-94). The code works correctly, though a comment clarifying this invariant would help maintainability.cmd/cozypkg/cmd/add.go (2)
253-307: Dependency tree building logic is sound.The recursive
buildDependencyTreecorrectly handles cycles via thevisitedmap and collects dependencies from all variants. The approach of aggregating dependencies across variants is appropriate for determining what might need to be installed.
309-362: Topological sort implementation is correct.Kahn's algorithm is properly implemented with cycle detection. The result ordering (dependencies first) aligns with the installation order requirement.
go.mod (1)
8-8: This change follows proper dependency management practices.The
emicklei/dot v1.10.0dependency is valid (published 2025-12-03T12:57:45Z) and is actively used incmd/cozypkg/cmd/dependencies.goas part of the cozypkg tool addition. The go.mod file header states "This is a generated file. Do not edit directly," confirming that dependencies were added viago get(not manual editing), which is exactly what the coding guideline requires.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (9)
cmd/cozypkg/cmd/del.go (2)
101-109: Consider reusing the installed packages list.The installed packages are listed here (lines 102-109) and then listed again in
findPackagesToDelete(lines 161-169). Consider passinginstalledMaptofindPackagesToDeleteto avoid the redundant API call.🔎 Proposed refactor
- // Find all packages to delete (including dependents) - packagesToDelete, err := findPackagesToDelete(ctx, k8sClient, packageNames) + // Find all packages to delete (including dependents) + packagesToDelete, err := findPackagesToDelete(ctx, k8sClient, packageNames, installedMap)Then update the function signature and remove the duplicate listing inside
findPackagesToDelete.
246-260: Consider sorting package lists for consistent output.Map iteration order is non-deterministic in Go. Sorting the
requestedanddependentsslices before printing would provide consistent, reproducible output across runs.🔎 Proposed fix
Add import for
sortand sort the slices:+import "sort" ... + sort.Strings(requested) + sort.Strings(dependents) + fmt.Fprintf(os.Stderr, "\nThe following packages will be deleted:\n\n")cmd/cozypkg/cmd/dependencies.go (3)
110-125: Consider consolidating init() functions.Having two
init()functions in the same file is valid Go but can reduce readability. Consider combining them or adding a comment explaining why they're separate.🔎 Proposed fix
func init() { rootCmd.AddCommand(dotCmd) dotCmd.Flags().BoolVarP(&dotCmdFlags.installed, "installed", "i", false, "show dependencies only for installed Package resources") dotCmd.Flags().BoolVar(&dotCmdFlags.components, "components", true, "show component-level dependencies (default: true)") dotCmd.Flags().StringArrayVarP(&dotCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") dotCmd.Flags().StringVar(&dotCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") -} - -var ( - dependenciesScheme = runtime.NewScheme() -) - -func init() { + + // Initialize scheme for Kubernetes client utilruntime.Must(clientgoscheme.AddToScheme(dependenciesScheme)) utilruntime.Must(cozyv1alpha1.AddToScheme(dependenciesScheme)) } + +var ( + dependenciesScheme = runtime.NewScheme() +)
180-197: Redundant filter logic when single package is specified.When exactly one package is specified, both
packageName(line 181-183) andselectedPackages(line 186-197) filters are applied, which is redundant. This works correctly but could be simplified.🔎 Proposed simplification
Consider using only
selectedPackagesfor filtering and removing thepackageNameparameter:-func buildGraphFromCluster(ctx context.Context, kubeconfig string, packagesOnly bool, installedOnly bool, packageName string, selectedPackages []string) (map[string][]string, map[string]bool, error) { +func buildGraphFromCluster(ctx context.Context, kubeconfig string, packagesOnly bool, installedOnly bool, selectedPackages []string) (map[string][]string, map[string]bool, error) { ... - // Filter by package name if specified - if packageName != "" && psName != packageName { - continue - } - // Filter by selected packages if specified if len(selectedPackages) > 0 {Then update the caller to always use
selectedPackages.
272-272: Avoid duplicatestrings.Splitcall.The node string is split twice on the same line. Extract to a variable for efficiency.
🔎 Proposed fix
- n.Attr("label", strings.Split(node, ".")[len(strings.Split(node, "."))-1]) + parts := strings.Split(node, ".") + n.Attr("label", parts[len(parts)-1])cmd/cozypkg/cmd/add.go (4)
114-121: Silent fallback may hide important errors.When
createPackageFromFilefails, the code silently falls back to interactive installation without logging the error. This could hide issues like permission errors or malformed YAML. Consider logging the error before falling back.🔎 Proposed fix
// Check if package comes from a file if filePath, fromFile := packagesFromFiles[packageName]; fromFile { // Try to create Package directly from file - if err := createPackageFromFile(ctx, k8sClient, filePath, packageName); err == nil { + err := createPackageFromFile(ctx, k8sClient, filePath, packageName) + if err == nil { fmt.Fprintf(os.Stderr, "✓ Added Package %s\n", packageName) continue } - // If failed, fall back to interactive installation + // Log the error and fall back to interactive installation + fmt.Fprintf(os.Stderr, "⚠ Could not create Package %s from file (%v), falling back to interactive installation\n", packageName, err) }
274-276: Dependency requester may be overwritten.If multiple packages depend on the same dependency,
dependencyRequesters[dep]will only store the last requester. This affects error messages only, but could be misleading when debugging missing dependencies.🔎 Proposed fix
Consider storing all requesters or the first one:
for dep := range deps { if _, exists := tree[pkgName]; !exists { tree[pkgName] = []string{} } tree[pkgName] = append(tree[pkgName], dep) // Track who requested this dependency - dependencyRequesters[dep] = pkgName + if _, exists := dependencyRequesters[dep]; !exists { + dependencyRequesters[dep] = pkgName + }
520-541: Consider adding a maximum retry limit.The infinite loop for variant selection will retry indefinitely on invalid input. While this is intentional, consider adding a retry limit to prevent user frustration or issues when stdin is unexpectedly non-interactive.
380-383: Handle "already exists" case explicitly.If the Package already exists in the cluster,
k8sClient.Createwill return an error. This bubbles up and causes a fallback to interactive installation, which will then report "already installed". Consider checking forapierrors.IsAlreadyExistsand returning success in that case.🔎 Proposed fix
+ apierrors "k8s.io/apimachinery/pkg/api/errors" ... // Create Package if err := k8sClient.Create(ctx, &pkg); err != nil { + if apierrors.IsAlreadyExists(err) { + return nil // Already exists is not an error + } return fmt.Errorf("failed to create Package: %w", err) }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
cmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/root.gocmd/cozypkg/main.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (2)
- go.mod
- cmd/cozypkg/cmd/list.go
🧰 Additional context used
📓 Path-based instructions (2)
cmd/**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Files:
cmd/cozypkg/cmd/root.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/main.go
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Do NOT manually edit vendor/ directory or auto-generated files (zz_generated.*.go)
Files:
cmd/cozypkg/cmd/root.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/main.go
🧠 Learnings (3)
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to cmd/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Applied to files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/main.go
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to pkg/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in pkg directory
Applied to files:
cmd/cozypkg/cmd/add.go
📚 Learning: 2025-06-04T06:22:17.306Z
Learnt from: lllamnyp
Repo: cozystack/cozystack PR: 1025
File: packages/apps/kafka/charts/cozy-lib:1-1
Timestamp: 2025-06-04T06:22:17.306Z
Learning: Files in packages/apps/*/charts/cozy-lib that contain the path "../../../library/cozy-lib" are symbolic links, not regular files. Git diffs may display symlinks as regular files with their target path as content, which can be misleading in code review.
Applied to files:
cmd/cozypkg/cmd/dependencies.go
🧬 Code graph analysis (3)
cmd/cozypkg/cmd/del.go (2)
api/v1alpha1/package_types.go (2)
PackageList(43-47)Package(32-38)api/v1alpha1/packagesource_types.go (1)
PackageSourceList(42-46)
cmd/cozypkg/cmd/add.go (3)
pkg/apiserver/apiserver.go (1)
Scheme(53-53)api/v1alpha1/packagesource_types.go (3)
PackageSource(31-37)PackageSourceList(42-46)Variant(65-82)api/v1alpha1/package_types.go (3)
Package(32-38)PackageList(43-47)PackageSpec(54-69)
cmd/cozypkg/cmd/dependencies.go (5)
internal/sse/server.go (1)
Options(17-44)pkg/apiserver/apiserver.go (1)
Scheme(53-53)api/v1alpha1/package_types.go (1)
PackageList(43-47)api/v1alpha1/packagesource_types.go (1)
PackageSourceList(42-46)pkg/apis/apps/install/install.go (1)
Install(26-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (3)
cmd/cozypkg/main.go (1)
25-29: LGTM!Clean and minimal CLI entrypoint that properly delegates to
cmd.Execute()and exits with status code 1 on error. This follows standard Go CLI patterns.cmd/cozypkg/cmd/root.go (1)
26-44: LGTM!Well-structured root command setup. The
SilenceErrorsandSilenceUsageflags combined with custom error printing inExecute()provide clean error handling. TheDisableAutoGenTagis a nice touch for generated documentation.cmd/cozypkg/cmd/add.go (1)
293-346: LGTM!Well-implemented topological sort using Kahn's algorithm with proper cycle detection at lines 340-343.
dbe129a to
6e9277b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
cmd/cozypkg/cmd/del.go (1)
346-366: Add cycle detection to prevent incomplete deletions.The topological sort using Kahn's algorithm will terminate early if cycles exist, returning an incomplete list without any error. This could lead to some packages not being deleted silently.
🔎 Proposed fix
var result []string for len(queue) > 0 { node := queue[0] queue = queue[1:] result = append(result, node) // Process dependents for _, dependent := range reverseGraph[node] { inDegree[dependent]-- if inDegree[dependent] == 0 { queue = append(queue, dependent) } } } + // Check for cycles - if not all nodes were processed, a cycle exists + if len(result) != len(allNodes) { + return nil, fmt.Errorf("dependency cycle detected, cannot determine safe delete order") + } + // Reverse the result to get dependents first, then dependencies for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { result[i], result[j] = result[j], result[i] } return result, nil
🧹 Nitpick comments (1)
cmd/cozypkg/cmd/dependencies.go (1)
170-402: Consider extracting helper functions to reduce complexity.The
buildGraphFromClusterfunction is quite long (~270 lines) with deeply nested loops. While the logic is correct, extracting helper functions for component dependency processing (lines 300-348) and edge variant tracking (lines 351-399) would improve readability and testability.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
cmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/root.gocmd/cozypkg/main.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (3)
- cmd/cozypkg/cmd/list.go
- cmd/cozypkg/main.go
- cmd/cozypkg/cmd/root.go
🧰 Additional context used
📓 Path-based instructions (3)
cmd/**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/add.go
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Do NOT manually edit vendor/ directory or auto-generated files (zz_generated.*.go)
Files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/add.go
go.mod
📄 CodeRabbit inference engine (AGENTS.md)
Do NOT modify go.mod manually; use
go getcommand instead
Files:
go.mod
🧠 Learnings (3)
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to cmd/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Applied to files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.go
📚 Learning: 2025-06-04T06:22:17.306Z
Learnt from: lllamnyp
Repo: cozystack/cozystack PR: 1025
File: packages/apps/kafka/charts/cozy-lib:1-1
Timestamp: 2025-06-04T06:22:17.306Z
Learning: Files in packages/apps/*/charts/cozy-lib that contain the path "../../../library/cozy-lib" are symbolic links, not regular files. Git diffs may display symlinks as regular files with their target path as content, which can be misleading in code review.
Applied to files:
cmd/cozypkg/cmd/dependencies.go
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to pkg/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in pkg directory
Applied to files:
cmd/cozypkg/cmd/add.go
🧬 Code graph analysis (3)
cmd/cozypkg/cmd/del.go (3)
pkg/apiserver/apiserver.go (1)
Scheme(53-53)api/v1alpha1/package_types.go (2)
PackageList(43-47)Package(32-38)api/v1alpha1/packagesource_types.go (1)
PackageSourceList(42-46)
cmd/cozypkg/cmd/dependencies.go (2)
api/v1alpha1/package_types.go (1)
PackageList(43-47)api/v1alpha1/packagesource_types.go (2)
PackageSourceList(42-46)PackageSource(31-37)
cmd/cozypkg/cmd/add.go (2)
api/v1alpha1/packagesource_types.go (3)
PackageSource(31-37)PackageSourceList(42-46)Variant(65-82)api/v1alpha1/package_types.go (3)
Package(32-38)PackageList(43-47)PackageSpec(54-69)
🔇 Additional comments (16)
cmd/cozypkg/cmd/del.go (5)
19-42: LGTM!Imports are properly organized and the use of
apierrorsfor checkingIsNotFoundis idiomatic. The flag structure follows Cobra conventions.
44-156: LGTM!The command implementation follows controller-runtime patterns correctly. Error handling is thorough, with proper use of
apierrors.IsNotFoundfor handling already-deleted packages. The flow from dependency analysis to confirmation to ordered deletion is well-structured.
158-228: LGTM!The function correctly builds a reverse dependency graph and recursively finds all dependents. The
visitedmap properly handles potential cycles in the dependency graph during traversal.
230-277: LGTM!Good UX design separating requested packages from dependents in the confirmation prompt. The input validation correctly handles both "y" and "yes" responses.
369-373: LGTM!Command registration and flag definitions follow Cobra conventions.
cmd/cozypkg/cmd/dependencies.go (3)
45-108: LGTM!The command definition is well-documented with clear usage examples for piping to graphviz. The flag definitions are appropriate for the use cases.
132-168: LGTM!The Kubernetes client setup follows controller-runtime patterns. The conditional fetching of installed packages only when
installedOnlyis true addresses efficiency concerns about unnecessary API calls.
404-503: LGTM!The DOT graph generation is well-implemented with clear visual distinction between node types (packages vs components) and proper handling of missing dependencies (red/dashed). The
isPackagehelper correctly uses thepackageNamesmap for determination rather than relying on naming conventions.cmd/cozypkg/cmd/add.go (8)
48-131: LGTM!The command implementation properly handles both file-based and interactive installation paths. The duplicate package warning (lines 75-77) provides clear feedback to users about conflicting definitions across files.
133-235: LGTM!File and YAML parsing is well-implemented. The consolidated handling of
PackageListandPackageSourceList(lines 214-229) reduces code duplication. Returning an empty list for files without packages is the correct user-friendly behavior.
237-291: LGTM!The dependency tree building correctly handles cycles via the
visitedmap. ThedependencyRequestersmap provides helpful context for error messages when dependencies are missing.
293-346: LGTM!Correct implementation of Kahn's algorithm with proper cycle detection (lines 340-343). This is the pattern that
getDeleteOrderindel.goshould follow.
348-390: LGTM!The function correctly parses YAML, finds the matching Package by name, and creates it via the Kubernetes client with proper error handling.
392-495: LGTM!The installation flow is well-structured: build dependency tree → topological sort → collect variant selections → create packages. The handling of already-installed packages with their existing variants ensures consistency.
497-542: LGTM!Good UX design with automatic default selection for single-variant packages. Using stderr for interactive prompts keeps stdout clean for potential piped output.
544-548: LGTM!Command registration and flag definitions follow Cobra conventions consistently with other commands in this package.
8f89395 to
9fb0a39
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
cmd/cozypkg/cmd/dependencies.go (1)
110-116: Consider consolidating init functions.While valid Go, having two separate
init()functions in the same file is unusual. You could merge them into a singleinit()for clarity.🔎 Consolidation example
-var dotCmd = &cobra.Command{ - // ... -} - -func init() { - rootCmd.AddCommand(dotCmd) - dotCmd.Flags().BoolVarP(&dotCmdFlags.installed, "installed", "i", false, "show dependencies only for installed Package resources") - dotCmd.Flags().BoolVar(&dotCmdFlags.components, "components", false, "show component-level dependencies") - dotCmd.Flags().StringArrayVarP(&dotCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") - dotCmd.Flags().StringVar(&dotCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") -} - -var ( - dependenciesScheme = runtime.NewScheme() -) - func init() { + // Initialize scheme utilruntime.Must(clientgoscheme.AddToScheme(dependenciesScheme)) utilruntime.Must(cozyv1alpha1.AddToScheme(dependenciesScheme)) + + // Register command + rootCmd.AddCommand(dotCmd) + dotCmd.Flags().BoolVarP(&dotCmdFlags.installed, "installed", "i", false, "show dependencies only for installed Package resources") + dotCmd.Flags().BoolVar(&dotCmdFlags.components, "components", false, "show component-level dependencies") + dotCmd.Flags().StringArrayVarP(&dotCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") + dotCmd.Flags().StringVar(&dotCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") }Also applies to: 122-125
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
cmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/dependencies.gocmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/root.gocmd/cozypkg/main.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (3)
- cmd/cozypkg/main.go
- go.mod
- cmd/cozypkg/cmd/root.go
🧰 Additional context used
📓 Path-based instructions (2)
cmd/**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Files:
cmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/dependencies.go
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
Do NOT manually edit vendor/ directory or auto-generated files (zz_generated.*.go)
Files:
cmd/cozypkg/cmd/list.gocmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.gocmd/cozypkg/cmd/dependencies.go
🧠 Learnings (3)
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to cmd/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in cmd directory
Applied to files:
cmd/cozypkg/cmd/del.gocmd/cozypkg/cmd/add.go
📚 Learning: 2025-12-25T09:45:26.511Z
Learnt from: CR
Repo: cozystack/cozystack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T09:45:26.511Z
Learning: Applies to pkg/**/*.go : Follow controller-runtime patterns and kubebuilder style for Go code in pkg directory
Applied to files:
cmd/cozypkg/cmd/add.go
📚 Learning: 2025-06-04T06:22:17.306Z
Learnt from: lllamnyp
Repo: cozystack/cozystack PR: 1025
File: packages/apps/kafka/charts/cozy-lib:1-1
Timestamp: 2025-06-04T06:22:17.306Z
Learning: Files in packages/apps/*/charts/cozy-lib that contain the path "../../../library/cozy-lib" are symbolic links, not regular files. Git diffs may display symlinks as regular files with their target path as content, which can be misleading in code review.
Applied to files:
cmd/cozypkg/cmd/dependencies.go
🧬 Code graph analysis (4)
cmd/cozypkg/cmd/list.go (3)
pkg/apiserver/apiserver.go (1)
Scheme(53-53)api/v1alpha1/packagesource_types.go (3)
PackageSourceList(42-46)PackageSource(31-37)Variant(65-82)api/v1alpha1/package_types.go (1)
PackageList(43-47)
cmd/cozypkg/cmd/del.go (2)
api/v1alpha1/package_types.go (2)
PackageList(43-47)Package(32-38)api/v1alpha1/packagesource_types.go (1)
PackageSourceList(42-46)
cmd/cozypkg/cmd/add.go (2)
api/v1alpha1/packagesource_types.go (3)
PackageSource(31-37)PackageSourceList(42-46)Variant(65-82)api/v1alpha1/package_types.go (3)
Package(32-38)PackageList(43-47)PackageSpec(54-69)
cmd/cozypkg/cmd/dependencies.go (3)
api/v1alpha1/package_types.go (1)
PackageList(43-47)api/v1alpha1/packagesource_types.go (2)
PackageSourceList(42-46)PackageSource(31-37)pkg/apis/apps/install/install.go (1)
Install(26-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (10)
cmd/cozypkg/cmd/list.go (2)
149-160: LGTM: N+1 query issue resolved.The code now fetches all PackageSource resources once upfront and stores them in a map for lookup, eliminating the N+1 query problem flagged in previous reviews.
93-95: LGTM: tabwriter properly adopted.The code now uses
text/tabwriterfor column alignment, making the table formatting robust and maintainable. This addresses the previous concern about hardcoded padding.Also applies to: 162-164
cmd/cozypkg/cmd/del.go (3)
151-151: LGTM: Idiomatic error checking.The code now uses
apierrors.IsNotFound(err)for NotFound error detection, which is more idiomatic and readable than the previous approach.
367-381: LGTM: Cycle detection implemented.The function now properly detects dependency cycles by comparing processed nodes against total nodes, returning a clear error with the list of unprocessed packages. This prevents incomplete deletions and provides actionable feedback.
285-389: Well-structured topological sort.The reverse topological sort correctly ensures dependents are deleted before their dependencies, using Kahn's algorithm with proper cycle detection. The implementation is clear and handles edge cases appropriately.
cmd/cozypkg/cmd/dependencies.go (1)
160-160: LGTM: Condition correctly guards API call.The installed packages list is now fetched only when
installedOnlyis true, eliminating the unnecessary API call mentioned in previous reviews.cmd/cozypkg/cmd/add.go (4)
75-77: LGTM: Duplicate package warning added.The code now warns users when a package is defined in multiple files, preventing silent overwrites and making the behavior transparent.
214-229: LGTM: Parsing consolidated.The code now handles both
PackageListandPackageSourceListin a single conditional block, eliminating duplication and improving maintainability.
232-234: LGTM: Returns empty list gracefully.The function no longer errors when no packages are found in a file, allowing users to provide files with other Kubernetes resources or empty files without failure. The actual validation happens later in
RunE.
392-495: Well-structured dependency installation.The
installPackagefunction handles dependency resolution, variant selection, and package creation in a clear, sequential flow. The two-phase approach (collect variants, then create resources) ensures all user interactions happen upfront before any API calls.
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com> <!-- Thank you for making a contribution! Here are some tips for you: - Start the PR title with the [label] of Cozystack component: - For system components: [platform], [system], [linstor], [cilium], [kube-ovn], [dashboard], [cluster-api], etc. - For managed apps: [apps], [tenant], [kubernetes], [postgres], [virtual-machine] etc. - For development and maintenance: [tests], [ci], [docs], [maintenance]. - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does ### Release note <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same [label] as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note [cozypkg] Add tool for managing Package and PackageSources ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * New cozy CLI with entrypoint and root command. * Interactive "add" command to install packages from args or files, with dependency resolution, topological install order, variant selection, and progress feedback. * "del" command to remove packages safely, including dependent analysis, confirmation prompts, and safe delete ordering. * "list" command to show available or installed packages, with optional per-component output and concise status lines. * "dependencies" command to emit Graphviz DOT graphs of package/component relationships. * Common flags: kubeconfig, file inputs, --installed and --components where applicable. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…d backup system (#1867) ## What this PR does Update changelog for v1.0.0-alpha.1 to include missing features: - **Cozystack Operator**: New operator for Package and PackageSource management (#1740, #1741, #1755, #1756, #1760, #1761) - **Backup System**: Comprehensive backup functionality with Velero integration (#1640, #1685, #1687, #1708, #1719, #1720, #1737, #1762) - Add @androndo to contributors - Update Full Changelog link to v0.38.0...v1.0.0-alpha.1 ### Release note ```release-note [docs] Update changelog for v1.0.0-alpha.1: add cozystack-operator and backup system ```
Signed-off-by: Andrei Kvapil kvapss@gmail.com
What this PR does
Release note
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.