forked from meteor/meteor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstats.js
177 lines (159 loc) · 6.12 KB
/
stats.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
var Fiber = require("fibers");
var _ = require("underscore");
var os = require("os");
var config = require("./config.js");
var files = require("./files.js");
var uniload = require("./uniload.js");
var project = require("./project.js");
var auth = require("./auth.js");
var ServiceConnection = require("./service-connection.js");
var release = require("./release.js");
var buildmessage = require("./buildmessage.js");
var httpHelpers = require("./http-helpers.js");
var Console = require("./console.js").Console;
// The name of the package that you add to your app to opt out of
// sending stats.
var optOutPackageName = "package-stats-opt-out";
// Return a list of packages used by this app, both directly and
// indirectly. Formatted as a list of objects with 'name', 'version'
// and 'direct', which is how the `recordAppPackages` method on the
// stats server expects to get this list.
//
// In tests, we want to use the same logic to calculate the list of
// packages for an app created in a sandbox, but we don't want to run
// the constraint solver, try to load local packages from the catalog,
// etc. In particular, we don't want to have to repoint project.project
// at whatever random app we just created in a sandbox and re-initialize
// the catalog with its local packages (and then have to undo all that
// after the test is over). So tests can pass a project.Project as an
// argument, and we'll calculate the list of packages just by looking at
// .meteor/packages and .meteor/versions, not by doing anything fancy
// like running the constraint solver.
// NOTE: This means that if you pass `_currentProjectForTest`, we assume
// that it is pointing to a root directory with an existing
// .meteor/versions file.
var packageList = function (_currentProjectForTest) {
var directDeps = (_currentProjectForTest || project.project).getConstraints();
var versions = (_currentProjectForTest || project.project).getVersions({
dontRunConstraintSolver: true
});
return _.map(
versions,
function (version, name) {
return {
name: name,
version: version,
direct: _.has(directDeps, name)
};
}
);
};
// 'what' should be one of "sdk.bundle", "sdk.deploy", "sdk.run".
// If it's a deploy, 'site' should be the name of the site
// ("foo.meteor.com") that we're deploying to.
var recordPackages = function (what, site) {
// Before doing anything, look at the app's dependencies to see if the
// opt-out package is there; if present, we don't record any stats.
var packages = packageList();
if (_.contains(_.pluck(packages, "name"), optOutPackageName)) {
// Print some output for the 'report-stats' self-test.
if (process.env.METEOR_PACKAGE_STATS_TEST_OUTPUT) {
process.stdout.write("PACKAGE STATS NOT SENT\n");
}
return;
}
// We do this inside a new fiber to avoid blocking anything on talking
// to the package stats server. If we can't connect, for example, we
// don't care; we'll just miss out on recording these packages.
// This also gives it its own buildmessage state.
Fiber(function () {
var details = {
what: what,
userAgent: httpHelpers.getUserAgent(),
sessionId: auth.getSessionId(config.getAccountsDomain()),
site: site
};
try {
var conn = connectToPackagesStatsServer();
var accountsConfiguration = auth.getAccountsConfiguration(conn);
if (auth.isLoggedIn()) {
try {
auth.loginWithTokenOrOAuth(
conn,
accountsConfiguration,
config.getPackageStatsServerUrl(),
config.getPackageStatsServerDomain(),
"package-stats-server"
);
} catch (err) {
// Do nothing. If we can't log in, we should continue and report
// stats anonymously.
//
// We log other errors with `logErrorIfInCheckout`, but login
// errors can happen in normal operation when nothing is wrong
// (e.g. login token expired or revoked) so we don't log them.
}
}
var result = conn.call("recordAppPackages",
project.project.getAppIdentifier(),
packages,
details);
// If the stats server sent us a new session, save it for use on
// subsequent requests.
if (result && result.newSessionId) {
auth.setSessionId(config.getAccountsDomain(), result.newSessionId);
}
if (process.env.METEOR_PACKAGE_STATS_TEST_OUTPUT) {
// Print some output for the 'report-stats' self-test.
process.stdout.write("PACKAGE STATS SENT\n");
}
} catch (err) {
logErrorIfInCheckout(err);
// Do nothing. A failure to record package stats shouldn't be
// visible to the end user and shouldn't affect whatever command
// they are running.
} finally {
conn && conn.close();
}
}).run();
};
var logErrorIfInCheckout = function (err) {
if (files.inCheckout()) {
Console.stderr.write("Failed to record package usage.\n");
Console.stderr.write(
"(This error is hidden when you are not running Meteor from a checkout.)\n");
Console.stderr.write(err.stack || err);
Console.stderr.write("\n\n");
}
};
// Used in a test (and can only be used against the testing packages
// server) to fetch one package stats entry for a given application.
var getPackagesForAppIdInTest = function (currentProject) {
var conn = connectToPackagesStatsServer();
var result;
try {
result = conn.call(
"getPackagesForAppId",
currentProject.getAppIdentifier());
if (result && result.details) {
result.details.packages = _.sortBy(result.details.packages, "name");
}
} finally {
conn.close();
}
return result;
};
var connectToPackagesStatsServer = function () {
var Package = uniload.load({
packages: ["meteor", "ddp"]
});
var conn = new ServiceConnection(
Package,
config.getPackageStatsServerUrl(),
{_dontPrintErrors: true}
);
return conn;
};
exports.recordPackages = recordPackages;
exports.packageList = packageList; // for use in the "stats" self-test.
exports.getPackagesForAppIdInTest = getPackagesForAppIdInTest;