Skip to content

Commit

Permalink
build(analytics): add support for tracking npm installs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
IgorMinar committed Oct 29, 2015
1 parent edd4b93 commit ae7f76e
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 30 deletions.
5 changes: 3 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)});
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
105 changes: 79 additions & 26 deletions tools/analytics/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,15 +22,16 @@ const analyticsOptions = {
let cid = fs.existsSync(analyticsFile) ? fs.readFileSync(analyticsFile, 'utf-8') : null;
let visitor;

if (cid) {
visitor = ua(analyticsId, cid, analyticsOptions);
} else {
visitor = ua(analyticsId, analyticsOptions);
cid = visitor.cid;
fs.writeFileSync(analyticsFile, cid, 'utf-8');
if (ua) {
if (cid) {
visitor = ua(analyticsId, cid, analyticsOptions);
} else {
visitor = ua(analyticsId, analyticsOptions);
cid = visitor.cid;
fs.writeFileSync(analyticsFile, cid, 'utf-8');
}
}


// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
let customParams = {
// OS Platform (darwin, win32, linux)
Expand Down Expand Up @@ -80,28 +87,74 @@ function getDartVersion() {
}


module.exports = {
install: (actionName, duration) => {
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);
visitor.
event('install', actionName, 'testLabel', duration, customParams).
timing('install', actionName, duration, customParams).
send();
}

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 = {

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 changes: 51 additions & 0 deletions tools/analytics/build-analytics
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit ae7f76e

Please sign in to comment.