diff --git a/cmd/pregen.go b/cmd/pregen.go index bda8252..6cc6e96 100644 --- a/cmd/pregen.go +++ b/cmd/pregen.go @@ -83,11 +83,14 @@ func pregenHandler(cmd *cobra.Command, args []string) error { logFn("[debug] fetching project graph from Supermodel API...") - zipData, err := zip.RepoZip(proj.RootDir) + zipData, truncated, err := zip.RepoZip(proj.RootDir) if err != nil { logFn("[warn] zip error: %v", err) return nil } + if truncated { + logFn("[warn] repo zip truncated at 10 MB limit — large repos may produce incomplete graph analysis") + } apiClient := api.New(cfg.BaseURL, cfg.APIKey, debug, logFn) graph, err := fetchGraphWithCircularDeps(ctx, apiClient, proj.Name, zipData, logFn) @@ -110,11 +113,14 @@ func pregenFetch(cfg *config.Config, proj *project.Info, logFn func(string, ...i ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute) defer cancel() - zipData, err := zip.RepoZip(proj.RootDir) + zipData, truncated, err := zip.RepoZip(proj.RootDir) if err != nil { logFn("[warn] zip error: %v", err) return nil } + if truncated { + logFn("[warn] repo zip truncated at 10 MB limit — large repos may produce incomplete graph analysis") + } apiClient := api.New(cfg.BaseURL, cfg.APIKey, debug, logFn) _, err = fetchGraphWithCircularDeps(ctx, apiClient, proj.Name, zipData, logFn) diff --git a/cmd/run.go b/cmd/run.go index 91a2eaa..62c0683 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -115,7 +115,7 @@ func runHandler(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() - zipData, err := zip.RepoZip(proj.RootDir) + zipData, truncated, err := zip.RepoZip(proj.RootDir) if err != nil { logFn("[warn] zip error: %v", err) if !stale || graph == nil { @@ -126,6 +126,9 @@ func runHandler(cmd *cobra.Command, args []string) error { } // else: fall through to use stale cache } else { + if truncated { + logFn("[warn] repo zip truncated at 10 MB limit — large repos may produce incomplete graph analysis") + } apiClient := api.New(cfg.BaseURL, cfg.APIKey, debug, logFn) freshGraph, err := fetchGraphWithCircularDeps(ctx, apiClient, proj.Name, zipData, logFn) if err != nil { @@ -189,7 +192,7 @@ func runWithoutCache(cfg *config.Config, proj *project.Info, wm *project.Working ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() - zipData, err := zip.RepoZip(proj.RootDir) + zipData, truncated, err := zip.RepoZip(proj.RootDir) if err != nil { logFn("[warn] zip error: %v", err) if fallback { @@ -197,6 +200,9 @@ func runWithoutCache(cfg *config.Config, proj *project.Info, wm *project.Working } return silentExit() } + if truncated { + logFn("[warn] repo zip truncated at 10 MB limit — large repos may produce incomplete graph analysis") + } apiClient := api.New(cfg.BaseURL, cfg.APIKey, debug, logFn) graph, err := fetchGraphWithCircularDeps(ctx, apiClient, proj.Name, zipData, logFn) diff --git a/internal/zip/zip.go b/internal/zip/zip.go index c262562..4f76ca8 100644 --- a/internal/zip/zip.go +++ b/internal/zip/zip.go @@ -43,11 +43,13 @@ const maxFileSize = 512 * 1024 // 512KB per file const maxTotalSize = 10 * 1024 * 1024 // 10MB total // RepoZip creates an in-memory ZIP archive of the project root. -func RepoZip(root string) ([]byte, error) { +// The second return value is true if the archive was truncated due to maxTotalSize. +func RepoZip(root string) ([]byte, bool, error) { var buf bytes.Buffer w := zip.NewWriter(&buf) var totalSize int64 + var truncated bool err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -92,6 +94,7 @@ func RepoZip(root string) ([]byte, error) { // Check total size budget if totalSize+info.Size() > maxTotalSize { + truncated = true return io.EOF // signal we're done } @@ -115,12 +118,12 @@ func RepoZip(root string) ([]byte, error) { }) if err != nil && err != io.EOF { - return nil, err + return nil, false, err } if err := w.Close(); err != nil { - return nil, err + return nil, false, err } - return buf.Bytes(), nil + return buf.Bytes(), truncated, nil }