From f417b691897734c6f13670b414994122f276f143 Mon Sep 17 00:00:00 2001 From: Liam Dalgarno Date: Tue, 20 Jan 2026 01:19:37 +0000 Subject: [PATCH] fix: Exit analysis on context cancel - Pass parent context down to analyzers - Check for ctx.Err() before recursing subdirectories or analyzing files. Running skaffold init in a directory with a large number of files can take an extremely long time as it crawls and analyzes all subdirectories. Previously, the use of context.Background() prevented signals (e.g. the user pressing CTRL-C) from cancelling the operation. --- cmd/skaffold/app/cmd/runner.go | 2 +- pkg/skaffold/initializer/analyze/analyze.go | 13 ++++++++++--- pkg/skaffold/initializer/analyze/analyze_test.go | 2 +- pkg/skaffold/initializer/helm_test.go | 2 +- pkg/skaffold/initializer/init.go | 6 +++--- pkg/skaffold/initializer/transparent.go | 2 +- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index df705d2ff5d..f19eb0fd909 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -114,7 +114,7 @@ func withFallbackConfig(ctx context.Context, out io.Writer, opts config.Skaffold if errors.As(err, &e) && e.StatusCode() == proto.StatusCode_CONFIG_FILE_NOT_FOUND_ERR { if (opts.AutoCreateConfig || opts.AutoInit) && initializer.ValidCmd(opts) { output.Default.Fprintf(out, "Skaffold config file %s not found - Trying to create one for you...\n", opts.ConfigurationFile) - config, err := initializer.Transparent(context.Background(), out, initConfig.Config{Opts: opts, EnableManifestGeneration: opts.AutoInit}) + config, err := initializer.Transparent(ctx, out, initConfig.Config{Opts: opts, EnableManifestGeneration: opts.AutoInit}) if err != nil { return nil, fmt.Errorf("unable to generate skaffold config file automatically - try running `skaffold init`: %w", err) } diff --git a/pkg/skaffold/initializer/analyze/analyze.go b/pkg/skaffold/initializer/analyze/analyze.go index 23e658a0c55..cc3aa45a25c 100644 --- a/pkg/skaffold/initializer/analyze/analyze.go +++ b/pkg/skaffold/initializer/analyze/analyze.go @@ -124,7 +124,11 @@ func NewAnalyzer(c config.Config) *ProjectAnalysis { // Analyze recursively walks a directory and notifies the analyzers of files and enterDir and exitDir events // at the end of the analyze function the analysis struct's analyzers should contain the state that we can // use to do further computation. -func (a *ProjectAnalysis) Analyze(dir string) error { +func (a *ProjectAnalysis) Analyze(ctx context.Context, dir string) error { + if err := ctx.Err(); err != nil { + return err + } + for _, analyzer := range a.analyzers() { analyzer.enterDir(dir) } @@ -180,7 +184,10 @@ func (a *ProjectAnalysis) Analyze(dir string) error { // to make skaffold.yaml more portable across OS-es we should always generate /-delimited filePaths filePath = strings.ReplaceAll(filePath, string(os.PathSeparator), "/") for _, analyzer := range a.analyzers() { - if err := analyzer.analyzeFile(context.Background(), filePath); err != nil { + if err := ctx.Err(); err != nil { + return err + } + if err := analyzer.analyzeFile(ctx, filePath); err != nil { return err } } @@ -188,7 +195,7 @@ func (a *ProjectAnalysis) Analyze(dir string) error { // Recurse into subdirectories for _, subdir := range subdirectories { - if err := a.Analyze(subdir); err != nil { + if err := a.Analyze(ctx, subdir); err != nil { return err } } diff --git a/pkg/skaffold/initializer/analyze/analyze_test.go b/pkg/skaffold/initializer/analyze/analyze_test.go index e6c04090f64..95636a0e308 100644 --- a/pkg/skaffold/initializer/analyze/analyze_test.go +++ b/pkg/skaffold/initializer/analyze/analyze_test.go @@ -388,7 +388,7 @@ deploy: t.Override(&jib.Validate, fakeValidateJibConfig) a := NewAnalyzer(test.config) - err := a.Analyze(".") + err := a.Analyze(t.Context(), ".") t.CheckError(test.shouldErr, err) if test.shouldErr { diff --git a/pkg/skaffold/initializer/helm_test.go b/pkg/skaffold/initializer/helm_test.go index 0886c7f9545..c5d86692119 100644 --- a/pkg/skaffold/initializer/helm_test.go +++ b/pkg/skaffold/initializer/helm_test.go @@ -107,7 +107,7 @@ spec: testutil.Run(t, test.description, func(t *testutil.T) { t.NewTempDir().WriteFiles(test.filesWithContents).Chdir() a := analyze.NewAnalyzer(config) - err := a.Analyze(".") + err := a.Analyze(t.Context(), ".") t.CheckError(test.shouldErr, err) d := deploy.NewInitializer(a.HelmChartInfo(), config) dc := d.DeployConfig() diff --git a/pkg/skaffold/initializer/init.go b/pkg/skaffold/initializer/init.go index 1952c91bc7e..d3b70ff5968 100644 --- a/pkg/skaffold/initializer/init.go +++ b/pkg/skaffold/initializer/init.go @@ -41,7 +41,7 @@ func DoInit(ctx context.Context, out io.Writer, c config.Config) error { } } - a, err := AnalyzeProject(c) + a, err := AnalyzeProject(ctx, c) if err != nil { return err } @@ -57,9 +57,9 @@ func DoInit(ctx context.Context, out io.Writer, c config.Config) error { } // AnalyzeProject scans the project directory for files and keeps track of what types of files it finds (builders, k8s manifests, etc.). -func AnalyzeProject(c config.Config) (*analyze.ProjectAnalysis, error) { +func AnalyzeProject(ctx context.Context, c config.Config) (*analyze.ProjectAnalysis, error) { a := analyze.NewAnalyzer(c) - if err := a.Analyze("."); err != nil { + if err := a.Analyze(ctx, "."); err != nil { return nil, err } return a, nil diff --git a/pkg/skaffold/initializer/transparent.go b/pkg/skaffold/initializer/transparent.go index 5c06676d511..66ed8d1759f 100644 --- a/pkg/skaffold/initializer/transparent.go +++ b/pkg/skaffold/initializer/transparent.go @@ -42,7 +42,7 @@ func Transparent(ctx context.Context, out io.Writer, c initConfig.Config) (*late } } - a, err := AnalyzeProject(c) + a, err := AnalyzeProject(ctx, c) if err != nil { return nil, err }