Remove Bluebird promises and use await/sync where we can. (#2205)

This commit is contained in:
Peter Hedenskog 2018-11-20 09:14:05 +01:00 committed by GitHub
parent c941b086ed
commit 7cc5562204
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 346 additions and 388 deletions

View File

@ -4,34 +4,17 @@
'use strict'; 'use strict';
const cli = require('../lib/cli/cli'), const cli = require('../lib/cli/cli');
sitespeed = require('../lib/sitespeed'), const sitespeed = require('../lib/sitespeed');
Promise = require('bluebird');
if (process.env.NODE_ENV !== 'production') { async function run(options) {
require('longjohn'); process.exitCode = 1;
} try {
const result = await sitespeed.run(options);
Promise.config({
warnings: true,
longStackTraces: true
});
process.exitCode = 1;
let parsed = cli.parseCommandLine();
let budgetFailing = false;
// hack for getting in the unchanged cli options
parsed.options.explicitOptions = parsed.explicitOptions;
parsed.options.urls = parsed.urls;
parsed.options.urlsMetaData = parsed.urlsMetaData;
sitespeed
.run(parsed.options)
.then(result => {
if (result.errors.length > 0) { if (result.errors.length > 0) {
throw new Error('Errors while running:\n' + result.errors.join('\n')); throw new Error('Errors while running:\n' + result.errors.join('\n'));
} }
if ( if (
parsed.options.budget && parsed.options.budget &&
Object.keys(result.budgetResult.failing).length > 0 Object.keys(result.budgetResult.failing).length > 0
@ -39,16 +22,24 @@ sitespeed
process.exitCode = 1; process.exitCode = 1;
budgetFailing = true; budgetFailing = true;
} }
})
.then(() => {
if ( if (
!budgetFailing || !budgetFailing ||
(parsed.options.budget && parsed.options.budget.suppressExitCode) (parsed.options.budget && parsed.options.budget.suppressExitCode)
) { ) {
process.exitCode = 0; process.exitCode = 0;
} }
}) } catch (e) {
.catch(() => {
process.exitCode = 1; process.exitCode = 1;
}) } finally {
.finally(() => process.exit()); process.exit();
}
}
let parsed = cli.parseCommandLine();
let budgetFailing = false;
// hack for getting in the unchanged cli options
parsed.options.explicitOptions = parsed.explicitOptions;
parsed.options.urls = parsed.urls;
parsed.options.urlsMetaData = parsed.urlsMetaData;
run(parsed.options);

View File

@ -1,10 +1,9 @@
'use strict'; 'use strict';
const Promise = require('bluebird'), const path = require('path');
path = require('path'), const fs = require('fs');
fs = require('fs'); const { promisify } = require('util');
const readdir = promisify(fs.readdir);
Promise.promisifyAll(fs);
const defaultPlugins = new Set([ const defaultPlugins = new Set([
'browsertime', 'browsertime',
@ -23,7 +22,7 @@ const defaultPlugins = new Set([
const pluginsDir = path.join(__dirname, '..', 'plugins'); const pluginsDir = path.join(__dirname, '..', 'plugins');
module.exports = { module.exports = {
parsePluginNames(options) { async parsePluginNames(options) {
// if we don't use the cli, this will work out fine as long // if we don't use the cli, this will work out fine as long
// we configure only what we need // we configure only what we need
const possibleConfiguredPlugins = options.explicitOptions || options; const possibleConfiguredPlugins = options.explicitOptions || options;
@ -39,31 +38,30 @@ module.exports = {
return pluginNames; return pluginNames;
}; };
return fs const files = await readdir(pluginsDir);
.readdirAsync(pluginsDir) const builtins = files.map(name => path.basename(name, '.js'));
.map(name => path.basename(name, '.js')) const plugins = builtins.filter(isDefaultOrConfigured);
.then(builtins => { return addMessageLoggerIfDebug(plugins);
let plugins = builtins.filter(isDefaultOrConfigured);
return addMessageLoggerIfDebug(plugins);
});
}, },
loadPlugins(pluginNames) { async loadPlugins(pluginNames) {
return Promise.resolve(pluginNames).map(name => { const plugins = [];
for (let name of pluginNames) {
try { try {
const plugin = require(path.join(pluginsDir, name)); const plugin = require(path.join(pluginsDir, name));
if (!plugin.name) { if (!plugin.name) {
plugin.name = () => name; plugin.name = () => name;
} }
return plugin; plugins.push(plugin);
} catch (err) { } catch (err) {
try { try {
return require(path.resolve(process.cwd(), name)); plugins.push(require(path.resolve(process.cwd(), name)));
} catch (error) { } catch (error) {
console.error("Couldn't load plugin %s: %s", name, err); // eslint-disable-line no-console console.error("Couldn't load plugin %s: %s", name, err); // eslint-disable-line no-console
// if it fails here, let it fail hard // if it fails here, let it fail hard
throw error; throw error;
} }
} }
}); }
return plugins;
} }
}; };

View File

@ -2,11 +2,10 @@
/* eslint no-console:0 */ /* eslint no-console:0 */
const cq = require('concurrent-queue'), const cq = require('concurrent-queue');
Promise = require('bluebird'), const log = require('intel').getLogger('sitespeedio.queuehandler');
log = require('intel').getLogger('sitespeedio.queuehandler'), const messageMaker = require('../support/messageMaker');
messageMaker = require('../support/messageMaker'), const queueStats = require('./queueStatistics');
queueStats = require('./queueStatistics');
const make = messageMaker('queueHandler').make; const make = messageMaker('queueHandler').make;
@ -165,7 +164,7 @@ class QueueHandler {
return { plugin, queue }; return { plugin, queue };
}); });
} }
run(sources) { async run(sources) {
/* /*
setup - plugins chance to talk to each other or setup what they need. setup - plugins chance to talk to each other or setup what they need.
url - urls passed around to analyze url - urls passed around to analyze
@ -176,7 +175,11 @@ class QueueHandler {
return this.startProcessingQueues() return this.startProcessingQueues()
.then(() => this.postMessage(make('sitespeedio.setup'))) .then(() => this.postMessage(make('sitespeedio.setup')))
.then(() => this.drainAllQueues()) .then(() => this.drainAllQueues())
.then(() => Promise.map(sources, source => source.findUrls(this))) .then(async () => {
for (let source of sources) {
await source.findUrls(this);
}
})
.then(() => this.drainAllQueues()) .then(() => this.drainAllQueues())
.then(() => this.postMessage(make('sitespeedio.summarize'))) .then(() => this.postMessage(make('sitespeedio.summarize')))
.then(() => this.drainAllQueues()) .then(() => this.drainAllQueues())
@ -186,8 +189,8 @@ class QueueHandler {
if (this.options.queueStats) { if (this.options.queueStats) {
log.info(JSON.stringify(queueStats.generateStatistics(), null, 2)); log.info(JSON.stringify(queueStats.generateStatistics(), null, 2));
} }
}) return this.errors;
.return(this.errors); });
} }
postMessage(message) { postMessage(message) {
@ -200,17 +203,17 @@ class QueueHandler {
} }
} }
startProcessingQueues() { async startProcessingQueues() {
return Promise.each(this.queues, item => { for (let item of this.queues) {
const queue = item.queue, const queue = item.queue,
plugin = item.plugin; plugin = item.plugin;
queue.process(message => queue.process(message =>
Promise.resolve(plugin.processMessage(message, this)) Promise.resolve(plugin.processMessage(message, this))
); );
}); }
} }
drainAllQueues() { async drainAllQueues() {
const queues = this.queues; const queues = this.queues;
return new Promise(resolve => { return new Promise(resolve => {
queues.forEach(item => queues.forEach(item =>

View File

@ -1,12 +1,10 @@
'use strict'; 'use strict';
const fs = require('fs-extra'); const fs = require('fs-extra');
const Promise = require('bluebird');
const path = require('path'); const path = require('path');
const pathToFolder = require('./pathToFolder'); const pathToFolder = require('./pathToFolder');
const { promisify } = require('util');
const mkdirp = Promise.promisify(require('mkdirp')); const mkdirp = promisify(require('mkdirp'));
function write(dirPath, filename, data) { function write(dirPath, filename, data) {
return fs.writeFile(path.join(dirPath, filename), data); return fs.writeFile(path.join(dirPath, filename), data);

View File

@ -1,14 +1,13 @@
'use strict'; 'use strict';
const merge = require('lodash.merge'), const merge = require('lodash.merge');
forEach = require('lodash.foreach'), const forEach = require('lodash.foreach');
path = require('path'), const path = require('path');
Promise = require('bluebird'), const browsertime = require('browsertime');
browsertime = require('browsertime'), const log = require('intel').getLogger('sitespeedio.plugin.browsertime');
log = require('intel').getLogger('sitespeedio.plugin.browsertime'), const set = require('lodash.set');
set = require('lodash.set'), const get = require('lodash.get');
get = require('lodash.get'), const coach = require('webcoach');
coach = require('webcoach');
const browserScripts = browsertime.browserScripts; const browserScripts = browsertime.browserScripts;
@ -20,44 +19,36 @@ const iphone6UserAgent =
'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' + 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' +
'(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25'; '(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';
function parseUserScripts(scripts) { async function parseUserScripts(scripts) {
if (!Array.isArray(scripts)) scripts = [scripts]; if (!Array.isArray(scripts)) scripts = [scripts];
const allUserScripts = {};
return Promise.reduce( for (let script of scripts) {
scripts, const myScript = await browserScripts.findAndParseScripts(
(results, script) => path.resolve(script),
browserScripts 'custom'
.findAndParseScripts(path.resolve(script), 'custom') );
.then(scripts => merge(results, scripts)), merge(allUserScripts, myScript);
{} }
); return allUserScripts;
} }
function addCoachScripts(scripts) { async function addCoachScripts(scripts) {
const coachAdvice = coach.getDomAdvice(); const coachAdvice = await coach.getDomAdvice();
return Promise.join(scripts, coachAdvice, (scripts, advice) => { scripts.coach = {
scripts.coach = { coachAdvice: coachAdvice
coachAdvice: advice };
}; return scripts;
return scripts;
});
} }
function addExtraScripts(scriptsByCategory, pluginScripts) { function addExtraScripts(scriptsByCategory, pluginScripts) {
return Promise.join( // For all different script in the array
scriptsByCategory, for (let scripts of pluginScripts) {
pluginScripts, // and then for all scripts in that category
(scriptsByCategory, pluginScripts) => { forEach(scripts.scripts, function(script, name) {
// For all different script in the array set(scriptsByCategory, scripts.category + '.' + name, script);
for (var scripts of pluginScripts) { });
// and then for all scripts in that category }
forEach(scripts.scripts, function(script, name) { return scriptsByCategory;
set(scriptsByCategory, scripts.category + '.' + name, script);
});
}
return scriptsByCategory;
}
);
} }
function setupAsynScripts(asyncScripts) { function setupAsynScripts(asyncScripts) {
@ -93,18 +84,13 @@ module.exports = {
} }
} }
const scriptCategories = await browserScripts.allScriptCategories; const scriptCategories = await browserScripts.allScriptCategories;
let scriptsByCategory = browserScripts.getScriptsForCategories( let scriptsByCategory = await browserScripts.getScriptsForCategories(
scriptCategories scriptCategories
); );
if (btOptions.script) { if (btOptions.script) {
const userScripts = parseUserScripts(btOptions.script); const userScripts = await parseUserScripts(btOptions.script);
scriptsByCategory = await Promise.join( scriptsByCategory = merge(scriptsByCategory, userScripts);
scriptsByCategory,
userScripts,
(scriptsByCategory, userScripts) =>
merge(scriptsByCategory, userScripts)
);
} }
if (btOptions.coach) { if (btOptions.coach) {

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const Promise = require('bluebird');
const path = require('path'); const path = require('path');
const merge = require('lodash.merge'); const merge = require('lodash.merge');
const log = require('intel').getLogger('sitespeedio.plugin.crawler'); const log = require('intel').getLogger('sitespeedio.plugin.crawler');

View File

@ -2,7 +2,6 @@
const http = require('http'); const http = require('http');
const https = require('https'); const https = require('https');
const log = require('intel').getLogger('sitespeedio.plugin.grafana'); const log = require('intel').getLogger('sitespeedio.plugin.grafana');
const Promise = require('bluebird');
const tsdbUtil = require('../../support/tsdbUtil'); const tsdbUtil = require('../../support/tsdbUtil');
const annotationsHelper = require('../../support/annotationsHelper'); const annotationsHelper = require('../../support/annotationsHelper');
const util = require('../../support/util'); const util = require('../../support/util');

View File

@ -1,8 +1,7 @@
'use strict'; 'use strict';
const net = require('net'), const net = require('net');
Promise = require('bluebird'), const Sender = require('./sender');
Sender = require('./sender');
class GraphiteSender extends Sender { class GraphiteSender extends Sender {
get facility() { get facility() {

View File

@ -2,7 +2,6 @@
const http = require('http'); const http = require('http');
const https = require('https'); const https = require('https');
const log = require('intel').getLogger('sitespeedio.plugin.graphite'); const log = require('intel').getLogger('sitespeedio.plugin.graphite');
const Promise = require('bluebird');
const graphiteUtil = require('../../support/tsdbUtil'); const graphiteUtil = require('../../support/tsdbUtil');
const annotationsHelper = require('../../support/annotationsHelper'); const annotationsHelper = require('../../support/annotationsHelper');
const util = require('../../support/util'); const util = require('../../support/util');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const Promise = require('bluebird'), const log = require('intel').getLogger('sitespeedio.plugin.graphite');
log = require('intel').getLogger('sitespeedio.plugin.graphite');
class Sender { class Sender {
constructor(host, port, bulkSize) { constructor(host, port, bulkSize) {

View File

@ -1,8 +1,7 @@
'use strict'; 'use strict';
const dgram = require('dgram'), const dgram = require('dgram');
Promise = require('bluebird'), const Sender = require('./sender');
Sender = require('./sender');
class StatsDSender extends Sender { class StatsDSender extends Sender {
get facility() { get facility() {

View File

@ -1,9 +1,8 @@
'use strict'; 'use strict';
const Promise = require('bluebird');
const zlib = require('zlib'); const zlib = require('zlib');
const { promisify } = require('util');
Promise.promisifyAll(zlib); const gzip = promisify(zlib.gzip);
module.exports = { module.exports = {
open(context, options) { open(context, options) {
@ -17,15 +16,15 @@ module.exports = {
const json = JSON.stringify(message.data); const json = JSON.stringify(message.data);
if (this.gzipHAR) { if (this.gzipHAR) {
return zlib return gzip(Buffer.from(json), {
.gzipAsync(Buffer.from(json), { level: 1 }) level: 1
.then(gziped => }).then(gziped =>
this.storageManager.writeDataForUrl( this.storageManager.writeDataForUrl(
gziped, gziped,
`${message.type}.gz`, `${message.type}.gz`,
message.url message.url
) )
); );
} else { } else {
return this.storageManager.writeDataForUrl( return this.storageManager.writeDataForUrl(
json, json,

View File

@ -1,17 +1,16 @@
'use strict'; 'use strict';
const helpers = require('../../support/helpers'), const helpers = require('../../support/helpers');
Promise = require('bluebird'), const path = require('path');
path = require('path'), const merge = require('lodash.merge');
merge = require('lodash.merge'), const get = require('lodash.get');
get = require('lodash.get'), const log = require('intel').getLogger('sitespeedio.plugin.html');
log = require('intel').getLogger('sitespeedio.plugin.html'), const chunk = require('lodash.chunk');
chunk = require('lodash.chunk'), const packageInfo = require('../../../package');
packageInfo = require('../../../package'), const renderer = require('./renderer');
renderer = require('./renderer'), const metricHelper = require('./metricHelper');
metricHelper = require('./metricHelper'), const markdown = require('markdown').markdown;
markdown = require('markdown').markdown, const isEmpty = require('lodash.isempty');
isEmpty = require('lodash.isempty');
const summaryBoxesSetup = require('./setup/summaryBoxes'), const summaryBoxesSetup = require('./setup/summaryBoxes'),
detailedSetup = require('./setup/detailed'); detailedSetup = require('./setup/detailed');
@ -55,7 +54,7 @@ class HTMLBuilder {
this.inlineCss.push(css); this.inlineCss.push(css);
} }
render(dataCollector) { async render(dataCollector) {
const options = this.options; const options = this.options;
const name = this.context.name; const name = this.context.name;
const timestamp = this.timestamp; const timestamp = this.timestamp;
@ -170,7 +169,8 @@ class HTMLBuilder {
) )
); );
const urlPageRenders = Promise.resolve(Object.keys(validPages)).map(url => { const urlPageRenders = [];
for (let url of Object.keys(validPages)) {
const pageInfo = validPages[url]; const pageInfo = validPages[url];
const runPages = dataCollector.getURLRuns(url); const runPages = dataCollector.getURLRuns(url);
const medianRun = metricHelper.pickMedianRun(runPages, pageInfo); const medianRun = metricHelper.pickMedianRun(runPages, pageInfo);
@ -181,7 +181,10 @@ class HTMLBuilder {
name: summaryPageHAR.log.browser.name, name: summaryPageHAR.log.browser.name,
version: summaryPageHAR.log.browser.version version: summaryPageHAR.log.browser.version
} }
: { name: '', version: '' }; : {
name: '',
version: ''
};
// if we are on the summary page we inline the HAR and then make sure // if we are on the summary page we inline the HAR and then make sure
// we only pick one HAR run (medianRun). But you can also choose to // we only pick one HAR run (medianRun). But you can also choose to
// fetch the HAR in the HTML, then it isn't included. // fetch the HAR in the HTML, then it isn't included.
@ -249,59 +252,60 @@ class HTMLBuilder {
} }
data.pugs = pugs; data.pugs = pugs;
return this._renderUrlPage(url, 'index', data).tap(() => await this._renderUrlPage(url, 'index', data);
Promise.resolve(Object.keys(runPages)).map(runIndex => { for (let runIndex of Object.keys(runPages)) {
const iteration = Number(runIndex) + 1; const iteration = Number(runIndex) + 1;
const pugs = {}; const pugs = {};
const pageInfo = runPages[runIndex]; const pageInfo = runPages[runIndex];
const runTimestamp = get( const runTimestamp = get(
pageInfo, pageInfo,
'data.browsertime.run.timestamp', 'data.browsertime.run.timestamp',
this.timestamp this.timestamp
); );
const pageRuns = this.pageRuns.filter( const pageRuns = this.pageRuns.filter(
run => !!get(pageInfo.data, [run.id, 'run']) run => !!get(pageInfo.data, [run.id, 'run'])
); );
let data = { let data = {
daurl: url, daurl: url,
daurlAlias, daurlAlias,
iteration, iteration,
runIndex, runIndex,
pageInfo, pageInfo,
options, options,
runPages, runPages,
browser, browser,
hasScreenShots: dataCollector.browsertimeScreenshots, hasScreenShots: dataCollector.browsertimeScreenshots,
screenShotType: dataCollector.browsertimeScreenshotsType, screenShotType: dataCollector.browsertimeScreenshotsType,
css, css,
h: helpers, h: helpers,
urlLink: './index.html', urlLink: './index.html',
JSON: JSON, JSON: JSON,
markdown: markdown, markdown: markdown,
rootPath: this.storageManager.rootPathFromUrl(url), rootPath: this.storageManager.rootPathFromUrl(url),
menu: 'pages', menu: 'pages',
pageTitle: `Run ${parseInt(runIndex) + pageTitle: `Run ${parseInt(runIndex) +
1} for ${url} at ${runTimestamp}`, 1} for ${url} at ${runTimestamp}`,
pageDescription: `${metricHelper.getMetricsFromRun( pageDescription: `${metricHelper.getMetricsFromRun(
pageInfo pageInfo
)} collected by sitespeed.io ${packageInfo.version}`, )} collected by sitespeed.io ${packageInfo.version}`,
headers: this.summary, headers: this.summary,
version: packageInfo.version, version: packageInfo.version,
timestamp: runTimestamp, timestamp: runTimestamp,
context: this.context, context: this.context,
pageRuns pageRuns
}; };
// Add pugs for extra plugins // Add pugs for extra plugins
for (const run of pageRuns) { for (const run of pageRuns) {
pugs[run.id] = renderer.renderTemplate(run.id, data); pugs[run.id] = renderer.renderTemplate(run.id, data);
} }
data.pugs = pugs; data.pugs = pugs;
return this._renderUrlRunPage(url, parseInt(runIndex) + 1, data); urlPageRenders.push(
}) this._renderUrlRunPage(url, parseInt(runIndex) + 1, data)
); );
}); }
}
// Aggregate/summarize data and write additional files // Aggregate/summarize data and write additional files
return this.storageManager return this.storageManager
.copyToResultDir(path.join(__dirname, 'assets')) .copyToResultDir(path.join(__dirname, 'assets'))
@ -314,7 +318,7 @@ class HTMLBuilder {
); );
} }
_renderUrlPage(url, name, locals) { async _renderUrlPage(url, name, locals) {
log.debug('Render URL page %s', name); log.debug('Render URL page %s', name);
return this.storageManager.writeHtmlForUrl( return this.storageManager.writeHtmlForUrl(
@ -324,7 +328,7 @@ class HTMLBuilder {
); );
} }
_renderUrlRunPage(url, name, locals) { async _renderUrlRunPage(url, name, locals) {
log.debug('Render URL run page %s', name); log.debug('Render URL run page %s', name);
return this.storageManager.writeHtmlForUrl( return this.storageManager.writeHtmlForUrl(
renderer.renderTemplate('url/iteration/index', locals), renderer.renderTemplate('url/iteration/index', locals),
@ -333,7 +337,7 @@ class HTMLBuilder {
); );
} }
_renderSummaryPage(name, locals) { async _renderSummaryPage(name, locals) {
log.debug('Render summary page %s', name); log.debug('Render summary page %s', name);
return this.storageManager.writeHtml( return this.storageManager.writeHtml(

View File

@ -2,7 +2,6 @@
const http = require('http'); const http = require('http');
const https = require('https'); const https = require('https');
const log = require('intel').getLogger('sitespeedio.plugin.influxdb'); const log = require('intel').getLogger('sitespeedio.plugin.influxdb');
const Promise = require('bluebird');
const querystring = require('querystring'); const querystring = require('querystring');
const tsdbUtil = require('../../support/tsdbUtil'); const tsdbUtil = require('../../support/tsdbUtil');
const annotationsHelper = require('../../support/annotationsHelper'); const annotationsHelper = require('../../support/annotationsHelper');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const Influx = require('influx'), const Influx = require('influx');
Promise = require('bluebird');
class InfluxDBSender { class InfluxDBSender {
constructor({ protocol, host, port, database, username, password }) { constructor({ protocol, host, port, database, username, password }) {

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const throwIfMissing = require('../../support/util').throwIfMissing; const throwIfMissing = require('../../support/util').throwIfMissing;
const Promise = require('bluebird');
const log = require('intel').getLogger('sitespeedio.plugin.slack'); const log = require('intel').getLogger('sitespeedio.plugin.slack');
const Slack = require('node-slack'); const Slack = require('node-slack');
const merge = require('lodash.merge'); const merge = require('lodash.merge');
@ -9,8 +8,7 @@ const set = require('lodash.set');
const DataCollector = require('./dataCollector'); const DataCollector = require('./dataCollector');
const getAttachments = require('./attachements'); const getAttachments = require('./attachements');
const getSummary = require('./summary'); const getSummary = require('./summary');
const { promisify } = require('util');
Promise.promisifyAll(Slack.prototype);
const defaultConfig = { const defaultConfig = {
userName: 'Sitespeed.io', userName: 'Sitespeed.io',
@ -23,6 +21,7 @@ const defaultConfig = {
function send(options, dataCollector, context, screenshotType) { function send(options, dataCollector, context, screenshotType) {
const slackOptions = merge({}, defaultConfig, options.slack); const slackOptions = merge({}, defaultConfig, options.slack);
const slack = new Slack(slackOptions.hookUrl); const slack = new Slack(slackOptions.hookUrl);
const send = promisify(slack.send.bind(slack));
const type = slackOptions.type; const type = slackOptions.type;
const pageErrors = []; const pageErrors = [];
let logo = 'https://www.sitespeed.io/img/slack/sitespeed-logo-slack.png'; let logo = 'https://www.sitespeed.io/img/slack/sitespeed-logo-slack.png';
@ -69,22 +68,20 @@ function send(options, dataCollector, context, screenshotType) {
slackOptions.channel, slackOptions.channel,
slackOptions.userName slackOptions.userName
); );
return slack return send({
.sendAsync({ text,
text, icon_url: logo,
icon_url: logo, channel,
channel, mrkdwn: true,
mrkdwn: true, username: slackOptions.userName,
username: slackOptions.userName, attachments
attachments }).catch(e => {
}) if (e.errno === 'ETIMEDOUT') {
.catch(e => { log.warn('Timeout sending Slack message.');
if (e.errno === 'ETIMEDOUT') { } else {
log.warn('Timeout sending Slack message.'); throw e;
} else { }
throw e; });
}
});
} }
} }

View File

@ -1,9 +1,8 @@
'use strict'; 'use strict';
const zlib = require('zlib'); const zlib = require('zlib');
const Promise = require('bluebird'); const { promisify } = require('util');
const gzip = promisify(zlib.gzip);
Promise.promisifyAll(zlib);
module.exports = { module.exports = {
open(context) { open(context) {
@ -14,15 +13,13 @@ module.exports = {
case 'webpagetest.chrometrace': { case 'webpagetest.chrometrace': {
const json = JSON.stringify(message.data); const json = JSON.stringify(message.data);
return zlib return gzip(Buffer.from(json), { level: 1 }).then(gziped =>
.gzipAsync(Buffer.from(json), { level: 1 }) this.storageManager.writeDataForUrl(
.then(gziped => gziped,
this.storageManager.writeDataForUrl( `${message.name}.gz`,
gziped, message.url
`${message.name}.gz`, )
message.url );
)
);
} }
} }
} }

View File

@ -1,15 +1,13 @@
'use strict'; 'use strict';
const Promise = require('bluebird');
const clone = require('lodash.clonedeep'); const clone = require('lodash.clonedeep');
const get = require('lodash.get'); const get = require('lodash.get');
const WebPageTest = require('webpagetest'); const WebPageTest = require('webpagetest');
const WPTAPIError = require('webpagetest/lib/helper').WPTAPIError; const WPTAPIError = require('webpagetest/lib/helper').WPTAPIError;
const { promisify } = require('util');
Promise.promisifyAll(WebPageTest.prototype);
module.exports = { module.exports = {
analyzeUrl(url, storageManager, log, wptOptions) { async analyzeUrl(url, storageManager, log, wptOptions) {
const wptClient = new WebPageTest(wptOptions.host, wptOptions.key); const wptClient = new WebPageTest(wptOptions.host, wptOptions.key);
wptOptions.firstViewOnly = !wptOptions.includeRepeatView; wptOptions.firstViewOnly = !wptOptions.includeRepeatView;
let urlOrScript = url; let urlOrScript = url;
@ -19,9 +17,24 @@ module.exports = {
urlOrScript = wptOptions.script.split('{{{URL}}}').join(url); urlOrScript = wptOptions.script.split('{{{URL}}}').join(url);
} }
// Setup WebPageTest methods
const runTest = promisify(wptClient.runTest.bind(wptClient));
const getHARData = promisify(wptClient.getHARData.bind(wptClient));
const getScreenshotImage = promisify(
wptClient.getScreenshotImage.bind(wptClient)
);
const getWaterfallImage = promisify(
wptClient.getWaterfallImage.bind(wptClient)
);
const getChromeTraceData = promisify(
wptClient.getChromeTraceData.bind(wptClient)
);
// See https://github.com/sitespeedio/sitespeed.io/issues/1367 // See https://github.com/sitespeedio/sitespeed.io/issues/1367
const options = clone(wptOptions); const options = clone(wptOptions);
return wptClient.runTestAsync(urlOrScript, options).then(function(data) {
try {
const data = await runTest(urlOrScript, options);
const id = data.data.id; const id = data.data.id;
log.info('Got %s analysed with id %s from %s', url, id, options.host); log.info('Got %s analysed with id %s from %s', url, id, options.host);
log.verbose('Got JSON from WebPageTest :%:2j', data); log.verbose('Got JSON from WebPageTest :%:2j', data);
@ -60,8 +73,7 @@ module.exports = {
const promises = []; const promises = [];
let har; let har;
promises.push( promises.push(
wptClient getHARData(id, {})
.getHARDataAsync(id, {})
.then(theHar => (har = theHar)) .then(theHar => (har = theHar))
.catch(WPTAPIError, error => .catch(WPTAPIError, error =>
log.warn( log.warn(
@ -82,8 +94,10 @@ module.exports = {
const repeatView = view === 'repeatView'; const repeatView = view === 'repeatView';
promises.push( promises.push(
wptClient getScreenshotImage(id, {
.getScreenshotImageAsync(id, { run, repeatView }) run,
repeatView
})
.then(img => .then(img =>
storageManager.writeDataForUrl( storageManager.writeDataForUrl(
img, img,
@ -102,8 +116,10 @@ module.exports = {
); );
promises.push( promises.push(
wptClient getWaterfallImage(id, {
.getWaterfallImageAsync(id, { run, repeatView }) run,
repeatView
})
.then(img => .then(img =>
storageManager.writeDataForUrl( storageManager.writeDataForUrl(
img, img,
@ -122,12 +138,11 @@ module.exports = {
); );
promises.push( promises.push(
wptClient getWaterfallImage(id, {
.getWaterfallImageAsync(id, { run,
run, chartType: 'connection',
chartType: 'connection', repeatView
repeatView })
})
.then(img => .then(img =>
storageManager.writeDataForUrl( storageManager.writeDataForUrl(
img, img,
@ -147,8 +162,10 @@ module.exports = {
if (wptOptions.timeline) { if (wptOptions.timeline) {
promises.push( promises.push(
wptClient getChromeTraceData(id, {
.getChromeTraceDataAsync(id, { run, repeatView }) run,
repeatView
})
.then( .then(
trace => (traces['trace-' + run + '-wpt-' + view] = trace) trace => (traces['trace-' + run + '-wpt-' + view] = trace)
) )
@ -171,6 +188,8 @@ module.exports = {
myResult.trace = traces; myResult.trace = traces;
return myResult; return myResult;
}); });
}); } catch (e) {
log.error('Could not run test for WebPageTest', e);
}
} }
}; };

View File

@ -173,7 +173,7 @@ module.exports = {
const group = message.group; const group = message.group;
return analyzer return analyzer
.analyzeUrl(url, this.storageManager, this.log, wptOptions) .analyzeUrl(url, this.storageManager, this.log, wptOptions)
.tap(result => { .then(result => {
addCustomMetric(result, filterRegistry); addCustomMetric(result, filterRegistry);
if (result.trace) { if (result.trace) {
forEach(result.trace, (value, key) => { forEach(result.trace, (value, key) => {

View File

@ -1,19 +1,18 @@
'use strict'; 'use strict';
const Promise = require('bluebird'), const dayjs = require('dayjs');
dayjs = require('dayjs'), const log = require('intel').getLogger('sitespeedio');
log = require('intel').getLogger('sitespeedio'), const intel = require('intel');
intel = require('intel'), const os = require('os');
os = require('os'), const process = require('process');
process = require('process'), const logging = require('./core/logging');
logging = require('./core/logging'), const toArray = require('./support/util').toArray;
toArray = require('./support/util').toArray, const pullAll = require('lodash.pullall');
pullAll = require('lodash.pullall'), const union = require('lodash.union');
union = require('lodash.union'), const messageMaker = require('./support/messageMaker');
messageMaker = require('./support/messageMaker'), const filterRegistry = require('./support/filterRegistry');
filterRegistry = require('./support/filterRegistry'), const statsHelpers = require('./support/statsHelpers');
statsHelpers = require('./support/statsHelpers'), const packageInfo = require('../package');
packageInfo = require('../package');
const QueueHandler = require('./core/queueHandler'), const QueueHandler = require('./core/queueHandler'),
resultsStorage = require('./core/resultsStorage'), resultsStorage = require('./core/resultsStorage'),
@ -35,13 +34,13 @@ function runOptionalFunction(objects, fN) {
for (let i = 2; i < arguments.length; i++) { for (let i = 2; i < arguments.length; i++) {
args[i - 2] = arguments[i]; args[i - 2] = arguments[i];
} }
return Promise.resolve(objects) return objects
.filter(hasFunctionFilter(fN)) .filter(hasFunctionFilter(fN))
.map(plugin => Promise.resolve(plugin[fN].apply(plugin, args))); .map(plugin => Promise.resolve(plugin[fN].apply(plugin, args)));
} }
module.exports = { module.exports = {
run(options) { async run(options) {
const url = options.urls[0]; const url = options.urls[0];
const timestamp = dayjs(); const timestamp = dayjs();
@ -57,96 +56,93 @@ module.exports = {
options.useHash options.useHash
); );
return storageManager // Setup logging
.createDirectory('logs') const logDir = await storageManager.createDirectory('logs');
.then(logDir => { logging.configure(options, logDir);
logging.configure(options, logDir);
}) // Tell the world what we are using
.then(() => { log.info(
if (log.isEnabledFor(log.VERBOSE)) { 'Versions OS: %s nodejs: %s sitespeed.io: %s browsertime: %s coach: %s',
Promise.longStackTraces(); os.platform() + ' ' + os.release(),
} process.version,
log.info( packageInfo.version,
'Versions OS: %s nodejs: %s sitespeed.io: %s browsertime: %s coach: %s', packageInfo.dependencies.browsertime,
os.platform() + ' ' + os.release(), packageInfo.dependencies.webcoach
process.version, );
packageInfo.version, log.verbose('Config options: %:2j', options);
packageInfo.dependencies.browsertime,
packageInfo.dependencies.webcoach let pluginNames = await loader.parsePluginNames(options);
const plugins = options.plugins;
// Deprecated setup
if (plugins) {
if (plugins.disable) {
log.warn(
'--plugins.disable is deprecated, use plugins.remove instead.'
); );
log.verbose('Config options: %:2j', options); plugins.remove = plugins.disable;
}) }
.then(() => { if (plugins.load) {
return loader.parsePluginNames(options); log.warn('--plugins.load is deprecated, use plugins.add instead.');
}) plugins.add = plugins.load;
.then(pluginNames => { }
const plugins = options.plugins;
if (plugins) {
if (plugins.disable) {
log.warn(
'--plugins.disable is deprecated, use plugins.remove instead.'
);
plugins.remove = plugins.disable;
}
if (plugins.load) {
log.warn('--plugins.load is deprecated, use plugins.add instead.');
plugins.add = plugins.load;
}
pullAll(pluginNames, toArray(plugins.remove)); // Finalize the plugins that we wanna run
pluginNames = union(pluginNames, toArray(plugins.add)); pullAll(pluginNames, toArray(plugins.remove));
pluginNames = union(pluginNames, toArray(plugins.add));
if (plugins.list) { if (plugins.list) {
log.info( log.info(
'The following plugins are enabled: %s', 'The following plugins are enabled: %s',
pluginNames.join(', ') pluginNames.join(', ')
); );
} }
} }
return pluginNames;
})
.then(pluginNames => {
return loader.loadPlugins(pluginNames).then(plugins => {
let urlSources = [urlSource];
const allPlugins = urlSources.concat(plugins),
queueHandler = new QueueHandler(plugins, options);
const context = { const runningPlugins = await loader.loadPlugins(pluginNames);
storageManager, let urlSources = [urlSource];
resultUrls, const allPlugins = urlSources.concat(runningPlugins);
timestamp, const queueHandler = new QueueHandler(runningPlugins, options);
budget: budgetResult,
name: url,
log,
intel,
messageMaker,
statsHelpers,
filterRegistry
};
return runOptionalFunction(allPlugins, 'open', context, options)
.then(() => queueHandler.run(urlSources))
.tap(errors =>
runOptionalFunction(allPlugins, 'close', options, errors)
);
});
})
.then(errors => {
log.info('Finished analysing %s', url);
if (resultUrls.hasBaseUrl()) {
log.info('Find the result at %s', resultUrls.reportSummaryUrl());
}
if (options.summary && options.summary.out) { // This is the contect where we wanna run our tests
console.log(options.summary.out); // eslint-disable-line no-console const context = {
} storageManager,
return { resultUrls,
errors, timestamp,
budgetResult budget: budgetResult,
}; name: url,
}) log,
.catch(err => { intel,
log.error(err); messageMaker,
throw err; statsHelpers,
}); filterRegistry
};
// Open/start each and every plugin
try {
await runOptionalFunction(allPlugins, 'open', context, options);
// Pass the URLs
const errors = await queueHandler.run(urlSources);
// Close the plugins
await runOptionalFunction(allPlugins, 'close', options, errors);
if (resultUrls.hasBaseUrl()) {
log.info('Find the result at %s', resultUrls.reportSummaryUrl());
}
if (options.summary && options.summary.out) {
console.log(options.summary.out); // eslint-disable-line no-console
}
return {
errors,
budgetResult
};
} catch (err) {
log.error(err);
throw err;
}
} }
}; };

25
package-lock.json generated
View File

@ -511,7 +511,8 @@
"bluebird": { "bluebird": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
"integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==",
"dev": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
@ -4884,14 +4885,6 @@
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
}, },
"longjohn": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/longjohn/-/longjohn-0.2.12.tgz",
"integrity": "sha1-fKdEawg2VcN351EiE9x1TVKmSn4=",
"requires": {
"source-map-support": "0.3.2 - 1.0.0"
}
},
"loud-rejection": { "loud-rejection": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@ -7004,20 +6997,6 @@
"is-plain-obj": "^1.0.0" "is-plain-obj": "^1.0.0"
} }
}, },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
"integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"sparkles": { "sparkles": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",

View File

@ -50,6 +50,7 @@
"node": ">=8.9.0" "node": ">=8.9.0"
}, },
"devDependencies": { "devDependencies": {
"bluebird": "3.5.2",
"chai": "^4.1.2", "chai": "^4.1.2",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"clean-css-cli": "^4.1.11", "clean-css-cli": "^4.1.11",
@ -68,7 +69,6 @@
"main": "./lib/sitespeed.js", "main": "./lib/sitespeed.js",
"dependencies": { "dependencies": {
"aws-sdk": "2.327.0", "aws-sdk": "2.327.0",
"bluebird": "3.5.2",
"browsertime": "3.12.0", "browsertime": "3.12.0",
"cli-color": "1.3.0", "cli-color": "1.3.0",
"concurrent-queue": "7.0.2", "concurrent-queue": "7.0.2",
@ -91,7 +91,6 @@
"lodash.reduce": "4.6.0", "lodash.reduce": "4.6.0",
"lodash.set": "4.3.2", "lodash.set": "4.3.2",
"lodash.union": "4.6.0", "lodash.union": "4.6.0",
"longjohn": "0.2.12",
"markdown": "0.5.0", "markdown": "0.5.0",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"node-slack": "0.0.7", "node-slack": "0.0.7",