Skip to content

Commit dbe9fa9

Browse files
committed
feat(perms): migrate owner-based to workspace
1 parent 7a1e912 commit dbe9fa9

57 files changed

Lines changed: 1311 additions & 1385 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/root.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func NewDryccCommand() *cobra.Command {
5454
rootCmd.AddCommand(parser.NewKeysCommand(&cmdr))
5555
rootCmd.AddCommand(parser.NewLabelsCommand(&cmdr))
5656
rootCmd.AddCommand(parser.NewLimitsCommand(&cmdr))
57-
rootCmd.AddCommand(parser.NewPermsCommand(&cmdr))
57+
rootCmd.AddCommand(parser.NewWorkspacesCommand(&cmdr))
5858
rootCmd.AddCommand(parser.NewPsCommand(&cmdr))
5959
rootCmd.AddCommand(parser.NewPtsCommand(&cmdr))
6060
rootCmd.AddCommand(parser.NewRegistryCommand(&cmdr))
@@ -68,7 +68,6 @@ func NewDryccCommand() *cobra.Command {
6868
rootCmd.AddCommand(parser.NewTLSCommand(&cmdr))
6969
rootCmd.AddCommand(parser.NewTokensCommand(&cmdr))
7070
rootCmd.AddCommand(parser.NewUpdateCommand(&cmdr))
71-
rootCmd.AddCommand(parser.NewUsersCommand(&cmdr))
7271
rootCmd.AddCommand(parser.NewVolumesCommand(&cmdr))
7372
rootCmd.AddCommand(parser.NewVersionCommand(&cmdr))
7473
// shortcuts

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
module github.com/drycc/workflow-cli
22

3-
go 1.25
3+
go 1.26
44

55
require (
66
github.com/chai2010/gettext-go v1.0.3
77
github.com/containerd/console v1.0.4
8-
github.com/drycc/controller-sdk-go v0.0.0-20251211045545-b196e6964a1c
8+
github.com/drycc/controller-sdk-go v0.0.0-20260511051139-2b7986fe96fd
99
github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf
1010
github.com/minio/selfupdate v0.6.0
1111
github.com/olekukonko/tablewriter v0.0.5
1212
github.com/spf13/cobra v1.9.1
13-
github.com/stretchr/testify v1.10.0
14-
golang.org/x/net v0.47.0
13+
github.com/stretchr/testify v1.11.1
14+
golang.org/x/net v0.54.0
1515
gopkg.in/yaml.v3 v3.0.1
1616
k8s.io/klog/v2 v2.130.1
1717
sigs.k8s.io/yaml v1.4.0
@@ -28,6 +28,6 @@ require (
2828
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2929
github.com/rivo/uniseg v0.4.7 // indirect
3030
github.com/spf13/pflag v1.0.6 // indirect
31-
golang.org/x/crypto v0.45.0 // indirect
32-
golang.org/x/sys v0.38.0 // indirect
31+
golang.org/x/crypto v0.51.0 // indirect
32+
golang.org/x/sys v0.44.0 // indirect
3333
)

go.sum

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
99
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
1010
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
1111
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12-
github.com/drycc/controller-sdk-go v0.0.0-20251211045545-b196e6964a1c h1:v7dQdercvt0o+/dOeCpQSEYedvo2lTY8eCwHBxx48hg=
13-
github.com/drycc/controller-sdk-go v0.0.0-20251211045545-b196e6964a1c/go.mod h1:eHcmYwg81ASlP55/U587xnBZnZoeZnPHXGeQ8nYWnsg=
12+
github.com/drycc/controller-sdk-go v0.0.0-20260511051139-2b7986fe96fd h1:zeDC7WbB3yGjuviC0u4eHaiylt7ixVbfL8Ope+FwEtM=
13+
github.com/drycc/controller-sdk-go v0.0.0-20260511051139-2b7986fe96fd/go.mod h1:jV1AUDHtY8aPMF95evHQGXZOX6tUXaf7wgqzUEnD5SM=
1414
github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf h1:CYy3NoPhfFhkGAbEppTOQfY/HC2s0FJDcBgbtRKeweg=
1515
github.com/drycc/pkg v0.0.0-20250917064731-345368da3dbf/go.mod h1:BrrNrNskHKm+nJYhXfGuI114w8nupi0AMo8QZHID7CM=
1616
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
@@ -44,26 +44,26 @@ github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
4444
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
4545
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
4646
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
47-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
48-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
47+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
48+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
4949
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
5050
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
5151
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
52-
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
53-
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
52+
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
53+
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
5454
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
5555
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
56-
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
57-
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
56+
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
57+
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
5858
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5959
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6060
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6161
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6262
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6363
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6464
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
65-
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
66-
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
65+
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
66+
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
6767
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
6868
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
6969
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

internal/commands/apps.go

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/drycc/controller-sdk-go/api"
1011
"github.com/drycc/controller-sdk-go/apps"
1112
"github.com/drycc/controller-sdk-go/appsettings"
1213
"github.com/drycc/controller-sdk-go/domains"
@@ -19,14 +20,14 @@ import (
1920

2021
// AppCreate creates an app.
2122
func (d *DryccCmd) AppCreate(id, remote string, noRemote bool) error {
22-
s, err := settings.Load(d.ConfigFile)
23+
workspace, s, err := loader.LoadWorkspace(d.ConfigFile)
2324
if err != nil {
2425
return err
2526
}
2627

2728
d.Print("Creating Application... ")
2829
quit := progress(d.WOut)
29-
app, err := apps.New(s.Client, id)
30+
app, err := apps.New(s.Client, id, workspace)
3031

3132
quit <- true
3233
<-quit
@@ -59,7 +60,7 @@ func (d *DryccCmd) AppCreate(id, remote string, noRemote bool) error {
5960

6061
// AppsList lists apps on the Drycc controller.
6162
func (d *DryccCmd) AppsList(results int) error {
62-
s, err := settings.Load(d.ConfigFile)
63+
workspace, s, err := loader.LoadWorkspace(d.ConfigFile)
6364
if err != nil {
6465
return err
6566
}
@@ -68,16 +69,16 @@ func (d *DryccCmd) AppsList(results int) error {
6869
results = s.Limit
6970
}
7071

71-
apps, count, err := apps.List(s.Client, results)
72+
apps, count, err := apps.List(s.Client, workspace, results)
7273
if d.checkAPICompatibility(s.Client, err) != nil {
7374
return err
7475
}
7576
if count > 0 {
76-
table := d.getDefaultFormatTable([]string{"ID", "OWNER", "CREATED", "UPDATED"})
77+
table := d.getDefaultFormatTable([]string{"ID", "WORKSPACE", "CREATED", "UPDATED"})
7778
for _, app := range apps {
7879
table.Append([]string{
7980
app.ID,
80-
app.Owner,
81+
app.Workspace,
8182
d.formatTime(app.Created),
8283
d.formatTime(app.Updated),
8384
})
@@ -110,7 +111,7 @@ func (d *DryccCmd) AppInfo(appID string) error {
110111
table.Append([]string{"App:", app.ID})
111112
table.Append([]string{"URL:", url})
112113
table.Append([]string{"UUID:", app.UUID})
113-
table.Append([]string{"Owner:", app.Owner})
114+
table.Append([]string{"Workspace:", app.Workspace})
114115
table.Append([]string{"Created:", d.formatTime(app.Created)})
115116
table.Append([]string{"Updated:", d.formatTime(app.Updated)})
116117

@@ -279,16 +280,16 @@ func (d *DryccCmd) AppDestroy(appID, confirm string) error {
279280
return nil
280281
}
281282

282-
// AppTransfer transfers app ownership to another user.
283-
func (d *DryccCmd) AppTransfer(appID, username string) error {
283+
// AppTransfer transfers app to another workspace.
284+
func (d *DryccCmd) AppTransfer(appID, workspace string) error {
284285
appID, s, err := loader.LoadAppSettings(d.ConfigFile, appID)
285286
if err != nil {
286287
return err
287288
}
288289

289-
d.Printf("Transferring %s to %s... ", appID, username)
290+
d.Printf("Transferring %s to %s... ", appID, workspace)
290291

291-
err = apps.Transfer(s.Client, appID, username)
292+
err = apps.Transfer(s.Client, appID, workspace)
292293
if d.checkAPICompatibility(s.Client, err) != nil {
293294
return err
294295
}
@@ -306,23 +307,10 @@ func (d *DryccCmd) appURL(s *settings.Settings, appID string) (string, error) {
306307
if d.checkAPICompatibility(s.Client, err) != nil {
307308
return "", err
308309
}
309-
310-
if len(domains) == 0 {
311-
return "", nil
312-
}
313-
314-
return expandURL(s.Client.ControllerURL.Host, domains[0].Domain), nil
315-
}
316-
317-
// expandURL expands an app url if necessary.
318-
func expandURL(host, u string) string {
319-
if strings.Contains(u, ".") {
320-
// If domain is a full url.
321-
return u
310+
for _, domain := range domains {
311+
if domain.Ptype == api.PtypeWeb {
312+
return domain.Domain, nil
313+
}
322314
}
323-
324-
// If domain is a subdomain, look up the controller url and replace the subdomain.
325-
parts := strings.Split(host, ".")
326-
parts[0] = u
327-
return strings.Join(parts, ".")
315+
return "", nil
328316
}

internal/commands/apps_test.go

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,19 @@ import (
1313
"github.com/stretchr/testify/assert"
1414
)
1515

16-
type expandURLCases struct {
17-
Input string
18-
Expected string
19-
}
20-
2116
func TestAppsList(t *testing.T) {
2217
t.Parallel()
2318
cf, server, err := testutil.NewTestServerAndClient()
2419
if err != nil {
2520
t.Fatal(err)
2621
}
2722
defer server.Close()
23+
24+
// Set workspace in config so AppsList can read it
25+
s, _ := settings.Load(cf)
26+
s.Workspace = "dolar-sit-amet"
27+
cf, _ = s.Save(cf)
28+
2829
var b bytes.Buffer
2930
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
3031

@@ -38,7 +39,7 @@ func TestAppsList(t *testing.T) {
3839
{
3940
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
4041
"id": "lorem-ipsum",
41-
"owner": "dolar-sit-amet",
42+
"workspace": "dolar-sit-amet",
4243
"created": "2016-08-22T17:40:16Z",
4344
"updated": "2016-08-22T17:40:16Z",
4445
"structure": {
@@ -48,7 +49,7 @@ func TestAppsList(t *testing.T) {
4849
{
4950
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
5051
"id": "consectetur",
51-
"owner": "adipiscing",
52+
"workspace": "adipiscing",
5253
"created": "2016-08-22T17:40:16Z",
5354
"updated": "2016-08-22T17:40:16Z",
5455
"structure": {
@@ -61,19 +62,46 @@ func TestAppsList(t *testing.T) {
6162

6263
err = cmdr.AppsList(-1)
6364
assert.NoError(t, err)
64-
testutil.AssertOutput(t, b.String(), `ID OWNER CREATED UPDATED
65+
testutil.AssertOutput(t, b.String(), `ID WORKSPACE CREATED UPDATED
6566
lorem-ipsum dolar-sit-amet 2016-08-22T17:40:16Z 2016-08-22T17:40:16Z
6667
consectetur adipiscing 2016-08-22T17:40:16Z 2016-08-22T17:40:16Z
6768
`)
6869
}
6970

71+
func TestAppsListNoWorkspace(t *testing.T) {
72+
t.Parallel()
73+
cf, _, err := testutil.NewTestServerAndClient()
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
78+
// Clear workspace from config
79+
s, _ := settings.Load(cf)
80+
s.Workspace = ""
81+
cf, _ = s.Save(cf)
82+
83+
var b bytes.Buffer
84+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
85+
86+
// When no default workspace in config, should return error
87+
err = cmdr.AppsList(-1)
88+
assert.Error(t, err)
89+
assert.Contains(t, err.Error(), "no workspace specified")
90+
}
91+
7092
func TestAppsListLimit(t *testing.T) {
7193
t.Parallel()
7294
cf, server, err := testutil.NewTestServerAndClient()
7395
if err != nil {
7496
t.Fatal(err)
7597
}
7698
defer server.Close()
99+
100+
// Set workspace in config so AppsList can read it
101+
s, _ := settings.Load(cf)
102+
s.Workspace = "dolar-sit-amet"
103+
cf, _ = s.Save(cf)
104+
77105
var b bytes.Buffer
78106
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
79107

@@ -87,7 +115,7 @@ func TestAppsListLimit(t *testing.T) {
87115
{
88116
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
89117
"id": "lorem-ipsum",
90-
"owner": "dolar-sit-amet",
118+
"workspace": "dolar-sit-amet",
91119
"created": "2016-08-22T17:40:16Z",
92120
"updated": "2016-08-22T17:40:16Z",
93121
"structure": {
@@ -100,7 +128,7 @@ func TestAppsListLimit(t *testing.T) {
100128

101129
err = cmdr.AppsList(1)
102130
assert.NoError(t, err)
103-
testutil.AssertOutput(t, b.String(), `ID OWNER CREATED UPDATED
131+
testutil.AssertOutput(t, b.String(), `ID WORKSPACE CREATED UPDATED
104132
lorem-ipsum dolar-sit-amet 2016-08-22T17:40:16Z 2016-08-22T17:40:16Z
105133
`)
106134
}
@@ -120,7 +148,7 @@ func TestAppsInfo(t *testing.T) {
120148
fmt.Fprintf(w, `{
121149
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
122150
"id": "lorem-ipsum",
123-
"owner": "dolar-sit-amet",
151+
"workspace": "dolar-sit-amet",
124152
"structure": {
125153
"cmd": 1
126154
},
@@ -189,7 +217,7 @@ func TestAppsInfo(t *testing.T) {
189217
testutil.AssertOutput(t, b.String(), `App: lorem-ipsum
190218
URL: `+url+`
191219
UUID: c4aed81c-d1ca-4ff1-ab89-d2151264e1a3
192-
Owner: dolar-sit-amet
220+
Workspace: dolar-sit-amet
193221
Created: 2016-08-22T17:40:16Z
194222
Updated: 2016-08-22T17:40:16Z
195223
Processes:
@@ -223,7 +251,7 @@ func TestAppDestroy(t *testing.T) {
223251
fmt.Fprintf(w, `{
224252
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
225253
"id": "lorem-ipsum",
226-
"owner": "dolar-sit-amet",
254+
"workspace": "dolar-sit-amet",
227255
"structure": {
228256
"cmd": 1
229257
},
@@ -256,7 +284,7 @@ func TestAppTransfer(t *testing.T) {
256284
fmt.Fprintf(w, `{
257285
"uuid": "c4aed81c-d1ca-4ff1-ab89-d2151264e1a3",
258286
"id": "lorem-ipsum",
259-
"owner": "dolar-sit-amet",
287+
"workspace": "dolar-sit-amet",
260288
"structure": {
261289
"cmd": 1
262290
},
@@ -271,34 +299,18 @@ func TestAppTransfer(t *testing.T) {
271299
`, "output")
272300
}
273301

274-
func TestExpandUrl(t *testing.T) {
275-
checks := []expandURLCases{
276-
{
277-
Input: "test.com",
278-
Expected: "test.com",
279-
},
280-
{
281-
Input: "test",
282-
Expected: "test.foo.com",
283-
},
284-
}
285-
286-
for _, check := range checks {
287-
out := expandURL("drycc.foo.com", check.Input)
288-
289-
if out != check.Expected {
290-
t.Errorf("Expected %s, Got %s", check.Expected, out)
291-
}
292-
}
293-
}
294-
295302
func TestRemoteExists(t *testing.T) {
296303
cf, server, err := testutil.NewTestServerAndClient()
297304
if err != nil {
298305
t.Fatal(err)
299306
}
300307
defer server.Close()
301308

309+
// Set workspace in config so AppCreate can read it
310+
s, _ := settings.Load(cf)
311+
s.Workspace = "test-workspace"
312+
cf, _ = s.Save(cf)
313+
302314
server.Mux.HandleFunc("/v2/apps/", func(w http.ResponseWriter, _ *http.Request) {
303315
testutil.SetHeaders(w)
304316
fmt.Fprintf(w, `{
@@ -329,6 +341,6 @@ func TestRemoteExists(t *testing.T) {
329341
// Check that an error occurred and it contains the remote name
330342
// This works for any language since the remote name "drycc" is always in the error
331343
assert.Error(t, err)
332-
assert.Contains(t, err.Error(), "drycc",
344+
assert.Contains(t, err.Error(), "drycc",
333345
"error message should contain the remote name 'drycc', got: %s", err.Error())
334346
}

0 commit comments

Comments
 (0)