diff --git a/cmd/app/app.go b/cmd/app/app.go index 637e4e5c..42113397 100644 --- a/cmd/app/app.go +++ b/cmd/app/app.go @@ -46,8 +46,9 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command { return runListCommand(cmd, clients) }, PostRunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() if cmd.CalledAs() == "workspace" { - clients.IO.PrintInfo(cmd.Context(), false, fmt.Sprintf( + clients.IO.PrintInfo(ctx, false, fmt.Sprintf( "\n%s It looks like you used %s. This command will be deprecated in an upcoming release.\n You can now use %s instead of %s.\n ", style.Emoji("bulb"), style.Commandf("workspace", true), diff --git a/cmd/app/link_test.go b/cmd/app/link_test.go index 227f045b..511b54f7 100644 --- a/cmd/app/link_test.go +++ b/cmd/app/link_test.go @@ -61,7 +61,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth1, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) cm.IO.On("SelectPrompt", mock.Anything, "Select the existing app team", @@ -118,7 +118,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth1, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) cm.IO.On("SelectPrompt", mock.Anything, "Select the existing app team", @@ -177,7 +177,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth2, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) existingApp := types.App{ AppID: mockLinkAppID1, TeamDomain: mockLinkSlackAuth1.TeamDomain, @@ -246,7 +246,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth2, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) existingApp := types.App{ AppID: mockLinkAppID1, TeamDomain: mockLinkSlackAuth1.TeamDomain, @@ -321,7 +321,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth2, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) existingApp := types.App{ AppID: mockLinkAppID2, TeamDomain: mockLinkSlackAuth2.TeamDomain, @@ -389,7 +389,7 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth2, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) cm.IO.On("SelectPrompt", mock.Anything, "Select the existing app team", @@ -437,9 +437,9 @@ func Test_Apps_Link(t *testing.T) { mockLinkSlackAuth1, }, nil) cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) // Set manifest source to project to trigger confirmation prompt - if err := cm.Config.ProjectConfig.SetManifestSource(t.Context(), config.MANIFEST_SOURCE_LOCAL); err != nil { + if err := cm.Config.ProjectConfig.SetManifestSource(ctx, config.MANIFEST_SOURCE_LOCAL); err != nil { require.FailNow(t, fmt.Sprintf("Failed to set the manifest source in the memory-based file system: %s", err)) } // Accept manifest source confirmation prompt @@ -506,9 +506,9 @@ func Test_Apps_Link(t *testing.T) { "decline manifest source prompt should not link app": { Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) { cm.AddDefaultMocks() - setupAppLinkCommandMocks(t, cm, cf) + setupAppLinkCommandMocks(t, ctx, cm, cf) // Set manifest source to project to trigger confirmation prompt - if err := cm.Config.ProjectConfig.SetManifestSource(t.Context(), config.MANIFEST_SOURCE_LOCAL); err != nil { + if err := cm.Config.ProjectConfig.SetManifestSource(ctx, config.MANIFEST_SOURCE_LOCAL); err != nil { require.FailNow(t, fmt.Sprintf("Failed to set the manifest source in the memory-based file system: %s", err)) } // Decline manifest source confirmation prompt @@ -593,8 +593,7 @@ func Test_Apps_LinkAppHeaderSection(t *testing.T) { } } -func setupAppLinkCommandMocks(t *testing.T, cm *shared.ClientsMock, cf *shared.ClientFactory) { - ctx := t.Context() +func setupAppLinkCommandMocks(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) { projectDirPath := slackdeps.MockWorkingDirectory cm.Os.On("Getwd").Return(projectDirPath, nil) diff --git a/cmd/auth/list.go b/cmd/auth/list.go index 23e5a834..5809a982 100644 --- a/cmd/auth/list.go +++ b/cmd/auth/list.go @@ -51,8 +51,9 @@ func NewListCommand(clients *shared.ClientFactory) *cobra.Command { // runListCommand will execute the list command func runListCommand(cmd *cobra.Command, clients *shared.ClientFactory) error { + ctx := cmd.Context() log := newListLogger(cmd, clients.IO) - userAuthList, err := listFunc(cmd.Context(), clients, log) + userAuthList, err := listFunc(ctx, clients, log) if err != nil { return err } @@ -87,6 +88,8 @@ func newListLogger(cmd *cobra.Command, IO iostreams.IOStreamer) *logger.Logger { // API Host: https://dev.slack.com (optional, only shown for custom API Hosts) // Last Updated: 2021-03-12 11:18:00 -0700 func printAuthList(cmd *cobra.Command, IO iostreams.IOStreamer, userAuthList []types.SlackAuth) { + ctx := cmd.Context() + // Based on loosely on time.RFC3339 timeFormat := "2006-01-02 15:04:05 Z07:00" @@ -121,7 +124,7 @@ func printAuthList(cmd *cobra.Command, IO iostreams.IOStreamer, userAuthList []t cmd.Println() // Print a trace with info about the authorization - IO.PrintTrace(cmd.Context(), slacktrace.AuthListInfo, authInfo.UserID, authInfo.TeamID) + IO.PrintTrace(ctx, slacktrace.AuthListInfo, authInfo.UserID, authInfo.TeamID) } // When there are no authorizations @@ -130,11 +133,12 @@ func printAuthList(cmd *cobra.Command, IO iostreams.IOStreamer, userAuthList []t } // Print a trace with the total number of authorized workspaces - IO.PrintTrace(cmd.Context(), slacktrace.AuthListCount, fmt.Sprint(len(userAuthList))) + IO.PrintTrace(ctx, slacktrace.AuthListCount, fmt.Sprint(len(userAuthList))) } // printAuthListSuccess is displayed at the very end and helps guide the developer toward next steps. func printAuthListSuccess(cmd *cobra.Command, IO iostreams.IOStreamer, userAuthList []types.SlackAuth) { + ctx := cmd.Context() commandText := style.Commandf("login", true) // When there are no authorizations, guide the user to creating an authorization. @@ -145,5 +149,5 @@ func printAuthListSuccess(cmd *cobra.Command, IO iostreams.IOStreamer, userAuthL ) } - IO.PrintTrace(cmd.Context(), slacktrace.AuthListSuccess) + IO.PrintTrace(ctx, slacktrace.AuthListSuccess) } diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 7a92cd81..4d03329b 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -127,6 +127,8 @@ func RunLoginCommand(clients *shared.ClientFactory, cmd *cobra.Command) (types.S } func printAuthSuccess(cmd *cobra.Command, IO iostreams.IOStreamer, credentialsPath string, token string) { + ctx := cmd.Context() + var secondaryLog string if credentialsPath != "" { secondaryLog = fmt.Sprintf("Authorization data was saved to %s", style.HomePath(credentialsPath)) @@ -134,7 +136,6 @@ func printAuthSuccess(cmd *cobra.Command, IO iostreams.IOStreamer, credentialsPa secondaryLog = fmt.Sprintf("Service token:\n\n %s\n\nMake sure to copy the token now and save it safely.", token) } - ctx := cmd.Context() IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{ Emoji: "key", Text: "You've successfully authenticated!", diff --git a/cmd/collaborators/add.go b/cmd/collaborators/add.go index d41219bb..33eb5b33 100644 --- a/cmd/collaborators/add.go +++ b/cmd/collaborators/add.go @@ -58,7 +58,8 @@ func NewAddCommand(clients *shared.ClientFactory) *cobra.Command { return cmdutil.IsValidProjectDirectory(clients) }, RunE: func(cmd *cobra.Command, args []string) error { - return runAddCommandFunc(cmd.Context(), clients, cmd, args) + ctx := cmd.Context() + return runAddCommandFunc(ctx, clients, cmd, args) }, } cmd.Flags().StringVarP(&addFlags.permissionType, "permission-type", "P", "", "collaborator permission type: reader, owner") diff --git a/cmd/collaborators/list.go b/cmd/collaborators/list.go index cd72d4e6..19633219 100644 --- a/cmd/collaborators/list.go +++ b/cmd/collaborators/list.go @@ -53,9 +53,8 @@ func NewListCommand(clients *shared.ClientFactory) *cobra.Command { // runListCommand will execute the list command func runListCommand(cmd *cobra.Command, clients *shared.ClientFactory) error { - var span opentracing.Span ctx := cmd.Context() - span, ctx = opentracing.StartSpanFromContext(ctx, "cmd.Collaborators.List") + span, _ := opentracing.StartSpanFromContext(ctx, "cmd.Collaborators.List") defer span.Finish() // Get the app auth selection from the flag or prompt diff --git a/cmd/collaborators/remove.go b/cmd/collaborators/remove.go index 5d78852b..bbc4f046 100644 --- a/cmd/collaborators/remove.go +++ b/cmd/collaborators/remove.go @@ -48,7 +48,8 @@ func NewRemoveCommand(clients *shared.ClientFactory) *cobra.Command { return cmdutil.IsValidProjectDirectory(clients) }, RunE: func(cmd *cobra.Command, args []string) error { - return runRemoveCommandFunc(cmd.Context(), clients, cmd, args) + ctx := cmd.Context() + return runRemoveCommandFunc(ctx, clients, cmd, args) }, } } diff --git a/cmd/collaborators/update.go b/cmd/collaborators/update.go index 7cf917d4..f613741a 100644 --- a/cmd/collaborators/update.go +++ b/cmd/collaborators/update.go @@ -74,9 +74,8 @@ func NewUpdateCommand(clients *shared.ClientFactory) *cobra.Command { // runUpdateCommand will execute the update command func runUpdateCommand(cmd *cobra.Command, clients *shared.ClientFactory, args []string) error { - var span opentracing.Span ctx := cmd.Context() - span, ctx = opentracing.StartSpanFromContext(ctx, "cmd.Collaborators.Update") + span, _ := opentracing.StartSpanFromContext(ctx, "cmd.Collaborators.Update") defer span.Finish() var slackUser types.SlackUser diff --git a/cmd/doctor/doctor_test.go b/cmd/doctor/doctor_test.go index 0ea22afb..4964e1e7 100644 --- a/cmd/doctor/doctor_test.go +++ b/cmd/doctor/doctor_test.go @@ -51,6 +51,7 @@ func TestDoctorCommand(t *testing.T) { expectedUpdateTime := "0001-01-01 00:00:00 Z" t.Run("creates a complete report", func(t *testing.T) { + ctx := slackcontext.MockContext(t.Context()) clientsMock := shared.NewClientsMock() clientsMock.AuthInterface.On("Auths", mock.Anything).Return([]types.SlackAuth{expectedCredentials}, nil) clientsMock.AuthInterface.On("ResolveApiHost", mock.Anything, mock.Anything, mock.Anything).Return("api.slack.com") @@ -90,7 +91,7 @@ func TestDoctorCommand(t *testing.T) { err := cmd.Execute() require.NoError(t, err) - report, err := performChecks(cmd.Context(), clients) + report, err := performChecks(ctx, clients) require.NoError(t, err) expectedValues := DoctorReport{ diff --git a/cmd/fingerprint/fingerprint.go b/cmd/fingerprint/fingerprint.go index 47c49e50..353ee203 100644 --- a/cmd/fingerprint/fingerprint.go +++ b/cmd/fingerprint/fingerprint.go @@ -43,10 +43,11 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command { {Command: "_fingerprint", Meaning: "Print the unique value that identifies the Slack CLI binary"}, }), RunE: func(cmd *cobra.Command, args []string) error { - var span, _ = opentracing.StartSpanFromContext(cmd.Context(), "cmd._fingerprint") + ctx := cmd.Context() + var span, _ = opentracing.StartSpanFromContext(ctx, "cmd._fingerprint") defer span.Finish() - clients.IO.PrintInfo(cmd.Context(), false, fingerprintHash) + clients.IO.PrintInfo(ctx, false, fingerprintHash) return nil }, } diff --git a/cmd/help/help.go b/cmd/help/help.go index 5395249f..c80795c6 100644 --- a/cmd/help/help.go +++ b/cmd/help/help.go @@ -30,9 +30,10 @@ func HelpFunc( aliases map[string]string, ) func(*cobra.Command, []string) { return func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() style.ToggleStyles(clients.IO.IsTTY() && !clients.Config.NoColor) if help, _ := clients.Config.Flags.GetBool("help"); help { - clients.Config.LoadExperiments(cmd.Context(), clients.IO.PrintDebug) + clients.Config.LoadExperiments(ctx, clients.IO.PrintDebug) } experiments := []string{} for _, exp := range clients.Config.GetExperiments() { diff --git a/cmd/platform/deploy.go b/cmd/platform/deploy.go index 70584f5d..3e689f61 100644 --- a/cmd/platform/deploy.go +++ b/cmd/platform/deploy.go @@ -244,6 +244,7 @@ func printDeployHosting(cmd *cobra.Command, event *logger.LogEvent) { } func printDeployHostingCompletion(clients *shared.ClientFactory, cmd *cobra.Command, event *logger.LogEvent) error { + var ctx = cmd.Context() var authSession api.AuthSession appName := event.DataToString("appName") @@ -289,7 +290,7 @@ func printDeployHostingCompletion(clients *shared.ClientFactory, cmd *cobra.Comm deploySpinner.Update(successfulDeployText, "").Stop() - clients.IO.PrintTrace(cmd.Context(), slacktrace.PlatformDeploySuccess) + clients.IO.PrintTrace(ctx, slacktrace.PlatformDeploySuccess) navigateText := style.Sectionf(style.TextSection{ Emoji: "cloud_with_lightning", @@ -300,7 +301,7 @@ func printDeployHostingCompletion(clients *shared.ClientFactory, cmd *cobra.Comm }, }) - clients.IO.PrintInfo(cmd.Context(), false, navigateText) + clients.IO.PrintInfo(ctx, false, navigateText) return nil } diff --git a/cmd/platform/run.go b/cmd/platform/run.go index e2dc88d2..95573feb 100644 --- a/cmd/platform/run.go +++ b/cmd/platform/run.go @@ -167,7 +167,7 @@ func newRunLogger(clients *shared.ClientFactory, cmd *cobra.Command) *logger.Log event.DataToString("teamName"), ))) case "on_cloud_run_connection_connected": - clients.IO.PrintTrace(cmd.Context(), slacktrace.PlatformRunReady) + clients.IO.PrintTrace(ctx, slacktrace.PlatformRunReady) cmd.Println(style.Secondary("Connected, awaiting events")) case "on_cloud_run_connection_message": message := event.DataToString("cloud_run_connection_message") diff --git a/cmd/project/create.go b/cmd/project/create.go index 4a99af68..ff5c67c3 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -76,6 +76,7 @@ func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command { } func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []string) error { + ctx := cmd.Context() // Set up event logger log := newCreateLogger(clients, cmd) @@ -102,7 +103,6 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args [] } clients.EventTracker.SetAppTemplate(template.GetTemplatePath()) - ctx := cmd.Context() appDirPath, err := CreateFunc(ctx, clients, log, createArgs) if err != nil { printAppCreateError(clients, cmd, err) @@ -239,11 +239,12 @@ func printCreateSuccess(ctx context.Context, clients *shared.ClientFactory, appP // printAppCreateError stops the creation spinners and displays the returned error message func printAppCreateError(clients *shared.ClientFactory, cmd *cobra.Command, err error) { + ctx := cmd.Context() switch { case appCreateSpinner.Active(): errorText := fmt.Sprintf("Error creating project directory: %s", err) appCreateSpinner.Update(errorText, "warning").Stop() default: } - clients.IO.PrintTrace(cmd.Context(), slacktrace.CreateError) + clients.IO.PrintTrace(ctx, slacktrace.CreateError) } diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index 9a98e8cd..c943861b 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -153,10 +153,10 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory) templateForCategory := getSelectionTemplate(clients) // Print a trace with info about the category title options provided by CLI - clients.IO.PrintTrace(cmd.Context(), slacktrace.CreateCategoryOptions, strings.Join(titlesForCategory, ", ")) + clients.IO.PrintTrace(ctx, slacktrace.CreateCategoryOptions, strings.Join(titlesForCategory, ", ")) // Prompt to choose a category - selection, err := clients.IO.SelectPrompt(cmd.Context(), promptForCategory, titlesForCategory, iostreams.SelectPromptConfig{ + selection, err := clients.IO.SelectPrompt(ctx, promptForCategory, titlesForCategory, iostreams.SelectPromptConfig{ Description: func(value string, index int) string { return optionsForCategory[index].Description }, @@ -189,10 +189,10 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory) template := getSelectionTemplate(clients) // Print a trace with info about the template title options provided by CLI - clients.IO.PrintTrace(cmd.Context(), slacktrace.CreateTemplateOptions, strings.Join(titles, ", ")) + clients.IO.PrintTrace(ctx, slacktrace.CreateTemplateOptions, strings.Join(titles, ", ")) // Prompt to choose a template - selection, err := clients.IO.SelectPrompt(cmd.Context(), prompt, titles, iostreams.SelectPromptConfig{ + selection, err := clients.IO.SelectPrompt(ctx, prompt, titles, iostreams.SelectPromptConfig{ Description: func(value string, index int) string { return options[index].Description }, @@ -238,7 +238,9 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory) // confirmExternalTemplateSelection prompts the user to confirm that they want to create an app from // an external template and saves their preference if they choose to ignore future warnings func confirmExternalTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory, template create.Template) (bool, error) { - trustSources, err := clients.Config.SystemConfig.GetTrustUnknownSources(cmd.Context()) + ctx := cmd.Context() + + trustSources, err := clients.Config.SystemConfig.GetTrustUnknownSources(ctx) if err != nil { return false, err } @@ -254,7 +256,7 @@ func confirmExternalTemplateSelection(cmd *cobra.Command, clients *shared.Client }, })) - selection, err := clients.IO.SelectPrompt(cmd.Context(), "Proceed?", []string{"Yes", "Yes, don't ask again", "No"}, iostreams.SelectPromptConfig{ + selection, err := clients.IO.SelectPrompt(ctx, "Proceed?", []string{"Yes", "Yes, don't ask again", "No"}, iostreams.SelectPromptConfig{ Required: true, Flag: clients.Config.Flags.Lookup("force"), }) @@ -263,7 +265,7 @@ func confirmExternalTemplateSelection(cmd *cobra.Command, clients *shared.Client } else if selection.Option == "No" { return false, nil } else if selection.Option == "Yes, don't ask again" { - err = clients.Config.SystemConfig.SetTrustUnknownSources(cmd.Context(), true) + err = clients.Config.SystemConfig.SetTrustUnknownSources(ctx, true) if err != nil { return true, slackerror.Wrap(err, "failed to set trust_unknown_sources property to config") } diff --git a/cmd/root.go b/cmd/root.go index 7e9f5d6f..42fa9dc0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -102,7 +102,9 @@ func NewRootCommand(clients *shared.ClientFactory, updateNotification *update.Up `{{Emoji "books"}}Get started by reading the docs: {{LinkText "https://api.slack.com/automation"}}`, }, "\n"), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() clients.IO.SetCmdIO(cmd) + // Set user-invoked command (for metrics) clients.Config.Command = strings.Join(strings.Split(cmd.CommandPath(), " ")[1:], " ") clients.Config.CommandCanonical = clients.Config.Command @@ -118,9 +120,9 @@ func NewRootCommand(clients *shared.ClientFactory, updateNotification *update.Up clients.Config.RawFlags = append(clients.Config.RawFlags, flag.Name) } }) + // Check for an CLI update in the background while the command runs updateNotification = update.New(clients, version.Get(), "SLACK_SKIP_UPDATE") - ctx := cmd.Context() updateNotification.CheckForUpdateInBackground(ctx, false) return nil }, @@ -137,7 +139,7 @@ func NewRootCommand(clients *shared.ClientFactory, updateNotification *update.Up } // Init bootstraps the CLI process. Put things that do not rely on specific arguments or flags passed to the CLI in here. If you need flag/argument values, InitConfig below. -func Init() (*cobra.Command, *shared.ClientFactory) { +func Init(ctx context.Context) (*cobra.Command, *shared.ClientFactory) { // clients stores shared clients and configurations used across the commands and handlers var clients *shared.ClientFactory // updateNotification will check for an update in the background and print a message after the command runs @@ -204,15 +206,15 @@ func Init() (*cobra.Command, *shared.ClientFactory) { // OnInitialize will execute before any root or child commands' Pre* methods. // This is a good place to house CLI bootup routines. cobra.OnInitialize(func() { - err := InitConfig(clients, rootCmd) + err := InitConfig(ctx, clients, rootCmd) if err != nil { - clients.IO.PrintError(rootCmd.Context(), err.Error()) + clients.IO.PrintError(ctx, err.Error()) clients.Os.Exit(int(iostreams.ExitError)) } }) // Since we use the *E cobra lifecycle methods, OnFinalize is one of the few ways we can ensure something _always_ runs at the end of any command invocation, regardless if an error is raised or not during execution. cobra.OnFinalize(func() { - cleanup(rootCmd.Context(), clients) + cleanup(ctx, clients) }) return rootCmd, clients } @@ -220,9 +222,7 @@ func Init() (*cobra.Command, *shared.ClientFactory) { // InitConfig reads in config files and ENV variables if set and sets up the CLI for functioning. Executes _before_ any Pre* methods from the root or child commands, but after Cobra parses flags and command arguments. // Put global CLI initialization routines that rely on flag and argument parsing in here please! // TODO: consider using arguments for this function for certain dependencies, like working directory and other OS-specific strings, that OnInitialize above can provide during actual execution, but that we can override with test values for easier testing. -func InitConfig(clients *shared.ClientFactory, rootCmd *cobra.Command) error { - ctx := rootCmd.Context() - +func InitConfig(ctx context.Context, clients *shared.ClientFactory, rootCmd *cobra.Command) error { // Get the current working directory (usually, but not always the project) workingDirPath, err := clients.Os.Getwd() if err != nil { diff --git a/cmd/root_test.go b/cmd/root_test.go index 065cb779..7c8936bb 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "fmt" "os" "strings" @@ -35,7 +36,7 @@ func TestRootCommand(t *testing.T) { defer os.RemoveAll(tmp) // Get command - cmd, _ := Init() + cmd, _ := Init(ctx) // Create mocks clientsMock := shared.NewClientsMock() @@ -77,6 +78,8 @@ func TestRootCommand(t *testing.T) { // FYI: do not try to run this test in vscode using the run/debug test inline test helper; as the assertions in this test will fail in that context func TestVersionFlags(t *testing.T) { + ctx := slackcontext.MockContext(t.Context()) + tmp, _ := os.MkdirTemp("", "") _ = os.Chdir(tmp) defer os.RemoveAll(tmp) @@ -84,7 +87,7 @@ func TestVersionFlags(t *testing.T) { var output string // Get command - cmd, _ := Init() + cmd, _ := Init(ctx) // Create mocks clientsMock := shared.NewClientsMock() @@ -110,12 +113,14 @@ func TestVersionFlags(t *testing.T) { } func Test_NewSuggestion(t *testing.T) { + ctx := slackcontext.MockContext(t.Context()) + tmp, _ := os.MkdirTemp("", "") _ = os.Chdir(tmp) defer os.RemoveAll(tmp) // Get command - cmd, clients := Init() + cmd, clients := Init(ctx) // Create mocks clientsMock := shared.NewClientsMock() @@ -131,11 +136,13 @@ func Test_NewSuggestion(t *testing.T) { } func Test_Aliases(t *testing.T) { + ctx := slackcontext.MockContext(t.Context()) + tmp, _ := os.MkdirTemp("", "") _ = os.Chdir(tmp) defer os.RemoveAll(tmp) - Init() + Init(ctx) tests := map[string]struct { args string @@ -176,7 +183,7 @@ func Test_Aliases(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - err, output := testExecCmd(strings.Fields(tt.args)) + err, output := testExecCmd(ctx, strings.Fields(tt.args)) require.NoError(t, err) require.Contains(t, output, tt.expected) }) @@ -184,9 +191,9 @@ func Test_Aliases(t *testing.T) { } // testExecCmd will execute the root cobra command with args and return the output -func testExecCmd(args []string) (error, string) { +func testExecCmd(ctx context.Context, args []string) (error, string) { // Get command - cmd, clients := Init() + cmd, clients := Init(ctx) // Create mocks clientsMock := shared.NewClientsMock() diff --git a/cmd/triggers/access.go b/cmd/triggers/access.go index e2c03259..fa575e5d 100644 --- a/cmd/triggers/access.go +++ b/cmd/triggers/access.go @@ -559,7 +559,7 @@ func printAccess(cmd *cobra.Command, clients *shared.ClientFactory, token string accessType, userAccessList, err := clients.ApiInterface().TriggerPermissionsList(ctx, token, accessFlags.triggerId) if err != nil { - clients.IO.PrintTrace(cmd.Context(), slacktrace.TriggersAccessError) + clients.IO.PrintTrace(ctx, slacktrace.TriggersAccessError) return err } @@ -572,18 +572,20 @@ func printAccess(cmd *cobra.Command, clients *shared.ClientFactory, token string } else if accessType == types.NAMED_ENTITIES { err = printNamedEntitiesHelper(cmd, clients, token, userAccessList, "list") } - clients.IO.PrintTrace(cmd.Context(), slacktrace.TriggersAccessSuccess) + clients.IO.PrintTrace(ctx, slacktrace.TriggersAccessSuccess) return err } // printCurrentAuthorizedEntities formats and displays current access information func printCurrentAuthorizedEntities(cmd *cobra.Command, clients *shared.ClientFactory, token string, app types.App, currentAccessList []string, currentAccessType types.Permission) error { + ctx := cmd.Context() + cmd.Println() if currentAccessType == types.EVERYONE { var everyoneAccessTypeDescription = types.GetAccessTypeDescriptionForEveryone(app) - clients.IO.PrintInfo(cmd.Context(), false, "Trigger '%s' can be found and run by %s\n", accessFlags.triggerId, everyoneAccessTypeDescription) + clients.IO.PrintInfo(ctx, false, "Trigger '%s' can be found and run by %s\n", accessFlags.triggerId, everyoneAccessTypeDescription) } else if currentAccessType == (types.APP_COLLABORATORS) { - clients.IO.PrintInfo(cmd.Context(), false, "Access is currently granted to %s:", style.Pluralize("app collaborator", "app collaborators", len(currentAccessList))) + clients.IO.PrintInfo(ctx, false, "Access is currently granted to %s:", style.Pluralize("app collaborator", "app collaborators", len(currentAccessList))) err := printAppCollaboratorsHelper(cmd, clients, token, currentAccessList) if err != nil { return err diff --git a/cmd/triggers/create.go b/cmd/triggers/create.go index b59e5610..8f85a283 100644 --- a/cmd/triggers/create.go +++ b/cmd/triggers/create.go @@ -268,12 +268,14 @@ func promptShouldRetryCreateWithInteractivity(cmd *cobra.Command, IO iostreams.I } func promptShouldRetryWithInteractivity(promptMsg string, cmd *cobra.Command, IO iostreams.IOStreamer, triggerArg api.TriggerRequest) (bool, error) { + ctx := cmd.Context() + pretty, err := json.MarshalIndent(triggerArg, "", " ") if err != nil { return false, err } - IO.PrintInfo(cmd.Context(), false, "\n%s", style.Sectionf(style.TextSection{ + IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{ Emoji: "memo", Text: "Workflow requires interactivity", Secondary: []string{ @@ -282,7 +284,7 @@ func promptShouldRetryWithInteractivity(promptMsg string, cmd *cobra.Command, IO }, })) - return IO.ConfirmPrompt(cmd.Context(), promptMsg, true) + return IO.ConfirmPrompt(ctx, promptMsg, true) } func triggerRequestFromFlags(flags createCmdFlags, isDev bool) api.TriggerRequest { diff --git a/cmd/triggers/info.go b/cmd/triggers/info.go index fd9eb64a..7f65ed95 100644 --- a/cmd/triggers/info.go +++ b/cmd/triggers/info.go @@ -61,7 +61,7 @@ func NewInfoCommand(clients *shared.ClientFactory) *cobra.Command { func runInfoCommand(cmd *cobra.Command, clients *shared.ClientFactory) error { ctx := cmd.Context() - var span, _ = opentracing.StartSpanFromContext(cmd.Context(), "cmd.triggers.info") + var span, _ = opentracing.StartSpanFromContext(ctx, "cmd.triggers.info") defer span.Finish() // Get the app from the flag or prompt diff --git a/cmd/triggers/list.go b/cmd/triggers/list.go index 2d0ebb0d..dfa4da61 100644 --- a/cmd/triggers/list.go +++ b/cmd/triggers/list.go @@ -72,7 +72,7 @@ func NewListCommand(clients *shared.ClientFactory) *cobra.Command { // runListCommand will execute the list command func runListCommand(cmd *cobra.Command, clients *shared.ClientFactory) error { ctx := cmd.Context() - var span, _ = opentracing.StartSpanFromContext(cmd.Context(), "cmd.triggers.list") + var span, _ = opentracing.StartSpanFromContext(ctx, "cmd.triggers.list") defer span.Finish() // Get the app selection and accompanying auth from the flag or prompt diff --git a/cmd/upgrade/upgrade.go b/cmd/upgrade/upgrade.go index bda4c22e..e047bc8c 100644 --- a/cmd/upgrade/upgrade.go +++ b/cmd/upgrade/upgrade.go @@ -54,6 +54,7 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command { // checkForUpdates will check for CLI/SDK updates and print a message when no updates are available. // When there are updates, the function will *not* print a message because the root command handles printing update notifications. func checkForUpdates(clients *shared.ClientFactory, cmd *cobra.Command) error { + ctx := cmd.Context() updateNotification := update.New(clients, version.Get(), "SLACK_SKIP_UPDATE") // TODO(@mbrooks) This update check is happening at the same time as the root command's `CheckForUpdateInBackground`. @@ -62,7 +63,7 @@ func checkForUpdates(clients *shared.ClientFactory, cmd *cobra.Command) error { // How can we improve this to avoid doing 2 update network requests/checks? // // Force an update check that is blocking and synchronous - if err := updateNotification.CheckForUpdate(cmd.Context(), true); err != nil { + if err := updateNotification.CheckForUpdate(ctx, true); err != nil { return err } diff --git a/cmd/version/version.go b/cmd/version/version.go index 51b0fbaa..4c9282b8 100644 --- a/cmd/version/version.go +++ b/cmd/version/version.go @@ -68,7 +68,8 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command { }, }), Run: func(cmd *cobra.Command, args []string) { - var span, _ = opentracing.StartSpanFromContext(cmd.Context(), "cmd.version") + ctx := cmd.Context() + span, _ := opentracing.StartSpanFromContext(ctx, "cmd.version") defer span.Finish() cmd.Println(Template()) diff --git a/internal/config/project_test.go b/internal/config/project_test.go index b53a00bb..3594d1cb 100644 --- a/internal/config/project_test.go +++ b/internal/config/project_test.go @@ -619,7 +619,7 @@ func Test_Config_CreateProjectConfigJSONFile(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - ctx := t.Context() + ctx := slackcontext.MockContext(t.Context()) fs := afero.NewMemMapFs() // Create the project directory and .slack directory diff --git a/internal/slacktrace/slacktrace.go b/internal/slacktrace/slacktrace.go index 6b8185e3..816b9218 100644 --- a/internal/slacktrace/slacktrace.go +++ b/internal/slacktrace/slacktrace.go @@ -35,7 +35,7 @@ package slacktrace // To add trace output to a command: // // use, -// clients.IO.PrintTrace(cmd.Context(), slacktrace.) +// clients.IO.PrintTrace(ctx, slacktrace.) const ( AdminAppApprovalRequestPending = "SLACK_TRACE_ADMIN_APPROVAL_REQUEST_PENDING" AdminAppApprovalRequestReasonSubmitted = "SLACK_TRACE_ADMIN_APPROVAL_REQUEST_REASON_SUBMITTED" diff --git a/internal/update/cli.go b/internal/update/cli.go index 4d2fe8fc..3e6a59db 100644 --- a/internal/update/cli.go +++ b/internal/update/cli.go @@ -69,6 +69,7 @@ func (c *CLIDependency) HasUpdate() (bool, error) { // PrintUpdateNotification notifies the user that a new version is available and provides upgrade instructions for Homebrew. Returns a bool representing whether the user wants the self-update to run func (c *CLIDependency) PrintUpdateNotification(cmd *cobra.Command) (bool, error) { + ctx := cmd.Context() processName := cmdutil.GetProcessName() isHomebrew := IsHomebrew(processName) @@ -92,7 +93,7 @@ func (c *CLIDependency) PrintUpdateNotification(cmd *cobra.Command) (bool, error style.CommandText("https://api.slack.com/automation/cli/install"), ) selfUpdatePrompt := fmt.Sprintf("%sDo you want to auto-update to the latest version now?", style.Emoji("rocket")) - return c.clients.IO.ConfirmPrompt(cmd.Context(), selfUpdatePrompt, false) + return c.clients.IO.ConfirmPrompt(ctx, selfUpdatePrompt, false) } return false, nil diff --git a/internal/update/sdk.go b/internal/update/sdk.go index 07a12b63..076ba2ca 100644 --- a/internal/update/sdk.go +++ b/internal/update/sdk.go @@ -189,6 +189,8 @@ func (c *SDKDependency) InstallUpdate(ctx context.Context) error { // SDK, including formatting and language that indicates if a breaking change // is included or an error has occurred func (c *SDKDependency) PrintUpdateNotification(cmd *cobra.Command) (bool, error) { + ctx := cmd.Context() + if c.releaseInfo.Update { // Standard "update is available" message cmd.Printf( @@ -308,7 +310,7 @@ func (c *SDKDependency) PrintUpdateNotification(cmd *cobra.Command) (bool, error // The update(s) includes an error if c.releaseInfo.Error.Message != "" { - c.clients.IO.PrintError(cmd.Context(), + c.clients.IO.PrintError(ctx, style.Indent("%s\n%s\n"), style.Error("Error:"), style.Indent(c.releaseInfo.Error.Message), @@ -318,7 +320,7 @@ func (c *SDKDependency) PrintUpdateNotification(cmd *cobra.Command) (bool, error // If `install-update` hook available, prompt to auto-update if c.clients.SDKConfig.Hooks.InstallUpdate.IsAvailable() { autoUpdatePrompt := fmt.Sprintf("%sDo you want to auto-update to the latest versions now?", style.Emoji("rocket")) - return c.clients.IO.ConfirmPrompt(cmd.Context(), autoUpdatePrompt, false) + return c.clients.IO.ConfirmPrompt(ctx, autoUpdatePrompt, false) } return false, nil diff --git a/internal/update/sdk_test.go b/internal/update/sdk_test.go index 22865276..76bf796a 100644 --- a/internal/update/sdk_test.go +++ b/internal/update/sdk_test.go @@ -21,6 +21,7 @@ import ( "github.com/slackapi/slack-cli/internal/hooks" "github.com/slackapi/slack-cli/internal/shared" + "github.com/slackapi/slack-cli/internal/slackcontext" "github.com/slackapi/slack-cli/test/testutil" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -270,6 +271,7 @@ func Test_SDK_HasUpdate(t *testing.T) { func Test_SDK_InstallUpdate(t *testing.T) { for _, s := range installScenarios { // Create mocks + ctx := slackcontext.MockContext(t.Context()) clientsMock := shared.NewClientsMock() // Create clients that is mocked for testing @@ -290,7 +292,6 @@ func Test_SDK_InstallUpdate(t *testing.T) { // Create the command cmd := &cobra.Command{} testutil.MockCmdIO(clients.IO, cmd) - ctx := cmd.Context() // output := clientsMock.GetCombinedOutput() diff --git a/internal/update/update.go b/internal/update/update.go index e1b23968..607ce02d 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -118,6 +118,8 @@ func (u *UpdateNotification) Dependencies() []Dependency { // PrintUpdates displays an update message after the command runs and prompts the user if they want to update, if applicable // Invoked from root command's post-run method. If an error occurs, we return it so it is raised to the user. func (u *UpdateNotification) PrintAndPromptUpdates(cmd *cobra.Command, cliVersion string) error { + ctx := cmd.Context() + if updateNotification.WaitForCheckForUpdateInBackground() { for _, dependency := range updateNotification.Dependencies() { hasUpdate, err := dependency.HasUpdate() @@ -131,7 +133,7 @@ func (u *UpdateNotification) PrintAndPromptUpdates(cmd *cobra.Command, cliVersio return err } if shouldSelfUpdate { - if err := dependency.InstallUpdate(cmd.Context()); err != nil { + if err := dependency.InstallUpdate(ctx); err != nil { return err } } diff --git a/main.go b/main.go index de3cf3ec..6eb884de 100644 --- a/main.go +++ b/main.go @@ -72,7 +72,7 @@ func main() { // add root span to context ctx = slackcontext.SetOpenTracingSpan(ctx, span) - rootCmd, clients := cmd.Init() + rootCmd, clients := cmd.Init(ctx) cmd.Execute(ctx, rootCmd, clients) }