MM-48120/MM-48623 Patch plugin bundle react-dom on webapp extract (#21171)

Automatic Merge
Этот коммит содержится в:
Michael Kochell
2022-12-06 12:44:48 -05:00
коммит произвёл GitHub
родитель 43e26ccda2
Коммит c3c81cb3d6
8 изменённых файлов: 93 добавлений и 13 удалений

Просмотреть файл

@@ -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"