Skip to content

Conversation

@mwbrooks
Copy link
Member

@mwbrooks mwbrooks commented Jul 11, 2025

CHANGELOG

Update the slack install command to create and install new Bolt Framework apps that are configured to use app settings as the source of truth (i.e. remote manifest).

Summary

This pull request updates the slack install command to create and install new apps that use app settings as the source of truth (remote manifest). It's a continuation of PR #111, which added similar support to slack run.

Affected commands:

  • slack install
  • slack deploy
  • slack trigger create

Follow-up PRs

Reviewers

Each use-case includes a collapsed section with manual steps.

Deno SDK - slack install

Should successfully install to both local and deployed app environments on the same (or different) workspaces.
Should successfully re-install as well.

2025-07-11-install-on-remote-manifest-deno-install.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/deno-starter-template
$ cd asdf/

# Test Install
$ lack install
# → Select Local
# → Select a team
$ lack install
# → Select Deploy
# → Select same team

# Test Re-install
$ lack install
# → Select Local
# → Select same team
$ lack install
# → Select Deploy
# → Select same team

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Deno SDK - slack deploy

Should successfully create, install, and deploy a new app.
Should successfully re-deploy.

2025-07-11-install-on-remote-manifest-deno-deploy.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/deno-starter-template
$ cd asdf/

# Test Deploy
$ lack deploy
# → Select a team

# Test Re-deploy
$ lack deploy
# → Select existing app

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Deno SDK - slack trigger create

Should successfully create a trigger on a new app.
Should successfully create a trigger on an existing app.

2025-07-11-install-on-remote-manifest-deno-trigger.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/deno-starter-template
$ cd asdf/

# Create Trigger on New App
$ lack trigger create --trigger-def triggers/sample_trigger.ts
# → Select a team

# Create Trigger on Existing App
$ lack trigger create --trigger-def triggers/sample_trigger.ts
# → Select existing app

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Bolt JS - slack install

Should successfully install to both local and deployed app environments on the same (or different) workspaces.
Should successfully re-install as well.

2025-07-11-install-on-remote-manifest-bolt-install.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Test Install
$ lack install
# → Select Local
# → Select a team
$ lack install
# → Select Deploy
# → Select same team

# Test Re-install
$ lack install
# → Select Local
# → Select same team
$ lack install
# → Select Deploy
# → Select same team

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Bolt JS - slack deploy

Should successfully create, install, and deploy a new app.
Should successfully re-deploy.

2025-07-11-install-on-remote-manifest-bolt-install.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Test No Deploy Script
$ lack deploy
# → Select a team
# → Confirm script tutorial error message

# Create Deploy Script
$ vim .slack/hooks.json
# → Add "deploy": "./deploy.sh"
$ vim deploy.sh
# #!/bin/bash
# 
# echo "Deployed!"
$ chmod +x deploy.sh

# Test Deploy
$ lack deploy
# → Select a team

# Test Re-deploy
$ lack deploy
# → Select existing app

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Scripting & Flags - slack install --team T0123 --environment local

Should default to --environment deployed when --team set
Should support --environment local
Should support --environment deployed

2025-07-11-install-on-remote-manifest-flags.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Test Defaulting to Deployed
$ lack install --team T0123
# → Confirm no app environment prompt
# → Confirm creates a deployed app (no local suffix)

# Test --environment local
$ lack install --team T0123 --environment local
# → Confirm no app environment prompt
# → Confirm creates a local app

# Test --environment deployed
$ lack install --team T0123 --environment deployed
# → Confirm no app environment prompt
# → Confirm creates a deployed app

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Scripting & Flags - slack install --team T0123

2025-07-24-install-team.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Test Defaulting to Deployed
$ lack install --team T0123
# → Confirm warning displayed
# → Confirm no app environment prompt
# → Confirm creates a deployed app (no local suffix)

# Cleanup
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Scripting & Flags - Flag Mismatch --app, --team, & --environment

2025-07-24-install-team-mismatch.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Install a valid app
$ lack install
# → Copy App ID

# Test Mismatched --app and --team
$ slack install --app <valid-app-id> --team <invalid-team-id>
# → Confirm error

# Test Mismatched --app and --environment
$ slack install --app <valid-app-id> --environment <invalid-environment>
# → Confirm error

# Cleanup
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Scripting & Flags - Support --app local and --app deployed

Working example:
https://github.com/user-attachments/assets/dcc19355-4b65-42ff-a115-56cd57610598

Mismatch errors:
https://github.com/user-attachments/assets/dea85d8d-8751-4aac-a571-f3d306b2dec1

Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Install a valid app for the local environemnt
$ lack install --app local
# → Confirm environment prompt is skipped
# → Confirm app is local

$ lack install --app deployed
# → Confirm environment prompt is skipped
# → Confirm app is deployed

$ lack install --app local --environment deployed
# → Confirm mismatched error message

$ lack install --app deployed --environment local
# → Confirm mismatched error message

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Scripting & Flags - Support --app A123

Working example:
https://github.com/user-attachments/assets/42caf961-2200-4db2-b683-c2c43ce16c35

Mismatch errors:

2025-08-07-install-w-app-id-mismatch.mov
Manual Test Steps
$ lack create asdf/ -t slack-samples/bolt-js-starter-template
$ cd asdf/

# Install a local and deployed app
$ lack install --app local
$ lack install --app deployed

# Copy Local App ID
$ lack app list
→ Copy local App ID

# Test Local App ID (replace A123 with your App ID)
$ lack install --app A123
# → Confirm local app is re-installed

# Test Deployed App ID (replace A456 with your App ID)
$ lack install --app A456
# → Confirm deployed app is re-installed

# Test mismatched flags
$ lack install --app A123 --environment local
# → Confirm mismatched error message
$ lack install --app A456 --environment local
# → Confirm mismatched error message

# Cleanup
$ lack delete -f
# → Select an app
$ lack delete -f
# → Select an app
$ cd ..
$ rm -rf asdf/

Requirements

@mwbrooks mwbrooks added this to the Next Release milestone Jul 11, 2025
@mwbrooks mwbrooks self-assigned this Jul 11, 2025
@mwbrooks mwbrooks added docs M-T: Documentation work only enhancement M-T: A feature request for new functionality changelog Use on updates to be included in the release notes semver:minor Use on pull requests to describe the release version increment area:bolt-js Related to github.com/slackapi/bolt-js area:bolt-python Related to github.com/slackapi/bolt-python labels Jul 11, 2025
@codecov
Copy link

codecov bot commented Jul 11, 2025

Codecov Report

❌ Patch coverage is 68.86792% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.05%. Comparing base (ffbe0b5) to head (e28a02e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/app/add.go 76.92% 10 Missing and 5 partials ⚠️
internal/pkg/apps/install.go 57.14% 10 Missing and 5 partials ⚠️
cmd/platform/deploy.go 50.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #154      +/-   ##
==========================================
+ Coverage   63.03%   63.05%   +0.02%     
==========================================
  Files         212      212              
  Lines       21608    21667      +59     
==========================================
+ Hits        13621    13663      +42     
- Misses       6942     6952      +10     
- Partials     1045     1052       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member Author

@mwbrooks mwbrooks left a comment

Choose a reason for hiding this comment

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

🧘🏻 Comments for the kind reviewers!

Comment on lines -88 to -107
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",
},
})
}
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.

cmd/app/add.go Outdated
Comment on lines 94 to 107
// Prompt for deployed or local app environment.
isProductionApp, err := promptIsProduction(ctx, clients)
if err != nil {
return ctx, "", types.App{}, err
}

var appEnvironmentType prompts.AppEnvironmentType
if isProductionApp {
appEnvironmentType = prompts.ShowHostedOnly
} else {
appEnvironmentType = prompts.ShowLocalOnly
}

selected, err := teamAppSelectPromptFunc(ctx, clients, appEnvironmentType, prompts.ShowAllApps)
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: New code that prompts the developer to choose an environment (local or deployed). Then displays the proper Team Prompt. We cannot prompts.ShowAllEnvironments because there is guard logic to avoid having 2 apps on one team. Unfortunately, the prompt isn't aware of the app's environment, so this was our workaround.

cmd/app/add.go Outdated
Comment on lines 113 to 115
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.

Comment on lines -88 to -102
"errors if manifest.source is remote with the bolt experiment": {
ExpectedError: 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",
},
}),
Copy link
Member Author

Choose a reason for hiding this comment

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

🪓 We no longer want to error on a remote manifest, so this test is removed and replaced with proceeds if manifest.source is remote with the bolt experiment (below)

Copy link
Member

Choose a reason for hiding this comment

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

🪓 thought: Once the bolt experiment is removed altogether, we can perhaps remove even more cases!

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes please!

func TestAppAddCommand(t *testing.T) {

testutil.TableTestCommand(t, testutil.CommandTests{
"adds a new local app": {
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: Added test for new local apps to complement the existing new deployed app test.

Comment on lines +172 to +173
// When the manifest source is remote and the app exists, we can get the manifest from the the API.
case manifestSource.Equals(config.ManifestSourceRemote) && app.AppID != "":
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 need to differentiate between a remote manifest with an app (get manifest from app settings) and a remote manifest without an app (get manifest from local file).

Copy link
Member

Choose a reason for hiding this comment

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

👁️‍🗨️ thought: An app.Exists method might be nice here-


* `bolt-install`: enables creating, installing, and running Bolt projects that manage their app manifest on app settings (remote manifest).
* `slack create` and `slack init` now set manifest source to "app settings" (remote) for Bolt JS & Bolt Python projects ([PR#96](https://github.com/slackapi/slack-cli/pull/96)).
* `slack run` and `slack install` support creating and installing Bolt Framework apps that have the manifest source set to "app settings (remote)" ([PR#111](https://github.com/slackapi/slack-cli/pull/111), [PR#TODO](https://github.com/slackapi/slack-cli/pull/TODO)).
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: I added the slack run in PR #111 but it may have been removed by a rogue docs sync 🥷

Copy link
Member Author

Choose a reason for hiding this comment

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

@lukegalbraithrussell Don't sweat it! The docs sync has improved a lot since then, thank to you!

Comment on lines +57 to +61

if !clients.Config.WithExperimentOn(experiment.BoltInstall) {
if !manifestUpdates && !manifestCreates {
return app, "", nil
}
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: Identical to installing a dev app in PR #111

Comment on lines +87 to +92
// When the BoltInstall experiment is enabled, we need to get the manifest from the local file
// if the manifest source is local or if we are creating a new app. After an app is created,
// app settings becomes the source of truth for remote manifests, so updates and installs always
// get the latest manifest from app settings.
// When the BoltInstall experiment is disabled, we get the manifest from the local file because
// this is how the original implementation worked.
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: Identical to installing a dev app in PR #111

Comment on lines 155 to 157
// TODO: add enterprise ID and user ID to app? See InstallLocalApp.
// app.EnterpriseID = config.GetContextEnterpriseID(ctx)
// app.UserID = *authSession.UserID
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: While comparing the dev install and this install, I noticed that we set the EnterpriseID and UserID in the other one. I've left a note incase we want to do it here as well.

Copy link
Member

Choose a reason for hiding this comment

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

@mwbrooks EnterpriseID makes a lot of sense to me!

I'm hesitant for the UserID at the moment since we might be using this - alongside the IsDev value - to differentiate "deployed" and "local" apps IIRC 🔍

No blocker for this PR, but the app UserID might be something to inspect more later!

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the feedback @zimeg!

I'll test again on an Enterprise Organization. If the app works properly, I'll hold off on making either of these changes. If it has been working until now, I'd be concerned about a regression. We can follow-up with a separate PR that sets EnterpriseID or something else to align the 2 methods.

Copy link
Member Author

Choose a reason for hiding this comment

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

@zimeg I've removed the UserID comment so that we don't accidentally implement it later on. I agree, there could be bits of code relying on it to identify a dev app.

@mwbrooks mwbrooks marked this pull request as ready for review July 11, 2025 08:08
@mwbrooks mwbrooks requested review from a team as code owners July 11, 2025 08:08

* `bolt-install`: enables creating, installing, and running Bolt projects that manage their app manifest on app settings (remote manifest).
* `slack create` and `slack init` now set manifest source to "app settings" (remote) for Bolt JS & Bolt Python projects ([PR#96](https://github.com/slackapi/slack-cli/pull/96)).
* `slack run` and `slack install` support creating and installing Bolt Framework apps that have the manifest source set to "app settings (remote)" ([PR#111](https://github.com/slackapi/slack-cli/pull/111), [PR#TODO](https://github.com/slackapi/slack-cli/pull/TODO)).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* `slack run` and `slack install` support creating and installing Bolt Framework apps that have the manifest source set to "app settings (remote)" ([PR#111](https://github.com/slackapi/slack-cli/pull/111), [PR#TODO](https://github.com/slackapi/slack-cli/pull/TODO)).
* `slack run` and `slack install` support creating and installing Bolt Framework apps that have the manifest source set to "app settings (remote)" ([PR#111](https://github.com/slackapi/slack-cli/pull/111), [PR#TODO](https://github.com/slackapi/slack-cli/pull/TODO)).

assuming this #TODO is still needed to be filled out?

Copy link
Member Author

Choose a reason for hiding this comment

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

Whew! Thank you, @lukegalbraithrussell! Commit 10966f7 adds the PR number.

}

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.

Comment on lines +417 to +418
// TODO(semver:major): Remove this test when the defaulting to deployed is removed.
"adds a new deployed app when team flag is provided and environment flag is not set": {
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: Test for defaulting to deployed when the team flag is specified and there is no --environment flag.

@lukegalbraithrussell lukegalbraithrussell requested a review from a team July 11, 2025 20:38
Copy link
Contributor

@lukegalbraithrussell lukegalbraithrussell left a comment

Choose a reason for hiding this comment

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

docs are good!

Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

@mwbrooks This is looking great so far! 🎉

I'm requesting changes now with a note on the --app flag but will continue a review and testing in the meantime.

Thanks for cleaning adjacent things up with these changes, as always 💌

}

func validateManifestForInstall(ctx context.Context, clients *shared.ClientFactory, app types.App, appManifest types.AppManifest) error {
var token = config.GetContextToken(ctx)
Copy link
Member

Choose a reason for hiding this comment

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

🪓 So helpful when walking through the code!

Comment on lines 155 to 157
// TODO: add enterprise ID and user ID to app? See InstallLocalApp.
// app.EnterpriseID = config.GetContextEnterpriseID(ctx)
// app.UserID = *authSession.UserID
Copy link
Member

Choose a reason for hiding this comment

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

@mwbrooks EnterpriseID makes a lot of sense to me!

I'm hesitant for the UserID at the moment since we might be using this - alongside the IsDev value - to differentiate "deployed" and "local" apps IIRC 🔍

No blocker for this PR, but the app UserID might be something to inspect more later!

cmd/app/add.go Outdated

// 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.

Copy link
Member Author

@mwbrooks mwbrooks left a comment

Choose a reason for hiding this comment

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

📝 Thanks for the thorough review @zimeg and for noticing the edge-cases with the --app, --team, and --environment flag combinations.

✨ A few updates since our discussion:

  • Display a better warning when the --team flag is used and --environment deploy is inferred for backwards compability
  • promptIsProduction(...) now reads the --environment flag properly
  • Mismatch flag combinations should be handled, such as --team T0123 --app A0123 and this is largely enforced by SelectPrompt(...)
  • Updated the original description with 2 new videos showcasing additional scripting use-cases

I know it's tough to think through these use-cases, but I'd appreciate your eye on whether we're overlooking additional use-cases! 🙇🏻

},
})
}
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 🪄

Comment on lines +101 to +105
err := clients.Config.Flags.Lookup("environment").Value.Set("deployed")
if err != nil {
return ctx, "", types.App{}, err
}
clients.Config.Flags.Lookup("environment").Changed = 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: 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.

Comment on lines +107 to +117
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.",
},
}))
}
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 🤖 ✨

Comment on lines 155 to 157
// TODO: add enterprise ID and user ID to app? See InstallLocalApp.
// app.EnterpriseID = config.GetContextEnterpriseID(ctx)
// app.UserID = *authSession.UserID
Copy link
Member Author

Choose a reason for hiding this comment

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

@zimeg I've removed the UserID comment so that we don't accidentally implement it later on. I agree, there could be bits of code relying on it to identify a dev app.

@mwbrooks mwbrooks requested a review from zimeg July 25, 2025 03:51
Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

@mwbrooks Thanks so much for keeping the description updated. It makes reviews so much easier 👾

I'm requesting changes here with a side effect of reinstalling apps with the app flag, though it's not so clear to me if the environment flag is also needed here:

$ slack app install --app A0986U6T03A | cat

Other comments include thoughts and a small suggestion or two, but I'm excited for this to land and it feels close! ✨

Comment on lines 43 to 46
type addCmdFlags struct {
orgGrantWorkspaceID string
environmentFlag string
}
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 🤔


manifest := slackYaml.AppManifest
if slackYaml.IsFunctionRuntimeSlackHosted() {
manifest := slackManifest.AppManifest
Copy link
Member

Choose a reason for hiding this comment

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

🤩 praise: The slackManifest including an app manifest is so much more clear than the prior slackYaml!

cmd/app/add.go Outdated

// 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.

🧠 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 🤓

cmd/app/add.go Outdated
Comment on lines 119 to 131
// 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!

Comment on lines -88 to -102
"errors if manifest.source is remote with the bolt experiment": {
ExpectedError: 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",
},
}),
Copy link
Member

Choose a reason for hiding this comment

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

🪓 thought: Once the bolt experiment is removed altogether, we can perhaps remove even more cases!

manifestSource: config.ManifestSourceLocal,
expectedError: slackerror.New(slackerror.ErrSDKHookNotFound),
},
"succeeds if the app exists and the manifest source is remote": {
Copy link
Member

Choose a reason for hiding this comment

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

😉 thought: These are clever tests. I think it calls out the importance of error codes in assertions. But seems right here!

Co-authored-by: Eden Zimbelman <[email protected]>
@mwbrooks
Copy link
Member Author

mwbrooks commented Jul 25, 2025

Thanks for the thorough eyes @zimeg! I'm seeing a few issues that we'll want to address before this PR is ready.

Required changes:

Follow-ups:

@zimeg zimeg modified the milestones: v3.5.2, Next Release Jul 25, 2025
@mwbrooks
Copy link
Member Author

mwbrooks commented Aug 7, 2025

@zimeg Thanks again for the amazing edge-case reviews and sorry about the slow follow-up. I've pushed a few commits to address the 3 tasks in #154 (comment)

  • Updated PR description to include Examples with videos and test steps for each of the 3 tasks
  • Added test coverage
  • Created 2 follow-up PRs

I noticed that our isProductionPrompt doesn't have test coverage. For this PR, I've decided to add the flag checks to the install command's implementation but the proper place is isProductionPrompt. To keep this PR crisp, I'd rather have a follow-up PR that refactor the isProductionPrompt to include the validations and test coverage. I think that's overall less risky in case the changes have side effects.

@mwbrooks mwbrooks requested a review from zimeg August 7, 2025 21:01
Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

@mwbrooks LGTM! And thank you for the follow ups across these comments 🙏 ✨

The demo cases are great and I agree that more changes to prompts can happen in follow up PRs.

One perhaps issue related to the app flag without an environment flag for a saved "local" app is noted below, but nothing blocking AFAICT. Moving towards the environment flag and deprecated "deploy" and "local" options elsewhere will be such a nice change for the code I hope 🗣️

Please do feel free to merge this if the case below tests alright to you! 🚢 💨

cmd/app/add.go Outdated

// 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.

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

Comment on lines +172 to +173
// When the manifest source is remote and the app exists, we can get the manifest from the the API.
case manifestSource.Equals(config.ManifestSourceRemote) && app.AppID != "":
Copy link
Member

Choose a reason for hiding this comment

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

👁️‍🗨️ thought: An app.Exists method might be nice here-

cmd/app/add.go Outdated

// 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.

👾 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.

// 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.

@mwbrooks mwbrooks merged commit bed795a into main Aug 8, 2025
6 checks passed
@mwbrooks mwbrooks deleted the mwbrooks-install-remote-manifest branch August 8, 2025 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:bolt-js Related to github.com/slackapi/bolt-js area:bolt-python Related to github.com/slackapi/bolt-python changelog Use on updates to be included in the release notes docs M-T: Documentation work only enhancement M-T: A feature request for new functionality semver:minor Use on pull requests to describe the release version increment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants