Skip to content

Commit ac343ee

Browse files
authored
Merge branch 'main' into x
2 parents 5d3e2f3 + f56f6ed commit ac343ee

27 files changed

Lines changed: 1122 additions & 35 deletions

cmd/docs/docs.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2022-2026 Salesforce, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package docs
16+
17+
import (
18+
"fmt"
19+
"net/url"
20+
"strings"
21+
22+
"github.com/slackapi/slack-cli/internal/shared"
23+
"github.com/slackapi/slack-cli/internal/slackerror"
24+
"github.com/slackapi/slack-cli/internal/slacktrace"
25+
"github.com/slackapi/slack-cli/internal/style"
26+
"github.com/spf13/cobra"
27+
)
28+
29+
var searchMode bool
30+
31+
func NewCommand(clients *shared.ClientFactory) *cobra.Command {
32+
cmd := &cobra.Command{
33+
Use: "docs",
34+
Short: "Open Slack developer docs",
35+
Long: "Open the Slack developer docs in your browser, with optional search functionality",
36+
Example: style.ExampleCommandsf([]style.ExampleCommand{
37+
{
38+
Meaning: "Open Slack developer docs homepage",
39+
Command: "docs",
40+
},
41+
{
42+
Meaning: "Search Slack developer docs for Block Kit",
43+
Command: "docs --search \"Block Kit\"",
44+
},
45+
{
46+
Meaning: "Open Slack docs search page",
47+
Command: "docs --search",
48+
},
49+
}),
50+
RunE: func(cmd *cobra.Command, args []string) error {
51+
return runDocsCommand(clients, cmd, args)
52+
},
53+
}
54+
55+
cmd.Flags().BoolVar(&searchMode, "search", false, "open Slack docs search page or search with query")
56+
57+
return cmd
58+
}
59+
60+
// runDocsCommand opens Slack developer docs in the browser
61+
func runDocsCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []string) error {
62+
ctx := cmd.Context()
63+
64+
var docsURL string
65+
var sectionText string
66+
67+
// Validate: if there are arguments, --search flag must be used
68+
if len(args) > 0 && !cmd.Flags().Changed("search") {
69+
query := strings.Join(args, " ")
70+
return slackerror.New(slackerror.ErrDocsSearchFlagRequired).WithRemediation(
71+
"Use --search flag: %s",
72+
style.Commandf(fmt.Sprintf("docs --search \"%s\"", query), false),
73+
)
74+
}
75+
76+
if cmd.Flags().Changed("search") {
77+
if len(args) > 0 {
78+
// --search "query" (space-separated) - join all args as the query
79+
query := strings.Join(args, " ")
80+
encodedQuery := url.QueryEscape(query)
81+
docsURL = fmt.Sprintf("https://docs.slack.dev/search/?q=%s", encodedQuery)
82+
sectionText = "Docs Search"
83+
} else {
84+
// --search (no argument) - open search page
85+
docsURL = "https://docs.slack.dev/search/"
86+
sectionText = "Docs Search"
87+
}
88+
} else {
89+
// No search flag: default homepage
90+
docsURL = "https://docs.slack.dev"
91+
sectionText = "Docs Open"
92+
}
93+
94+
clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
95+
Emoji: "books",
96+
Text: sectionText,
97+
Secondary: []string{
98+
docsURL,
99+
},
100+
}))
101+
102+
clients.Browser().OpenURL(docsURL)
103+
104+
if cmd.Flags().Changed("search") {
105+
traceValue := ""
106+
if len(args) > 0 {
107+
traceValue = strings.Join(args, " ")
108+
}
109+
clients.IO.PrintTrace(ctx, slacktrace.DocsSearchSuccess, traceValue)
110+
} else {
111+
clients.IO.PrintTrace(ctx, slacktrace.DocsSuccess)
112+
}
113+
114+
return nil
115+
}

cmd/docs/docs_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2022-2026 Salesforce, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package docs
16+
17+
import (
18+
"context"
19+
"testing"
20+
21+
"github.com/slackapi/slack-cli/internal/shared"
22+
"github.com/slackapi/slack-cli/internal/slacktrace"
23+
"github.com/slackapi/slack-cli/test/testutil"
24+
"github.com/spf13/cobra"
25+
"github.com/stretchr/testify/mock"
26+
)
27+
28+
func Test_Docs_DocsCommand(t *testing.T) {
29+
testutil.TableTestCommand(t, testutil.CommandTests{
30+
"opens docs homepage without search": {
31+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
32+
},
33+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
34+
expectedURL := "https://docs.slack.dev"
35+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
36+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSuccess, mock.Anything)
37+
},
38+
ExpectedOutputs: []string{
39+
"Docs Open",
40+
"https://docs.slack.dev",
41+
},
42+
},
43+
"fails when positional argument provided without search flag": {
44+
CmdArgs: []string{"Block Kit"},
45+
ExpectedErrorStrings: []string{"Invalid docs command. Did you mean to search?"},
46+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
47+
// No browser calls should be made when command fails
48+
cm.Browser.AssertNotCalled(t, "OpenURL")
49+
},
50+
},
51+
"fails when multiple positional arguments provided without search flag": {
52+
CmdArgs: []string{"webhook", "send", "message"},
53+
ExpectedErrorStrings: []string{"Invalid docs command. Did you mean to search?"},
54+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
55+
// No browser calls should be made when command fails
56+
cm.Browser.AssertNotCalled(t, "OpenURL")
57+
},
58+
},
59+
"opens docs with search query using space syntax": {
60+
CmdArgs: []string{"--search", "messaging"},
61+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
62+
expectedURL := "https://docs.slack.dev/search/?q=messaging"
63+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
64+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
65+
},
66+
ExpectedOutputs: []string{
67+
"Docs Search",
68+
"https://docs.slack.dev/search/?q=messaging",
69+
},
70+
},
71+
"handles search with multiple arguments": {
72+
CmdArgs: []string{"--search", "Block", "Kit", "Element"},
73+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
74+
expectedURL := "https://docs.slack.dev/search/?q=Block+Kit+Element"
75+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
76+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
77+
},
78+
ExpectedOutputs: []string{
79+
"Docs Search",
80+
"https://docs.slack.dev/search/?q=Block+Kit+Element",
81+
},
82+
},
83+
"handles search query with multiple words": {
84+
CmdArgs: []string{"--search", "socket mode"},
85+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
86+
expectedURL := "https://docs.slack.dev/search/?q=socket+mode"
87+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
88+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
89+
},
90+
ExpectedOutputs: []string{
91+
"Docs Search",
92+
"https://docs.slack.dev/search/?q=socket+mode",
93+
},
94+
},
95+
"handles special characters in search query": {
96+
CmdArgs: []string{"--search", "messages & webhooks"},
97+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
98+
expectedURL := "https://docs.slack.dev/search/?q=messages+%26+webhooks"
99+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
100+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
101+
},
102+
ExpectedOutputs: []string{
103+
"Docs Search",
104+
"https://docs.slack.dev/search/?q=messages+%26+webhooks",
105+
},
106+
},
107+
"handles search query with quotes": {
108+
CmdArgs: []string{"--search", "webhook \"send message\""},
109+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
110+
expectedURL := "https://docs.slack.dev/search/?q=webhook+%22send+message%22"
111+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
112+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
113+
},
114+
ExpectedOutputs: []string{
115+
"Docs Search",
116+
"https://docs.slack.dev/search/?q=webhook+%22send+message%22",
117+
},
118+
},
119+
"handles search flag without argument": {
120+
CmdArgs: []string{"--search"},
121+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
122+
expectedURL := "https://docs.slack.dev/search/"
123+
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
124+
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
125+
},
126+
ExpectedOutputs: []string{
127+
"Docs Search",
128+
"https://docs.slack.dev/search/",
129+
},
130+
},
131+
}, func(cf *shared.ClientFactory) *cobra.Command {
132+
return NewCommand(cf)
133+
})
134+
}

cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/slackapi/slack-cli/cmd/collaborators"
2929
"github.com/slackapi/slack-cli/cmd/datastore"
3030
"github.com/slackapi/slack-cli/cmd/docgen"
31+
"github.com/slackapi/slack-cli/cmd/docs"
3132
"github.com/slackapi/slack-cli/cmd/doctor"
3233
"github.com/slackapi/slack-cli/cmd/env"
3334
"github.com/slackapi/slack-cli/cmd/externalauth"
@@ -95,6 +96,7 @@ func NewRootCommand(clients *shared.ClientFactory, updateNotification *update.Up
9596
{Command: "init", Meaning: "Initialize an existing Slack app"},
9697
{Command: "run", Meaning: "Start a local development server"},
9798
{Command: "deploy", Meaning: "Deploy to the Slack Platform"},
99+
{Command: "docs", Meaning: "Open Slack developer docs"},
98100
}),
99101
Long: strings.Join([]string{
100102
`{{Emoji "sparkles"}}CLI to create, run, and deploy Slack apps`,
@@ -184,6 +186,7 @@ func Init(ctx context.Context) (*cobra.Command, *shared.ClientFactory) {
184186
rootCmd.CompletionOptions.HiddenDefaultCmd = true
185187

186188
topLevelCommands := []*cobra.Command{
189+
docs.NewCommand(clients),
187190
doctor.NewDoctorCommand(clients),
188191
feedback.NewFeedbackCommand(clients),
189192
}

docs/guides/installing-the-slack-cli-for-mac-and-linux.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ Manual installation allows you to customize certain paths used when installing t
9999

100100
**2\. Download the** `slack` **CLI installer for your environment.**
101101

102-
🍎 ⚡️ [**Download for macOS Apple Silicon (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_macOS_arm64.tar.gz)
102+
🍎 ⚡️ [**Download for macOS Apple Silicon (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.14.0_macOS_arm64.tar.gz)
103103

104-
🍏 🪨 [**Download for macOS Intel (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_macOS_amd64.tar.gz)
104+
🍏 🪨 [**Download for macOS Intel (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.14.0_macOS_amd64.tar.gz)
105105

106-
🐧 💾 [**Download for Linux (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_linux_64-bit.tar.gz)
106+
🐧 💾 [**Download for Linux (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.14.0_linux_64-bit.tar.gz)
107107

108108
**3\. Add the** `slack` **CLI to your path.**
109109

@@ -121,7 +121,7 @@ We recommend using an alias if another `slack` binary exists. To do this, change
121121

122122
```sh
123123
$ slack version
124-
Using slack v3.13.0
124+
Using slack v3.14.0
125125
```
126126

127127
</TabItem>

docs/guides/installing-the-slack-cli-for-windows.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Manual installation allows you to customize certain paths used when installing t
8888

8989
**2\. Download the** `slack` **CLI installer for your environment.**
9090

91-
<ts-icon class="ts_icon_windows"></ts-icon> &nbsp; <a href="https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_windows_64-bit.zip"><strong>Windows (.zip)</strong></a>
91+
<ts-icon class="ts_icon_windows"></ts-icon> &nbsp; <a href="https://downloads.slack-edge.com/slack-cli/slack_cli_3.14.0_windows_64-bit.zip"><strong>Windows (.zip)</strong></a>
9292

9393
**3\. Add the** `slack` **CLI to your path.**
9494

@@ -104,7 +104,7 @@ We recommend using an alias if another `slack` binary exists. To do this, change
104104

105105
```pwsh
106106
$ slack version
107-
Using slack v3.13.0
107+
Using slack v3.14.0
108108
```
109109

110110
</TabItem>

docs/reference/commands/slack.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ $ slack create # Create a new Slack app
3535
$ slack init # Initialize an existing Slack app
3636
$ slack run # Start a local development server
3737
$ slack deploy # Deploy to the Slack Platform
38+
$ slack docs # Open Slack developer docs
3839
```
3940

4041
## See also
@@ -47,6 +48,7 @@ $ slack deploy # Deploy to the Slack Platform
4748
* [slack datastore](slack_datastore) - Interact with an app's datastore
4849
* [slack delete](slack_delete) - Delete the app
4950
* [slack deploy](slack_deploy) - Deploy the app to the Slack Platform
51+
* [slack docs](slack_docs) - Open Slack developer docs
5052
* [slack doctor](slack_doctor) - Check and report on system and app information
5153
* [slack env](slack_env) - Add, remove, or list environment variables
5254
* [slack external-auth](slack_external-auth) - Adjust settings of external authentication providers

docs/reference/commands/slack_create.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ slack create [name | agent <name>] [flags]
2020
-h, --help help for create
2121
--list list available app templates
2222
-n, --name string name for your app (overrides the name argument)
23+
--subdir string subdirectory in the template to use as project
2324
-t, --template string template URL for your app
2425
```
2526

@@ -51,6 +52,9 @@ $ slack create my-project -t slack-samples/deno-hello-world
5152
5253
# Create a project named 'my-project'
5354
$ slack create --name my-project
55+
56+
# Create from a subdirectory of a template
57+
$ slack create my-project -t org/monorepo --subdir apps/my-app
5458
```
5559

5660
## See also
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# `slack docs`
2+
3+
Open Slack developer docs
4+
5+
## Description
6+
7+
Open the Slack developer docs in your browser, with optional search functionality
8+
9+
```
10+
slack docs [flags]
11+
```
12+
13+
## Flags
14+
15+
```
16+
-h, --help help for docs
17+
--search open Slack docs search page or search with query
18+
```
19+
20+
## Global flags
21+
22+
```
23+
-a, --app string use a specific app ID or environment
24+
--config-dir string use a custom path for system config directory
25+
-e, --experiment strings use the experiment(s) in the command
26+
-f, --force ignore warnings and continue executing command
27+
--no-color remove styles and formatting from outputs
28+
-s, --skip-update skip checking for latest version of CLI
29+
-w, --team string select workspace or organization by team name or ID
30+
--token string set the access token associated with a team
31+
-v, --verbose print debug logging and additional info
32+
```
33+
34+
## Examples
35+
36+
```
37+
$ slack docs # Open Slack developer docs homepage
38+
39+
# Search Slack developer docs for Block Kit
40+
$ slack docs --search "Block Kit"
41+
$ slack docs --search # Open Slack docs search page
42+
```
43+
44+
## See also
45+
46+
* [slack](slack) - Slack command-line tool
47+

0 commit comments

Comments
 (0)