Skip to content

feat(create): cleanup app creation outputs when slack create -app passed#598

Open
srtaalej wants to merge 6 commits into
mainfrom
ale-create-app-outputs
Open

feat(create): cleanup app creation outputs when slack create -app passed#598
srtaalej wants to merge 6 commits into
mainfrom
ale-create-app-outputs

Conversation

@srtaalej

@srtaalej srtaalej commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Changelog

Reduced verbose output when using create --app to link an existing app during project creation.

Summary

When --app is provided to slack create, the linking step previously printed multiple noisy sections (App Link header, App Manifest details, App footer, and "Added existing app" message). Now it only prints the single 🏠 App summary section showing team, App ID, Team ID, User ID, and Status.

Preview

Before

before

After

after

Testing

  • make test
  • Manual test - ./bin/slack create my-test -t slack-samples/bolt-js-starter-template --app A123 --team T123 --environment local

Notes

Adresses comments on PR #565 - Improve the output experience for create when --app is provided.

Requirements

@srtaalej srtaalej added this to the Next Release milestone Jun 22, 2026
@srtaalej srtaalej self-assigned this Jun 22, 2026
@srtaalej srtaalej requested a review from a team as a code owner June 22, 2026 20:21
@srtaalej srtaalej added enhancement M-T: A feature request for new functionality semver:minor Use on pull requests to describe the release version increment labels Jun 22, 2026
@srtaalej srtaalej changed the title feat: cleanup app creation outputs when passed feat(create): cleanup app creation outputs when slack create -app passed Jun 22, 2026
@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.19608% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.68%. Comparing base (44ce627) to head (925c458).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/project/init.go 73.33% 2 Missing and 2 partials ⚠️
cmd/project/create.go 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #598      +/-   ##
==========================================
- Coverage   71.68%   71.68%   -0.01%     
==========================================
  Files         226      226              
  Lines       19176    19183       +7     
==========================================
+ Hits        13747    13751       +4     
+ Misses       4220     4218       -2     
- Partials     1209     1214       +5     

☔ View full report in Codecov by Harness.
📢 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.

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@srtaalej Super thanks for taking a pass at better create outputs. I'm leaving some comments to help us maintain these commands as ongoing changes are introduced with more "DRY" functions 👁️‍🗨️

Comment thread cmd/app/link.go Outdated
Comment on lines +195 to +224
// LinkExistingAppQuiet links an existing app without verbose output. It resolves
// the team and environment, validates the app, saves it, and prints only the app
// summary. Used by `create --app` where the surrounding create output is enough.
func LinkExistingAppQuiet(ctx context.Context, clients *shared.ClientFactory, app *types.App) error {
linkedApp, auth, err := promptExistingApp(ctx, clients)
if err != nil {
return err
}
*app = linkedApp

appIDs := []string{app.AppID}
_, err = clients.API().GetAppStatus(ctx, auth.Token, appIDs, app.TeamID)
if err != nil {
return err
}

err = saveAppToJSON(ctx, clients, *app)
if err != nil {
clients.IO.PrintDebug(ctx, "Error saving app to file when linking existing app: %s", err)
return err
}

clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
Emoji: "house",
Text: "App",
Secondary: formatListSuccess([]types.App{*app}),
}))

return nil
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔭 thought: This is near identical to the lines above and I'm wondering if we can somehow keep a single source of truth for this logic?

slack-cli/cmd/app/link.go

Lines 170 to 190 in a0f86d7

var auth *types.SlackAuth
*app, auth, err = promptExistingApp(ctx, clients)
if err != nil {
return err
}
appIDs := []string{app.AppID}
_, err = clients.API().GetAppStatus(ctx, auth.Token, appIDs, app.TeamID)
if err != nil {
return err
}
// Save the app to the project
err = saveAppToJSON(ctx, clients, *app)
if err != nil {
clients.IO.PrintDebug(ctx, "Error saving app to file when linking existing app: %s", err)
return err
}
// Footer section
LinkAppFooterSection(ctx, clients, app)

@srtaalej srtaalej Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure good call! @zimeg to clarify though, do you mean the entire link command should return the way LinkExistingAppQuiet is returning? or should we have something like

func LinkExistingApp(ctx context.Context, clients *shared.ClientFactory, app *types.App, shouldConfirm bool, quiet bool) (err error) {
	if !quiet {
		// Header section
		LinkAppHeaderSection(ctx, clients, shouldConfirm)
	}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@srtaalej I missed the edited mention! question But separating output from logic might make this possible? I think outputs and even the "should confirm" logic should be moved to adjacent functions:

  • app.LinkExistingApp: What I permalink above
  • app.LinkCommandRunE: Perhaps most the "link" outputs not meant to share

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ah got it 🙌 !

@srtaalej srtaalej requested a review from zimeg June 25, 2026 16:11
@srtaalej

Copy link
Copy Markdown
Contributor Author

@zimeg thank you for your earlier review! i believe your feedback has been addressed in 7b615b7 🚀 good call on separating the outputs and linking logic! 💟

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@srtaalej Super appreciate changes toward this! I'm feeling a lot better about how the code structures are and I'm leaving a few more thoughts ongoing if these might move things in a good direction?

Comment thread cmd/app/link.go
Comment thread cmd/project/init.go Outdated

// Add an existing app to the project
err = app.LinkExistingApp(ctx, clients, &types.App{}, true)
err = app.LinkCommandRunE(ctx, clients, &types.App{}, true, false)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

💡 thought: No blocker but we might want to refactor the confirmation prompt to be here instead of within the link command?

⚡ ramble: I find boolean arguments unclear and also think a command should run if it's called - it'd be up to the caller to decide if that command should run instead!

@zimeg zimeg added the changelog Use on updates to be included in the release notes label Jun 25, 2026
@mwbrooks mwbrooks modified the milestones: v4.4.0, Next Release Jun 30, 2026

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@srtaalej Thanks for the huge cleanups to output 🎨 ✨

I'm leaving a handful more comments with some suggestions on tests and error handling and one more line of output. I'll update the preview with a current difference now too 📸

Comment thread cmd/app/link_test.go
}

func Test_Apps_LinkAppHeaderSection(t *testing.T) {
tests := map[string]struct {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🏗️ quibble: Keeping table tests structured - even if one case - instead of a flattened single case might encourage additional cases

Comment thread cmd/app/link_test.go
Comment on lines -590 to -597
"When shouldConfirm is true": {
shouldConfirm: true,
expectedOutputs: []string{
"Add an existing app from app settings",
"Find your existing apps at: https://api.slack.com/apps",
"Manually add apps later with",
},
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔬 note: Existing tests continue to cover this removed case in the init command.

"init a project and do not link an existing app": {
CmdArgs: []string{},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
setupProjectInitCommandMocks(t, ctx, cm, cf)
// Do not link an existing app
cm.IO.On("ConfirmPrompt", mock.Anything, app.LinkAppConfirmPromptText, mock.Anything).Return(false, nil)
},
ExpectedStdoutOutputs: []string{
"Project Initialization", // Assert section header
"App Link", // Assert section header
"Next Steps", // Assert section header
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
// Assert installing project dependencies
output := cm.GetCombinedOutput()
require.Contains(t, output, "Project Dependencies")
require.Contains(t, output, "Added "+filepath.Join("project-name", ".slack"))
require.Contains(t, output, "Added "+filepath.Join("project-name", ".slack", ".gitignore"))
require.Contains(t, output, "Added "+filepath.Join("project-name", ".slack", "hooks.json"))
require.Contains(t, output, `Updated app manifest source to "project" (local)`)
// Assert prompt to add existing apps was called
cm.IO.AssertCalled(
t,
"ConfirmPrompt",
mock.Anything,
app.LinkAppConfirmPromptText,
mock.Anything,
)
// Assert Trace
cm.IO.AssertCalled(
t,
"PrintTrace",
mock.Anything,
slacktrace.ProjectInitSuccess,
mock.Anything,
)
cm.IO.AssertNotCalled(
t,
"PrintTrace",
mock.Anything,
slacktrace.AppLinkStart,
mock.Anything,
)
},
},
"init a project and link an existing app": {
CmdArgs: []string{},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
// Mocks auths to match against team and app
cm.Auth.On("Auths", mock.Anything).Return([]types.SlackAuth{
mockLinkSlackAuth2,
mockLinkSlackAuth1,
}, nil)
// Default setup
setupProjectInitCommandMocks(t, ctx, cm, cf)
// Link an existing app
cm.IO.On("ConfirmPrompt", mock.Anything, app.LinkAppConfirmPromptText, mock.Anything).Return(true, nil)
// Mock prompt for team
cm.IO.On("SelectPrompt",
mock.Anything,
"Select the existing app team",
mock.Anything,
mock.Anything,
mock.Anything,
).Return(iostreams.SelectPromptResponse{
Prompt: true,
Index: 1,
Option: mockLinkSlackAuth2.TeamDomain,
}, nil)
// Mock prompt for App ID
cm.IO.On("InputPrompt",
mock.Anything,
"Enter the existing app ID",
mock.Anything,
).Return(mockLinkAppID1, nil)
// Mock prompt for environment
cm.IO.On("SelectPrompt",
mock.Anything,
"Choose the app environment",
mock.Anything,
mock.Anything,
mock.Anything,
).Return(iostreams.SelectPromptResponse{
Prompt: true,
Option: "local",
}, nil)
// Mock status of app for footer
cm.API.On("GetAppStatus",
mock.Anything,
mockLinkSlackAuth2.Token,
[]string{mockLinkAppID1},
mockLinkSlackAuth2.TeamID,
).Return(api.GetAppStatusResult{}, nil)
},
ExpectedStdoutOutputs: []string{
"Project Initialization", // Assert section header
"App Link", // Assert section header
"Next Steps", // Assert section header
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
// Assert prompt to add existing apps was called
cm.IO.AssertCalled(t, "ConfirmPrompt",
mock.Anything,
app.LinkAppConfirmPromptText,
mock.Anything,
)
// Assert prompt for team
cm.IO.AssertCalled(t, "SelectPrompt",
mock.Anything,
"Select the existing app team",
mock.Anything,
mock.Anything,
mock.Anything,
)
// Assert prompt for app
cm.IO.AssertCalled(t, "InputPrompt",
mock.Anything,
"Enter the existing app ID",
mock.Anything,
)
// Assert prompt for environment
cm.IO.AssertCalled(t, "SelectPrompt",
mock.Anything,
"Choose the app environment",
mock.Anything,
mock.Anything,
mock.Anything,
)
// Assert trace
cm.IO.AssertCalled(
t,
"PrintTrace",
mock.Anything,
slacktrace.ProjectInitSuccess,
mock.Anything,
)
// Assert app written to file
expectedApp := types.App{
AppID: mockLinkAppID1,
TeamDomain: mockLinkSlackAuth2.TeamDomain,
TeamID: mockLinkSlackAuth2.TeamID,
IsDev: true,
UserID: mockLinkSlackAuth2.UserID,
}
actualApp, err := cm.AppClient.GetLocal(
ctx,
mockLinkSlackAuth2.TeamID,
)
require.NoError(t, err)
assert.Equal(t, expectedApp, actualApp)
},
},

Comment thread cmd/project/create.go
Comment on lines +236 to +240
clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{
Emoji: "house",
Text: "App",
Secondary: app.FormatListSuccess([]types.App{*linkedApp}),
}))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🌟 praise: Inlining this output within create seems to've fixed an extra linebreak!

Comment thread cmd/project/create.go
_ = os.Chdir(originalDir)
}()
if err := app.LinkExistingApp(ctx, clients, &types.App{}, false); err != nil {
linkedApp := &types.App{}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔭 note: IIRC adjacent PR has discussion about returning instead of providing an app argument. No blocker but it has support in eventual follow up!

Comment thread cmd/app/link.go
Comment thread cmd/app/link.go
Comment on lines 35 to 36
// LinkAppConfirmPromptText is displayed when prompting to add an existing app
const LinkAppConfirmPromptText = "Do you want to add an existing app?"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👾 suggestion: This is now used for init and not within link so I suggest we move it to the "project" package and make it private?

Comment thread cmd/project/init.go
if err != nil {
// Display the error but continue to init
clients.IO.PrintError(ctx, "%s", err.Error())
clients.IO.PrintDebug(ctx, "Error prompting to add an existing app: %s", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
clients.IO.PrintDebug(ctx, "Error prompting to add an existing app: %s", err)
return err

🗣️ suggestion: The earlier debug might've been too generic? Since we've moved this prompt callsite to init we might want to return this prompt error to stop the command after interrupts?

Comment thread cmd/project/init.go
err = app.LinkExistingApp(ctx, clients, &types.App{}, true)
// Prompt to add an existing app to the project
app.LinkAppHeaderSection(ctx, clients)
clients.IO.PrintInfo(ctx, false, " %s\n", "Manually add apps later with "+style.Commandf("app link", true))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
clients.IO.PrintInfo(ctx, false, " %s\n", "Manually add apps later with "+style.Commandf("app link", true))

🪓 suggestion: Let's remove this to avoid duplication in later output

Image

Comment thread cmd/project/init.go
// Prompt to add an existing app to the project
app.LinkAppHeaderSection(ctx, clients)
clients.IO.PrintInfo(ctx, false, " %s\n", "Manually add apps later with "+style.Commandf("app link", true))
proceed, err := clients.IO.ConfirmPrompt(ctx, app.LinkAppConfirmPromptText, true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🌚 praise: Bringing this confirmation prompt to the command that uses it is great! It unlocks more meaningful checks IMHO like checking if an app is saved before prompting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog Use on updates to be included in the release notes 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.

3 participants