Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
70 changes: 47 additions & 23 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 @@ -56,10 +57,11 @@ func NewAddCommand(clients *shared.ClientFactory) *cobra.Command {
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 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,73 @@ 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)
// When team flag is provided, default app environment to deployed if not specified.
// TODO(semver:major): Remove defaulting to deployed and require the environment flag to be set.
if clients.Config.TeamFlag != "" && addFlags.environmentFlag == "" {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if clients.Config.TeamFlag != "" && addFlags.environmentFlag == "" {
if (clients.Config.TeamFlag != "" || clients.Config.AppFlag != "") && addFlags.environmentFlag == "" {

I'm not certain that this is the right logic, but we might want to use a default value if the --app flag is provided 🔍

Copy link
Member

Choose a reason for hiding this comment

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

🗣️ This made me question if --app should accept local or deployed in future versions!

Preferring an --environment flag for that choice and leaving app IDs to --app might bring more straightforward logic here and in other selections, but this isn't a change we ought make now!

Copy link
Member Author

Choose a reason for hiding this comment

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

Flag combinations can be confusing! 😝

I think we want the original combination of:

if clients.Config.TeamFlag != "" && addFlags.environmentFlag == "" {
  • When --team is provided and --environment is not, then we default to --environment deployed.
  • When --app is provided, we resolve the --environment from apps.json and apps.dev.json.
  • When there is a mismatched combination, then the AppSelect will return an error that the app cannot be found.
    • Example: --team T0123 --app A0123 where A0123 doesn't belong to T0123

Copy link
Member

Choose a reason for hiding this comment

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

🧠 question: I noticed the warning below hints at --team needing the --environment flag and was wondering if you think --app should also remove support for "local" or "deployed" in the next semver:major?

🧠 praise: Also, thanks for clearing up these default cases and flag setups alike 🤓

Copy link
Member Author

Choose a reason for hiding this comment

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

🧠 question: I noticed the warning below hints at --team needing the --environment flag and was wondering if you think --app should also remove support for "local" or "deployed" in the next semver:major?

I won't lie, I thought --app local and --app deployed support was removed in v3.0.0 when we introduced --environment. This should definitely be removed in the next semver:major in favour of --environment. :two-cents:

In the meantime, we'll need to handle that use-case here, I suppose 🤔

Copy link
Member

Choose a reason for hiding this comment

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

🪓 praise: Having a shared environment flag will be so nice. I agree on the continued support until this!

Copy link
Member

Choose a reason for hiding this comment

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

👾 issue(non-blocking): Hmm I think we might still want to skip this section if an app ID is provided since the environment would be known?

🤖 question: It's not so clear to me if an install can target different environments for a known app. This might not be an issue if so, but I'm wondering what you think.

$ slack install --team tinyspek --app A099WJ22LPK

⚠️ Warning: Default App Environment
   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.


🏠 App Install
   Installing "vigilant-shark-290 (local)" app to "Tinyspek"
   Finished in 0.4s

📌 note: IMO we should not suggest an app and team flag together once the environment flag becomes more standard.

Copy link
Member Author

@mwbrooks mwbrooks Aug 8, 2025

Choose a reason for hiding this comment

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

👾 issue(non-blocking): Hmm I think we might still want to skip this section if an app ID is provided since the environment would be known?

Thanks, after looking over it, I think you're right too. Commit 0e65092 & e28a02e now skips when the --app <id> is provided. If --app <env> then the --environment <env> is set in the lines above.

Can't wait until we can move this logic out of the commands and to a higher-level. 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

🤖 question: It's not so clear to me if an install can target different environments for a known app. This might not be an issue if so, but I'm wondering what you think.

An App ID can only be associated with one environment (1-to-1 relationship), so we don't want to allow the developer to override that. Right now, we error if the --app and --environment flags are used together. That seems the safest. A better approach is that we don't error if --environment matches the saved --app, but atm the error message asks you to remove the --environment flag.

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 🤖 ✨


// 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
}
Copy link
Member

Choose a reason for hiding this comment

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

👁️‍🗨️ issue: This prompt might appear if the app flag is included for an installed app.

🗣️ suggestion: We might want to guard against this case inline and with a test since I'm not sure how else promptIsProduction is used, but returning early might be a solid option too!

Copy link
Member

Choose a reason for hiding this comment

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

💟 praise: I like the environment selection before app selection too!


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

if !isProductionApp {
selection.App.IsDev = true
}
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: Normally, we'd also update the UserID for dev apps, but it's already been set.

}

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