diff --git a/api4/team.go b/api4/team.go index e8a4a035f9..449caf44ba 100644 --- a/api4/team.go +++ b/api4/team.go @@ -1177,6 +1177,7 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) { } emailList := model.ArrayFromJson(r.Body) + for i := range emailList { emailList[i] = strings.ToLower(emailList[i]) } @@ -1193,7 +1194,21 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("emails", emailList) if graceful { - invitesWithError, err := c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId) + cloudUserLimit := *c.App.Config().ExperimentalSettings.CloudUserLimit + var invitesOverLimit []*model.EmailInviteWithError + if cloudUserLimit > 0 && c.IsSystemAdmin() { + emailList, invitesOverLimit, _ = c.App.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit) + } + var invitesWithError []*model.EmailInviteWithError + var err *model.AppError + if emailList != nil { + invitesWithError, err = c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId) + } + + if len(invitesOverLimit) > 0 { + invitesWithError = append(invitesWithError, invitesOverLimit...) + } + if invitesWithError != nil { errList := make([]string, 0, len(invitesWithError)) for _, inv := range invitesWithError { diff --git a/api4/team_test.go b/api4/team_test.go index e3143b3977..047271f410 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -2721,6 +2721,69 @@ func TestImportTeam(t *testing.T) { }) } +func TestInviteUsersToTeamWithUserLimit(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + email1 := th.GenerateTestEmail() + email2 := th.GenerateTestEmail() + email3 := th.GenerateTestEmail() + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.EnableEmailInvitations = true + *cfg.ExperimentalSettings.CloudUserLimit = 2 + }) + + t.Run("System admin, invite when at limit should fail", func(t *testing.T) { + invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2}) + CheckNoError(t, resp) + require.Len(t, invitesWithErrors, 2) + require.NotNil(t, invitesWithErrors[0].Error) + assert.Equal(t, invitesWithErrors[0].Error.Message, "You've reached the user limit of your current tier") + require.NotNil(t, invitesWithErrors[1].Error) + assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier") + }) + + t.Run("Regular user, invite when at limit should succeed", func(t *testing.T) { + invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email3}) + CheckNoError(t, resp) + require.Len(t, invitesWithErrors, 1) + assert.Nil(t, invitesWithErrors[0].Error) + + }) + + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ExperimentalSettings.CloudUserLimit = 5 + }) + + t.Run("With one remaining user inviting more than one user as admin invites only one user", func(t *testing.T) { + invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2}) + CheckNoError(t, resp) + require.Len(t, invitesWithErrors, 2) + require.Nil(t, invitesWithErrors[0].Error) + require.NotNil(t, invitesWithErrors[1].Error) + assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier") + + }) + + t.Run("With one remaining user inviting more than one user as a regular user sends all invites", func(t *testing.T) { + invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2}) + CheckNoError(t, resp) + require.Len(t, invitesWithErrors, 2) + assert.Nil(t, invitesWithErrors[0].Error) + assert.Nil(t, invitesWithErrors[1].Error) + + }) + + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ExperimentalSettings.CloudUserLimit = 100 + }) + t.Run("Invited user count is well below limit", func(t *testing.T) { + invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2}) + CheckNoError(t, resp) + require.Len(t, invitesWithErrors, 2) + require.Nil(t, invitesWithErrors[0].Error) + }) +} + func TestInviteUsersToTeam(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/app/app_iface.go b/app/app_iface.go index 15878e5eca..445b377415 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -537,6 +537,7 @@ type AppIface interface { GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) GetEmojiList(page, perPage int, sort string) ([]*model.Emoji, *model.AppError) + GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) GetFile(fileId string) ([]byte, *model.AppError) GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) GetFileInfos(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError) diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 01ae67f4aa..fce15f375a 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -5069,6 +5069,28 @@ func (a *OpenTracingAppLayer) GetEnvironmentConfig() map[string]interface{} { return resultVar0 } +func (a *OpenTracingAppLayer) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetErrorListForEmailsOverLimit") + + a.ctx = newCtx + a.app.Srv().Store.SetContext(newCtx) + defer func() { + a.app.Srv().Store.SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0, resultVar1, resultVar2 := a.app.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit) + + if resultVar2 != nil { + span.LogFields(spanlog.Error(resultVar2)) + ext.Error.Set(span, true) + } + + return resultVar0, resultVar1, resultVar2 +} + func (a *OpenTracingAppLayer) GetFile(fileId string) ([]byte, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFile") diff --git a/app/team.go b/app/team.go index 46a71d2b03..6d67e15882 100644 --- a/app/team.go +++ b/app/team.go @@ -1127,6 +1127,47 @@ func (a *App) prepareInviteNewUsersToTeam(teamId, senderId string) (*model.User, return user, team, nil } +func genEmailInviteWithErrorList(emailList []string) []*model.EmailInviteWithError { + invitesNotSent := make([]*model.EmailInviteWithError, len(emailList)) + for i := range emailList { + invite := &model.EmailInviteWithError{ + Email: emailList[i], + Error: model.NewAppError("inviteUsersToTeam", "api.team.invite_members.limit_reached.app_error", map[string]interface{}{"Addresses": emailList[i]}, "", http.StatusBadRequest), + } + invitesNotSent[i] = invite + } + return invitesNotSent +} + +func (a *App) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) { + var invitesNotSent []*model.EmailInviteWithError + if cloudUserLimit <= 0 { + return emailList, invitesNotSent, nil + } + systemUserCount, _ := a.Srv().Store.User().Count(model.UserCountOptions{}) + remainingUsers := cloudUserLimit - systemUserCount + if remainingUsers <= 0 { + // No remaining users so all fail + invitesNotSent = genEmailInviteWithErrorList(emailList) + emailList = nil + } else if remainingUsers < int64(len(emailList)) { + // Trim the email list to only invite as many users as are remaining in subscription + // Set graceful errors for the remaining email addresses + emailsAboveLimit := emailList[remainingUsers:] + invitesNotSent = genEmailInviteWithErrorList(emailsAboveLimit) + // If 1 user remaining we have to prevent 0:0 reslicing + if remainingUsers == 1 { + email := emailList[0] + emailList = nil + emailList = append(emailList, email) + } else { + emailList = emailList[:(remainingUsers - 1)] + } + } + + return emailList, invitesNotSent, nil +} + func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderId string) ([]*model.EmailInviteWithError, *model.AppError) { if !*a.Config().ServiceSettings.EnableEmailInvitations { return nil, model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented) @@ -1136,7 +1177,6 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderI err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.no_one.app_error", nil, "", http.StatusBadRequest) return nil, err } - user, team, err := a.prepareInviteNewUsersToTeam(teamId, senderId) if err != nil { return nil, err diff --git a/config/client.go b/config/client.go index d50c1a7ae0..c4f93c6074 100644 --- a/config/client.go +++ b/config/client.go @@ -52,6 +52,7 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L props["ExperimentalEnablePostMetadata"] = "true" props["ExperimentalEnableClickToReply"] = strconv.FormatBool(*c.ExperimentalSettings.EnableClickToReply) + props["ExperimentalCloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 8) if *c.ServiceSettings.ExperimentalChannelOrganization || *c.ServiceSettings.ExperimentalGroupUnreadChannels != model.GROUP_UNREAD_CHANNELS_DISABLED { props["ExperimentalChannelOrganization"] = strconv.FormatBool(true) } else { @@ -96,6 +97,8 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L props["EnableEmailInvitations"] = strconv.FormatBool(*c.ServiceSettings.EnableEmailInvitations) + props["CloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 10) + // Set default values for all options that require a license. props["ExperimentalHideTownSquareinLHS"] = "false" props["ExperimentalTownSquareIsReadOnly"] = "false" diff --git a/go.mod b/go.mod index ecb1c5822a..74a56f8a6a 100644 --- a/go.mod +++ b/go.mod @@ -85,7 +85,7 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect github.com/rs/cors v1.7.0 github.com/rudderlabs/analytics-go v3.2.1+incompatible - github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 + github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 // indirect github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect github.com/sirupsen/logrus v1.6.0 @@ -108,6 +108,9 @@ require ( github.com/wiggin77/merror v1.0.2 github.com/wiggin77/srslog v1.0.1 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect + github.com/ziutek/mymysql v1.5.4 // indirect + github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a // indirect + go.etcd.io/bbolt v1.3.5 // indirect go.uber.org/zap v1.15.0 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/image v0.0.0-20200618115811-c13761719519 @@ -116,7 +119,8 @@ require ( golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd // indirect golang.org/x/text v0.3.3 - golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f + golang.org/x/tools v0.0.0-20200806022845-90696ccdc692 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.6 // indirect google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5 // indirect google.golang.org/grpc v1.30.0 // indirect diff --git a/go.sum b/go.sum index d944a8374a..c061dac946 100644 --- a/go.sum +++ b/go.sum @@ -760,8 +760,11 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a h1:00UFliGZl2UciXe8o/2iuEsRQ9u7z0rzDTVzuj6EYY0= +github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -866,6 +869,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -919,6 +923,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -939,10 +944,14 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f h1:JcoF/bowzCDI+MXu1yLqQGNO3ibqWsWq+Sk7pOT218w= golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200806022845-90696ccdc692 h1:fsn47thVa7Ar/TMyXYlZgOoT7M4+kRpb+KpSAqRQx1w= +golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= diff --git a/go.tools.mod b/go.tools.mod index ed1a0c2910..269a6ae394 100644 --- a/go.tools.mod +++ b/go.tools.mod @@ -4,8 +4,8 @@ go 1.14 require ( github.com/jstemmer/go-junit-report v0.9.1 // indirect - github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // indirect - github.com/philhofer/fwd v1.0.0 // indirect + github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 // indirect + github.com/philhofer/fwd v1.0.0 // indirect github.com/reflog/struct2interface v0.6.1 // indirect github.com/tinylib/msgp v1.1.2 // indirect github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect diff --git a/go.tools.sum b/go.tools.sum index 895eb1d65f..a48ca77a77 100644 --- a/go.tools.sum +++ b/go.tools.sum @@ -58,6 +58,8 @@ github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38 github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38dcd293/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM= github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 h1:+JXFDXEkbMieBt54Rihj3ppnZ/Pm1kZxkLOD0hrWFzs= github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM= +github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 h1:swpUQyq7HWhc0aStUAgdZI6o0UOD9OzXJxbxUTLo1fo= +github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/i18n/en.json b/i18n/en.json index af271adaa4..96d61338ac 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2214,6 +2214,10 @@ "id": "api.team.invite_members.invalid_email.app_error", "translation": "The following email addresses do not belong to an accepted domain: {{.Addresses}}. Please contact your System Administrator for details." }, + { + "id": "api.team.invite_members.limit_reached.app_error", + "translation": "You've reached the user limit of your current tier" + }, { "id": "api.team.invite_members.no_one.app_error", "translation": "No one to invite." diff --git a/model/config.go b/model/config.go index da40ec1d38..183c639e33 100644 --- a/model/config.go +++ b/model/config.go @@ -861,6 +861,7 @@ type ExperimentalSettings struct { EnableClickToReply *bool `restricted:"true"` LinkMetadataTimeoutMilliseconds *int64 `restricted:"true"` RestrictSystemAdmin *bool `restricted:"true"` + CloudUserLimit *int64 `restricted:"true"` UseNewSAMLLibrary *bool } @@ -884,6 +885,11 @@ func (s *ExperimentalSettings) SetDefaults() { if s.RestrictSystemAdmin == nil { s.RestrictSystemAdmin = NewBool(false) } + + if s.CloudUserLimit == nil { + // User limit 0 is treated as no limit + s.CloudUserLimit = NewInt64(0) + } if s.UseNewSAMLLibrary == nil { s.UseNewSAMLLibrary = NewBool(false) } diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 6e91391ce2..b4d232be70 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -89,6 +89,10 @@ type golistState struct { rootDirsError error rootDirs map[string]string + goVersionOnce sync.Once + goVersionError error + goVersion string // third field of 'go version' + // vendorDirs caches the (non)existence of vendor directories. vendorDirs map[string]bool } @@ -635,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse pkg.CompiledGoFiles = pkg.GoFiles } + // Temporary work-around for golang/go#39986. Parse filenames out of + // error messages. This happens if there are unrecoverable syntax + // errors in the source, so we can't match on a specific error message. + if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { + addFilenameFromPos := func(pos string) bool { + split := strings.Split(pos, ":") + if len(split) < 1 { + return false + } + filename := strings.TrimSpace(split[0]) + if filename == "" { + return false + } + if !filepath.IsAbs(filename) { + filename = filepath.Join(state.cfg.Dir, filename) + } + info, _ := os.Stat(filename) + if info == nil { + return false + } + pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename) + pkg.GoFiles = append(pkg.GoFiles, filename) + return true + } + found := addFilenameFromPos(err.Pos) + // In some cases, go list only reports the error position in the + // error text, not the error position. One such case is when the + // file's package name is a keyword (see golang.org/issue/39763). + if !found { + addFilenameFromPos(err.Err) + } + } + if p.Error != nil { msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363. // Address golang.org/issue/35964 by appending import stack to error message. @@ -664,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse return &response, nil } +func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool { + if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 { + return false + } + + goV, err := state.getGoVersion() + if err != nil { + return false + } + + // On Go 1.14 and earlier, only add filenames from errors if the import stack is empty. + // The import stack behaves differently for these versions than newer Go versions. + if strings.HasPrefix(goV, "go1.13") || strings.HasPrefix(goV, "go1.14") { + return len(p.Error.ImportStack) == 0 + } + + // On Go 1.15 and later, only parse filenames out of error if there's no import stack, + // or the current package is at the top of the import stack. This is not guaranteed + // to work perfectly, but should avoid some cases where files in errors don't belong to this + // package. + return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath +} + +func (state *golistState) getGoVersion() (string, error) { + state.goVersionOnce.Do(func() { + var b *bytes.Buffer + // Invoke go version. Don't use invokeGo because it will supply build flags, and + // go version doesn't expect build flags. + inv := gocommand.Invocation{ + Verb: "version", + Env: state.cfg.Env, + Logf: state.cfg.Logf, + } + gocmdRunner := state.cfg.gocmdRunner + if gocmdRunner == nil { + gocmdRunner = &gocommand.Runner{} + } + b, _, _, state.goVersionError = gocmdRunner.RunRaw(state.cfg.Context, inv) + if state.goVersionError != nil { + return + } + + sp := strings.Split(b.String(), " ") + if len(sp) < 3 { + state.goVersionError = fmt.Errorf("go version output: expected 'go version ', got '%s'", b.String()) + return + } + state.goVersion = sp[2] + }) + return state.goVersion, state.goVersionError +} + // getPkgPath finds the package path of a directory if it's relative to a root directory. func (state *golistState) getPkgPath(dir string) (string, bool, error) { absDir, err := filepath.Abs(dir) diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index 338d6f6232..4eabfd98c6 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -102,8 +102,11 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif } } } - // The overlay could have included an entirely new package. - if pkg == nil { + // The overlay could have included an entirely new package or an + // ad-hoc package. An ad-hoc package is one that we have manually + // constructed from inadequate `go list` results for a file= query. + // It will have the ID command-line-arguments. + if pkg == nil || pkg.ID == "command-line-arguments" { // Try to find the module or gopath dir the file is contained in. // Then for modules, add the module opath to the beginning. pkgPath, ok, err := state.getPkgPath(dir) @@ -127,35 +130,40 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) } } - // Try to reclaim a package with the same ID, if it exists in the response. - for _, p := range response.dr.Packages { - if reclaimPackage(p, id, opath, contents) { - pkg = p - break - } - } - // Otherwise, create a new package. - if pkg == nil { - pkg = &Package{ - PkgPath: pkgPath, - ID: id, - Name: pkgName, - Imports: make(map[string]*Package), - } - response.addPackage(pkg) - havePkgs[pkg.PkgPath] = id - // Add the production package's sources for a test variant. - if isTestFile && !isXTest && testVariantOf != nil { - pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) - // Add the package under test and its imports to the test variant. - pkg.forTest = testVariantOf.PkgPath - for k, v := range testVariantOf.Imports { - pkg.Imports[k] = &Package{ID: v.ID} + if pkg != nil { + // TODO(rstambler): We should change the package's path and ID + // here. The only issue is that this messes with the roots. + } else { + // Try to reclaim a package with the same ID, if it exists in the response. + for _, p := range response.dr.Packages { + if reclaimPackage(p, id, opath, contents) { + pkg = p + break } } - if isXTest { - pkg.forTest = forTest + // Otherwise, create a new package. + if pkg == nil { + pkg = &Package{ + PkgPath: pkgPath, + ID: id, + Name: pkgName, + Imports: make(map[string]*Package), + } + response.addPackage(pkg) + havePkgs[pkg.PkgPath] = id + // Add the production package's sources for a test variant. + if isTestFile && !isXTest && testVariantOf != nil { + pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) + pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) + // Add the package under test and its imports to the test variant. + pkg.forTest = testVariantOf.PkgPath + for k, v := range testVariantOf.Imports { + pkg.Imports[k] = &Package{ID: v.ID} + } + } + if isXTest { + pkg.forTest = forTest + } } } } diff --git a/vendor/golang.org/x/tools/imports/forward.go b/vendor/golang.org/x/tools/imports/forward.go index 83f4e44731..a4e40adba0 100644 --- a/vendor/golang.org/x/tools/imports/forward.go +++ b/vendor/golang.org/x/tools/imports/forward.go @@ -3,8 +3,10 @@ package imports // import "golang.org/x/tools/imports" import ( + "io/ioutil" "log" + "golang.org/x/tools/internal/gocommand" intimp "golang.org/x/tools/internal/imports" ) @@ -29,25 +31,34 @@ var Debug = false var LocalPrefix string // Process formats and adjusts imports for the provided file. -// If opt is nil the defaults are used. +// If opt is nil the defaults are used, and if src is nil the source +// is read from the filesystem. // // Note that filename's directory influences which imports can be chosen, // so it is important that filename be accurate. // To process data ``as if'' it were in filename, pass the data as a non-nil src. func Process(filename string, src []byte, opt *Options) ([]byte, error) { + var err error + if src == nil { + src, err = ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + } if opt == nil { opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} } intopt := &intimp.Options{ Env: &intimp.ProcessEnv{ - LocalPrefix: LocalPrefix, + GocmdRunner: &gocommand.Runner{}, }, - AllErrors: opt.AllErrors, - Comments: opt.Comments, - FormatOnly: opt.FormatOnly, - Fragment: opt.Fragment, - TabIndent: opt.TabIndent, - TabWidth: opt.TabWidth, + LocalPrefix: LocalPrefix, + AllErrors: opt.AllErrors, + Comments: opt.Comments, + FormatOnly: opt.FormatOnly, + Fragment: opt.Fragment, + TabIndent: opt.TabIndent, + TabWidth: opt.TabWidth, } if Debug { intopt.Env.Logf = log.Printf diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index 2c70307656..ecd13e87ad 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -32,25 +32,25 @@ import ( // importToGroup is a list of functions which map from an import path to // a group number. -var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool){ - func(env *ProcessEnv, importPath string) (num int, ok bool) { - if env.LocalPrefix == "" { +var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){ + func(localPrefix, importPath string) (num int, ok bool) { + if localPrefix == "" { return } - for _, p := range strings.Split(env.LocalPrefix, ",") { + for _, p := range strings.Split(localPrefix, ",") { if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath { return 3, true } } return }, - func(_ *ProcessEnv, importPath string) (num int, ok bool) { + func(_, importPath string) (num int, ok bool) { if strings.HasPrefix(importPath, "appengine") { return 2, true } return }, - func(_ *ProcessEnv, importPath string) (num int, ok bool) { + func(_, importPath string) (num int, ok bool) { firstComponent := strings.Split(importPath, "/")[0] if strings.Contains(firstComponent, ".") { return 1, true @@ -59,9 +59,9 @@ var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool }, } -func importGroup(env *ProcessEnv, importPath string) int { +func importGroup(localPrefix, importPath string) int { for _, fn := range importToGroup { - if n, ok := fn(env, importPath); ok { + if n, ok := fn(localPrefix, importPath); ok { return n } } @@ -278,7 +278,12 @@ func (p *pass) loadPackageNames(imports []*ImportInfo) error { unknown = append(unknown, imp.ImportPath) } - names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir) + resolver, err := p.env.GetResolver() + if err != nil { + return err + } + + names, err := resolver.loadPackageNames(unknown, p.srcDir) if err != nil { return err } @@ -640,15 +645,23 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena wrappedCallback.exportsLoaded(pkg, exports) }, } - return env.GetResolver().scan(ctx, scanFilter) + resolver, err := env.GetResolver() + if err != nil { + return err + } + return resolver.scan(ctx, scanFilter) } -func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) map[string]int { +func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]int, error) { result := make(map[string]int) - for _, path := range paths { - result[path] = env.GetResolver().scoreImportPath(ctx, path) + resolver, err := env.GetResolver() + if err != nil { + return nil, err } - return result + for _, path := range paths { + result[path] = resolver.scoreImportPath(ctx, path) + } + return result, nil } func PrimeCache(ctx context.Context, env *ProcessEnv) error { @@ -674,8 +687,9 @@ func candidateImportName(pkg *pkg) string { return "" } -// getAllCandidates gets all of the candidates to be imported, regardless of if they are needed. -func getAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { +// GetAllCandidates gets all of the packages starting with prefix that can be +// imported by filename, sorted by import path. +func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { callback := &scanCallback{ rootFound: func(gopathwalk.Root) bool { return true @@ -714,7 +728,8 @@ type PackageExport struct { Exports []string } -func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error { +// GetPackageExports returns all known packages with name pkg and their exports. +func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error { callback := &scanCallback{ rootFound: func(gopathwalk.Root) bool { return true @@ -749,8 +764,6 @@ var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD" // ProcessEnv contains environment variables and settings that affect the use of // the go command, the go/build package, etc. type ProcessEnv struct { - LocalPrefix string - GocmdRunner *gocommand.Runner BuildFlags []string @@ -830,16 +843,19 @@ func (e *ProcessEnv) env() []string { return env } -func (e *ProcessEnv) GetResolver() Resolver { +func (e *ProcessEnv) GetResolver() (Resolver, error) { if e.resolver != nil { - return e.resolver + return e.resolver, nil + } + if err := e.init(); err != nil { + return nil, err } if len(e.Env["GOMOD"]) == 0 { e.resolver = newGopathResolver(e) - return e.resolver + return e.resolver, nil } e.resolver = newModuleResolver(e) - return e.resolver + return e.resolver, nil } func (e *ProcessEnv) buildContext() *build.Context { @@ -964,10 +980,13 @@ func addExternalCandidates(pass *pass, refs references, filename string) error { return false // We'll do our own loading after we sort. }, } - err := pass.env.GetResolver().scan(context.Background(), callback) + resolver, err := pass.env.GetResolver() if err != nil { return err } + if err = resolver.scan(context.Background(), callback); err != nil { + return err + } // Search for imports matching potential package references. type result struct { @@ -1408,6 +1427,10 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir) } } + resolver, err := pass.env.GetResolver() + if err != nil { + return nil, err + } // Collect exports for packages with matching names. rescv := make([]chan *pkg, len(candidates)) @@ -1446,7 +1469,7 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa } // If we're an x_test, load the package under test's test variant. includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir - _, exports, err := pass.env.GetResolver().loadExports(ctx, c.pkg, includeTest) + _, exports, err := resolver.loadExports(ctx, c.pkg, includeTest) if err != nil { if pass.env.Logf != nil { pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go index 04ecdfda37..2815edc33d 100644 --- a/vendor/golang.org/x/tools/internal/imports/imports.go +++ b/vendor/golang.org/x/tools/internal/imports/imports.go @@ -11,7 +11,6 @@ package imports import ( "bufio" "bytes" - "context" "fmt" "go/ast" "go/format" @@ -19,19 +18,22 @@ import ( "go/printer" "go/token" "io" - "io/ioutil" "regexp" "strconv" "strings" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/internal/gocommand" ) // Options is golang.org/x/tools/imports.Options with extra internal-only options. type Options struct { Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state. + // LocalPrefix is a comma-separated string of import path prefixes, which, if + // set, instructs Process to sort the import paths with the given prefixes + // into another group after 3rd-party packages. + LocalPrefix string + Fragment bool // Accept fragment of a source file (no package statement) AllErrors bool // Report all errors (not just the first 10 on different lines) @@ -42,13 +44,8 @@ type Options struct { FormatOnly bool // Disable the insertion and deletion of imports } -// Process implements golang.org/x/tools/imports.Process with explicit context in env. +// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env. func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) { - src, opt, err = initialize(filename, src, opt) - if err != nil { - return nil, err - } - fileSet := token.NewFileSet() file, adjust, err := parse(fileSet, filename, src, opt) if err != nil { @@ -64,16 +61,12 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e } // FixImports returns a list of fixes to the imports that, when applied, -// will leave the imports in the same state as Process. +// will leave the imports in the same state as Process. src and opt must +// be specified. // // Note that filename's directory influences which imports can be chosen, // so it is important that filename be accurate. func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { - src, opt, err = initialize(filename, src, opt) - if err != nil { - return nil, err - } - fileSet := token.NewFileSet() file, _, err := parse(fileSet, filename, src, opt) if err != nil { @@ -84,13 +77,9 @@ func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, } // ApplyFixes applies all of the fixes to the file and formats it. extraMode -// is added in when parsing the file. +// is added in when parsing the file. src and opts must be specified, but no +// env is needed. func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) { - src, opt, err = initialize(filename, src, opt) - if err != nil { - return nil, err - } - // Don't use parse() -- we don't care about fragments or statement lists // here, and we need to work with unparseable files. fileSet := token.NewFileSet() @@ -114,60 +103,9 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e return formatFile(fileSet, file, src, nil, opt) } -// GetAllCandidates gets all of the packages starting with prefix that can be -// imported by filename, sorted by import path. -func GetAllCandidates(ctx context.Context, callback func(ImportFix), searchPrefix, filename, filePkg string, opt *Options) error { - _, opt, err := initialize(filename, []byte{}, opt) - if err != nil { - return err - } - return getAllCandidates(ctx, callback, searchPrefix, filename, filePkg, opt.Env) -} - -// GetPackageExports returns all known packages with name pkg and their exports. -func GetPackageExports(ctx context.Context, callback func(PackageExport), searchPkg, filename, filePkg string, opt *Options) error { - _, opt, err := initialize(filename, []byte{}, opt) - if err != nil { - return err - } - return getPackageExports(ctx, callback, searchPkg, filename, filePkg, opt.Env) -} - -// initialize sets the values for opt and src. -// If they are provided, they are not changed. Otherwise opt is set to the -// default values and src is read from the file system. -func initialize(filename string, src []byte, opt *Options) ([]byte, *Options, error) { - // Use defaults if opt is nil. - if opt == nil { - opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} - } - - // Set the env if the user has not provided it. - if opt.Env == nil { - opt.Env = &ProcessEnv{} - } - // Set the gocmdRunner if the user has not provided it. - if opt.Env.GocmdRunner == nil { - opt.Env.GocmdRunner = &gocommand.Runner{} - } - if err := opt.Env.init(); err != nil { - return nil, nil, err - } - - if src == nil { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, nil, err - } - src = b - } - - return src, opt, nil -} - func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) { - mergeImports(opt.Env, fileSet, file) - sortImports(opt.Env, fileSet, file) + mergeImports(fileSet, file) + sortImports(opt.LocalPrefix, fileSet, file) imps := astutil.Imports(fileSet, file) var spacesBefore []string // import paths we need spaces before for _, impSection := range imps { @@ -178,7 +116,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func( lastGroup := -1 for _, importSpec := range impSection { importPath, _ := strconv.Unquote(importSpec.Path.Value) - groupNum := importGroup(opt.Env, importPath) + groupNum := importGroup(opt.LocalPrefix, importPath) if groupNum != lastGroup && lastGroup != -1 { spacesBefore = append(spacesBefore, importPath) } diff --git a/vendor/golang.org/x/tools/internal/imports/sortimports.go b/vendor/golang.org/x/tools/internal/imports/sortimports.go index 226279471d..be8ffa25fe 100644 --- a/vendor/golang.org/x/tools/internal/imports/sortimports.go +++ b/vendor/golang.org/x/tools/internal/imports/sortimports.go @@ -15,7 +15,7 @@ import ( // sortImports sorts runs of consecutive import lines in import blocks in f. // It also removes duplicate imports when it is possible to do so without data loss. -func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { +func sortImports(localPrefix string, fset *token.FileSet, f *ast.File) { for i, d := range f.Decls { d, ok := d.(*ast.GenDecl) if !ok || d.Tok != token.IMPORT { @@ -40,11 +40,11 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { for j, s := range d.Specs { if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { // j begins a new run. End this one. - specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:j])...) + specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:j])...) i = j } } - specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:])...) + specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:])...) d.Specs = specs // Deduping can leave a blank line before the rparen; clean that up. @@ -60,7 +60,7 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { // mergeImports merges all the import declarations into the first one. // Taken from golang.org/x/tools/ast/astutil. -func mergeImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { +func mergeImports(fset *token.FileSet, f *ast.File) { if len(f.Decls) <= 1 { return } @@ -142,7 +142,7 @@ type posSpan struct { End token.Pos } -func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec { +func sortSpecs(localPrefix string, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec { // Can't short-circuit here even if specs are already sorted, // since they might yet need deduplication. // A lone import, however, may be safely ignored. @@ -191,7 +191,7 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp // Reassign the import paths to have the same position sequence. // Reassign each comment to abut the end of its spec. // Sort the comments by new position. - sort.Sort(byImportSpec{env, specs}) + sort.Sort(byImportSpec{localPrefix, specs}) // Dedup. Thanks to our sorting, we can just consider // adjacent pairs of imports. @@ -245,8 +245,8 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp } type byImportSpec struct { - env *ProcessEnv - specs []ast.Spec // slice of *ast.ImportSpec + localPrefix string + specs []ast.Spec // slice of *ast.ImportSpec } func (x byImportSpec) Len() int { return len(x.specs) } @@ -255,8 +255,8 @@ func (x byImportSpec) Less(i, j int) bool { ipath := importPath(x.specs[i]) jpath := importPath(x.specs[j]) - igroup := importGroup(x.env, ipath) - jgroup := importGroup(x.env, jpath) + igroup := importGroup(x.localPrefix, ipath) + jgroup := importGroup(x.localPrefix, jpath) if igroup != jgroup { return igroup < jgroup } diff --git a/vendor/golang.org/x/tools/internal/imports/zstdlib.go b/vendor/golang.org/x/tools/internal/imports/zstdlib.go index 16252111ff..7b573b9830 100644 --- a/vendor/golang.org/x/tools/internal/imports/zstdlib.go +++ b/vendor/golang.org/x/tools/internal/imports/zstdlib.go @@ -56,6 +56,7 @@ var stdlib = map[string][]string{ }, "bufio": []string{ "ErrAdvanceTooFar", + "ErrBadReadCount", "ErrBufferFull", "ErrFinalToken", "ErrInvalidUnreadByte", @@ -303,7 +304,9 @@ var stdlib = map[string][]string{ "PrivateKey", "PublicKey", "Sign", + "SignASN1", "Verify", + "VerifyASN1", }, "crypto/ed25519": []string{ "GenerateKey", @@ -322,11 +325,13 @@ var stdlib = map[string][]string{ "CurveParams", "GenerateKey", "Marshal", + "MarshalCompressed", "P224", "P256", "P384", "P521", "Unmarshal", + "UnmarshalCompressed", }, "crypto/hmac": []string{ "Equal", @@ -432,6 +437,7 @@ var stdlib = map[string][]string{ "CurveP521", "Dial", "DialWithDialer", + "Dialer", "ECDSAWithP256AndSHA256", "ECDSAWithP384AndSHA384", "ECDSAWithP521AndSHA512", @@ -507,6 +513,7 @@ var stdlib = map[string][]string{ "ConstraintViolationError", "CreateCertificate", "CreateCertificateRequest", + "CreateRevocationList", "DSA", "DSAWithSHA1", "DSAWithSHA256", @@ -581,6 +588,7 @@ var stdlib = map[string][]string{ "PublicKeyAlgorithm", "PureEd25519", "RSA", + "RevocationList", "SHA1WithRSA", "SHA256WithRSA", "SHA256WithRSAPSS", @@ -694,6 +702,7 @@ var stdlib = map[string][]string{ "String", "Tx", "TxOptions", + "Validator", "Value", "ValueConverter", "Valuer", @@ -2349,6 +2358,27 @@ var stdlib = map[string][]string{ "IMAGE_DIRECTORY_ENTRY_RESOURCE", "IMAGE_DIRECTORY_ENTRY_SECURITY", "IMAGE_DIRECTORY_ENTRY_TLS", + "IMAGE_DLLCHARACTERISTICS_APPCONTAINER", + "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", + "IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", + "IMAGE_DLLCHARACTERISTICS_GUARD_CF", + "IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", + "IMAGE_DLLCHARACTERISTICS_NO_BIND", + "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", + "IMAGE_DLLCHARACTERISTICS_NO_SEH", + "IMAGE_DLLCHARACTERISTICS_NX_COMPAT", + "IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", + "IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", + "IMAGE_FILE_32BIT_MACHINE", + "IMAGE_FILE_AGGRESIVE_WS_TRIM", + "IMAGE_FILE_BYTES_REVERSED_HI", + "IMAGE_FILE_BYTES_REVERSED_LO", + "IMAGE_FILE_DEBUG_STRIPPED", + "IMAGE_FILE_DLL", + "IMAGE_FILE_EXECUTABLE_IMAGE", + "IMAGE_FILE_LARGE_ADDRESS_AWARE", + "IMAGE_FILE_LINE_NUMS_STRIPPED", + "IMAGE_FILE_LOCAL_SYMS_STRIPPED", "IMAGE_FILE_MACHINE_AM33", "IMAGE_FILE_MACHINE_AMD64", "IMAGE_FILE_MACHINE_ARM", @@ -2371,6 +2401,25 @@ var stdlib = map[string][]string{ "IMAGE_FILE_MACHINE_THUMB", "IMAGE_FILE_MACHINE_UNKNOWN", "IMAGE_FILE_MACHINE_WCEMIPSV2", + "IMAGE_FILE_NET_RUN_FROM_SWAP", + "IMAGE_FILE_RELOCS_STRIPPED", + "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", + "IMAGE_FILE_SYSTEM", + "IMAGE_FILE_UP_SYSTEM_ONLY", + "IMAGE_SUBSYSTEM_EFI_APPLICATION", + "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", + "IMAGE_SUBSYSTEM_EFI_ROM", + "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", + "IMAGE_SUBSYSTEM_NATIVE", + "IMAGE_SUBSYSTEM_NATIVE_WINDOWS", + "IMAGE_SUBSYSTEM_OS2_CUI", + "IMAGE_SUBSYSTEM_POSIX_CUI", + "IMAGE_SUBSYSTEM_UNKNOWN", + "IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", + "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", + "IMAGE_SUBSYSTEM_WINDOWS_CUI", + "IMAGE_SUBSYSTEM_WINDOWS_GUI", + "IMAGE_SUBSYSTEM_XBOX", "ImportDirectory", "NewFile", "Open", @@ -4188,6 +4237,7 @@ var stdlib = map[string][]string{ "DevNull", "Environ", "ErrClosed", + "ErrDeadlineExceeded", "ErrExist", "ErrInvalid", "ErrNoDeadline", @@ -4646,6 +4696,7 @@ var stdlib = map[string][]string{ "ErrRange", "ErrSyntax", "FormatBool", + "FormatComplex", "FormatFloat", "FormatInt", "FormatUint", @@ -4655,6 +4706,7 @@ var stdlib = map[string][]string{ "Itoa", "NumError", "ParseBool", + "ParseComplex", "ParseFloat", "ParseInt", "ParseUint", diff --git a/vendor/modules.txt b/vendor/modules.txt index bd603c06a0..dbfc04ec59 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -515,7 +515,12 @@ github.com/willf/bitset # github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c ## explicit github.com/xtgo/uuid -# go.etcd.io/bbolt v1.3.4 +# github.com/ziutek/mymysql v1.5.4 +## explicit +# github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a +## explicit +# go.etcd.io/bbolt v1.3.5 +## explicit go.etcd.io/bbolt # go.uber.org/atomic v1.6.0 go.uber.org/atomic @@ -612,7 +617,7 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f +# golang.org/x/tools v0.0.0-20200806022845-90696ccdc692 ## explicit golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/gcexportdata @@ -630,7 +635,8 @@ golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/imports golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/typesinternal -# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 +# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +## explicit golang.org/x/xerrors golang.org/x/xerrors/internal # google.golang.org/appengine v1.6.6