-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Supporting streaming components to the catalog #5914
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,14 @@ | |
| import ( | ||
| "context" | ||
| "fmt" | ||
| "runtime" | ||
|
|
||
| "github.com/gruntwork-io/terragrunt/internal/cli/commands/catalog/tui" | ||
| "golang.org/x/sync/errgroup" | ||
|
|
||
| "github.com/gruntwork-io/terragrunt/internal/cli/commands/catalog/tui/redesign" | ||
| "github.com/gruntwork-io/terragrunt/internal/configbridge" | ||
| "github.com/gruntwork-io/terragrunt/internal/services/catalog" | ||
| "github.com/gruntwork-io/terragrunt/internal/util" | ||
| "github.com/gruntwork-io/terragrunt/internal/services/catalog/module" | ||
| "github.com/gruntwork-io/terragrunt/pkg/config" | ||
| "github.com/gruntwork-io/terragrunt/pkg/log" | ||
| "github.com/gruntwork-io/terragrunt/pkg/options" | ||
|
|
@@ -19,55 +22,123 @@ | |
| // It launches the TUI immediately with a loading screen, then runs source | ||
| // discovery and module loading in the background. When loading completes, | ||
| // the TUI transitions to the module list or shows a welcome screen. | ||
| func runRedesign(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, repoURL string) error { | ||
|
Check failure on line 25 in internal/cli/commands/catalog/catalog_redesign.go
|
||
| // If an explicit URL was passed via CLI, use the default path | ||
| if repoURL != "" { | ||
| return runDefault(ctx, l, opts, repoURL) | ||
| } | ||
|
|
||
| return tui.RunRedesign(ctx, l, opts, func(ctx context.Context, status tui.StatusFunc) (catalog.CatalogService, error) { | ||
| status("Scanning terragrunt.hcl files for module sources...") | ||
| return redesign.RunRedesign( | ||
| ctx, l, opts, | ||
| func( | ||
| ctx context.Context, status redesign.StatusFunc, moduleCh chan<- *module.Module, | ||
| ) (catalog.CatalogService, error) { | ||
| svc := catalog.NewCatalogService(opts) | ||
|
|
||
| onModule := func(mod *module.Module) { | ||
| select { | ||
| case moduleCh <- mod: | ||
| case <-ctx.Done(): | ||
| } | ||
| } | ||
|
|
||
| urlCh := make(chan string, 10) //nolint:mnd | ||
|
|
||
| g, gctx := errgroup.WithContext(ctx) | ||
|
|
||
| g.Go(func() error { | ||
| return discoverCatalogConfigURLs(gctx, l, opts, urlCh) | ||
| }) | ||
|
|
||
| g.Go(func() error { | ||
| return discoverSourceFileURLs(gctx, l, opts, urlCh) | ||
| }) | ||
|
|
||
| go func() { | ||
| _ = g.Wait() | ||
|
|
||
| // Create parsing context for source discovery and catalog config | ||
| ctx, pctx := configbridge.NewParsingContext(ctx, l, opts) | ||
| close(urlCh) | ||
| }() | ||
|
|
||
| // Discover source URLs from terraform.source in terragrunt.hcl files | ||
| discoveredURLs, err := DiscoverSourceURLs(ctx, l, pctx) | ||
| if err != nil { | ||
| l.Warnf("Failed to discover source URLs: %v", err) | ||
| } | ||
| status("Discovering catalog sources...") | ||
|
|
||
| // Also read catalog config if it exists | ||
| catalogCfg, catalogErr := config.ReadCatalogConfig(ctx, l, pctx) | ||
| if catalogErr != nil { | ||
| l.Debugf("No catalog config found: %v", catalogErr) | ||
| } | ||
| maxWorkers := max(1, min(opts.Parallelism, runtime.GOMAXPROCS(0))) | ||
|
|
||
| // Merge: catalog URLs first, then discovered URLs | ||
| var allURLs []string | ||
| if catalogCfg != nil { | ||
| allURLs = append(allURLs, catalogCfg.URLs...) | ||
| } | ||
| loaders, loadCtx := errgroup.WithContext(gctx) | ||
| loaders.SetLimit(maxWorkers) | ||
|
|
||
| allURLs = append(allURLs, discoveredURLs...) | ||
| allURLs = util.RemoveDuplicates(allURLs) | ||
| seen := make(map[string]struct{}) | ||
|
|
||
| if len(allURLs) == 0 { | ||
| return nil, nil | ||
| } | ||
| for repoURL := range urlCh { | ||
| if _, ok := seen[repoURL]; ok { | ||
| continue | ||
| } | ||
|
|
||
| status(fmt.Sprintf("Found %d source(s), cloning repositories...", len(allURLs))) | ||
| seen[repoURL] = struct{}{} | ||
|
|
||
| // Load modules from all discovered repos | ||
| svc := catalog.NewCatalogService(opts) | ||
| svc.WithRepoURLs(allURLs) | ||
| loaders.Go(func() error { | ||
| if err := svc.LoadStreamingURL(loadCtx, l, repoURL, onModule); err != nil { | ||
| // Individual repo failures are non-critical — warn and | ||
| // continue so remaining repos can still load. | ||
| l.Warnf("Error loading %s: %v", repoURL, err) | ||
| } | ||
|
|
||
| return nil | ||
| }) | ||
| } | ||
|
|
||
| if err := loaders.Wait(); err != nil { | ||
| return nil, fmt.Errorf("loading modules: %w", err) | ||
| } | ||
|
|
||
| if err := g.Wait(); err != nil { | ||
| return nil, fmt.Errorf("discovering sources: %w", err) | ||
| } | ||
|
|
||
| if len(svc.Modules()) == 0 { | ||
| return nil, nil | ||
| } | ||
|
|
||
| return svc, nil | ||
|
Comment on lines
+79
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't convert loader failures into "no sources found". Every 🤖 Prompt for AI Agents |
||
| }) | ||
| } | ||
|
|
||
| if err := svc.Load(ctx, l); err != nil { | ||
| return svc, err | ||
| } | ||
| // discoverCatalogConfigURLs reads catalog URLs from the root config and | ||
| // sends each to urlCh. | ||
| func discoverCatalogConfigURLs(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, urlCh chan<- string) error { | ||
| _, pctx := configbridge.NewParsingContext(ctx, l, opts) | ||
|
|
||
| status(fmt.Sprintf("Found %d module(s), loading catalog...", len(svc.Modules()))) | ||
| catalogCfg, err := config.ReadCatalogConfig(ctx, l, pctx) | ||
| if err != nil { | ||
| l.Debugf("No catalog config found: %v", err) | ||
| return nil | ||
| } | ||
|
|
||
| if catalogCfg == nil { | ||
| return nil | ||
| } | ||
|
|
||
| for _, u := range catalogCfg.URLs { | ||
| urlCh <- u | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // discoverSourceFileURLs walks terragrunt.hcl files, extracts | ||
| // terraform.source URLs, and sends each repo URL to urlCh. | ||
| func discoverSourceFileURLs(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, urlCh chan<- string) error { | ||
| ctx, pctx := configbridge.NewParsingContext(ctx, l, opts) | ||
|
|
||
| urls, err := redesign.DiscoverSourceURLs(ctx, l, pctx) | ||
| if err != nil { | ||
| l.Warnf("Failed to discover source URLs: %v", err) | ||
| return nil | ||
| } | ||
|
|
||
| for _, u := range urls { | ||
| urlCh <- u | ||
| } | ||
|
|
||
| return svc, nil | ||
| }) | ||
| return nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.