Remove Webpack dev servers for Boards/Playbooks (#23378)
* Add option to run web app build without dev servers * Completely remove product dev servers * Update unit test * Fix another test
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e1a2443f1a
Коммит
4cbf6e93d2
@@ -8,7 +8,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -132,25 +131,6 @@ func generateDevCSP(c Context) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add flags for Webpack dev servers used by other products during development
|
|
||||||
if model.BuildNumber == "dev" {
|
|
||||||
boardsURL := os.Getenv("MM_BOARDS_DEV_SERVER_URL")
|
|
||||||
if boardsURL == "" {
|
|
||||||
// Focalboard runs on http://localhost:9006 by default
|
|
||||||
boardsURL = "http://localhost:9006"
|
|
||||||
}
|
|
||||||
|
|
||||||
devCSP = append(devCSP, boardsURL)
|
|
||||||
|
|
||||||
playbooksURL := os.Getenv("MM_PLAYBOOKS_DEV_SERVER_URL")
|
|
||||||
if playbooksURL == "" {
|
|
||||||
// Playbooks runs on http://localhost:9007 by default
|
|
||||||
playbooksURL = "http://localhost:9007"
|
|
||||||
}
|
|
||||||
|
|
||||||
devCSP = append(devCSP, playbooksURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(devCSP) == 0 {
|
if len(devCSP) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ func TestHandlerServeCSPHeader(t *testing.T) {
|
|||||||
response := httptest.NewRecorder()
|
response := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(response, request)
|
handler.ServeHTTP(response, request)
|
||||||
assert.Equal(t, 200, response.Code)
|
assert.Equal(t, 200, response.Code)
|
||||||
assert.Equal(t, []string{"frame-ancestors 'self'; script-src 'self' cdn.rudderlabs.com js.stripe.com/v3 'unsafe-eval' 'unsafe-inline' http://localhost:9006 http://localhost:9007"}, response.Header()["Content-Security-Policy"])
|
assert.Equal(t, []string{"frame-ancestors 'self'; script-src 'self' cdn.rudderlabs.com js.stripe.com/v3 'unsafe-eval' 'unsafe-inline'"}, response.Header()["Content-Security-Policy"])
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -472,7 +472,7 @@ func TestGenerateDevCSP(t *testing.T) {
|
|||||||
|
|
||||||
devCSP := generateDevCSP(*c)
|
devCSP := generateDevCSP(*c)
|
||||||
|
|
||||||
assert.Equal(t, " 'unsafe-eval' 'unsafe-inline' http://localhost:9006 http://localhost:9007", devCSP)
|
assert.Equal(t, " 'unsafe-eval' 'unsafe-inline'", devCSP)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("allowed dev flags", func(t *testing.T) {
|
t.Run("allowed dev flags", func(t *testing.T) {
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode=production",
|
"build": "webpack --mode=production",
|
||||||
"build:watch": "webpack --mode=production --watch",
|
"build:watch": "webpack --mode=production --watch",
|
||||||
"start:product": "webpack serve --mode=development",
|
"start:product": "webpack --mode=development --watch",
|
||||||
"debug": "webpack --mode=none",
|
|
||||||
"debug:watch": "webpack --mode=development --watch",
|
|
||||||
"check-lint": "eslint --ignore-pattern ../.git-ignore --ignore-pattern dist --ext js,jsx,tsx,ts . --quiet --cache",
|
"check-lint": "eslint --ignore-pattern ../.git-ignore --ignore-pattern dist --ext js,jsx,tsx,ts . --quiet --cache",
|
||||||
"check-lint:fix": "npm run check-lint -- --fix",
|
"check-lint:fix": "npm run check-lint -- --fix",
|
||||||
"check-style": "stylelint **/*.scss",
|
"check-style": "stylelint **/*.scss",
|
||||||
|
|||||||
@@ -186,36 +186,4 @@ config.plugins.push(new MiniCssExtractPlugin({
|
|||||||
chunkFilename: '[name].[contenthash].css',
|
chunkFilename: '[name].[contenthash].css',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (NPM_TARGET === 'start:product') {
|
|
||||||
const url = new URL(process.env.MM_BOARDS_DEV_SERVER_URL ?? 'http://localhost:9006');
|
|
||||||
|
|
||||||
const protocol = url.protocol.substring(0, url.protocol.length - 1);
|
|
||||||
const hostname = url.hostname;
|
|
||||||
let port = url.port;
|
|
||||||
if (!port) {
|
|
||||||
port = protocol === 'https' ? '443' : '80';
|
|
||||||
}
|
|
||||||
|
|
||||||
config.devServer = {
|
|
||||||
server: {
|
|
||||||
type: protocol,
|
|
||||||
options: {
|
|
||||||
minVersion: process.env.MM_SERVICESETTINGS_TLSMINVER ?? 'TLSv1.2',
|
|
||||||
key: process.env.MM_SERVICESETTINGS_TLSKEYFILE,
|
|
||||||
cert: process.env.MM_SERVICESETTINGS_TLSCERTFILE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
host: hostname,
|
|
||||||
port,
|
|
||||||
devMiddleware: {
|
|
||||||
writeToDisk: false,
|
|
||||||
},
|
|
||||||
static: {
|
|
||||||
directory: path.join(__dirname, 'static'),
|
|
||||||
publicPath: '/static',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/* eslint-enable no-process-env */
|
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
/* eslint-disable no-console, no-process-env */
|
/* eslint-disable no-console, no-process-env */
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
@@ -29,9 +30,6 @@ const targetIsEslint = NPM_TARGET === 'check' || NPM_TARGET === 'fix' || process
|
|||||||
|
|
||||||
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
|
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
|
||||||
|
|
||||||
const boardsDevServerUrl = process.env.MM_BOARDS_DEV_SERVER_URL ?? 'http://localhost:9006';
|
|
||||||
const playbooksDevServerUrl = process.env.MM_PLAYBOOKS_DEV_SERVER_URL ?? 'http://localhost:9007';
|
|
||||||
|
|
||||||
const STANDARD_EXCLUDE = [
|
const STANDARD_EXCLUDE = [
|
||||||
/node_modules/,
|
/node_modules/,
|
||||||
];
|
];
|
||||||
@@ -280,16 +278,34 @@ var config = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (DEV) {
|
||||||
|
config.plugins.push({
|
||||||
|
apply: (compiler) => {
|
||||||
|
compiler.hooks.afterEmit.tap('AfterEmitPlugin', () => {
|
||||||
|
const boardsDist = path.resolve(__dirname, '../boards/dist');
|
||||||
|
const boardsSymlink = './dist/products/boards';
|
||||||
|
const playbooksDist = path.resolve(__dirname, '../playbooks/dist');
|
||||||
|
const playbooksSymlink = './dist/products/playbooks';
|
||||||
|
|
||||||
|
fs.mkdir('./dist/products', () => {
|
||||||
|
if (!fs.existsSync(boardsSymlink)) {
|
||||||
|
fs.symlinkSync(boardsDist, boardsSymlink, 'dir');
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(playbooksSymlink)) {
|
||||||
|
fs.symlinkSync(playbooksDist, playbooksSymlink, 'dir');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function generateCSP() {
|
function generateCSP() {
|
||||||
let csp = 'script-src \'self\' cdn.rudderlabs.com/ js.stripe.com/v3';
|
let csp = 'script-src \'self\' cdn.rudderlabs.com/ js.stripe.com/v3';
|
||||||
|
|
||||||
if (DEV) {
|
if (DEV) {
|
||||||
// react-hot-loader and development source maps require eval
|
// react-hot-loader and development source maps require eval
|
||||||
csp += ' \'unsafe-eval\'';
|
csp += ' \'unsafe-eval\'';
|
||||||
|
|
||||||
csp += ' ' + boardsDevServerUrl;
|
|
||||||
|
|
||||||
csp += ' ' + playbooksDevServerUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return csp;
|
return csp;
|
||||||
@@ -321,42 +337,19 @@ async function initializeModuleFederation() {
|
|||||||
|
|
||||||
async function getRemoteContainers() {
|
async function getRemoteContainers() {
|
||||||
const products = [
|
const products = [
|
||||||
{name: 'boards', baseUrl: boardsDevServerUrl},
|
{name: 'boards'},
|
||||||
{name: 'playbooks', baseUrl: playbooksDevServerUrl},
|
{name: 'playbooks'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const remotes = {};
|
const remotes = {};
|
||||||
|
|
||||||
if (process.env.MM_DONT_INCLUDE_PRODUCTS) {
|
|
||||||
console.warn('Skipping initialization of products');
|
|
||||||
} else if (DEV) {
|
|
||||||
// For development, we use Webpack dev servers for each product
|
|
||||||
for (const product of products) {
|
|
||||||
remotes[product.name] = `${product.name}@${product.baseUrl}/remote_entry.js`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// For production, hardcode the URLs of product containers to be based on the web app URL
|
|
||||||
for (const product of products) {
|
for (const product of products) {
|
||||||
remotes[product.name] = `${product.name}@[window.basename]/static/products/${product.name}/remote_entry.js?bt=${buildTimestamp}`;
|
remotes[product.name] = `${product.name}@[window.basename]/static/products/${product.name}/remote_entry.js?bt=${buildTimestamp}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {remotes};
|
||||||
}
|
}
|
||||||
|
|
||||||
const aliases = {};
|
const {remotes} = await getRemoteContainers();
|
||||||
|
|
||||||
for (const product of products) {
|
|
||||||
if (remotes[product.name]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add false aliases to prevent Webpack from trying to resolve the missing modules
|
|
||||||
aliases[product.name] = false;
|
|
||||||
aliases[`${product.name}/manifest`] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {remotes, aliases};
|
|
||||||
}
|
|
||||||
|
|
||||||
const {remotes, aliases} = await getRemoteContainers();
|
|
||||||
|
|
||||||
const moduleFederationPluginOptions = {
|
const moduleFederationPluginOptions = {
|
||||||
name: 'mattermost_webapp',
|
name: 'mattermost_webapp',
|
||||||
@@ -404,11 +397,6 @@ async function initializeModuleFederation() {
|
|||||||
// Add this plugin to perform the substitution of window.basename when loading remote containers
|
// Add this plugin to perform the substitution of window.basename when loading remote containers
|
||||||
config.plugins.push(new ExternalTemplateRemotesPlugin());
|
config.plugins.push(new ExternalTemplateRemotesPlugin());
|
||||||
|
|
||||||
config.resolve.alias = {
|
|
||||||
...config.resolve.alias,
|
|
||||||
...aliases,
|
|
||||||
};
|
|
||||||
|
|
||||||
config.plugins.push(new webpack.DefinePlugin({
|
config.plugins.push(new webpack.DefinePlugin({
|
||||||
REMOTE_CONTAINERS: JSON.stringify(remotes),
|
REMOTE_CONTAINERS: JSON.stringify(remotes),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -108,7 +108,6 @@
|
|||||||
"build:watch": "webpack --mode=production --watch",
|
"build:watch": "webpack --mode=production --watch",
|
||||||
"debug": "webpack --mode=development",
|
"debug": "webpack --mode=development",
|
||||||
"debug:watch": "webpack --mode=development --watch",
|
"debug:watch": "webpack --mode=development --watch",
|
||||||
"dev-server": "webpack serve --mode=development",
|
|
||||||
"check": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext .js --ext .jsx --ext tsx --ext ts . --quiet --cache",
|
"check": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext .js --ext .jsx --ext tsx --ext ts . --quiet --cache",
|
||||||
"fix": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext .js --ext .jsx --ext tsx --ext ts . --quiet --fix --cache",
|
"fix": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext .js --ext .jsx --ext tsx --ext ts . --quiet --fix --cache",
|
||||||
"test": "cross-env TZ=Etc/UTC jest",
|
"test": "cross-env TZ=Etc/UTC jest",
|
||||||
@@ -120,8 +119,7 @@
|
|||||||
"i18n-extract": "formatjs extract \"src/**/*.{ts,tsx}\" --ignore \"**/*.d.ts\" --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format simple --out-file i18n/en.json",
|
"i18n-extract": "formatjs extract \"src/**/*.{ts,tsx}\" --ignore \"**/*.d.ts\" --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format simple --out-file i18n/en.json",
|
||||||
"graphql": "graphql-codegen --config graphql_gen.ts",
|
"graphql": "graphql-codegen --config graphql_gen.ts",
|
||||||
"report-unused-exports": "ts-prune",
|
"report-unused-exports": "ts-prune",
|
||||||
"build:product": "webpack --mode=production",
|
"start:product": "webpack --mode=development --watch",
|
||||||
"start:product": "webpack serve --mode=development",
|
|
||||||
"deploy:product": "node scripts/deploy.js",
|
"deploy:product": "node scripts/deploy.js",
|
||||||
"clean": "rm -rf node_modules"
|
"clean": "rm -rf node_modules"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,31 +133,4 @@ config.output = {
|
|||||||
chunkFilename: '[name].[contenthash].js',
|
chunkFilename: '[name].[contenthash].js',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (NPM_TARGET === 'start:product') {
|
|
||||||
const url = new URL(process.env.MM_PLAYBOOKS_DEV_SERVER_URL ?? 'http://localhost:9007');
|
|
||||||
|
|
||||||
const protocol = url.protocol.substring(0, url.protocol.length - 1);
|
|
||||||
const hostname = url.hostname;
|
|
||||||
let port = url.port;
|
|
||||||
if (!port) {
|
|
||||||
port = protocol === 'https' ? '443' : '80';
|
|
||||||
}
|
|
||||||
|
|
||||||
config.devServer = {
|
|
||||||
server: {
|
|
||||||
type: protocol,
|
|
||||||
options: {
|
|
||||||
minVersion: process.env.MM_SERVICESETTINGS_TLSMINVER ?? 'TLSv1.2',
|
|
||||||
key: process.env.MM_SERVICESETTINGS_TLSKEYFILE,
|
|
||||||
cert: process.env.MM_SERVICESETTINGS_TLSCERTFILE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
host: hostname,
|
|
||||||
port,
|
|
||||||
devMiddleware: {
|
|
||||||
writeToDisk: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user