diff --git a/gulpfile.js b/gulpfile.js index a262dc5ac3..d94514f84d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -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)}); diff --git a/package.json b/package.json index d9d68a24f4..5522983b42 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/tools/analytics/analytics.js b/tools/analytics/analytics.js index 8615758bd6..bbfe4c2c02 100644 --- a/tools/analytics/analytics.js +++ b/tools/analytics/analytics.js @@ -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,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) @@ -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); } }; diff --git a/tools/analytics/build-analytics b/tools/analytics/build-analytics new file mode 100755 index 0000000000..e2a31dfb2c --- /dev/null +++ b/tools/analytics/build-analytics @@ -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); +}