Skip to content

Commit 0477df7

Browse files
authored
Merge branch 'main' into ale-feat-subdir-flag
2 parents 1c1969e + cf7214c commit 0477df7

20 files changed

Lines changed: 638 additions & 66 deletions

File tree

.github/workflows/dependencies.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
ref: main
5757
token: ${{ steps.credentials.outputs.token }}
5858
- name: Install Golang
59-
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
59+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
6060
with:
6161
go-version: "stable"
6262
- name: Get the latest version

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
fetch-depth: 0
2424
persist-credentials: false
2525
- name: Set up Go
26-
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
26+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
2727
with:
2828
go-version: "1.26.0"
2929
- name: Lint
@@ -55,7 +55,7 @@ jobs:
5555
with:
5656
persist-credentials: false
5757
- name: Set up Go
58-
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
58+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
5959
with:
6060
go-version: "1.26.0"
6161
- name: Report health score

cmd/auth/login.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import (
1818
"context"
1919
"fmt"
2020

21-
"github.com/slackapi/slack-cli/internal/config"
22-
"github.com/slackapi/slack-cli/internal/experiment"
2321
"github.com/slackapi/slack-cli/internal/iostreams"
2422
authpkg "github.com/slackapi/slack-cli/internal/pkg/auth"
2523
"github.com/slackapi/slack-cli/internal/shared"
@@ -111,7 +109,7 @@ func RunLoginCommand(clients *shared.ClientFactory, cmd *cobra.Command) (types.S
111109
return types.SlackAuth{}, err
112110
}
113111
if selectedAuth.Token != "" {
114-
printAuthSuccess(cmd, clients.Config, clients.IO, credentialsPath, selectedAuth.Token)
112+
printAuthSuccess(cmd, clients.IO, credentialsPath, selectedAuth.Token)
115113
printAuthNextSteps(ctx, clients)
116114
}
117115
return selectedAuth, err
@@ -121,14 +119,14 @@ func RunLoginCommand(clients *shared.ClientFactory, cmd *cobra.Command) (types.S
121119
if err != nil {
122120
return types.SlackAuth{}, err
123121
} else {
124-
printAuthSuccess(cmd, clients.Config, clients.IO, credentialsPath, selectedAuth.Token)
122+
printAuthSuccess(cmd, clients.IO, credentialsPath, selectedAuth.Token)
125123
printAuthNextSteps(ctx, clients)
126124
}
127125

128126
return selectedAuth, nil
129127
}
130128

131-
func printAuthSuccess(cmd *cobra.Command, config *config.Config, IO iostreams.IOStreamer, credentialsPath string, token string) {
129+
func printAuthSuccess(cmd *cobra.Command, IO iostreams.IOStreamer, credentialsPath string, token string) {
132130
ctx := cmd.Context()
133131

134132
var secondaryLog string
@@ -138,13 +136,7 @@ func printAuthSuccess(cmd *cobra.Command, config *config.Config, IO iostreams.IO
138136
secondaryLog = fmt.Sprintf("Service token:\n\n %s\n\nMake sure to copy the token now and save it safely.", token)
139137
}
140138

141-
// The legacy prompt leaves no blank line before the success message, so
142-
// print one here. The Charm-based prompt already handles spacing.
143-
if !config.WithExperimentOn(experiment.Charm) {
144-
IO.PrintInfo(ctx, false, "")
145-
}
146-
147-
IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{
139+
IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
148140
Emoji: "key",
149141
Text: "You've successfully authenticated!",
150142
Secondary: []string{secondaryLog},

cmd/project/create.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ package project
1717
import (
1818
"context"
1919
"fmt"
20+
"math/rand"
2021
"path/filepath"
2122
"strings"
23+
"time"
2224

25+
"github.com/slackapi/slack-cli/internal/iostreams"
2326
"github.com/slackapi/slack-cli/internal/logger"
2427
"github.com/slackapi/slack-cli/internal/pkg/create"
2528
"github.com/slackapi/slack-cli/internal/shared"
@@ -144,6 +147,25 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []
144147
return err
145148
}
146149

150+
// Prompt for app name if not provided via flag or argument
151+
if appNameArg == "" {
152+
if clients.IO.IsTTY() {
153+
defaultName := generateRandomAppName()
154+
cmd.Print(style.Secondary(fmt.Sprintf(" Press Enter to use the generated name: %s", defaultName)), "\n")
155+
name, err := clients.IO.InputPrompt(ctx, "Name your app:", iostreams.InputPromptConfig{})
156+
if err != nil {
157+
return err
158+
}
159+
if name != "" {
160+
appNameArg = name
161+
} else {
162+
appNameArg = defaultName
163+
}
164+
} else {
165+
appNameArg = generateRandomAppName()
166+
}
167+
}
168+
147169
// Set up spinners
148170
appCreateSpinner = style.NewSpinner(cmd.OutOrStdout())
149171

@@ -288,6 +310,15 @@ func printCreateSuccess(ctx context.Context, clients *shared.ClientFactory, appP
288310
clients.IO.PrintTrace(ctx, slacktrace.CreateSuccess)
289311
}
290312

313+
// generateRandomAppName will create a random app name based on two words and a number
314+
func generateRandomAppName() string {
315+
rand.New(rand.NewSource(time.Now().UnixNano()))
316+
var firstRandomNum = rand.Intn(len(create.Adjectives))
317+
var secondRandomNum = rand.Intn(len(create.Animals))
318+
var randomName = fmt.Sprintf("%s-%s-%d", create.Adjectives[firstRandomNum], create.Animals[secondRandomNum], rand.Intn(1000))
319+
return randomName
320+
}
321+
291322
// printAppCreateError stops the creation spinners and displays the returned error message
292323
func printAppCreateError(clients *shared.ClientFactory, cmd *cobra.Command, err error) {
293324
ctx := cmd.Context()

cmd/project/create_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestCreateCommand(t *testing.T) {
4646
testutil.TableTestCommand(t, testutil.CommandTests{
4747
"creates a bolt application from prompts": {
4848
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
49+
cm.IO.On("IsTTY").Return(true)
4950
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
5051
Return(
5152
iostreams.SelectPromptResponse{
@@ -62,6 +63,8 @@ func TestCreateCommand(t *testing.T) {
6263
},
6364
nil,
6465
)
66+
cm.IO.On("InputPrompt", mock.Anything, "Name your app:", mock.Anything).
67+
Return("my-app", nil)
6568
createClientMock = new(CreateClientMock)
6669
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
6770
CreateFunc = createClientMock.Create
@@ -70,14 +73,17 @@ func TestCreateCommand(t *testing.T) {
7073
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template")
7174
require.NoError(t, err)
7275
expected := create.CreateArgs{
76+
AppName: "my-app",
7377
Template: template,
7478
}
7579
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
80+
cm.IO.AssertCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
7681
},
7782
},
7883
"creates a deno application from flags": {
7984
CmdArgs: []string{"--template", "slack-samples/deno-starter-template"},
8085
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
86+
cm.IO.On("IsTTY").Return(true)
8187
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
8288
Return(
8389
iostreams.SelectPromptResponse{
@@ -94,6 +100,8 @@ func TestCreateCommand(t *testing.T) {
94100
},
95101
nil,
96102
)
103+
cm.IO.On("InputPrompt", mock.Anything, "Name your app:", mock.Anything).
104+
Return("my-deno-app", nil)
97105
createClientMock = new(CreateClientMock)
98106
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
99107
CreateFunc = createClientMock.Create
@@ -102,14 +110,17 @@ func TestCreateCommand(t *testing.T) {
102110
template, err := create.ResolveTemplateURL("slack-samples/deno-starter-template")
103111
require.NoError(t, err)
104112
expected := create.CreateArgs{
113+
AppName: "my-deno-app",
105114
Template: template,
106115
}
107116
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
117+
cm.IO.AssertCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
108118
},
109119
},
110120
"creates an agent app using agent argument shortcut": {
111121
CmdArgs: []string{"agent"},
112122
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
123+
cm.IO.On("IsTTY").Return(true)
113124
// Should skip category prompt and go directly to language selection
114125
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
115126
Return(
@@ -119,6 +130,8 @@ func TestCreateCommand(t *testing.T) {
119130
},
120131
nil,
121132
)
133+
cm.IO.On("InputPrompt", mock.Anything, "Name your app:", mock.Anything).
134+
Return("my-agent", nil)
122135
createClientMock = new(CreateClientMock)
123136
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
124137
CreateFunc = createClientMock.Create
@@ -127,11 +140,13 @@ func TestCreateCommand(t *testing.T) {
127140
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-assistant-template")
128141
require.NoError(t, err)
129142
expected := create.CreateArgs{
143+
AppName: "my-agent",
130144
Template: template,
131145
}
132146
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
133147
// Verify that category prompt was NOT called
134148
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
149+
cm.IO.AssertCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
135150
},
136151
},
137152
"creates an agent app with app name using agent argument": {
@@ -160,6 +175,8 @@ func TestCreateCommand(t *testing.T) {
160175
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
161176
// Verify that category prompt was NOT called
162177
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
178+
// Verify that name prompt was NOT called since name was provided as arg
179+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
163180
},
164181
},
165182
"creates an app named agent when template flag is provided": {
@@ -193,6 +210,8 @@ func TestCreateCommand(t *testing.T) {
193210
Template: template,
194211
}
195212
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
213+
// Verify that name prompt was NOT called since name was provided as arg
214+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
196215
},
197216
},
198217
"creates an app named agent using name flag without triggering shortcut": {
@@ -229,6 +248,8 @@ func TestCreateCommand(t *testing.T) {
229248
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
230249
// Verify that category prompt WAS called (shortcut was not triggered)
231250
cm.IO.AssertCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
251+
// Verify that name prompt was NOT called since --name flag was provided
252+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
232253
},
233254
},
234255
"creates an agent app with name flag overriding positional arg": {
@@ -290,6 +311,8 @@ func TestCreateCommand(t *testing.T) {
290311
Template: template,
291312
}
292313
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
314+
// Verify that name prompt was NOT called since --name flag was provided
315+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
293316
},
294317
},
295318
"name flag overrides positional app name argument with agent shortcut": {
@@ -318,6 +341,110 @@ func TestCreateCommand(t *testing.T) {
318341
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
319342
// Verify that category prompt was NOT called (agent shortcut was triggered)
320343
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
344+
// Verify that name prompt was NOT called since --name flag was provided
345+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
346+
},
347+
},
348+
"user accepts default name from prompt": {
349+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
350+
cm.IO.On("IsTTY").Return(true)
351+
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
352+
Return(
353+
iostreams.SelectPromptResponse{
354+
Prompt: true,
355+
Index: 0,
356+
},
357+
nil,
358+
)
359+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
360+
Return(
361+
iostreams.SelectPromptResponse{
362+
Prompt: true,
363+
Index: 0,
364+
},
365+
nil,
366+
)
367+
// Return empty string to simulate pressing Enter (accepting default)
368+
cm.IO.On("InputPrompt", mock.Anything, "Name your app:", mock.Anything).
369+
Return("", nil)
370+
createClientMock = new(CreateClientMock)
371+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
372+
CreateFunc = createClientMock.Create
373+
},
374+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
375+
cm.IO.AssertCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
376+
// When the user accepts the default (empty return), the generated name is used
377+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(args create.CreateArgs) bool {
378+
return args.AppName != ""
379+
}))
380+
},
381+
},
382+
"non-TTY without name falls back to generated name": {
383+
CmdArgs: []string{"--template", "slack-samples/bolt-js-starter-template"},
384+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
385+
// IsTTY defaults to false via AddDefaultMocks, simulating piped output
386+
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
387+
Return(
388+
iostreams.SelectPromptResponse{
389+
Flag: true,
390+
Option: "slack-samples/bolt-js-starter-template",
391+
},
392+
nil,
393+
)
394+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
395+
Return(
396+
iostreams.SelectPromptResponse{
397+
Flag: true,
398+
Option: "slack-samples/bolt-js-starter-template",
399+
},
400+
nil,
401+
)
402+
createClientMock = new(CreateClientMock)
403+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
404+
CreateFunc = createClientMock.Create
405+
},
406+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
407+
// Should NOT prompt for name since not a TTY
408+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
409+
// Should still call Create with a non-empty generated name
410+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(args create.CreateArgs) bool {
411+
return args.AppName != ""
412+
}))
413+
},
414+
},
415+
"positional arg skips name prompt": {
416+
CmdArgs: []string{"my-project"},
417+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
418+
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
419+
Return(
420+
iostreams.SelectPromptResponse{
421+
Prompt: true,
422+
Index: 0,
423+
},
424+
nil,
425+
)
426+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
427+
Return(
428+
iostreams.SelectPromptResponse{
429+
Prompt: true,
430+
Index: 0,
431+
},
432+
nil,
433+
)
434+
createClientMock = new(CreateClientMock)
435+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
436+
CreateFunc = createClientMock.Create
437+
},
438+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
439+
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template")
440+
require.NoError(t, err)
441+
expected := create.CreateArgs{
442+
AppName: "my-project",
443+
Template: template,
444+
}
445+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
446+
// Verify that name prompt was NOT called since name was provided as positional arg
447+
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
321448
},
322449
},
323450
"subdir without template flag returns error": {

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ require (
5555
github.com/emirpasic/gods v1.18.1 // indirect
5656
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
5757
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
58-
github.com/go-git/go-billy/v5 v5.7.0 // indirect
58+
github.com/go-git/go-billy/v5 v5.8.0 // indirect
5959
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
6060
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect
6161
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -83,7 +83,7 @@ require (
8383
require (
8484
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
8585
github.com/fatih/color v1.18.0 // indirect
86-
github.com/go-git/go-git/v5 v5.16.5
86+
github.com/go-git/go-git/v5 v5.17.0
8787
github.com/inconshreveable/mousetrap v1.1.0 // indirect
8888
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
8989
github.com/kubescape/go-git-url v0.0.31

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
9494
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
9595
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
9696
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
97-
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
98-
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
97+
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
98+
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
9999
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
100100
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
101-
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
102-
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
101+
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
102+
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
103103
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
104104
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
105105
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=

0 commit comments

Comments
 (0)