MM-48120/MM-48623 Patch plugin bundle react-dom on webapp extract (#21171)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
43e26ccda2
Коммит
c3c81cb3d6
@@ -243,7 +243,15 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
|
||||
return New(ServerConnector(ch)).NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newAPIFunc, NewDriverImpl(ch.srv), pluginDir, webappPluginDir, ch.srv.Log(), ch.srv.GetMetrics())
|
||||
env, err := plugin.NewEnvironment(
|
||||
newAPIFunc,
|
||||
NewDriverImpl(ch.srv),
|
||||
pluginDir,
|
||||
webappPluginDir,
|
||||
*ch.cfgSvc.Config().ExperimentalSettings.PatchPluginsReactDOM,
|
||||
ch.srv.Log(),
|
||||
ch.srv.GetMetrics(),
|
||||
)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to start up plugins", mlog.Err(err))
|
||||
return
|
||||
|
||||
@@ -92,7 +92,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
|
||||
return app.NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(pluginCodes), len(pluginIDs))
|
||||
@@ -849,7 +849,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
|
||||
@@ -937,7 +937,7 @@ func TestInstallPlugin(t *testing.T) {
|
||||
return app.NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
app.ch.SetPluginsEnvironment(env)
|
||||
@@ -1632,7 +1632,7 @@ func TestAPIMetrics(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), metricsMock)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
@@ -2079,7 +2079,7 @@ func TestRegisterCollectionAndTopic(t *testing.T) {
|
||||
return th.App.NewPluginAPI(th.Context, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
@@ -2179,7 +2179,7 @@ func TestPluginUploadsAPI(t *testing.T) {
|
||||
newPluginAPI := func(manifest *model.Manifest) plugin.API {
|
||||
return th.App.NewPluginAPI(th.Context, manifest)
|
||||
}
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
|
||||
@@ -33,7 +33,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
app.ch.SetPluginsEnvironment(env)
|
||||
@@ -1030,7 +1030,7 @@ func TestHookMetrics(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), metricsMock)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
|
||||
@@ -968,6 +968,7 @@ type ExperimentalSettings struct {
|
||||
EnableSharedChannels *bool `access:"experimental_features"`
|
||||
EnableRemoteClusterService *bool `access:"experimental_features"`
|
||||
EnableAppBar *bool `access:"experimental_features"`
|
||||
PatchPluginsReactDOM *bool `access:"experimental_features"`
|
||||
}
|
||||
|
||||
func (s *ExperimentalSettings) SetDefaults() {
|
||||
@@ -1002,6 +1003,10 @@ func (s *ExperimentalSettings) SetDefaults() {
|
||||
if s.EnableAppBar == nil {
|
||||
s.EnableAppBar = NewBool(false)
|
||||
}
|
||||
|
||||
if s.PatchPluginsReactDOM == nil {
|
||||
s.PatchPluginsReactDOM = NewBool(false)
|
||||
}
|
||||
}
|
||||
|
||||
type AnalyticsSettings struct {
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -57,15 +59,20 @@ type Environment struct {
|
||||
dbDriver Driver
|
||||
pluginDir string
|
||||
webappPluginDir string
|
||||
patchReactDOM bool
|
||||
prepackagedPlugins []*PrepackagedPlugin
|
||||
prepackagedPluginsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewEnvironment(newAPIImpl apiImplCreatorFunc,
|
||||
func NewEnvironment(
|
||||
newAPIImpl apiImplCreatorFunc,
|
||||
dbDriver Driver,
|
||||
pluginDir string, webappPluginDir string,
|
||||
pluginDir string,
|
||||
webappPluginDir string,
|
||||
patchReactDOM bool,
|
||||
logger *mlog.Logger,
|
||||
metrics einterfaces.MetricsInterface) (*Environment, error) {
|
||||
metrics einterfaces.MetricsInterface,
|
||||
) (*Environment, error) {
|
||||
return &Environment{
|
||||
logger: logger,
|
||||
metrics: metrics,
|
||||
@@ -73,6 +80,7 @@ func NewEnvironment(newAPIImpl apiImplCreatorFunc,
|
||||
dbDriver: dbDriver,
|
||||
pluginDir: pluginDir,
|
||||
webappPluginDir: webappPluginDir,
|
||||
patchReactDOM: patchReactDOM,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -451,6 +459,17 @@ func (env *Environment) UnpackWebappBundle(id string) (*model.Manifest, error) {
|
||||
return nil, errors.Wrapf(err, "unable to read webapp bundle: %v", id)
|
||||
}
|
||||
|
||||
if env.patchReactDOM {
|
||||
newContents, changed := patchReactDOM(sourceBundleFileContents)
|
||||
if changed {
|
||||
sourceBundleFileContents = newContents
|
||||
err = os.WriteFile(sourceBundleFilepath, sourceBundleFileContents, 0644)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to overwrite webapp bundle: %v", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hash := fnv.New64a()
|
||||
if _, err = hash.Write(sourceBundleFileContents); err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to generate hash for webapp bundle: %v", id)
|
||||
@@ -467,6 +486,52 @@ func (env *Environment) UnpackWebappBundle(id string) (*model.Manifest, error) {
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func patchReactDOM(initialBytes []byte) ([]byte, bool) {
|
||||
if !bytes.Contains(initialBytes, []byte("react-dom.production.min.js")) {
|
||||
return initialBytes, false
|
||||
}
|
||||
|
||||
initial := string(initialBytes)
|
||||
nameIndex := strings.Index(initial, "react-dom.production.min.js")
|
||||
|
||||
beginning := strings.LastIndex(initial[:nameIndex], "{")
|
||||
var end int
|
||||
|
||||
argDefBeginning := strings.LastIndex(initial[:beginning], "function") + 9
|
||||
argDefEnd := strings.LastIndex(initial[:beginning], ")") - 1
|
||||
argsNames := strings.Split(initial[argDefBeginning:argDefEnd], ",")
|
||||
if len(argsNames) != 3 {
|
||||
return initialBytes, false
|
||||
}
|
||||
|
||||
exportsArgName := strings.TrimSpace(argsNames[1])
|
||||
|
||||
numOpenBraces := 0
|
||||
for i, c := range initial[beginning:] {
|
||||
if end != 0 {
|
||||
break
|
||||
}
|
||||
switch c {
|
||||
case '}':
|
||||
numOpenBraces--
|
||||
|
||||
if numOpenBraces == 0 {
|
||||
end = beginning + i
|
||||
}
|
||||
case '{':
|
||||
numOpenBraces++
|
||||
}
|
||||
}
|
||||
|
||||
beforePatch := initial[:end]
|
||||
afterPatch := initial[end:]
|
||||
|
||||
patch := fmt.Sprintf("; Object.assign(%s, window.ReactDOM)", exportsArgName)
|
||||
|
||||
result := fmt.Sprintf("%s%s%s", beforePatch, patch, afterPatch)
|
||||
return []byte(result), true
|
||||
}
|
||||
|
||||
// HooksForPlugin returns the hooks API for the plugin with the given id.
|
||||
//
|
||||
// Consider using RunMultiPluginHook instead.
|
||||
|
||||
@@ -734,6 +734,7 @@ func (ts *TelemetryService) trackConfig() {
|
||||
"enable_shared_channels": *cfg.ExperimentalSettings.EnableSharedChannels,
|
||||
"enable_remote_cluster_service": *cfg.ExperimentalSettings.EnableRemoteClusterService && cfg.FeatureFlags.EnableRemoteClusterService,
|
||||
"enable_app_bar": *cfg.ExperimentalSettings.EnableAppBar,
|
||||
"patch_plugins_react_dom": *cfg.ExperimentalSettings.PatchPluginsReactDOM,
|
||||
})
|
||||
|
||||
ts.SendTelemetry(TrackConfigAnalytics, map[string]any{
|
||||
|
||||
@@ -165,6 +165,7 @@ func initializeMocks(cfg *model.Config, cloudLicense bool) (*mocks.ServerIface,
|
||||
func(m *model.Manifest) plugin.API { return pluginsAPIMock },
|
||||
nil,
|
||||
pluginDir, webappPluginDir,
|
||||
false,
|
||||
logger,
|
||||
nil)
|
||||
serverIfaceMock.On("GetPluginsEnvironment").Return(pluginEnv, nil)
|
||||
|
||||
@@ -280,7 +280,7 @@ func TestPublicFilesRequest(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginID := "com.mattermost.sample"
|
||||
|
||||
Ссылка в новой задаче
Block a user