Compare commits

...

2 Commits

Author SHA1 Message Date
Paul Irish
b62740be69 add tracking ID 2013-10-03 19:41:41 +01:00
Pascal Hartig
51dfa159b3 Basic usage tracking with insight
The user is prompted with a question whether he would like to provide usage data
on the first start, when run in interactive mode. The current implementation
tracks calls to `install`, `info`, `search` and `uninstall` when run with at
least one parameter.

Fixes #260
2013-10-03 19:40:51 +01:00
9 changed files with 158 additions and 61 deletions

124
bin/bower
View File

@@ -11,6 +11,7 @@ var bower = require('../lib');
var pkg = require(path.join(__dirname, '..', 'package.json'));
var cli = require('../lib/util/cli');
var rootCheck = require('../lib/util/rootCheck');
var analytics = require('../lib/util/analytics');
// --------
@@ -69,71 +70,74 @@ while (options.argv.remain.length) {
options.argv.remain.pop();
}
// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');
// Ask for Insights on first run.
analytics.setup().then(function () {
// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');
// If no command was specified, show bower help
// Do the same if the command is unknown
if (!commandFunc) {
logger = bower.commands.help();
command = 'help';
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
} else if (options.help || !commandFunc.line) {
logger = bower.commands.help(command);
command = 'help';
// Call the line method
} else {
logger = commandFunc.line(process.argv);
// If the method failed to interpret the process arguments
// show the command help
if (!logger) {
// If no command was specified, show bower help
// Do the same if the command is unknown
if (!commandFunc) {
logger = bower.commands.help();
command = 'help';
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
} else if (options.help || !commandFunc.line) {
logger = bower.commands.help(command);
command = 'help';
}
}
// Call the line method
} else {
logger = commandFunc.line(process.argv);
// Get the renderer and configure it with the executed command
renderer = cli.getRenderer(command, logger.json, bower.config);
logger
.on('end', function (data) {
if (!bower.config.silent) {
renderer.end(data);
}
})
.on('error', function (err) {
if (levels.error >= loglevel) {
renderer.error(err);
// If the method failed to interpret the process arguments
// show the command help
if (!logger) {
logger = bower.commands.help(command);
command = 'help';
}
}
process.exit(1);
})
.on('log', function (log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function (prompt, callback) {
renderer.prompt(prompt)
.then(function (answer) {
callback(answer);
// Get the renderer and configure it with the executed command
renderer = cli.getRenderer(command, logger.json, bower.config);
logger
.on('end', function (data) {
if (!bower.config.silent) {
renderer.end(data);
}
})
.on('error', function (err) {
if (levels.error >= loglevel) {
renderer.error(err);
}
process.exit(1);
})
.on('log', function (log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function (prompt, callback) {
renderer.prompt(prompt)
.then(function (answer) {
callback(answer);
});
});
// Warn if HOME is not SET
if (!osenv.home()) {
logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
}
// Check for newer version of Bower
notifier = updateNotifier({
packageName: pkg.name,
packageVersion: pkg.version
});
if (notifier.update && levels.info >= loglevel) {
renderer.updateNotice(notifier.update);
}
});
// Warn if HOME is not SET
if (!osenv.home()) {
logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
}
// Check for newer version of Bower
notifier = updateNotifier({
packageName: pkg.name,
packageVersion: pkg.version
});
if (notifier.update && levels.info >= loglevel) {
renderer.updateNotice(notifier.update);
}

View File

@@ -4,17 +4,21 @@ var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var PackageRepository = require('../core/PackageRepository');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function info(endpoint, property, config) {
var repository;
var decEndpoint;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
repository = new PackageRepository(config, logger);
tracker = new Tracker(config);
decEndpoint = endpointParser.decompose(endpoint);
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
Q.all([
getPkgMeta(repository, decEndpoint, property),

View File

@@ -3,25 +3,30 @@ var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var Project = require('../core/Project');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function install(endpoints, options, config) {
var project;
var decEndpoints;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
tracker = new Tracker(config);
// Convert endpoints to decomposed endpoints
endpoints = endpoints || [];
decEndpoints = endpoints.map(function (endpoint) {
return endpointParser.decompose(endpoint);
});
tracker.trackDecomposedEndpoints('install', decEndpoints);
project.install(decEndpoints, options)
.done(function (installed) {
tracker.trackPackages('installed', installed);
logger.emit('end', installed);
}, function (error) {
logger.emit('error', error);

View File

@@ -4,6 +4,7 @@ var chalk = require('chalk');
var PackageRepository = require('../core/PackageRepository');
var Logger = require('bower-logger');
var Config = require('bower-config');
var Tracker = require('../util/analytics').Tracker;
var cli = require('../util/cli');
var createError = require('../util/createError');
var defaultConfig = require('../config');
@@ -12,11 +13,13 @@ var GitHubResolver = require('../core/resolvers/GitHubResolver');
function register(name, url, config) {
var repository;
var registryClient;
var tracker;
var logger = new Logger();
var force;
config = mout.object.deepFillIn(config || {}, defaultConfig);
force = config.force;
tracker = new Tracker(config);
// Bypass any cache
config.offline = false;
@@ -42,6 +45,8 @@ function register(name, url, config) {
}
}
tracker.track('register');
// Attempt to resolve the package referenced by the URL to ensure
// everything is ok before registering
repository = new PackageRepository(config, logger);
@@ -81,6 +86,7 @@ function register(name, url, config) {
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
})
.done(function (result) {
tracker.track('registered');
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);

View File

@@ -3,17 +3,20 @@ var Q = require('q');
var Logger = require('bower-logger');
var RegistryClient = require('bower-registry-client');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function search(name, config) {
var registryClient;
var promise;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config.cache = config.storage.registry;
registryClient = new RegistryClient(config, logger);
tracker = new Tracker(config);
// If no name was specified, list all packages
if (!name) {
@@ -25,6 +28,7 @@ function search(name, config) {
promise
.done(function (results) {
tracker.track('searched', name);
logger.emit('end', results);
}, function (error) {
logger.emit('error', error);

View File

@@ -3,15 +3,20 @@ var Logger = require('bower-logger');
var Q = require('q');
var Project = require('../core/Project');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function uninstall(names, options, config) {
var project;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
tracker = new Tracker(config);
tracker.trackNames('uninstall', names);
project.getTree()
.spread(function (tree, flattened) {
@@ -35,6 +40,7 @@ function uninstall(names, options, config) {
})
.done(function (uninstalled) {
logger.emit('end', uninstalled);
tracker.trackNames('uninstalled', Object.keys(uninstalled));
}, function (error) {
logger.emit('error', error);
});

View File

@@ -12,6 +12,12 @@ if (config.interactive == null) {
config.interactive = process.bin === 'bower' && tty.isatty(1);
}
// If `analytics` hasn't been explicitly set, we disable
// it when ran programatically.
if (config.analytics == null) {
config.analytics = config.interactive;
}
// Merge common CLI options into the config
mout.object.mixIn(config, cli.readOptions({
force: { type: Boolean, shorthand: 'f' },

61
lib/util/analytics.js Normal file
View File

@@ -0,0 +1,61 @@
var Q = require('q');
var Insight = require('insight');
var mout = require('mout');
var config = require('../config');
var pkg = require('../../package.json');
var analytics = module.exports;
var insight;
// Initializes the application-wide insight singleton and asks for the
// permission on the CLI during the first run.
analytics.setup = function setup() {
var deferred = Q.defer();
insight = new Insight({
trackingCode: 'UA-43531210-1',
packageName: pkg.name,
packageVersion: pkg.version
});
// Display the ask prompt only if it hasn't been answered before
// and the current session is interactive.
if (insight.optOut === undefined && config.interactive) {
insight.askPermission(null, deferred.resolve);
} else {
deferred.resolve();
}
return deferred.promise;
};
var Tracker = analytics.Tracker = function Tracker(config) {
if (!config.analytics) {
this.track = function noop() {};
}
};
Tracker.prototype.track = function track() {
if (!insight) {
throw new Error('You must call analytics.setup() prior to tracking.');
}
insight.track.apply(insight, arguments);
};
Tracker.prototype.trackDecomposedEndpoints = function trackDecomposedEndpoints(command, endpoints) {
endpoints.forEach(function (endpoint) {
this.track(command, endpoint.source, endpoint.target);
}.bind(this));
};
Tracker.prototype.trackPackages = function trackPackages(command, packages) {
mout.object.forOwn(packages, function (package) {
var meta = package.pkgMeta;
this.track(command, meta.name, meta.version);
}.bind(this));
};
Tracker.prototype.trackNames = function trackNames(command, names) {
names.forEach(function (name) {
this.track(command, name);
}.bind(this));
};

View File

@@ -55,7 +55,8 @@
"tmp": "~0.0.20",
"unzip": "~0.1.7",
"update-notifier": "~0.1.3",
"which": "~1.0.5"
"which": "~1.0.5",
"insight": "~0.2.0"
},
"devDependencies": {
"expect.js": "~0.2.0",