Skip to content

Commit 2724306

Browse files
authored
Merge branch 'main' into ale-list-flag
2 parents d103e22 + 3eef3bd commit 2724306

7 files changed

Lines changed: 156 additions & 40 deletions

File tree

.github/pull_request_template.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
### Changelog
2+
3+
(Add a note with features or fixes that change the user experience for release notes)
4+
15
### Summary
26

37
(Please describe the goal of this pull request and mention any related issue numbers)
48

59
### Requirements
610

7-
* [ ] I've read and understood the [Contributing Guidelines](/blob/main/.github/contributing.md) and have done my best effort to follow them.
8-
* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
11+
- [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/slack-cli/blob/main/.github/CONTRIBUTING.md) and have done my best effort to follow them.
12+
- [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).

cmd/platform/deploy.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,16 +316,13 @@ func printDeployHostingCompletion(clients *shared.ClientFactory, cmd *cobra.Comm
316316
func errorMissingDeployHook(clients *shared.ClientFactory) error {
317317
if !clients.SDKConfig.Hooks.Deploy.IsAvailable() {
318318
return slackerror.New(slackerror.ErrSDKHookNotFound).
319-
WithMessage("Missing the `deploy` hook from the `%s` file", config.GetProjectHooksJSONFilePath()).
319+
WithMessage("No deploy script found").
320320
WithRemediation("%s", strings.Join([]string{
321-
"Provide a command or script to run with the deploy command by adding a new hook.",
321+
"For deployment options, see:",
322+
" https://docs.slack.dev/tools/slack-cli/reference/hooks/#deploy",
322323
"",
323-
fmt.Sprintf("Example `%s` `deploy` hook:", config.GetProjectHooksJSONFilePath()),
324-
"{",
325-
` "hooks": {`,
326-
` "deploy": "./deploy.sh"`,
327-
" }",
328-
"}",
324+
"To start a local development server, use:",
325+
fmt.Sprintf(" %s", style.Commandf("run", false)),
329326
}, "\n"))
330327
}
331328
return nil

cmd/platform/deploy_test.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,14 @@ func TestDeployCommand(t *testing.T) {
111111

112112
func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
113113
tests := map[string]struct {
114-
app types.App
115-
manifest types.SlackYaml
116-
manifestError error
117-
manifestSource config.ManifestSource
118-
deployScript string
119-
expectedError error
114+
app types.App
115+
manifest types.SlackYaml
116+
manifestError error
117+
manifestSource config.ManifestSource
118+
deployScript string
119+
expectedError error
120+
expectedMessage string
121+
expectedRemediation []string
120122
}{
121123
"fails when no manifest exists": {
122124
manifestError: slackerror.New(slackerror.ErrInvalidManifest),
@@ -142,8 +144,10 @@ func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
142144
deployScript: "sleep 4",
143145
},
144146
"fails if no deploy hook is provided": {
145-
manifestSource: config.ManifestSourceLocal,
146-
expectedError: slackerror.New(slackerror.ErrSDKHookNotFound),
147+
manifestSource: config.ManifestSourceLocal,
148+
expectedError: slackerror.New(slackerror.ErrSDKHookNotFound),
149+
expectedMessage: "No deploy script found",
150+
expectedRemediation: []string{"https://docs.slack.dev/tools/slack-cli/reference/hooks/#deploy", "run"},
147151
},
148152
"succeeds if the app exists and the manifest source is remote": {
149153
app: types.App{
@@ -183,11 +187,14 @@ func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
183187
err := hasValidDeploymentMethod(ctx, clients, app, types.SlackAuth{})
184188
if tc.expectedError != nil {
185189
require.Error(t, err)
186-
assert.Equal(
187-
t,
188-
slackerror.ToSlackError(tc.expectedError).Code,
189-
slackerror.ToSlackError(err).Code,
190-
)
190+
slackErr := slackerror.ToSlackError(err)
191+
assert.Equal(t, slackerror.ToSlackError(tc.expectedError).Code, slackErr.Code)
192+
if tc.expectedMessage != "" {
193+
assert.Contains(t, slackErr.Message, tc.expectedMessage)
194+
}
195+
for _, r := range tc.expectedRemediation {
196+
assert.Contains(t, slackErr.Remediation, r)
197+
}
191198
} else {
192199
require.NoError(t, err)
193200
}

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ require (
2323
github.com/spf13/cobra v1.10.2
2424
github.com/stretchr/testify v1.11.1
2525
github.com/uber/jaeger-client-go v2.30.0+incompatible
26-
golang.org/x/mod v0.32.0
27-
golang.org/x/sys v0.40.0
28-
golang.org/x/text v0.33.0
26+
golang.org/x/mod v0.33.0
27+
golang.org/x/sys v0.41.0
28+
golang.org/x/text v0.34.0
2929
gopkg.in/yaml.v2 v2.4.0
3030
)
3131

@@ -59,7 +59,7 @@ require (
5959
require (
6060
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
6161
github.com/fatih/color v1.18.0 // indirect
62-
github.com/go-git/go-git/v5 v5.16.4
62+
github.com/go-git/go-git/v5 v5.16.5
6363
github.com/inconshreveable/mousetrap v1.1.0 // indirect
6464
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
6565
github.com/kubescape/go-git-url v0.0.31

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
5252
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
5353
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
5454
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
55-
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
56-
github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
55+
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
56+
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
5757
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
5858
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
5959
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
@@ -181,8 +181,8 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o
181181
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
182182
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
183183
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
184-
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
185-
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
184+
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
185+
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
186186
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
187187
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
188188
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -206,8 +206,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
206206
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
207207
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
208208
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
209-
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
210-
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
209+
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
210+
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
211211
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
212212
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
213213
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
@@ -217,8 +217,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
217217
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
218218
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
219219
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
220-
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
221-
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
220+
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
221+
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
222222
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
223223
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
224224
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

internal/prompts/app_select.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,21 @@ func getAuths(ctx context.Context, clients *shared.ClientFactory) ([]types.Slack
245245
}
246246
}
247247
if len(allAuths) == 0 {
248-
auth := types.SlackAuth{}
249-
err := validateAuth(ctx, clients, &auth)
248+
// No workspaces connected - prompt user to login if interactive
249+
if !clients.IO.IsTTY() {
250+
return nil, slackerror.New(slackerror.ErrNotAuthed).
251+
WithMessage("No workspaces connected").
252+
WithRemediation("Run %s to sign in to a workspace", style.Commandf("login", false))
253+
}
254+
clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
255+
Emoji: "wave",
256+
Text: "No workspaces connected. Sign in to get started.",
257+
}))
258+
newAuth, _, err := authpkg.LoginWithClients(ctx, clients, "", false)
250259
if err != nil {
251-
return nil, slackerror.New(slackerror.ErrNotAuthed)
260+
return nil, slackerror.New(slackerror.ErrNotAuthed).WithRootCause(err)
252261
}
253-
allAuths = append(allAuths, auth)
262+
allAuths = append(allAuths, newAuth)
254263
}
255264
return allAuths, nil
256265
}

internal/prompts/app_select_test.go

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@ func TestPrompt_AppSelectPrompt_GetApps(t *testing.T) {
543543
},
544544
},
545545
},
546-
"returns unknown installation statuses for apps without auths": {
546+
"returns unknown installation statuses for apps when status check fails": {
547+
mockAuths: fakeAuthsByTeamDomainSlice,
547548
mockAppsSavedDeployed: []types.App{
548549
deployedTeam1InstalledApp,
549550
},
@@ -559,6 +560,7 @@ func TestPrompt_AppSelectPrompt_GetApps(t *testing.T) {
559560
TeamID: team1TeamID,
560561
InstallStatus: types.AppInstallationStatusUnknown,
561562
},
563+
Auth: fakeAuthsByTeamDomain[team1TeamDomain],
562564
},
563565
},
564566
},
@@ -1093,6 +1095,7 @@ func TestPrompt_AppSelectPrompt(t *testing.T) {
10931095
expectedError: slackerror.New(slackerror.ErrLocalAppNotSupported),
10941096
},
10951097
"errors if team id flag does not have authorization": {
1098+
mockAuths: []types.SlackAuth{fakeAuthsByTeamDomain[team2TeamDomain]},
10961099
mockFlagTeam: team1TeamID,
10971100
appPromptConfigEnvironment: ShowHostedOnly,
10981101
appPromptConfigStatus: ShowInstalledAndNewApps,
@@ -1807,3 +1810,99 @@ func Test_ValidateAuth(t *testing.T) {
18071810
})
18081811
}
18091812
}
1813+
1814+
// Test_getAuths_NoWorkspacesConnected tests the login prompt behavior when no
1815+
// workspaces are connected
1816+
func Test_getAuths_NoWorkspacesConnected(t *testing.T) {
1817+
tests := map[string]struct {
1818+
isTTY bool
1819+
expectedErr error
1820+
apiExchangeAuthTicketResultResponse api.ExchangeAuthTicketResult
1821+
apiExchangeAuthTicketResultError error
1822+
apiGenerateAuthTicketResultResponse api.GenerateAuthTicketResult
1823+
apiGenerateAuthTicketResultError error
1824+
}{
1825+
"returns error when not interactive and no workspaces connected": {
1826+
isTTY: false,
1827+
expectedErr: slackerror.New(slackerror.ErrNotAuthed),
1828+
},
1829+
"prompts for login when interactive and no workspaces connected": {
1830+
isTTY: true,
1831+
apiExchangeAuthTicketResultResponse: api.ExchangeAuthTicketResult{
1832+
TeamDomain: team1TeamDomain,
1833+
TeamID: team1TeamID,
1834+
Token: team1Token,
1835+
UserID: team1UserID,
1836+
},
1837+
apiGenerateAuthTicketResultResponse: api.GenerateAuthTicketResult{
1838+
Ticket: "ticket123",
1839+
},
1840+
expectedErr: nil,
1841+
},
1842+
}
1843+
for name, tc := range tests {
1844+
t.Run(name, func(t *testing.T) {
1845+
ctx := slackcontext.MockContext(t.Context())
1846+
clientsMock := shared.NewClientsMock()
1847+
clientsMock.Auth.On(Auths, mock.Anything).Return([]types.SlackAuth{}, nil)
1848+
clientsMock.IO.On("IsTTY").Return(tc.isTTY)
1849+
clientsMock.API.On(
1850+
"GenerateAuthTicket",
1851+
mock.Anything,
1852+
mock.Anything,
1853+
mock.Anything,
1854+
).Return(
1855+
tc.apiGenerateAuthTicketResultResponse,
1856+
tc.apiGenerateAuthTicketResultError,
1857+
)
1858+
clientsMock.API.On(
1859+
"ExchangeAuthTicket",
1860+
mock.Anything,
1861+
mock.Anything,
1862+
mock.Anything,
1863+
mock.Anything,
1864+
).Return(
1865+
tc.apiExchangeAuthTicketResultResponse,
1866+
tc.apiExchangeAuthTicketResultError,
1867+
)
1868+
clientsMock.Auth.On(
1869+
"IsAPIHostSlackProd",
1870+
mock.Anything,
1871+
).Return(true)
1872+
clientsMock.Auth.On(
1873+
"SetAuth",
1874+
mock.Anything,
1875+
mock.Anything,
1876+
).Return(
1877+
types.SlackAuth{},
1878+
"",
1879+
nil,
1880+
)
1881+
clientsMock.IO.On(
1882+
"InputPrompt",
1883+
mock.Anything,
1884+
"Enter challenge code",
1885+
iostreams.InputPromptConfig{Required: true},
1886+
).Return(
1887+
"challengeCode",
1888+
nil,
1889+
)
1890+
clientsMock.AddDefaultMocks()
1891+
clients := shared.NewClientFactory(clientsMock.MockClientFactory())
1892+
1893+
auths, err := getAuths(ctx, clients)
1894+
1895+
if tc.expectedErr != nil {
1896+
require.Error(t, err)
1897+
assert.Equal(t, tc.expectedErr.(*slackerror.Error).Code, slackerror.ToSlackError(err).Code)
1898+
assert.Contains(t, slackerror.ToSlackError(err).Message, "No workspaces connected")
1899+
} else {
1900+
require.NoError(t, err)
1901+
require.Len(t, auths, 1)
1902+
assert.Equal(t, team1TeamID, auths[0].TeamID)
1903+
// Verify the welcome message was printed
1904+
assert.Contains(t, clientsMock.GetStdoutOutput(), "No workspaces connected")
1905+
}
1906+
})
1907+
}
1908+
}

0 commit comments

Comments
 (0)