From 902f7296d211c9b2e72ea6d36dc721b5bc044d7d Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Tue, 22 Nov 2022 07:47:07 +0400 Subject: [PATCH 01/15] Add playbooks product support. Add new methods to post service --- Makefile | 10 ++++++++++ app/post.go | 8 ++++++++ product/api.go | 2 ++ scripts/setup_go_work.sh | 1 + 4 files changed, 21 insertions(+) diff --git a/Makefile b/Makefile index d792c30126..b0a0085eed 100644 --- a/Makefile +++ b/Makefile @@ -195,6 +195,16 @@ else IGNORE:=$(shell rm -f imports/boards_imports.go) endif +# Playbooks +BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks +BUILD_PLAYBOOKS ?= false + +ifeq ($(BUILD_PLAYBOOKS),true) +IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/) +else + IGNORE:=$(shell rm -f imports/playbooks_imports.go) +endif + all: run ## Alias for 'run'. -include config.override.mk diff --git a/app/post.go b/app/post.go index ff6cab3397..40c0b1836d 100644 --- a/app/post.go +++ b/app/post.go @@ -46,6 +46,14 @@ func (s *postServiceWrapper) CreatePost(ctx *request.Context, post *model.Post) return s.app.CreatePostMissingChannel(ctx, post, true) } +func (s *postServiceWrapper) GetPostsByIds(postIDs []string) ([]*model.Post, int64, *model.AppError) { + return s.app.GetPostsByIds(postIDs) +} + +func (s *postServiceWrapper) SendEphemeralPost(ctx *request.Context, userID string, post *model.Post) *model.Post { + return s.app.SendEphemeralPost(ctx, userID, post) +} + func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId string, setOnline bool) (*model.Post, *model.AppError) { // Check that channel has not been deleted channel, errCh := a.Srv().Store().Channel().Get(post.ChannelId, true) diff --git a/product/api.go b/product/api.go index 88eb9744e4..f8e2a3999d 100644 --- a/product/api.go +++ b/product/api.go @@ -31,6 +31,8 @@ type RouterService interface { // The service shall be registered via app.PostKey service key. type PostService interface { CreatePost(context *request.Context, post *model.Post) (*model.Post, *model.AppError) + GetPostsByIds(postIDs []string) ([]*model.Post, int64, *model.AppError) + SendEphemeralPost(ctx *request.Context, userID string, post *model.Post) *model.Post } // PermissionService provides permissions related utilities. For now, the service implementation diff --git a/scripts/setup_go_work.sh b/scripts/setup_go_work.sh index 76c7018736..1a683fee91 100755 --- a/scripts/setup_go_work.sh +++ b/scripts/setup_go_work.sh @@ -16,5 +16,6 @@ then txt="${txt}use ../focalboard/server\nuse ../focalboard/mattermost-plugin\n" fi + txt="${txt}use ../mattermost-plugin-playbooks\n" printf "$txt" > "go.work" fi \ No newline at end of file From 1a2c08f1c64515f2d9a412ac0bbeee9d1f3fa62e Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Tue, 22 Nov 2022 11:13:49 +0400 Subject: [PATCH 02/15] Add kvget function to store service interface --- product/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/product/api.go b/product/api.go index f8e2a3999d..b77ba0d4f8 100644 --- a/product/api.go +++ b/product/api.go @@ -160,6 +160,7 @@ type CloudService interface { // The service shall be registered via app.KVStoreKey service key. type KVStoreService interface { SetPluginKeyWithOptions(pluginID string, key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) + KVGet(productID, key string) ([]byte, *model.AppError) } // LogService is the API for accessing the log service APIs. From 9668733615d3b4f01693992eb229bfc3fd93bdcb Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Tue, 13 Dec 2022 17:11:57 +0400 Subject: [PATCH 03/15] Add new functions to channel service and kv store service interfaces --- Makefile | 2 +- app/channel.go | 17 +++++++++++++++++ app/server.go | 3 ++- product/api.go | 6 ++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e305cf3a5c..d317df5b9b 100644 --- a/Makefile +++ b/Makefile @@ -200,7 +200,7 @@ endif # Playbooks BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks -BUILD_PLAYBOOKS ?= false +BUILD_PLAYBOOKS ?= true ifeq ($(BUILD_PLAYBOOKS),true) IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/) diff --git a/app/channel.go b/app/channel.go index fdc8d17da3..64d322af59 100644 --- a/app/channel.go +++ b/app/channel.go @@ -27,6 +27,7 @@ import ( // channelsWrapper provides an implementation of `product.ChannelService` to be used by products. type channelsWrapper struct { srv *Server + app *App } func (s *channelsWrapper) GetDirectChannel(userID1, userID2 string) (*model.Channel, *model.AppError) { @@ -47,6 +48,22 @@ func (s *channelsWrapper) GetChannelsForTeamForUser(teamID string, userID string return s.srv.getChannelsForTeamForUser(request.EmptyContext(s.srv.Log()), teamID, userID, opts) } +func (s *channelsWrapper) GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { + return s.app.GetSidebarCategoriesForTeamForUser(request.EmptyContext(s.srv.Log()), userID, teamID) +} + +func (s *channelsWrapper) GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) { + return s.app.GetChannelMembersPage(request.EmptyContext(s.srv.Log()), channelID, page, perPage) +} + +func (s *channelsWrapper) CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) { + return s.app.CreateSidebarCategory(request.EmptyContext(s.srv.Log()), userID, teamID, newCategory) +} + +func (s *channelsWrapper) UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) { + return s.app.UpdateSidebarCategories(request.EmptyContext(s.srv.Log()), userID, teamID, categories) +} + // Ensure the wrapper implements the product service. var _ product.ChannelService = (*channelsWrapper)(nil) diff --git a/app/server.go b/app/server.go index d39e66705c..11e4329a4e 100644 --- a/app/server.go +++ b/app/server.go @@ -262,8 +262,9 @@ func NewServer(options ...Option) (*Server, error) { // ensure app implements `product.UserService` var _ product.UserService = (*App)(nil) + app := New(ServerConnector(s.Channels())) serviceMap := map[ServiceKey]any{ - ChannelKey: &channelsWrapper{srv: s}, + ChannelKey: &channelsWrapper{srv: s, app: app}, ConfigKey: s.platform, LicenseKey: s.licenseWrapper, FilestoreKey: s.platform.FileBackend(), diff --git a/product/api.go b/product/api.go index b77ba0d4f8..3012608bcb 100644 --- a/product/api.go +++ b/product/api.go @@ -65,6 +65,10 @@ type ChannelService interface { GetChannelByID(channelID string) (*model.Channel, *model.AppError) GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError) GetChannelsForTeamForUser(teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError) + GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) + GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) + CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) + UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) } // LicenseService provides license related utilities. @@ -161,6 +165,8 @@ type CloudService interface { type KVStoreService interface { SetPluginKeyWithOptions(pluginID string, key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) KVGet(productID, key string) ([]byte, *model.AppError) + KVDelete(productID, key string) *model.AppError + KVList(productID string, page, perPage int) ([]string, *model.AppError) } // LogService is the API for accessing the log service APIs. From df1865001e79acce58505934093052ef7cf8eafd Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Tue, 13 Dec 2022 19:47:01 +0400 Subject: [PATCH 04/15] Add new functions to channel, permissions, post and team services --- app/channel.go | 37 +++++++++++++++++++++++++++++++++++++ app/permissions.go | 4 ++++ app/post.go | 12 ++++++++++++ app/team.go | 13 +++++++++++++ product/api.go | 12 ++++++++++++ 5 files changed, 78 insertions(+) diff --git a/app/channel.go b/app/channel.go index 64d322af59..53cfd3d8ec 100644 --- a/app/channel.go +++ b/app/channel.go @@ -64,6 +64,43 @@ func (s *channelsWrapper) UpdateChannelSidebarCategories(userID, teamID string, return s.app.UpdateSidebarCategories(request.EmptyContext(s.srv.Log()), userID, teamID, categories) } +func (s *channelsWrapper) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { + return s.app.CreateChannel(request.EmptyContext(s.srv.Log()), channel, false) +} + +func (s *channelsWrapper) AddUserToChannel(channelID, userID, asUserID string) (*model.ChannelMember, *model.AppError) { + ctx := request.EmptyContext(s.srv.Log()) + channel, err := s.app.GetChannel(ctx, channelID) + if err != nil { + return nil, err + } + + return s.app.AddChannelMember(ctx, userID, channel, ChannelMemberOpts{ + UserRequestorID: asUserID, + }) +} + +func (s *channelsWrapper) UpdateChannelMemberRoles(channelID, userID, newRoles string) (*model.ChannelMember, *model.AppError) { + return s.app.UpdateChannelMemberRoles(request.EmptyContext(s.srv.Log()), channelID, userID, newRoles) +} + +func (s *channelsWrapper) DeleteChannelMember(channelID, userID string) *model.AppError { + return s.app.LeaveChannel(request.EmptyContext(s.srv.Log()), channelID, userID) +} + +func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.ChannelMember, *model.AppError) { + channel, err := s.GetChannelByID(channelID) + if err != nil { + return nil, err + } + + return s.app.AddChannelMember(request.EmptyContext(s.srv.Log()), userID, channel, ChannelMemberOpts{ + // For now, don't allow overriding these via the plugin API. + UserRequestorID: "", + PostRootID: "", + }) +} + // Ensure the wrapper implements the product service. var _ product.ChannelService = (*channelsWrapper)(nil) diff --git a/app/permissions.go b/app/permissions.go index 2b34b85bb3..251dc4722d 100644 --- a/app/permissions.go +++ b/app/permissions.go @@ -41,6 +41,10 @@ func (s *permissionsServiceWrapper) HasPermissionToChannel(askingUserID string, return s.app.HasPermissionToChannel(request.EmptyContext(s.app.Log()), askingUserID, channelID, permission) } +func (s *permissionsServiceWrapper) RolesGrantPermission(roleNames []string, permissionId string) bool { + return s.app.RolesGrantPermission(roleNames, permissionId) +} + func (a *App) ResetPermissionsSystem() *model.AppError { // Reset all Teams to not have a scheme. if err := a.Srv().Store().Team().ResetAllTeamSchemes(); err != nil { diff --git a/app/post.go b/app/post.go index 14a9dbe47f..759bb3c4a2 100644 --- a/app/post.go +++ b/app/post.go @@ -54,6 +54,18 @@ func (s *postServiceWrapper) SendEphemeralPost(ctx *request.Context, userID stri return s.app.SendEphemeralPost(ctx, userID, post) } +func (s *postServiceWrapper) GetPost(postID string) (*model.Post, *model.AppError) { + return s.app.GetSinglePost(postID, false) +} + +func (s *postServiceWrapper) DeletePost(ctx *request.Context, postID, productID string) (*model.Post, *model.AppError) { + return s.app.DeletePost(ctx, postID, productID) +} + +func (s *postServiceWrapper) UpdatePost(ctx *request.Context, post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) { + return s.app.UpdatePost(ctx, post, false) +} + func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId string, setOnline bool) (*model.Post, *model.AppError) { // Check that channel has not been deleted channel, errCh := a.Srv().Store().Channel().Get(post.ChannelId, true) diff --git a/app/team.go b/app/team.go index 222d10e442..12d9af0e91 100644 --- a/app/team.go +++ b/app/team.go @@ -44,6 +44,19 @@ func (w *teamServiceWrapper) CreateMember(ctx *request.Context, teamID, userID s return w.app.AddTeamMember(ctx, teamID, userID) } +func (w *teamServiceWrapper) GetGroup(groupID string) (*model.Group, *model.AppError) { + return w.app.GetGroup(groupID, nil, nil) +} + +func (w *teamServiceWrapper) GetTeam(teamID string) (*model.Team, *model.AppError) { + return w.app.GetTeam(teamID) +} + +func (w *teamServiceWrapper) GetGroupMemberUsers(groupID string, page, perPage int) ([]*model.User, *model.AppError) { + users, _, err := w.app.GetGroupMemberUsersPage(groupID, page, perPage, nil) + return users, err +} + // Ensure the wrapper implements the product service. var _ product.TeamService = (*teamServiceWrapper)(nil) diff --git a/product/api.go b/product/api.go index 3012608bcb..764ca82fdf 100644 --- a/product/api.go +++ b/product/api.go @@ -33,6 +33,9 @@ type PostService interface { CreatePost(context *request.Context, post *model.Post) (*model.Post, *model.AppError) GetPostsByIds(postIDs []string) ([]*model.Post, int64, *model.AppError) SendEphemeralPost(ctx *request.Context, userID string, post *model.Post) *model.Post + GetPost(postID string) (*model.Post, *model.AppError) + DeletePost(ctx *request.Context, postID, productID string) (*model.Post, *model.AppError) + UpdatePost(c *request.Context, post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) } // PermissionService provides permissions related utilities. For now, the service implementation @@ -44,6 +47,7 @@ type PermissionService interface { HasPermissionTo(userID string, permission *model.Permission) bool HasPermissionToTeam(userID, teamID string, permission *model.Permission) bool HasPermissionToChannel(askingUserID string, channelID string, permission *model.Permission) bool + RolesGrantPermission(roleNames []string, permissionID string) bool } // ClusterService enables to publish cluster events. In addition to that, It's being used for @@ -69,6 +73,11 @@ type ChannelService interface { GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) + CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) + AddUserToChannel(channelID, userID, asUserID string) (*model.ChannelMember, *model.AppError) + UpdateChannelMemberRoles(channelID, userID, newRoles string) (*model.ChannelMember, *model.AppError) + DeleteChannelMember(channelID, userID string) *model.AppError + AddChannelMember(channelID, userID string) (*model.ChannelMember, *model.AppError) } // LicenseService provides license related utilities. @@ -99,6 +108,9 @@ type UserService interface { type TeamService interface { GetMember(teamID, userID string) (*model.TeamMember, *model.AppError) CreateMember(ctx *request.Context, teamID, userID string) (*model.TeamMember, *model.AppError) + GetGroup(groupId string) (*model.Group, *model.AppError) + GetTeam(teamID string) (*model.Team, *model.AppError) + GetGroupMemberUsers(groupID string, page, perPage int) ([]*model.User, *model.AppError) } // BotService is just a copy implementation of mattermost-plugin-api EnsureBot method. From 9a4963ecb00f1d96ac727864b16de366f94c2cd0 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Mon, 26 Dec 2022 15:59:49 +0400 Subject: [PATCH 05/15] Add session, frontend and command services. --- app/channels.go | 2 ++ app/server.go | 2 ++ product/api.go | 22 ++++++++++++++++++++++ product/service.go | 3 +++ 4 files changed, 29 insertions(+) diff --git a/app/channels.go b/app/channels.go index 7ab024e45a..7f8ae06944 100644 --- a/app/channels.go +++ b/app/channels.go @@ -239,6 +239,8 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) { app: &App{ch: ch}, } + services[product.CommandKey] = &App{ch: ch} + return ch, nil } diff --git a/app/server.go b/app/server.go index c530bae271..7893e1aea4 100644 --- a/app/server.go +++ b/app/server.go @@ -253,6 +253,8 @@ func NewServer(options ...Option) (*Server, error) { product.KVStoreKey: s.platform, product.StoreKey: store.NewStoreServiceAdapter(s.Store()), product.SystemKey: &systemServiceAdapter{server: s}, + product.SessionKey: app, + product.FrontendKey: app, } // Step 4: Initialize products. diff --git a/product/api.go b/product/api.go index 6b37aae0a5..1ea0263b6f 100644 --- a/product/api.go +++ b/product/api.go @@ -231,3 +231,25 @@ type BoardsService interface { DeleteCard(cardID string, userID string) error HasPermissionToBoard(userID, boardID string, permission *model.Permission) bool } + +// SessionService is the API for accessing the session. +// +// The service shall be registered via app.SessionKey service key. +type SessionService interface { + GetSessionById(sessionID string) (*model.Session, *model.AppError) +} + +// FrontendService is the API for interacting with front end. +// +// The service shall be registered via app.FrontendKey service key. +type FrontendService interface { + OpenInteractiveDialog(dialog model.OpenDialogRequest) *model.AppError +} + +// CommandService is the API for interacting with front end. +// +// The service shall be registered via app.CommandKey service key. +type CommandService interface { + ExecuteCommand(c request.CTX, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) + RegisterPluginCommand(pluginID string, command *model.Command) error +} diff --git a/product/service.go b/product/service.go index a0a92e6adc..f8100e789b 100644 --- a/product/service.go +++ b/product/service.go @@ -26,4 +26,7 @@ const ( SystemKey ServiceKey = "systemkey" PreferencesKey ServiceKey = "preferenceskey" BoardsKey ServiceKey = "boards" + SessionKey ServiceKey = "sessionkey" + FrontendKey ServiceKey = "frontendkey" + CommandKey ServiceKey = "commandkey" ) From a57beb229cafd8a7da4693535540c46bf0be3d60 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Thu, 29 Dec 2022 13:38:38 +0400 Subject: [PATCH 06/15] Add threads service --- app/channels.go | 2 ++ app/collection.go | 2 +- app/plugin_api.go | 2 +- product/api.go | 7 +++++++ product/service.go | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/channels.go b/app/channels.go index 7f8ae06944..53a2e74000 100644 --- a/app/channels.go +++ b/app/channels.go @@ -241,6 +241,8 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) { services[product.CommandKey] = &App{ch: ch} + services[product.ThreadsKey] = &App{ch: ch} + return ch, nil } diff --git a/app/collection.go b/app/collection.go index 9b895e3bc0..ad7e4b4029 100644 --- a/app/collection.go +++ b/app/collection.go @@ -10,7 +10,7 @@ import ( "github.com/mattermost/mattermost-server/v6/shared/mlog" ) -func (a *App) registerCollectionAndTopic(pluginID, collectionType, topicType string) error { +func (a *App) RegisterCollectionAndTopic(pluginID, collectionType, topicType string) error { // we have a race condition due to multiple plugins calling this method a.ch.collectionAndTopicTypesMut.Lock() defer a.ch.collectionAndTopicTypesMut.Unlock() diff --git a/app/plugin_api.go b/app/plugin_api.go index 5c8ee9fad1..d1bf97aef1 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -1235,7 +1235,7 @@ func (api *PluginAPI) GetCloudLimits() (*model.ProductLimits, error) { // RegisterCollectionAndTopic informs the server that this plugin handles // the given collection and topic types. func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error { - return api.app.registerCollectionAndTopic(api.id, collectionType, topicType) + return api.app.RegisterCollectionAndTopic(api.id, collectionType, topicType) } func (api *PluginAPI) CreateUploadSession(us *model.UploadSession) (*model.UploadSession, error) { diff --git a/product/api.go b/product/api.go index 64d847d179..272fcd85c0 100644 --- a/product/api.go +++ b/product/api.go @@ -254,3 +254,10 @@ type CommandService interface { ExecuteCommand(c request.CTX, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) RegisterPluginCommand(pluginID string, command *model.Command) error } + +// ThreadsService is the API for interacting with threads anywhere. +// +// The service shall be registered via app.ThreadsKey service key. +type ThreadsService interface { + RegisterCollectionAndTopic(productID string, collectionType, topicType string) error +} diff --git a/product/service.go b/product/service.go index f8100e789b..7e629fc488 100644 --- a/product/service.go +++ b/product/service.go @@ -29,4 +29,5 @@ const ( SessionKey ServiceKey = "sessionkey" FrontendKey ServiceKey = "frontendkey" CommandKey ServiceKey = "commandkey" + ThreadsKey ServiceKey = "threadskey" ) From 6391cb0b22e58b54644536804ee3c546253aab7c Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Thu, 29 Dec 2022 15:23:09 +0400 Subject: [PATCH 07/15] Disable product mode for playbooks --- Makefile | 3 ++- scripts/setup_go_work.sh | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 85d24d69f7..d93117a46f 100644 --- a/Makefile +++ b/Makefile @@ -200,7 +200,7 @@ endif # Playbooks BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks -BUILD_PLAYBOOKS ?= true +BUILD_PLAYBOOKS ?= false ifeq ($(BUILD_PLAYBOOKS),true) IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/) @@ -431,6 +431,7 @@ endif setup-go-work: export BUILD_ENTERPRISE_READY := $(BUILD_ENTERPRISE_READY) setup-go-work: export BUILD_BOARDS := $(BUILD_BOARDS) +setup-go-work: export BUILD_PLAYBOOKS := $(BUILD_PLAYBOOKS) setup-go-work: ## Sets up your go.work file ./scripts/setup_go_work.sh $(IGNORE_GO_WORK_IF_EXISTS) diff --git a/scripts/setup_go_work.sh b/scripts/setup_go_work.sh index 1a683fee91..590e8e9bad 100755 --- a/scripts/setup_go_work.sh +++ b/scripts/setup_go_work.sh @@ -16,6 +16,10 @@ then txt="${txt}use ../focalboard/server\nuse ../focalboard/mattermost-plugin\n" fi - txt="${txt}use ../mattermost-plugin-playbooks\n" + if [ "$BUILD_PLAYBOOKS" == "true" ] + then + txt="${txt}use ../mattermost-plugin-playbooks\n" + fi + printf "$txt" > "go.work" -fi \ No newline at end of file +fi From c76a571067662031424f012375da11597d3b2a37 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Thu, 29 Dec 2022 15:40:03 +0400 Subject: [PATCH 08/15] Update app layer --- app/app_iface.go | 1 + app/opentracing/opentracing_layer.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/app_iface.go b/app/app_iface.go index f10ca2d8b9..4989828611 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -958,6 +958,7 @@ type AppIface interface { RegenOutgoingWebhookToken(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError) RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) RegenerateTeamInviteId(teamID string) (*model.Team, *model.AppError) + RegisterCollectionAndTopic(pluginID, collectionType, topicType string) error RegisterPluginCommand(pluginID string, command *model.Command) error ReloadConfig() error RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 367d98ec39..f0282ec2e9 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -13638,6 +13638,28 @@ func (a *OpenTracingAppLayer) RegenerateTeamInviteId(teamID string) (*model.Team return resultVar0, resultVar1 } +func (a *OpenTracingAppLayer) RegisterCollectionAndTopic(pluginID string, collectionType string, topicType string) error { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RegisterCollectionAndTopic") + + a.ctx = newCtx + a.app.Srv().Store().SetContext(newCtx) + defer func() { + a.app.Srv().Store().SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0 := a.app.RegisterCollectionAndTopic(pluginID, collectionType, topicType) + + if resultVar0 != nil { + span.LogFields(spanlog.Error(resultVar0)) + ext.Error.Set(span, true) + } + + return resultVar0 +} + func (a *OpenTracingAppLayer) RegisterPluginCommand(pluginID string, command *model.Command) error { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RegisterPluginCommand") From 084c30bbc187303dc23b8f6c45b7c9c466508231 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Thu, 5 Jan 2023 12:22:52 +0400 Subject: [PATCH 09/15] Add new method to channel service Fix channel service App.ch nil bug --- app/channel.go | 4 ++++ app/channels.go | 5 +++++ app/server.go | 2 +- product/api.go | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/channel.go b/app/channel.go index 53cfd3d8ec..d4f67ab3d3 100644 --- a/app/channel.go +++ b/app/channel.go @@ -101,6 +101,10 @@ func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.Cha }) } +func (s *channelsWrapper) GetDirectChannelOrCreate(userID1, userID2 string) (*model.Channel, *model.AppError) { + return s.app.GetOrCreateDirectChannel(request.EmptyContext(s.srv.Log()), userID1, userID2) +} + // Ensure the wrapper implements the product service. var _ product.ChannelService = (*channelsWrapper)(nil) diff --git a/app/channels.go b/app/channels.go index 53a2e74000..b22bfda2d0 100644 --- a/app/channels.go +++ b/app/channels.go @@ -213,6 +213,11 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) { pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest) pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest) + services[product.ChannelKey] = &channelsWrapper{ + srv: s, + app: &App{ch: ch}, + } + services[product.PostKey] = &postServiceWrapper{ app: &App{ch: ch}, } diff --git a/app/server.go b/app/server.go index 7893e1aea4..2d20462539 100644 --- a/app/server.go +++ b/app/server.go @@ -247,7 +247,7 @@ func NewServer(options ...Option) (*Server, error) { product.FilestoreKey: s.platform.FileBackend(), product.FileInfoStoreKey: &fileInfoWrapper{srv: s}, product.ClusterKey: s.platform, - product.UserKey: New(ServerConnector(s.Channels())), + product.UserKey: app, product.LogKey: s.platform.Log(), product.CloudKey: &cloudWrapper{cloud: s.Cloud}, product.KVStoreKey: s.platform, diff --git a/product/api.go b/product/api.go index 272fcd85c0..2d65be8f45 100644 --- a/product/api.go +++ b/product/api.go @@ -68,6 +68,7 @@ type ClusterService interface { // The service shall be registered via app.ChannelKey service key. type ChannelService interface { GetDirectChannel(userID1, userID2 string) (*model.Channel, *model.AppError) + GetDirectChannelOrCreate(userID1, userID2 string) (*model.Channel, *model.AppError) GetChannelByID(channelID string) (*model.Channel, *model.AppError) GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError) GetChannelsForTeamForUser(teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError) From 901756afe5cb8a8a60ddadefc92c4c0051978df0 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Fri, 13 Jan 2023 13:22:01 +0400 Subject: [PATCH 10/15] Add translations support for products --- shared/i18n/i18n.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/shared/i18n/i18n.go b/shared/i18n/i18n.go index 5bf0f13bfe..d3c1853c32 100644 --- a/shared/i18n/i18n.go +++ b/shared/i18n/i18n.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/mattermost/go-i18n/i18n" + "github.com/mattermost/go-i18n/i18n/bundle" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) @@ -22,6 +23,9 @@ const defaultLocale = "en" // TranslateFunc is the type of the translate functions type TranslateFunc func(translationID string, args ...any) string +// TranslationFuncByLocal is the type of function that takes local as a string and returns the translation function +type TranslationFuncByLocal func(locale string) TranslateFunc + // T is the translate function using the default server language as fallback language var T TranslateFunc @@ -74,6 +78,40 @@ func initTranslationsWithDir(dir string) error { return nil } +// GetTranslationFuncForDir loads translations from the filesystem into a new instance of the bundle. +// It returns a function to access loaded translations. +func GetTranslationFuncForDir(dir string) (TranslationFuncByLocal, error) { + var locales map[string]string = make(map[string]string) + bundle := bundle.New() + files, _ := os.ReadDir(dir) + for _, f := range files { + if filepath.Ext(f.Name()) == ".json" { + filename := f.Name() + locales[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) + + if err := bundle.LoadTranslationFile(filepath.Join(dir, filename)); err != nil { + return nil, err + } + } + } + + return func(locale string) TranslateFunc { + if _, ok := locales[locale]; !ok { + locale = defaultLocale + } + + t, _ := bundle.Tfunc(locale) + return func(translationID string, args ...any) string { + if translated := t(translationID, args...); translated != translationID { + return translated + } + + t, _ := bundle.Tfunc(defaultLocale) + return t(translationID, args...) + } + }, nil +} + func getTranslationsBySystemLocale() (TranslateFunc, error) { locale := defaultServerLocale if _, ok := locales[locale]; !ok { From 98c2e9f518df80fc87a69d46f77675929c273e9e Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Mon, 16 Jan 2023 13:19:54 +0400 Subject: [PATCH 11/15] Improve local variable nameing --- shared/i18n/i18n.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/i18n/i18n.go b/shared/i18n/i18n.go index d3c1853c32..747b7d9e99 100644 --- a/shared/i18n/i18n.go +++ b/shared/i18n/i18n.go @@ -81,13 +81,13 @@ func initTranslationsWithDir(dir string) error { // GetTranslationFuncForDir loads translations from the filesystem into a new instance of the bundle. // It returns a function to access loaded translations. func GetTranslationFuncForDir(dir string) (TranslationFuncByLocal, error) { - var locales map[string]string = make(map[string]string) + var availableLocals map[string]string = make(map[string]string) bundle := bundle.New() files, _ := os.ReadDir(dir) for _, f := range files { if filepath.Ext(f.Name()) == ".json" { filename := f.Name() - locales[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) + availableLocals[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) if err := bundle.LoadTranslationFile(filepath.Join(dir, filename)); err != nil { return nil, err @@ -96,7 +96,7 @@ func GetTranslationFuncForDir(dir string) (TranslationFuncByLocal, error) { } return func(locale string) TranslateFunc { - if _, ok := locales[locale]; !ok { + if _, ok := availableLocals[locale]; !ok { locale = defaultLocale } From a83728bca2b77da6978c857b42478529c583ffba Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Fri, 20 Jan 2023 14:48:10 +0400 Subject: [PATCH 12/15] Address review comments Switch channel to always use app Remove channel wrapper init from server --- app/channel.go | 29 ++++++++++++++--------------- app/channels.go | 1 - app/server.go | 1 - shared/i18n/i18n.go | 13 +++++++------ 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/app/channel.go b/app/channel.go index d4f67ab3d3..80877f1e93 100644 --- a/app/channel.go +++ b/app/channel.go @@ -26,50 +26,49 @@ import ( // channelsWrapper provides an implementation of `product.ChannelService` to be used by products. type channelsWrapper struct { - srv *Server app *App } func (s *channelsWrapper) GetDirectChannel(userID1, userID2 string) (*model.Channel, *model.AppError) { - return s.srv.getDirectChannel(request.EmptyContext(s.srv.Log()), userID1, userID2) + return s.app.getDirectChannel(request.EmptyContext(s.app.Log()), userID1, userID2) } // GetChannelByID gets a Channel by its ID. func (s *channelsWrapper) GetChannelByID(channelID string) (*model.Channel, *model.AppError) { - return s.srv.getChannel(request.EmptyContext(s.srv.Log()), channelID) + return s.app.GetChannel(request.EmptyContext(s.app.Log()), channelID) } // GetChannelMember gets a channel member by userID. func (s *channelsWrapper) GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError) { - return s.srv.getChannelMember(request.EmptyContext(s.srv.Log()), channelID, userID) + return s.app.GetChannelMember(request.EmptyContext(s.app.Log()), channelID, userID) } func (s *channelsWrapper) GetChannelsForTeamForUser(teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError) { - return s.srv.getChannelsForTeamForUser(request.EmptyContext(s.srv.Log()), teamID, userID, opts) + return s.app.GetChannelsForTeamForUser(request.EmptyContext(s.app.Log()), teamID, userID, opts) } func (s *channelsWrapper) GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { - return s.app.GetSidebarCategoriesForTeamForUser(request.EmptyContext(s.srv.Log()), userID, teamID) + return s.app.GetSidebarCategoriesForTeamForUser(request.EmptyContext(s.app.Log()), userID, teamID) } func (s *channelsWrapper) GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) { - return s.app.GetChannelMembersPage(request.EmptyContext(s.srv.Log()), channelID, page, perPage) + return s.app.GetChannelMembersPage(request.EmptyContext(s.app.Log()), channelID, page, perPage) } func (s *channelsWrapper) CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) { - return s.app.CreateSidebarCategory(request.EmptyContext(s.srv.Log()), userID, teamID, newCategory) + return s.app.CreateSidebarCategory(request.EmptyContext(s.app.Log()), userID, teamID, newCategory) } func (s *channelsWrapper) UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) { - return s.app.UpdateSidebarCategories(request.EmptyContext(s.srv.Log()), userID, teamID, categories) + return s.app.UpdateSidebarCategories(request.EmptyContext(s.app.Log()), userID, teamID, categories) } func (s *channelsWrapper) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { - return s.app.CreateChannel(request.EmptyContext(s.srv.Log()), channel, false) + return s.app.CreateChannel(request.EmptyContext(s.app.Log()), channel, false) } func (s *channelsWrapper) AddUserToChannel(channelID, userID, asUserID string) (*model.ChannelMember, *model.AppError) { - ctx := request.EmptyContext(s.srv.Log()) + ctx := request.EmptyContext(s.app.Log()) channel, err := s.app.GetChannel(ctx, channelID) if err != nil { return nil, err @@ -81,11 +80,11 @@ func (s *channelsWrapper) AddUserToChannel(channelID, userID, asUserID string) ( } func (s *channelsWrapper) UpdateChannelMemberRoles(channelID, userID, newRoles string) (*model.ChannelMember, *model.AppError) { - return s.app.UpdateChannelMemberRoles(request.EmptyContext(s.srv.Log()), channelID, userID, newRoles) + return s.app.UpdateChannelMemberRoles(request.EmptyContext(s.app.Log()), channelID, userID, newRoles) } func (s *channelsWrapper) DeleteChannelMember(channelID, userID string) *model.AppError { - return s.app.LeaveChannel(request.EmptyContext(s.srv.Log()), channelID, userID) + return s.app.LeaveChannel(request.EmptyContext(s.app.Log()), channelID, userID) } func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.ChannelMember, *model.AppError) { @@ -94,7 +93,7 @@ func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.Cha return nil, err } - return s.app.AddChannelMember(request.EmptyContext(s.srv.Log()), userID, channel, ChannelMemberOpts{ + return s.app.AddChannelMember(request.EmptyContext(s.app.Log()), userID, channel, ChannelMemberOpts{ // For now, don't allow overriding these via the plugin API. UserRequestorID: "", PostRootID: "", @@ -102,7 +101,7 @@ func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.Cha } func (s *channelsWrapper) GetDirectChannelOrCreate(userID1, userID2 string) (*model.Channel, *model.AppError) { - return s.app.GetOrCreateDirectChannel(request.EmptyContext(s.srv.Log()), userID1, userID2) + return s.app.GetOrCreateDirectChannel(request.EmptyContext(s.app.Log()), userID1, userID2) } // Ensure the wrapper implements the product service. diff --git a/app/channels.go b/app/channels.go index b22bfda2d0..470493ec60 100644 --- a/app/channels.go +++ b/app/channels.go @@ -214,7 +214,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) { pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest) services[product.ChannelKey] = &channelsWrapper{ - srv: s, app: &App{ch: ch}, } diff --git a/app/server.go b/app/server.go index 46f0b7f6a7..8358ef4986 100644 --- a/app/server.go +++ b/app/server.go @@ -241,7 +241,6 @@ func NewServer(options ...Option) (*Server, error) { app := New(ServerConnector(s.Channels())) serviceMap := map[product.ServiceKey]any{ ServerKey: s, - product.ChannelKey: &channelsWrapper{srv: s, app: app}, product.ConfigKey: s.platform, product.LicenseKey: s.licenseWrapper, product.FilestoreKey: s.platform.FileBackend(), diff --git a/shared/i18n/i18n.go b/shared/i18n/i18n.go index 747b7d9e99..1bf063546b 100644 --- a/shared/i18n/i18n.go +++ b/shared/i18n/i18n.go @@ -85,13 +85,14 @@ func GetTranslationFuncForDir(dir string) (TranslationFuncByLocal, error) { bundle := bundle.New() files, _ := os.ReadDir(dir) for _, f := range files { - if filepath.Ext(f.Name()) == ".json" { - filename := f.Name() - availableLocals[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) + if filepath.Ext(f.Name()) != ".json" { + continue + } - if err := bundle.LoadTranslationFile(filepath.Join(dir, filename)); err != nil { - return nil, err - } + filename := f.Name() + availableLocals[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) + if err := bundle.LoadTranslationFile(filepath.Join(dir, filename)); err != nil { + return nil, err } } From 94e5e4975399ef85a9a9fce2208f9f2bba517e2e Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Mon, 23 Jan 2023 16:17:24 +0400 Subject: [PATCH 13/15] Add build hash to makefile --- Makefile | 14 ++++++++++++++ config/client.go | 1 + model/version.go | 1 + 3 files changed, 16 insertions(+) diff --git a/Makefile b/Makefile index aa08613ceb..385f8e3f60 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,18 @@ endif # Playbooks BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks BUILD_PLAYBOOKS ?= false +BUILD_HASH_PLAYBOOKS = none + +ifneq ($(wildcard $(BUILD_PLAYBOOKS_DIR)/.),) + ifeq ($(BUILD_PLAYBOOKS),true) + BUILD_PLAYBOOKS = true + BUILD_HASH_PLAYBOOKS = $(shell cd $(BUILD_PLAYBOOKS_DIR) && git rev-parse HEAD) + else + BUILD_PLAYBOOKS = false + endif +else + BUILD_PLAYBOOKS = false +endif ifeq ($(BUILD_PLAYBOOKS),true) IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/) @@ -208,6 +220,8 @@ else IGNORE:=$(shell rm -f imports/playbooks_imports.go) endif +LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashPlaybooks=$(BUILD_HASH_PLAYBOOKS)" + all: run ## Alias for 'run'. -include config.override.mk diff --git a/config/client.go b/config/client.go index 047d9eef1a..589a88688b 100644 --- a/config/client.go +++ b/config/client.go @@ -225,6 +225,7 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m props["BuildEnterpriseReady"] = model.BuildEnterpriseReady props["BuildHashBoards"] = model.BuildHashBoards props["BuildBoards"] = model.BuildBoards + props["BuildHashPlaybooks"] = model.BuildHashPlaybooks props["EnableBotAccountCreation"] = strconv.FormatBool(*c.ServiceSettings.EnableBotAccountCreation) props["EnableFile"] = strconv.FormatBool(*c.LogSettings.EnableFile) diff --git a/model/version.go b/model/version.go index 6090666ca3..be9168d9b2 100644 --- a/model/version.go +++ b/model/version.go @@ -117,6 +117,7 @@ var BuildHashEnterprise string var BuildEnterpriseReady string var BuildHashBoards string var BuildBoards string +var BuildHashPlaybooks string var versionsWithoutHotFixes []string func init() { From 4bdc49d11de23a8bff80a5a43aea04d639020a14 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Mon, 23 Jan 2023 16:33:45 +0400 Subject: [PATCH 14/15] Fix potential nil dereference error --- app/server.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/server.go b/app/server.go index 8358ef4986..78cea110c4 100644 --- a/app/server.go +++ b/app/server.go @@ -263,6 +263,13 @@ func NewServer(options ...Option) (*Server, error) { return nil, errors.Wrap(err, "failed to initialize products") } + // After channel is initialized set it to the App object + channelsWrapper, ok := serviceMap[product.ChannelKey].(*channelsWrapper) + if !ok { + return nil, errors.Wrap(err, "channels product is not initialized") + } + app.ch = channelsWrapper.app.ch + // It is important to initialize the hub only after the global logger is set // to avoid race conditions while logging from inside the hub. // Step 5: Start hub in platform which the hub depends on s.Channels() (step 4) From 5bf0daceb7287591242b59a4b342152ff5159bd2 Mon Sep 17 00:00:00 2001 From: Giorgi Bochorishvili Date: Mon, 23 Jan 2023 19:34:30 +0400 Subject: [PATCH 15/15] Improve Makefile style --- Makefile | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 129379df10..570bf1823d 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,22 @@ else BUILD_BOARDS = false endif +# Playbooks +BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks +BUILD_PLAYBOOKS ?= false +BUILD_HASH_PLAYBOOKS = none + +ifneq ($(wildcard $(BUILD_PLAYBOOKS_DIR)/.),) + ifeq ($(BUILD_PLAYBOOKS),true) + BUILD_PLAYBOOKS = true + BUILD_HASH_PLAYBOOKS = $(shell cd $(BUILD_PLAYBOOKS_DIR) && git rev-parse HEAD) + else + BUILD_PLAYBOOKS = false + endif +else + BUILD_PLAYBOOKS = false +endif + # We need current user's UID for `run-haserver` so docker compose does not run server # as root and mess up file permissions for devs. When running like this HOME will be blank # and docker will add '/', so we need to set the go-build cache location or we'll get @@ -116,6 +132,7 @@ LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashEnterpr LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)" LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashBoards=$(BUILD_HASH_BOARDS)" LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildBoards=$(BUILD_BOARDS)" +LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashPlaybooks=$(BUILD_HASH_PLAYBOOKS)" GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) @@ -198,30 +215,12 @@ else IGNORE:=$(shell rm -f imports/boards_imports.go) endif -# Playbooks -BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks -BUILD_PLAYBOOKS ?= false -BUILD_HASH_PLAYBOOKS = none - -ifneq ($(wildcard $(BUILD_PLAYBOOKS_DIR)/.),) - ifeq ($(BUILD_PLAYBOOKS),true) - BUILD_PLAYBOOKS = true - BUILD_HASH_PLAYBOOKS = $(shell cd $(BUILD_PLAYBOOKS_DIR) && git rev-parse HEAD) - else - BUILD_PLAYBOOKS = false - endif -else - BUILD_PLAYBOOKS = false -endif - ifeq ($(BUILD_PLAYBOOKS),true) IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/) else IGNORE:=$(shell rm -f imports/playbooks_imports.go) endif -LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashPlaybooks=$(BUILD_HASH_PLAYBOOKS)" - all: run ## Alias for 'run'. -include config.override.mk