Skip to content

Commit 998080a

Browse files
committed
Merge branch 'main' into mwbrooks-table-test-consistent-p2-map-pattern-align
2 parents 3933f61 + 7ef1ac9 commit 998080a

14 files changed

Lines changed: 468 additions & 71 deletions

File tree

.claude/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"Bash(gofmt:*)",
66
"Bash(make build:*)",
77
"Bash(make build-ci:*)",
8-
"Bash(make lint:*)",
8+
"Bash(make lint:*)"
99
]
1010
}
1111
}

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"deno",
1616
"iostreams",
1717
"opentracing",
18+
"pyproject",
1819
"safeexec",
1920
"slackcontext",
2021
"slackdeps",

cmd/app/settings.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func NewSettingsCommand(clients *shared.ClientFactory) *cobra.Command {
4545
}, "\n"),
4646
Example: style.ExampleCommandsf([]style.ExampleCommand{
4747
{
48-
Meaning: "Open app settings for a prompted app",
48+
Meaning: "Open app settings dashboard",
4949
Command: "app settings",
5050
},
5151
{
@@ -65,11 +65,12 @@ func NewSettingsCommand(clients *shared.ClientFactory) *cobra.Command {
6565
}
6666

6767
// appSettingsCommandPreRunE determines if the command can be run in a project
68+
// or if the command is run outside of a project
6869
func appSettingsCommandPreRunE(clients *shared.ClientFactory, cmd *cobra.Command, args []string) error {
6970
ctx := cmd.Context()
7071
err := cmdutil.IsValidProjectDirectory(clients)
7172
if err != nil {
72-
return err
73+
return nil
7374
}
7475
// Allow the force flag to ignore hosted apps and try to open app settings
7576
if clients.Config.ForceFlag {
@@ -98,6 +99,28 @@ func appSettingsCommandRunE(clients *shared.ClientFactory, cmd *cobra.Command, a
9899

99100
app, err := settingsAppSelectPromptFunc(ctx, clients, prompts.ShowAllEnvironments, prompts.ShowInstalledAndUninstalledApps)
100101
if err != nil {
102+
// If no apps exist, open the list of all apps known to the developer
103+
if slackerror.Is(err, slackerror.ErrInstallationRequired) {
104+
host := clients.API().Host()
105+
parsed, err := url.Parse(host)
106+
if err != nil {
107+
return err
108+
}
109+
parsed.Host = "api." + parsed.Host
110+
settingsURL := fmt.Sprintf("%s/apps", parsed.String())
111+
112+
clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
113+
Emoji: "house",
114+
Text: "App Settings",
115+
Secondary: []string{
116+
settingsURL,
117+
},
118+
}))
119+
clients.Browser().OpenURL(settingsURL)
120+
121+
clients.IO.PrintTrace(ctx, slacktrace.AppSettingsSuccess, settingsURL)
122+
return nil
123+
}
101124
return err
102125
}
103126
host := clients.API().Host()

cmd/app/settings_test.go

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,28 @@ import (
3232

3333
func Test_App_SettingsCommand(t *testing.T) {
3434
testutil.TableTestCommand(t, testutil.CommandTests{
35-
"requires a valid project directory": {
36-
ExpectedError: slackerror.New(slackerror.ErrInvalidAppDirectory),
35+
"opens app listing page when not in a project directory": {
36+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
37+
appSelectMock := prompts.NewAppSelectMock()
38+
appSelectMock.On(
39+
"AppSelectPrompt",
40+
mock.Anything,
41+
mock.Anything,
42+
prompts.ShowAllEnvironments,
43+
prompts.ShowInstalledAndUninstalledApps,
44+
).Return(
45+
prompts.SelectedApp{},
46+
slackerror.New(slackerror.ErrInstallationRequired),
47+
)
48+
settingsAppSelectPromptFunc = appSelectMock.AppSelectPrompt
49+
cm.API.On("Host").Return("https://slack.com")
50+
},
51+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
52+
expectedURL := "https://api.slack.com/apps"
53+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
54+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsStart, mock.Anything)
55+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsSuccess, []string{expectedURL})
56+
},
3757
},
3858
"errors for rosi applications": {
3959
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
@@ -113,7 +133,40 @@ func Test_App_SettingsCommand(t *testing.T) {
113133
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsSuccess, []string{expectedURL})
114134
},
115135
},
116-
"requires an existing application": {
136+
"opens app listing page when no apps exist": {
137+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
138+
cf.SDKConfig.WorkingDirectory = "."
139+
projectConfigMock := config.NewProjectConfigMock()
140+
projectConfigMock.On(
141+
"GetManifestSource",
142+
mock.Anything,
143+
).Return(
144+
config.ManifestSourceRemote,
145+
nil,
146+
)
147+
cm.Config.ProjectConfig = projectConfigMock
148+
appSelectMock := prompts.NewAppSelectMock()
149+
appSelectMock.On(
150+
"AppSelectPrompt",
151+
mock.Anything,
152+
mock.Anything,
153+
prompts.ShowAllEnvironments,
154+
prompts.ShowInstalledAndUninstalledApps,
155+
).Return(
156+
prompts.SelectedApp{},
157+
slackerror.New(slackerror.ErrInstallationRequired),
158+
)
159+
settingsAppSelectPromptFunc = appSelectMock.AppSelectPrompt
160+
cm.API.On("Host").Return("https://slack.com")
161+
},
162+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
163+
expectedURL := "https://api.slack.com/apps"
164+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
165+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsStart, mock.Anything)
166+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsSuccess, []string{expectedURL})
167+
},
168+
},
169+
"opens app listing page for development environment when no apps exist": {
117170
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
118171
cf.SDKConfig.WorkingDirectory = "."
119172
projectConfigMock := config.NewProjectConfigMock()
@@ -137,8 +190,14 @@ func Test_App_SettingsCommand(t *testing.T) {
137190
slackerror.New(slackerror.ErrInstallationRequired),
138191
)
139192
settingsAppSelectPromptFunc = appSelectMock.AppSelectPrompt
193+
cm.API.On("Host").Return("https://dev1234.slack.com")
194+
},
195+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
196+
expectedURL := "https://api.dev1234.slack.com/apps"
197+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
198+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsStart, mock.Anything)
199+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.AppSettingsSuccess, []string{expectedURL})
140200
},
141-
ExpectedError: slackerror.New(slackerror.ErrInstallationRequired),
142201
},
143202
"opens the url to app settings of an app in production": {
144203
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {

cmd/project/create_template.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ func getSelectionOptionsForCategory(clients *shared.ClientFactory) []promptObjec
8282
Repository: "slack-cli#getting-started",
8383
},
8484
{
85-
Title: fmt.Sprintf("Automation app %s", style.Secondary("Custom steps and workflows")),
86-
Repository: "slack-cli#automation-apps",
85+
Title: fmt.Sprintf("AI Agent app %s", style.Secondary("Slack agents and assistants")),
86+
Repository: "slack-cli#ai-apps",
8787
},
8888
{
89-
Title: fmt.Sprintf("AI app %s %s", style.Secondary("Slack agents and assistants"), style.Emoji("sparkles")),
90-
Repository: "slack-cli#ai-apps",
89+
Title: fmt.Sprintf("Automation app %s", style.Secondary("Custom steps and workflows")),
90+
Repository: "slack-cli#automation-apps",
9191
},
9292
{
9393
Title: "View more samples",

cmd/project/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func NewInitCommand(clients *shared.ClientFactory) *cobra.Command {
4747
"Adds the Slack CLI hooks dependency to your project:",
4848
"- Deno: Unsupported",
4949
"- Node.js: Updates package.json",
50-
"- Python: Updates requirements.txt",
50+
"- Python: Updates requirements.txt or pyproject.toml",
5151
"",
5252
"Installs your project dependencies when supported:",
5353
"- Deno: Supported",

docs/guides/using-environment-variables-with-the-slack-cli.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ MY_ENV_VAR=asdf1234
2222

2323
Note that changes to your `.env` file will be reflected when you restart your local development server.
2424

25-
While the `.env` file should **never** be committed to source control for security reasons, you can see a sample `.env` file we've included in the [Timesheet approval sample app](https://github.com/slack-samples/deno-timesheet-approval) and the [Incident management sample app](https://github.com/slack-samples/deno-incident-management).
25+
While the `.env` file should **never** be committed to source control for security reasons, you can see a sample `.env` file we've included in the [Timesheet approval sample app](https://github.com/slack-samples/deno-timesheet-approval) and the [Incident management sample app](https://github.com/slack-samples/deno-incident-management).
2626

2727
### Storing deployed environment variables {#deployed-env-vars}
2828

@@ -40,7 +40,7 @@ If your token contains non-alphanumeric characters, wrap it in quotes like this:
4040
slack env add SLACK_API_URL "https://dev<yournumber>.slack.com/api/"
4141
```
4242

43-
Your environment variables are always encrypted before being stored on our servers and will be automatically decrypted when you use them&mdash;including when listing environment variables with `slack env list`.
43+
Your environment variables are always encrypted before being stored on our servers and will be automatically decrypted when you use them&mdash;including when listing environment variables with `slack env list`.
4444

4545
### Access variables from within function {#access-function}
4646

@@ -108,7 +108,7 @@ export default Manifest({
108108
outgoingDomains: [
109109
Deno.env.get("CHATBOT_API_URL")!,
110110
],
111-
botScopes: ["commands", "chat:write", "chat:write.public"],
111+
botScopes: ["commands", "chat:write"],
112112
});
113113
```
114114

@@ -134,7 +134,7 @@ With this addition, running `slack deploy` without defining a value for `CHATBOT
134134

135135
## Enabling debug mode {#debug}
136136

137-
The included environment variable `SLACK_DEBUG` can enable a basic debug mode. Set `SLACK_DEBUG` to `true` to have all function-related payloads logged.
137+
The included environment variable `SLACK_DEBUG` can enable a basic debug mode. Set `SLACK_DEBUG` to `true` to have all function-related payloads logged.
138138

139139
For local apps, add the following to your `.env` file:
140140

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
1616
github.com/oliamb/cutter v0.2.2
1717
github.com/opentracing/opentracing-go v1.2.0
18+
github.com/pelletier/go-toml/v2 v2.2.4
1819
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
1920
github.com/pkg/errors v0.9.1
2021
github.com/radovskyb/watcher v1.0.7

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
114114
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
115115
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
116116
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
117+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
118+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
117119
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
118120
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
119121
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=

internal/goutils/strings_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ func Test_RedactPII(t *testing.T) {
326326
},
327327
{
328328
name: "Escape sensitive data from mock HTTP response",
329-
text: `{"ok":true,"app_id":"A123","credentials":{"client_id":"123","client_secret":"123","verification_token":"123","signing_secret":"123"},"oauth_authorize_url":"123":\/\/slack.com\/oauth\/v2\/authorize?client_id=123&scope=commands,chat:write,chat:write.public"}`,
330-
expected: `{"ok":true,"app_id":"A123","credentials":{"client_id":"...","client_secret":"...","verification_token":"...","signing_secret":"..."},"oauth_authorize_url":"...":\/\/slack.com\/oauth\/v2\/authorize?client_id=...&scope=commands,chat:write,chat:write.public"}`,
329+
text: `{"ok":true,"app_id":"A123","credentials":{"client_id":"123","client_secret":"123","verification_token":"123","signing_secret":"123"},"oauth_authorize_url":"123":\/\/slack.com\/oauth\/v2\/authorize?client_id=123&scope=commands,chat:write"}`,
330+
expected: `{"ok":true,"app_id":"A123","credentials":{"client_id":"...","client_secret":"...","verification_token":"...","signing_secret":"..."},"oauth_authorize_url":"...":\/\/slack.com\/oauth\/v2\/authorize?client_id=...&scope=commands,chat:write"}`,
331331
},
332332
{
333333
name: "Escape from `Command` for external-auth add-secret",

0 commit comments

Comments
 (0)