build(analytics): add support for tracking npm installs

Since the very first npm install is called while node_modules is empty, we need to ignore it, but we can track
the start timestamp and record the install even once the installation is completed.
This commit is contained in:
Igor Minar 2015-10-27 23:19:52 +01:00
parent edd4b93589
commit ae7f76e91f
4 changed files with 135 additions and 30 deletions

View File

@ -1317,5 +1317,6 @@ process.on('beforeExit', function() {
});
gulp.on('task_stop', (e) => { analytics.build('gulp ' + e.task, e.duration*1000)});
gulp.on('task_err', (e) => { analytics.build('gulp ' + e.task + ' (errored)', e.duration*1000)});
gulp.on('task_start', (e) => { analytics.buildStart('gulp ' + e.task)});
gulp.on('task_stop', (e) => { analytics.buildSuccess('gulp ' + e.task, e.duration*1000)});
gulp.on('task_err', (e) => { analytics.buildError('gulp ' + e.task, e.duration*1000)});

View File

@ -23,8 +23,8 @@
"url": "https://github.com/angular/angular.git"
},
"scripts": {
"preinstall": "node tools/npm/check-node-modules --purge",
"postinstall": "node tools/npm/copy-npm-shrinkwrap && node tools/chromedriverpatch.js && webdriver-manager update && bower install && gulp pubget.dart && tsd reinstall --overwrite --clean --config modules/angular2/tsd.json && tsd reinstall --overwrite --clean --config tools/tsd.json && tsd reinstall --overwrite --config modules/angular1_router/tsd.json",
"preinstall": "node tools/analytics/build-analytics start install npm-install; node tools/analytics/build-analytics start install npm-preinstall; node tools/npm/check-node-modules --purge; node tools/analytics/build-analytics success install npm-preinstall; node tools/analytics/build-analytics start install npm-install-net",
"postinstall": "node tools/analytics/build-analytics success install npm-install-net; node tools/analytics/build-analytics start install npm-postinstall; node tools/npm/copy-npm-shrinkwrap && node tools/chromedriverpatch.js && webdriver-manager update && bower install && gulp pubget.dart && tsd reinstall --overwrite --clean --config modules/angular2/tsd.json && tsd reinstall --overwrite --clean --config tools/tsd.json && tsd reinstall --overwrite --config modules/angular1_router/tsd.json; node tools/analytics/build-analytics success install npm-postinstall; node tools/analytics/build-analytics success install npm-install",
"test": "gulp test.all.js && gulp test.all.dart"
},
"dependencies": {

View File

@ -4,7 +4,13 @@ let execSync = require('child_process').execSync;
let fs = require('fs');
let path = require('path');
let os = require('os');
let ua = require('universal-analytics');
let ua;
try {
ua = require('universal-analytics');
} catch(e) {
// ignore errors due to invoking analytics before the first npm install
}
const analyticsFile = path.resolve(path.join(__dirname, '..', '..', '.build-analytics'));
const analyticsId = "UA-8594346-17"; // Owned by the Angular account
@ -16,6 +22,7 @@ const analyticsOptions = {
let cid = fs.existsSync(analyticsFile) ? fs.readFileSync(analyticsFile, 'utf-8') : null;
let visitor;
if (ua) {
if (cid) {
visitor = ua(analyticsId, cid, analyticsOptions);
} else {
@ -23,7 +30,7 @@ if (cid) {
cid = visitor.cid;
fs.writeFileSync(analyticsFile, cid, 'utf-8');
}
}
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
let customParams = {
@ -80,28 +87,74 @@ function getDartVersion() {
}
function recordEvent(eventType, actionCategory, actionName, duration) {
// if universal-analytics is not yet installed, don't bother doing anything (e.g. when tracking initial npm install)
// build-analytics will however store the starting timestamp, so at least we can record the success/error event with duration
if (!ua) return;
if (duration) {
duration = Math.round(duration);
}
switch (eventType) {
case 'start':
visitor.
event(actionCategory, actionName + ' (start)', 'testLabel', null, customParams).
send();
break;
case 'success':
visitor.
event(actionCategory, actionName, 'testLabel', duration, customParams).
timing(actionCategory, actionName, duration, customParams).
send();
break;
case 'error':
visitor.
event(actionCategory, actionName + ' (errored)', 'testLabel', duration, customParams).
timing(actionCategory, actionName, duration, customParams).
send();
break;
default:
throw new Error(`unknown event type "${eventType}"`);
}
}
module.exports = {
install: (actionName, duration) => {
duration = Math.round(duration);
visitor.
event('install', actionName, 'testLabel', duration, customParams).
timing('install', actionName, duration, customParams).
send();
installStart: (actionName) => {
recordEvent('start', 'install', actionName);
},
build: (actionName, duration) => {
duration = Math.round(duration);
visitor.
event('build', actionName, 'testLabel', duration, customParams).
timing('build', actionName, duration, customParams).
send();
installSuccess: (actionName, duration) => {
recordEvent('success', 'install', actionName, duration);
},
test: (actionName, duration) => {
duration = Math.round(duration);
visitor.
event('test', actionName, 'testLabel', duration, customParams).
timing('test', actionName, duration, customParams).
send();
installError: (actionName, duration) => {
recordEvent('error', 'install', actionName, duration);
},
buildStart: (actionName) => {
recordEvent('start', 'build', actionName);
},
buildSuccess: (actionName, duration) => {
recordEvent('success', 'build', actionName, duration);
},
buildError: (actionName, duration) => {
recordEvent('error', 'build', actionName, duration);
},
ciStart: (actionName) => {
recordEvent('start', 'ci', actionName);
},
ciSuccess: (actionName, duration) => {
recordEvent('success', 'ci', actionName, duration);
},
ciError: (actionName, duration) => {
recordEvent('success', 'ci', actionName, duration);
}
};

51
tools/analytics/build-analytics Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env node
'use strict';
var analytics = require('./analytics');
var fs = require('fs');
var path = require('path');
var eventType = process.argv[2];
var actionCategory = process.argv[3];
var actionName = process.argv[4];
if (!analytics[actionCategory + 'Start']) {
throw new Error('Unknown build-analytics actionCategory "' + actionCategory + '"');
}
if (eventType != 'start' && eventType != 'success' && eventType != 'error') {
throw new Error('Unknown build-analytics eventType "' + eventType + '"');
}
var startTimestampFilePath = path.resolve(path.join(__dirname, '..', '..', 'tmp', 'analytics', actionCategory + '-' + actionName));
var analyticsDirPath = path.dirname(startTimestampFilePath);
var tmpDirPath = path.dirname(analyticsDirPath);
if (!fs.existsSync(tmpDirPath)) {
fs.mkdirSync(tmpDirPath);
}
if (!fs.existsSync(analyticsDirPath)) {
fs.mkdirSync(analyticsDirPath);
}
switch (eventType) {
case 'start':
analytics[actionCategory + 'Start'](actionName);
fs.writeFileSync(startTimestampFilePath, Date.now(), 'utf-8');
break;
case 'success':
var startTime = fs.readFileSync(startTimestampFilePath, 'utf-8');
analytics[actionCategory + 'Success'](actionName, Date.now() - startTime);
fs.unlinkSync(startTimestampFilePath);
break;
case 'error':
var startTime = fs.readFileSync(startTimestampFilePath, 'utf-8');
analytics[actionCategory + 'Error'](actionName, Date.now() - startTime);
fs.unlinkSync(startTimestampFilePath);
}