Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
73
webapp/scripts/build.js
Обычный файл
73
webapp/scripts/build.js
Обычный файл
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const chalk = require('chalk');
|
||||
const concurrently = require('concurrently');
|
||||
|
||||
const {getPlatformCommands} = require('./utils.js');
|
||||
|
||||
async function buildAll() {
|
||||
console.log(chalk.inverse.bold('Building subpackages...') + '\n');
|
||||
|
||||
try {
|
||||
const {result} = concurrently(
|
||||
getPlatformCommands('build'),
|
||||
{
|
||||
killOthers: 'failure',
|
||||
},
|
||||
);
|
||||
|
||||
await result;
|
||||
} catch (e) {
|
||||
console.error(chalk.inverse.bold.red('Failed to build subpackages'), e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n' + chalk.inverse.bold('Subpackages built! Building web app...') + '\n');
|
||||
|
||||
// It's not necessary to run these commands through concurrently, but it makes the output consistent
|
||||
|
||||
try {
|
||||
const {result} = concurrently([
|
||||
{command: 'npm:build --workspace=channels', name: 'webapp', prefixColor: 'cyan'},
|
||||
]);
|
||||
await result;
|
||||
} catch (e) {
|
||||
console.error(chalk.inverse.bold.red('Failed to build web app'), e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n' + chalk.inverse.bold('Web app built! '));
|
||||
|
||||
console.log(chalk.inverse.bold('Building Boards...') + '\n');
|
||||
|
||||
try {
|
||||
const {result} = concurrently([
|
||||
{command: 'npm:build --workspace=boards', name: 'boards', prefixColor: 'blue'},
|
||||
]);
|
||||
await result;
|
||||
} catch (e) {
|
||||
console.error(chalk.inverse.bold.red('Failed to build Boards'), e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n' + chalk.inverse.bold('Boards built! '));
|
||||
|
||||
console.log(chalk.inverse.bold('Building Playbooks...') + '\n');
|
||||
|
||||
try {
|
||||
const {result} = concurrently([
|
||||
{command: 'npm:build --workspace=playbooks', name: 'playbooks', prefixColor: 'red'},
|
||||
]);
|
||||
await result;
|
||||
} catch (e) {
|
||||
console.error(chalk.inverse.bold.red('Failed to build Playbooks'), e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n' + chalk.inverse.bold('Playbooks built! '));
|
||||
}
|
||||
|
||||
buildAll();
|
||||
33
webapp/scripts/dev-server.js
Обычный файл
33
webapp/scripts/dev-server.js
Обычный файл
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const chalk = require('chalk');
|
||||
const concurrently = require('concurrently');
|
||||
|
||||
const {getWorkspaceCommands} = require('./utils.js');
|
||||
|
||||
async function watchAllWithDevServer() {
|
||||
console.log(chalk.inverse.bold('Watching web app and all subpackages...'));
|
||||
|
||||
const commands = [
|
||||
{command: 'npm:dev-server:webapp', name: 'webapp', prefixColor: 'cyan'},
|
||||
{command: 'npm:start:product --workspace=boards', name: 'boards', prefixColor: 'blue'},
|
||||
{command: 'npm:start:product --workspace=playbooks', name: 'playbooks', prefixColor: 'red'},
|
||||
];
|
||||
|
||||
commands.push(...getWorkspaceCommands('run'));
|
||||
|
||||
console.log('\n');
|
||||
|
||||
const {result} = concurrently(
|
||||
commands,
|
||||
{
|
||||
killOthers: 'failure',
|
||||
},
|
||||
);
|
||||
await result;
|
||||
}
|
||||
|
||||
watchAllWithDevServer();
|
||||
51
webapp/scripts/run.js
Обычный файл
51
webapp/scripts/run.js
Обычный файл
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable no-console, no-process-env */
|
||||
|
||||
const chalk = require('chalk');
|
||||
const concurrently = require('concurrently');
|
||||
|
||||
const {makeRunner} = require('./runner.js');
|
||||
const {getPlatformCommands} = require('./utils.js');
|
||||
|
||||
async function watchAll(useRunner) {
|
||||
if (!useRunner) {
|
||||
console.log(chalk.inverse.bold('Watching web app and all subpackages...'));
|
||||
}
|
||||
|
||||
const commands = [
|
||||
{command: 'npm:run --workspace=channels', name: 'webapp', prefixColor: 'cyan'},
|
||||
{command: 'npm:start:product --workspace=boards', name: 'boards', prefixColor: 'blue'},
|
||||
{command: 'npm:start:product --workspace=playbooks', name: 'playbooks', prefixColor: 'red'},
|
||||
];
|
||||
|
||||
commands.push(...getPlatformCommands('run'));
|
||||
|
||||
let runner;
|
||||
if (useRunner) {
|
||||
runner = makeRunner(commands);
|
||||
}
|
||||
|
||||
console.log('\n');
|
||||
|
||||
const {result, commands: runningCommands} = concurrently(
|
||||
commands,
|
||||
{
|
||||
killOthers: 'failure',
|
||||
outputStream: runner?.getOutputStream(),
|
||||
},
|
||||
);
|
||||
|
||||
runner?.addCloseListener(() => {
|
||||
for (const command of runningCommands) {
|
||||
command.kill('SIGINT');
|
||||
}
|
||||
});
|
||||
|
||||
await result;
|
||||
}
|
||||
|
||||
const useRunner = process.argv[2] === '--runner' || process.env.MM_USE_WEBAPP_RUNNER;
|
||||
|
||||
watchAll(useRunner);
|
||||
238
webapp/scripts/runner.js
Обычный файл
238
webapp/scripts/runner.js
Обычный файл
@@ -0,0 +1,238 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const {Writable} = require('stream');
|
||||
|
||||
const blessed = require('blessed');
|
||||
const stripAnsi = require('strip-ansi');
|
||||
|
||||
class Runner {
|
||||
commands;
|
||||
filter = '';
|
||||
ui;
|
||||
|
||||
scrollLocked = true;
|
||||
|
||||
buffer = [];
|
||||
partialBuffer = '';
|
||||
|
||||
outputStream;
|
||||
|
||||
closeListeners = new Set();
|
||||
|
||||
constructor(commands) {
|
||||
this.commands = commands;
|
||||
this.outputStream = new Writable({
|
||||
write: (chunk, encoding, callback) => this.writeToStream(chunk, encoding, callback),
|
||||
});
|
||||
|
||||
this.makeUi(commands.map((command) => command.name), this.onFilter);
|
||||
this.registerHotkeys();
|
||||
}
|
||||
|
||||
// Initialization
|
||||
|
||||
makeUi(commandNames) {
|
||||
// Set up screen and output panes
|
||||
const screen = blessed.screen({
|
||||
smartCSR: true,
|
||||
dockBorders: true,
|
||||
});
|
||||
|
||||
const output = blessed.box({
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%-3',
|
||||
content: 'THE END IS NEVER '.repeat(1000),
|
||||
tags: true,
|
||||
alwaysScroll: true,
|
||||
scrollable: true,
|
||||
scrollbar: {
|
||||
ch: '#',
|
||||
style: {},
|
||||
track: {
|
||||
ch: '|',
|
||||
},
|
||||
},
|
||||
style: {},
|
||||
});
|
||||
|
||||
screen.append(output);
|
||||
|
||||
// Set up the menu bar
|
||||
const menu = blessed.listbar({
|
||||
top: '100%-3',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: 3,
|
||||
border: {
|
||||
type: 'line',
|
||||
},
|
||||
style: {
|
||||
item: {
|
||||
bg: 'red',
|
||||
hover: {
|
||||
bg: 'green',
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
bg: 'blue',
|
||||
},
|
||||
},
|
||||
tags: true,
|
||||
autoCommandKeys: true,
|
||||
mouse: true,
|
||||
});
|
||||
|
||||
menu.add('All', () => this.onFilter(''));
|
||||
for (const name of commandNames) {
|
||||
menu.add(name, () => this.onFilter(name));
|
||||
}
|
||||
|
||||
screen.append(menu);
|
||||
|
||||
this.ui = {
|
||||
menu,
|
||||
output,
|
||||
screen,
|
||||
};
|
||||
}
|
||||
|
||||
registerHotkeys() {
|
||||
this.ui.screen.key(['escape', 'q', 'C-c'], () => {
|
||||
for (const listener of this.closeListeners) {
|
||||
listener();
|
||||
}
|
||||
});
|
||||
|
||||
this.ui.screen.key(['up', 'down'], (char, key) => {
|
||||
this.scrollDelta(key.name === 'up' ? -1 : 1);
|
||||
});
|
||||
this.ui.screen.on('wheelup', () => {
|
||||
this.scrollDelta(-3);
|
||||
});
|
||||
this.ui.screen.on('wheeldown', () => {
|
||||
this.scrollDelta(3);
|
||||
});
|
||||
this.ui.screen.key('end', () => {
|
||||
this.scrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
// Rendering and internal logic
|
||||
|
||||
renderUi() {
|
||||
const filtered = this.buffer.filter((line) => this.filter === '' || line.tag === this.filter);
|
||||
|
||||
this.ui.output.setContent(filtered.map((line) => this.formatLine(line)).join('\n'));
|
||||
|
||||
if (this.scrollLocked) {
|
||||
this.ui.output.scrollbar.style.inverse = true;
|
||||
this.ui.output.setScrollPerc(100);
|
||||
} else {
|
||||
this.ui.output.scrollbar.style.inverse = false;
|
||||
}
|
||||
|
||||
this.ui.screen.render();
|
||||
}
|
||||
|
||||
formatLine(line) {
|
||||
const color = this.commands.find((command) => command.name === line.tag)?.prefixColor;
|
||||
|
||||
return color ? `{bold}{${color}-fg}[${line.tag}]{/} ${line.text}` : `[${line.tag}] ${line.text}`;
|
||||
}
|
||||
|
||||
onFilter(newFilter) {
|
||||
this.filter = newFilter;
|
||||
|
||||
this.scrollLocked = true;
|
||||
this.renderUi();
|
||||
}
|
||||
|
||||
scrollDelta(delta) {
|
||||
this.ui.output.scroll(delta);
|
||||
|
||||
if (this.ui.output.getScrollPerc() >= 100 || this.ui.output.getScrollHeight() <= this.ui.output.height) {
|
||||
this.scrollLocked = true;
|
||||
} else {
|
||||
this.scrollLocked = false;
|
||||
}
|
||||
|
||||
this.renderUi();
|
||||
}
|
||||
|
||||
scrollToBottom() {
|
||||
this.scrollLocked = true;
|
||||
this.renderUi();
|
||||
}
|
||||
|
||||
// Terminal output handling
|
||||
|
||||
getOutputStream() {
|
||||
return this.outputStream;
|
||||
}
|
||||
|
||||
writeToStream(chunk, encoding, callback) {
|
||||
const str = String(chunk);
|
||||
|
||||
if (str.includes('\n')) {
|
||||
const parts = str.split('\n');
|
||||
|
||||
// Add completed lines to buffer
|
||||
this.appendToBuffer(this.partialBuffer + parts[0]);
|
||||
|
||||
for (let i = 1; i < parts.length - 1; i++) {
|
||||
this.appendToBuffer(parts[i]);
|
||||
}
|
||||
|
||||
// Track partial line
|
||||
this.partialBuffer = parts[parts.length - 1];
|
||||
} else {
|
||||
// Track partial line
|
||||
this.partialBuffer += str;
|
||||
}
|
||||
|
||||
this.renderUi();
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
appendToBuffer(line) {
|
||||
// This regex is more complicated than expected because it
|
||||
const match = (/^\[([^\]]*)\]\s*(.*)$/).exec(stripAnsi(line));
|
||||
|
||||
if (match) {
|
||||
this.buffer.push({tag: match[1], text: match[2]});
|
||||
} else {
|
||||
this.buffer.push({tag: '', text: 'Line not recognized correctly: ' + line});
|
||||
}
|
||||
|
||||
// Keep the buffer from using too much memory by removing the oldest chunk of it every time it goes over 5000 lines
|
||||
const bufferCapacity = 5000;
|
||||
const capacityReduction = 1000;
|
||||
|
||||
if (this.buffer.length > bufferCapacity) {
|
||||
this.buffer = this.buffer.slice(this.buffer.length - capacityReduction);
|
||||
}
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
addCloseListener(listener) {
|
||||
this.closeListeners.add(listener);
|
||||
}
|
||||
|
||||
removeCloseListener(listener) {
|
||||
this.closeListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
function makeRunner(commands) {
|
||||
const runner = new Runner(commands);
|
||||
|
||||
runner.renderUi();
|
||||
|
||||
return runner;
|
||||
}
|
||||
exports.makeRunner = makeRunner;
|
||||
14
webapp/scripts/skip_integrity_check.js
Обычный файл
14
webapp/scripts/skip_integrity_check.js
Обычный файл
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const fs = require('fs');
|
||||
const content = JSON.parse(fs.readFileSync('package-lock.json', 'utf-8'));
|
||||
|
||||
// Skip integrity check for mmjstool, which differs on Apple Silicon M1.
|
||||
// @see https://github.com/npm/cli/issues/2846
|
||||
delete content.dependencies.mmjstool.integrity;
|
||||
delete content.packages['node_modules/mmjstool'].integrity;
|
||||
delete content.dependencies.marked.integrity;
|
||||
delete content.packages['node_modules/marked'].integrity;
|
||||
|
||||
fs.writeFileSync('package-lock.json', JSON.stringify(content, null, 2) + '\n');
|
||||
47
webapp/scripts/utils.js
Обычный файл
47
webapp/scripts/utils.js
Обычный файл
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const chalk = require('chalk');
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
function getWorkspaces() {
|
||||
return packageJson.workspaces;
|
||||
}
|
||||
|
||||
function getPlatformPackagesContainingCommand(scriptName) {
|
||||
return getWorkspaces().filter((workspace) => {
|
||||
if (!workspace.startsWith('platform/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line global-require
|
||||
const workspacePackageJson = require(path.join(__dirname, '..', workspace, 'package.json'));
|
||||
|
||||
return workspacePackageJson?.scripts?.[scriptName];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of concurrently commands to run a given script on every platform workspace that contains it.
|
||||
*/
|
||||
function getPlatformCommands(scriptName) {
|
||||
return getPlatformPackagesContainingCommand(scriptName).map((workspace) => ({
|
||||
command: `npm:${scriptName} --workspace=${workspace}`,
|
||||
name: workspace.substring(workspace.lastIndexOf('/') + 1),
|
||||
prefixColor: getColorForWorkspace(workspace),
|
||||
}));
|
||||
}
|
||||
|
||||
const workspaceColors = ['green', 'magenta', 'yellow', 'red', 'blue'];
|
||||
function getColorForWorkspace(workspace) {
|
||||
const index = getWorkspaces().indexOf(workspace);
|
||||
|
||||
return index === -1 ? chalk.white : workspaceColors[index % workspaceColors.length];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getPlatformCommands,
|
||||
};
|
||||
Ссылка в новой задаче
Block a user