Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7578b6e
feat(bolt-install): support using 'install' command with remote manif…
mwbrooks Jul 10, 2025
46be447
feat(bolt-install): support using 'install' command with remote manif…
mwbrooks Jul 11, 2025
7678bb4
feat(bolt-install): default --environment deployed when --team provid…
mwbrooks Jul 11, 2025
9847d3c
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Jul 11, 2025
10966f7
feat(bolt-install): add PR number
mwbrooks Jul 11, 2025
fda7732
feat(bolt-install): add example of using the --environment flag
mwbrooks Jul 11, 2025
aa42052
feat(bolt-install): fix --environment flag support
mwbrooks Jul 11, 2025
9b04aa7
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Jul 24, 2025
521ee30
fix: allow mixing-and-matching --team and --environment
mwbrooks Jul 25, 2025
886568f
fix: warning message
mwbrooks Jul 25, 2025
12d89f4
test: fix failing add_test.go
mwbrooks Jul 25, 2025
97fe3f0
fix: refactor code to be tighter
mwbrooks Jul 25, 2025
edbcaf9
fix: remove the UserID comment to avoid unexpected behaviour
mwbrooks Jul 25, 2025
976d285
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Jul 25, 2025
d62e7c6
Update cmd/app/add.go
mwbrooks Jul 25, 2025
013ad0d
test: refactor mockLocalManifest to mockManifestAppLocal (and Remote)
mwbrooks Jul 25, 2025
1e2bd54
fix: install command to handle --app local and --app deployed
mwbrooks Aug 7, 2025
701a794
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Aug 7, 2025
9e757b1
fix: install command skips environment prompt when --app is set
mwbrooks Aug 7, 2025
c868e1b
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Aug 7, 2025
69e3284
Merge branch 'main' into mwbrooks-install-remote-manifest
mwbrooks Aug 8, 2025
0e65092
fix: skip defaulting to deployed environment when app id is provided
mwbrooks Aug 8, 2025
e28a02e
fix: skip defaulting to deployed environment when --team provided
mwbrooks Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 84 additions & 27 deletions cmd/app/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var appSelectPromptFunc = prompts.AppSelectPrompt

type addCmdFlags struct {
orgGrantWorkspaceID string
environmentFlag string
}
Comment on lines 43 to 46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 thought: IMHO this pattern is so interesting. We might want to encourage it and follow up with changes that bring the app and team and token flags to specific commands soon?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧠 Hmm... we could consider doing that. We may also want to consider --environment to be a global flag, since it should be used in combination with --team 🤔


var addFlags addCmdFlags
Expand All @@ -55,11 +56,12 @@ func NewAddCommand(clients *shared.ClientFactory) *cobra.Command {
Long: "Install the app to a team",
Example: style.ExampleCommandsf([]style.ExampleCommand{
{Command: "app install", Meaning: "Install a production app to a team"},
{Command: "app install --team T0123456", Meaning: "Install a production app to a specific team"},
{Command: "app install --team T0123456 --environment deployed", Meaning: "Install a production app to a specific team"},
{Command: "app install --team T0123456 --environment local", Meaning: "Install a local dev app to a specific team"},
}),
PreRunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
return preRunAddCommand(ctx, clients)
return preRunAddCommand(ctx, clients, cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
Expand All @@ -72,51 +74,106 @@ func NewAddCommand(clients *shared.ClientFactory) *cobra.Command {
}

cmd.Flags().StringVar(&addFlags.orgGrantWorkspaceID, cmdutil.OrgGrantWorkspaceFlag, "", cmdutil.OrgGrantWorkspaceDescription())
cmd.Flags().StringVarP(&addFlags.environmentFlag, "environment", "E", "", "environment of app (local, deployed)")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: The slack install command now supports the --environment <local|deployed> values. This flag can be used to by-pass the prompt.


return cmd
}

// preRunAddCommand confirms an app is available for installation
func preRunAddCommand(ctx context.Context, clients *shared.ClientFactory) error {
func preRunAddCommand(ctx context.Context, clients *shared.ClientFactory, cmd *cobra.Command) error {
err := cmdutil.IsValidProjectDirectory(clients)
if err != nil {
return err
}
if !clients.Config.WithExperimentOn(experiment.BoltFrameworks) {
return nil
}
manifestSource, err := clients.Config.ProjectConfig.GetManifestSource(ctx)
if err != nil {
return err
}
if manifestSource.Equals(config.ManifestSourceRemote) {
return slackerror.New(slackerror.ErrAppInstall).
WithMessage("Apps cannot be installed due to project configurations").
WithRemediation(
"Install an app on app settings: %s\nLink an app to this project with %s\nList apps saved with this project using %s",
style.LinkText("https://api.slack.com/apps"),
style.Commandf("app link", false),
style.Commandf("app list", false),
).
WithDetails(slackerror.ErrorDetails{
slackerror.ErrorDetail{
Code: slackerror.ErrProjectConfigManifestSource,
Message: "Cannot install apps with manifests sourced from app settings",
},
})
}
Comment on lines -88 to -107
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪓 Removing this section that prevents the slack install command from executing on a remote manifest.

clients.Config.SetFlags(cmd)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: This was the magic, thanks @zimeg 🪄

return nil
}

// RunAddCommand executes the workspace install command, prints output, and returns any errors.
func RunAddCommand(ctx context.Context, clients *shared.ClientFactory, selection *prompts.SelectedApp, orgGrantWorkspaceID string) (context.Context, types.InstallState, types.App, error) {
if selection == nil {
selected, err := appSelectPromptFunc(ctx, clients, prompts.ShowHostedOnly, prompts.ShowAllApps)
if err != nil {
return ctx, "", types.App{}, err
// TODO: Move to the promptIsProduction when the prompt is refactored and tested.
// Validate that the --app flag is not an app ID when the --environment flag is set.
if types.IsAppID(clients.Config.AppFlag) && addFlags.environmentFlag != "" {
return ctx, "", types.App{}, slackerror.New(slackerror.ErrMismatchedFlags).WithRemediation("When '--app <app_id>' is set, please do not set the flag --environment.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕊️ praise: This is a pleasant remediation.

}

// TODO: Move to the promptIsProduction when the prompt is refactored and tested.
// Validate that the --environment flag matches the --app flag, when the value is `--app local` or `--app deployed`.
if types.IsAppFlagEnvironment(clients.Config.AppFlag) {
if addFlags.environmentFlag != "" && addFlags.environmentFlag != clients.Config.AppFlag {
return ctx, "", types.App{}, slackerror.New(slackerror.ErrMismatchedFlags).WithRemediation("When '--app local' or '--app deployed' is set, please set the flag --environment to match the --app flag.")
}

if addFlags.environmentFlag == "" {
err := clients.Config.Flags.Lookup("environment").Value.Set(clients.Config.AppFlag)
if err != nil {
return ctx, "", types.App{}, err
}
clients.Config.Flags.Lookup("environment").Changed = true
}
}

// Default to `--environment deployed` when there is no `--environment` flag and `--team <id>` is set.
// Skip when `--app <id>` flag is set, because the environment is looked up in the app selector prompt.
// TODO(semver:major): This is backwards compatibility for when `install` only supported deployed environments.
if !types.IsAppID(clients.Config.AppFlag) && (addFlags.environmentFlag == "" && clients.Config.TeamFlag != "") {
err := clients.Config.Flags.Lookup("environment").Value.Set("deployed")
if err != nil {
return ctx, "", types.App{}, err
}
clients.Config.Flags.Lookup("environment").Changed = true
Comment on lines +124 to +128
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: We now set the flag value (and mark it as changed), so that the promptIsProduction(...) function can be used to read the --environment flag and optionally prompt for an app environment.


clients.IO.PrintInfo(ctx, false, "\n"+style.Sectionf(style.TextSection{
Emoji: "warning",
Text: "Warning: Default App Environment",
Secondary: []string{
"App environment is set to deployed when only the --team flag is provided.",
"The next major version will change this behavior.",
"When the --team flag is provided, the --environment flag will be required.",
"Add the '--environment deployed' to avoid breaking changes.",
},
}))
}
Comment on lines +130 to +140
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: A warning is now displayed that in the next major release, we'll require the --environment flag when the --team flag is provided.

Image

The double newline haunts me 😱 but it only appear when the warning is shown... which will be rare.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mwbrooks I'm such a big fan of making this flag combination required! What a good call 🤖 ✨


// When the app flag is an app ID, the app select prompt can resolve the app.
// Otherwise, prompt for the app environment and app.
if types.IsAppID(clients.Config.AppFlag) {
selected, err := appSelectPromptFunc(ctx, clients, prompts.ShowAllEnvironments, prompts.ShowAllApps)
if err != nil {
return ctx, "", types.App{}, err
}
selection = &selected
} else {
// Prompt for deployed or local app environment.
isProductionApp, err := promptIsProduction(ctx, clients)
if err != nil {
return ctx, "", types.App{}, err
}

// Set the app environment type based on the prompt.
var appEnvironmentType prompts.AppEnvironmentType
if isProductionApp {
appEnvironmentType = prompts.ShowHostedOnly
} else {
appEnvironmentType = prompts.ShowLocalOnly
}

selected, err := appSelectPromptFunc(ctx, clients, appEnvironmentType, prompts.ShowAllApps)
if err != nil {
return ctx, "", types.App{}, err
}
selection = &selected

if !isProductionApp {
selection.App.IsDev = true
}
}
selection = &selected
}

if selection.Auth.TeamDomain == "" {
return ctx, "", types.App{}, slackerror.New(slackerror.ErrCredentialsNotFound)
}
Expand Down
Loading
Loading