Swap out the complicated canary stuff for serial execution

Fixes https://github.com/vector-im/riot-web/issues/7386
This commit is contained in:
Travis Ralston 2018-09-25 15:49:14 -06:00
parent 27c23058dc
commit 91304e70a1
5 changed files with 33 additions and 155 deletions

View file

@ -1,46 +0,0 @@
const path = require('path');
const chokidar = require('chokidar');
// This script waits for a signal that an underlying SDK (js or react) has finished
// enough of the build process to be safe to rely on. In riot-web's case, this means
// that the underlying SDK has finished an initial build and is getting ready to watch
// for changes. This is done through use of a canary file that is deleted when it is
// safe to continue (see build-watch-sdk.js for why we listen for a delete event).
// Why do we block in the first place? Because if riot-web starts it's initial
// build (via webpack-dev-server) and the react-sdk or js-sdk are halfway through
// their initial builds, then riot-web's initial build fails out of the box. This
// can sometimes be corrected by waiting for the SDK build to complete and triggering
// a file change, thereby causing a cascading build, however it isn't great if the
// initial build of riot-web fails out of the box. We block at the js-sdk first so
// that the react-sdk build doesn't fall victim to the same problem, which also
// slows down the riot-web build. After the js-sdk completes, we start the react-sdk
// build which riot-web is waiting for. When complete, riot-web starts building as
// per normal.
function waitForCanary(canaryName) {
return new Promise((resolve, reject) => {
const filename = path.resolve(path.join(".tmp", canaryName + ".canary"));
// See build-watch-sdk.js for why we listen for 'unlink' specifically.
const watcher = chokidar.watch(filename).on('unlink', (path) => {
console.log("[block-on-build] Received signal to start watching for builds");
watcher.close();
resolve();
});
});
}
const sdkName = process.argv[2];
if (!sdkName) {
console.error("[block-on-build] No SDK name provided");
process.exit(1);
}
console.log("[block-on-build] Waiting for SDK: " + sdkName);
waitForCanary(sdkName).then(() => {
console.log("[block-on-build] Unblocked");
process.exit(0);
});

View file

@ -1,99 +0,0 @@
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const task = process.argv[2];
if (task !== "build" && task !== "watch") {
console.error("Expected a task of 'build' or 'watch'");
process.exit(1);
}
const sdkName = process.argv[3];
if (!sdkName) {
console.error("Missing SDK name");
process.exit(1);
}
const sdkPath = path.dirname(require.resolve(`matrix-${sdkName}-sdk/package.json`));
// Note: we intentionally create then delete the canary file to work
// around a file watching problem where if the file exists on startup it
// may fire a "created" event for the file. By having the behaviour be "do
// something on delete" we avoid accidentally firing the signal too early.
// We also need to ensure the create and delete events are not too close
// together, otherwise the filesystem may not fire the watcher. Therefore
// we create the canary as early as possible and delete it as late as possible.
prepareCanarySignal(sdkName);
// We only want to build the SDK if it looks like it was `npm link`ed
if (fs.existsSync(path.join(sdkPath, '.git'))) {
// Install the develop dependencies just in case they were forgotten by the developer.
console.log("Installing develop dependencies");
const devEnv = Object.assign({}, process.env, {NODE_ENV: "development"});
child_process.execSync("npm install --only=dev", {
env: devEnv,
cwd: sdkPath,
});
// Because webpack is made of fail
if (sdkName === "js") {
console.log("Installing source-map-loader");
child_process.execSync("npm install source-map-loader", {
env: devEnv,
cwd: sdkPath,
});
}
// Prepare an initial build of the SDK
child_process.execSync("npm run start:init", {
env: process.env,
cwd: sdkPath,
});
// Send a signal to unblock the build for other processes. Used by block-on-sdk-build.js
console.log("Sending signal that other processes may unblock");
triggerCanarySignal(sdkName);
// Actually start the watcher process for the SDK (without an initial build)
console.log("Performing task: " + task);
const watchTask = sdkName === 'js' ? "start:watch" : "start:all";
const buildTask = "build";
child_process.execSync(`npm run ${task === "build" ? buildTask : watchTask}`, {
env: process.env,
cwd: sdkPath,
});
} else triggerCanarySignal(sdkName);
function triggerCanarySignal(sdkName) {
fs.unlinkSync(getCanaryPath(sdkName));
}
function prepareCanarySignal(sdkName) {
const canaryPath = getCanaryPath(sdkName);
const canaryDir = path.dirname(canaryPath);
try {
console.log("Creating canary temp path...");
fs.mkdirSync(canaryDir);
} catch (e) {
if (e.code !== 'EEXIST') {
console.error(e);
throw "Failed to create temporary directory";
}
}
try {
console.log("Creating canary file: " + canaryPath);
fs.closeSync(fs.openSync(canaryPath, 'w'));
} catch (e) {
if (e.code !== 'EEXIST') {
console.error(e);
throw "Failed to create canary file";
}
}
}
function getCanaryPath(sdkName) {
return path.join(path.resolve(".tmp"), sdkName + ".canary");
}

21
scripts/npm-sub.js Normal file
View file

@ -0,0 +1,21 @@
const path = require('path');
const child_process = require('child_process');
const moduleName = process.argv[2];
if (!moduleName) {
console.error("Expected module name");
process.exit(1);
}
const argString = process.argv.length > 3 ? process.argv.slice(3).join(" ") : "";
if (!argString) {
console.error("Expected an npm argument string to use");
process.exit(1);
}
const modulePath = path.dirname(require.resolve(`${moduleName}/package.json`));
child_process.execSync("npm " + argString, {
env: process.env,
cwd: modulePath,
});