diff --git a/.golangci.yml b/.golangci.yml index 5d08fcb6de..6d7146734e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -28,6 +28,7 @@ linters: - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] - canonicalheader # canonicalheader checks whether net/http.Header uses canonical header [fast: false, auto-fix: false] - containedctx # Containedctx is a linter that detects struct contained context.Context field. + - contextcheck # Check whether the function uses a non-inherited context. - copyloopvar # copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false] - decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false] - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] @@ -148,6 +149,11 @@ linters: linters: - tagalign + # cobra API requires func(*cobra.Command) error signature; context extracted from command + - linters: + - contextcheck + text: "should pass the context parameter" + issues: max-issues-per-linter: 0 max-same-issues: 0 diff --git a/core/bootstrap.go b/core/bootstrap.go index ddeca67357..841fce695e 100644 --- a/core/bootstrap.go +++ b/core/bootstrap.go @@ -199,9 +199,6 @@ func Bootstrap(ctx context.Context, config *BootstrapConfig) (exitCode int, resu meta.OverrideExec = defaultOverrideExec } - if ctx == nil { - ctx = context.Background() - } ctx = account.InjectHTTPClient(ctx, httpClient) ctx = InjectMeta(ctx, meta) diff --git a/core/build_info.go b/core/build_info.go index ffe0dfddb5..cb36f8ecc4 100644 --- a/core/build_info.go +++ b/core/build_info.go @@ -72,7 +72,7 @@ func (b *BuildInfo) checkVersion(ctx context.Context) { } // pull latest version - latestVersion, err := getLatestVersion(ExtractHTTPClient(ctx)) + latestVersion, err := getLatestVersion(ctx) if err != nil { ExtractLogger(ctx).Debugf("failed to retrieve latest version: %s\n", err) @@ -89,15 +89,15 @@ func (b *BuildInfo) checkVersion(ctx context.Context) { } // getLatestVersion attempt to read the latest version of the remote file at latestVersionFileURL. -func getLatestVersion(client *http.Client) (*version.Version, error) { - ctx, cancelTimeout := context.WithTimeout(context.Background(), latestVersionRequestTimeout) +func getLatestVersion(ctx context.Context) (*version.Version, error) { + ctx, cancelTimeout := context.WithTimeout(ctx, latestVersionRequestTimeout) defer cancelTimeout() req, err := http.NewRequestWithContext(ctx, http.MethodGet, latestGithubReleaseURL, nil) if err != nil { return nil, err } - resp, err := client.Do(req) + resp, err := ExtractHTTPClient(ctx).Do(req) if resp != nil { defer resp.Body.Close() } diff --git a/core/cobra_builder.go b/core/cobra_builder.go index edda33201b..f22cd6a177 100644 --- a/core/cobra_builder.go +++ b/core/cobra_builder.go @@ -150,8 +150,7 @@ func (b *cobraBuilder) hydrateCobra( // Use a custom function to print usage // This function will build usage to avoid building it for each commands - cobraCmd.SetUsageFunc(usageFuncBuilder(cobraCmd, func() { - ctx := cobraCmd.Context() + cobraCmd.SetUsageFunc(usageFuncBuilder(cobraCmd, func(ctx context.Context) { if cobraCmd.Annotations == nil { cobraCmd.Annotations = make(map[string]string) } @@ -180,7 +179,7 @@ func (b *cobraBuilder) hydrateCobra( })) if cmd.Run != nil { - cobraCmd.RunE = cobraRun(context.TODO(), cmd) + cobraCmd.RunE = cobraRun(cmd) } else { // If command is not runnable we create a default run function that // will print usage of the parent command and exit with code 1 diff --git a/core/cobra_usage_builder.go b/core/cobra_usage_builder.go index 6b18c9b53c..f92b8d884e 100644 --- a/core/cobra_usage_builder.go +++ b/core/cobra_usage_builder.go @@ -153,16 +153,27 @@ func buildExamples(binaryName string, cmd *Command) string { // usageFuncBuilder returns the usage function that will be used by cobra to print usage, // the builder also takes a function that will fill annotations used by the usage template, // this is done like this to avoid build annotations for each command if not required -func usageFuncBuilder(cmd *cobra.Command, annotationBuilder func()) func(*cobra.Command) error { +func usageFuncBuilder( + cmd *cobra.Command, + annotationBuilder func(context.Context), +) func(*cobra.Command) error { return func(command *cobra.Command) error { - annotationBuilder() - // after building annotation we remove this function as we prefer to use default UsageFunc - cmd.SetUsageFunc(nil) + executeWithCtx(command.Context(), cmd, annotationBuilder) return cmd.UsageFunc()(command) } } +func executeWithCtx( + ctx context.Context, + cmd *cobra.Command, + annotationBuilder func(context.Context), +) { + annotationBuilder(ctx) + // after building annotation we remove this function as we prefer to use default UsageFunc + cmd.SetUsageFunc(nil) +} + func orderCobraCommands(cobraCommands []*cobra.Command) []*cobra.Command { commands := make([]*cobra.Command, len(cobraCommands)) copy(commands, cobraCommands) diff --git a/core/cobra_utils.go b/core/cobra_utils.go index ec38040847..b968ee13e5 100644 --- a/core/cobra_utils.go +++ b/core/cobra_utils.go @@ -13,10 +13,10 @@ import ( ) // cobraRun returns a cobraRun command that wrap a CommandRunner function. -func cobraRun(ctx context.Context, cmd *Command) func(*cobra.Command, []string) error { +func cobraRun(cmd *Command) func(*cobra.Command, []string) error { return func(cobraCmd *cobra.Command, rawArgsStr []string) error { // Get context from cobra command, which should have been set during bootstrap - ctx = cobraCmd.Context() + ctx := cobraCmd.Context() rawArgs := args.RawArgs(rawArgsStr) diff --git a/internal/namespaces/mcp/server/streamable_http.go b/internal/namespaces/mcp/server/streamable_http.go index e9492755d4..ce6af12147 100644 --- a/internal/namespaces/mcp/server/streamable_http.go +++ b/internal/namespaces/mcp/server/streamable_http.go @@ -45,7 +45,7 @@ func RunStreamableHTTPServer( <-ctx.Done() // Graceful shutdown with timeout - shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 10*time.Second) defer shutdownCancel() return server.Shutdown(shutdownCtx) diff --git a/internal/tasks/tasks.go b/internal/tasks/tasks.go index 47d28e6ba6..52688edc84 100644 --- a/internal/tasks/tasks.go +++ b/internal/tasks/tasks.go @@ -134,7 +134,7 @@ func (ts *Tasks) Execute(ctx context.Context, data any) (any, error) { cancelableCtx, cleanCtx := setupContext(ctx) defer cleanCtx() - logger, err := NewTasksLogger(context.Background(), ts.LoggerMode) + logger, err := NewTasksLogger(cancelableCtx, ts.LoggerMode) if err != nil { return nil, err }