mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'packaging-more-stats' into packaging
This commit is contained in:
@@ -1009,8 +1009,9 @@ session.registrationUrl + "\n\n");
|
||||
|
||||
exports.tryRevokeOldTokens = tryRevokeOldTokens;
|
||||
|
||||
exports.getSessionId = function (domain) {
|
||||
return getSession(readSessionData(), domain).session;
|
||||
exports.getSessionId = function (domain, sessionData) {
|
||||
sessionData = sessionData || readSessionData();
|
||||
return getSession(sessionData, domain).session;
|
||||
};
|
||||
|
||||
exports.setSessionId = function (domain, sessionId) {
|
||||
|
||||
@@ -330,7 +330,9 @@ _.extend(Project.prototype, {
|
||||
return path.join(self.rootDir, '.meteor', 'packages');
|
||||
},
|
||||
|
||||
// Give the contents of the project's .meteor/versions file to the caller.
|
||||
// Give the contents of the project's .meteor/versions file to the
|
||||
// caller, possibly after recalculating dependencies and rewriting the
|
||||
// versions file.
|
||||
//
|
||||
// Returns an object mapping package name to its string version.
|
||||
getVersions : function () {
|
||||
@@ -676,3 +678,4 @@ _.extend(Project.prototype, {
|
||||
// cumbersome, that is our general design pattern for singletons (ex:
|
||||
// packageCache.packageCache, etc)
|
||||
project.project = new Project();
|
||||
project.Project = Project;
|
||||
|
||||
@@ -275,7 +275,7 @@ _.extend(AppProcess.prototype, {
|
||||
//
|
||||
// - Other options: appDirForVersionCheck (defaults to appDir), port,
|
||||
// mongoUrl, oplogUrl, buildOptions, rootUrl, settingsFile, program,
|
||||
// proxy, dontRecordPackageUsage
|
||||
// proxy, recordPackageUsage
|
||||
//
|
||||
// To use, construct an instance of AppRunner, and then call start() to start it
|
||||
// running. To stop it, either return false from onRunEnd, or call stop(). (But
|
||||
|
||||
@@ -9,6 +9,9 @@ var catalog = require('./catalog.js');
|
||||
var archinfo = require('./archinfo.js');
|
||||
var packageLoader = require('./package-loader.js');
|
||||
var Future = require('fibers/future');
|
||||
var uniload = require('./uniload.js');
|
||||
|
||||
var Package = uniload.load({ packages: ["ejson"] });
|
||||
|
||||
// Exception representing a test failure
|
||||
var TestFailure = function (reason, details) {
|
||||
@@ -34,8 +37,7 @@ var fail = markStack(function (reason) {
|
||||
// with 'actual' being the value that the test got and 'expected'
|
||||
// being the expected value
|
||||
var expectEqual = markStack(function (actual, expected) {
|
||||
// XXX Super janky. Should use EJSON.equals for a deep equality test.
|
||||
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
||||
if (! Package.ejson.EJSON.equals(actual, expected)) {
|
||||
throw new TestFailure("not-equal", {
|
||||
expected: expected,
|
||||
actual: actual
|
||||
@@ -803,6 +805,7 @@ _.extend(Run.prototype, {
|
||||
stop: markStack(function () {
|
||||
var self = this;
|
||||
if (self.exitStatus === undefined) {
|
||||
self._ensureStarted();
|
||||
self.proc.kill();
|
||||
self.expectExit();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var Fiber = require("fibers");
|
||||
var _ = require("underscore");
|
||||
var os = require("os");
|
||||
|
||||
var config = require("./config.js");
|
||||
var files = require("./files.js");
|
||||
@@ -7,6 +8,7 @@ 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");
|
||||
|
||||
// The name of the package that you add to your app to opt out of
|
||||
// sending stats.
|
||||
@@ -16,16 +18,37 @@ var optOutPackageName = "package-stats-opt-out";
|
||||
// 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.
|
||||
var packageList = function () {
|
||||
var directDeps = project.project.getConstraints();
|
||||
//
|
||||
// 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;
|
||||
if (_currentProjectForTest) {
|
||||
versions = _currentProjectForTest.dependencies;
|
||||
} else {
|
||||
versions = project.project.getVersions();
|
||||
}
|
||||
|
||||
return _.map(
|
||||
project.project.getVersions(),
|
||||
versions,
|
||||
function (version, name) {
|
||||
return {
|
||||
name: name,
|
||||
version: version,
|
||||
direct: _.contains(directDeps, name)
|
||||
direct: _.has(directDeps, name)
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -43,6 +66,23 @@ var recordPackages = function () {
|
||||
// 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.
|
||||
Fiber(function () {
|
||||
|
||||
var userAgentInfo = {
|
||||
hostname: os.hostname(),
|
||||
osPlatform: os.platform(),
|
||||
osType: os.type(),
|
||||
osRelease: os.release(),
|
||||
osArch: os.arch(),
|
||||
sessionId: auth.getSessionId(config.getAccountsDomain())
|
||||
};
|
||||
|
||||
if (! release.current.isCheckout()) {
|
||||
userAgentInfo.meteorReleaseTrack = release.current.getReleaseTrack();
|
||||
userAgentInfo.meteorReleaseVersion = release.current.getReleaseVersion();
|
||||
userAgentInfo.meteorToolsPackageWithVersion =
|
||||
release.current.getToolsPackageAtVersion();
|
||||
}
|
||||
|
||||
try {
|
||||
var conn = connectToPackagesStatsServer();
|
||||
|
||||
@@ -63,7 +103,8 @@ var recordPackages = function () {
|
||||
|
||||
conn.call("recordAppPackages",
|
||||
project.project.getAppIdentifier(),
|
||||
packages);
|
||||
packages,
|
||||
userAgentInfo);
|
||||
} catch (err) {
|
||||
logErrorIfRunningMeteorRelease(err);
|
||||
// Do nothing. A failure to record package stats shouldn't be
|
||||
@@ -83,10 +124,10 @@ var logErrorIfRunningMeteorRelease = function (err) {
|
||||
|
||||
// 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 () {
|
||||
var getPackagesForAppIdInTest = function (currentProject) {
|
||||
return connectToPackagesStatsServer().call(
|
||||
"getPackagesForAppId",
|
||||
project.project.getAppIdentifier());
|
||||
currentProject.getAppIdentifier());
|
||||
};
|
||||
|
||||
var connectToPackagesStatsServer = function () {
|
||||
|
||||
1
tools/tests/apps/package-stats-tests/.meteor/.gitignore
vendored
Normal file
1
tools/tests/apps/package-stats-tests/.meteor/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
local
|
||||
5
tools/tests/apps/package-stats-tests/.meteor/packages
Normal file
5
tools/tests/apps/package-stats-tests/.meteor/packages
Normal file
@@ -0,0 +1,5 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
local-package
|
||||
1
tools/tests/apps/package-stats-tests/.meteor/release
Normal file
1
tools/tests/apps/package-stats-tests/.meteor/release
Normal file
@@ -0,0 +1 @@
|
||||
none
|
||||
10
tools/tests/apps/package-stats-tests/package-stats-tests.js
Normal file
10
tools/tests/apps/package-stats-tests/package-stats-tests.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// This app functions with no packages loaded, so it's good for testing releases
|
||||
// with no packages. All it does is print "RUNNING" and run forever.
|
||||
main = function () {
|
||||
// Tell the runner we're up.
|
||||
console.log("LISTENING");
|
||||
// Ensure Node doesn't kill us.
|
||||
process.stdin.resume();
|
||||
// Ensure boot.js doesn't kill us.
|
||||
return 'DAEMON';
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
console.log("BLAH");
|
||||
@@ -0,0 +1,8 @@
|
||||
Package.describe({
|
||||
summary: "a package",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.add_files("blah.js");
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
// This is a replica of the core package-stats-opt-out package. It
|
||||
// exists to make it so that we can add package-stats-opt-out to this
|
||||
// app without needing to be using a release that has core packages in
|
||||
// it (such as using a sandbox created with a `warehouse` argument).
|
||||
|
||||
Package.describe({
|
||||
summary: "a replica of a core package",
|
||||
version: "1.0.0"
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"isControlProgram": true}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Empty program. Means we don't need a ctl package.
|
||||
Package.describe({
|
||||
version: "1.0.0"
|
||||
});
|
||||
@@ -1,5 +1,9 @@
|
||||
var _ = require('underscore');
|
||||
var os = require("os");
|
||||
|
||||
var auth = require("../auth.js");
|
||||
var config = require("../config.js");
|
||||
var release = require("../release.js");
|
||||
var selftest = require('../selftest.js');
|
||||
var testUtils = require('../test-utils.js');
|
||||
var stats = require('../stats.js');
|
||||
@@ -9,81 +13,192 @@ var project = require('../project.js');
|
||||
var testStatsServer = "https://test-package-stats.meteor.com";
|
||||
process.env.METEOR_PACKAGE_STATS_SERVER_URL = testStatsServer;
|
||||
|
||||
var clientAddress;
|
||||
|
||||
var checkMeta = function (appPackages, sessionId, useFakeRelease) {
|
||||
if (! clientAddress) {
|
||||
clientAddress = getClientAddress();
|
||||
}
|
||||
|
||||
var expectedUserAgentInfo = {
|
||||
hostname: os.hostname(),
|
||||
osPlatform: os.platform(),
|
||||
osType: os.type(),
|
||||
osRelease: os.release(),
|
||||
osArch: os.arch(),
|
||||
clientAddress: clientAddress
|
||||
};
|
||||
|
||||
if (sessionId) {
|
||||
expectedUserAgentInfo.sessionId = sessionId;
|
||||
}
|
||||
|
||||
if (useFakeRelease) {
|
||||
expectedUserAgentInfo.meteorReleaseTrack =
|
||||
"METEOR-CORE";
|
||||
expectedUserAgentInfo.meteorReleaseVersion =
|
||||
"v1";
|
||||
|
||||
// Check the tools package version against a regexp and then
|
||||
// delete it from `appPackages.meta` so that we can check the
|
||||
// rest of `appPackages.meta` with `expectEqual`.
|
||||
var toolsPackageWithVersion =
|
||||
appPackages.meta.meteorToolsPackageWithVersion;
|
||||
if (! toolsPackageWithVersion.match(
|
||||
/meteor-tool@\d.\d.\d(\+[a-z0-9]+)?/)) {
|
||||
selftest.fail("Tools package with version is " +
|
||||
"not correct: " + toolsPackageWithVersion);
|
||||
}
|
||||
delete appPackages.meta.meteorToolsPackageWithVersion;
|
||||
}
|
||||
|
||||
selftest.expectEqual(appPackages.meta, expectedUserAgentInfo);
|
||||
|
||||
};
|
||||
|
||||
// NOTE: This test will fail if your machine's time is skewed by more
|
||||
// than 30 minutes. This is because the `fetchAppPackageUsage` method
|
||||
// works by passing an hour time range.
|
||||
selftest.define("report-stats", ["slow"], function () {
|
||||
var s = new Sandbox;
|
||||
_.each(
|
||||
// If we are currently running from a checkout, then we run this
|
||||
// test twice (once in which the sandbox uses the current checkout,
|
||||
// and another in which the sandbox uses a simulated release). If we
|
||||
// are currently running from a release, then we can only have the
|
||||
// sandbox use a simulated release that is the same as our current
|
||||
// release (we can't simulate a checkout).
|
||||
release.current.isCheckout() ? [true, false] : [false],
|
||||
function (useFakeRelease) {
|
||||
var sandboxOpts;
|
||||
if (useFakeRelease) {
|
||||
sandboxOpts = {
|
||||
warehouse: {
|
||||
v1: { recommended: true }
|
||||
}
|
||||
};
|
||||
}
|
||||
var s = new Sandbox(sandboxOpts);
|
||||
var run;
|
||||
|
||||
var run = s.run("create", "foo");
|
||||
run.expectExit(0);
|
||||
s.cd("foo");
|
||||
if (useFakeRelease) {
|
||||
// This makes packages not depend on meteor (specifically, makes
|
||||
// our empty control program not depend on meteor). We don't
|
||||
// want to depend on meteor when running from a fake release,
|
||||
// because fake releases don't have any packages.
|
||||
s.set("NO_METEOR_PACKAGE", "t");
|
||||
}
|
||||
|
||||
project.project.setRootDir(s.cwd);
|
||||
s.createApp("foo", "package-stats-tests");
|
||||
s.cd("foo");
|
||||
if (useFakeRelease) {
|
||||
s.write('.meteor/release', 'METEOR-CORE@v1');
|
||||
}
|
||||
|
||||
// verify that identifier file exists for new apps
|
||||
var identifier = s.read(".meteor/identifier");
|
||||
selftest.expectEqual(!! identifier, true);
|
||||
selftest.expectEqual(identifier.length > 0, true);
|
||||
var sandboxProject = new project.Project();
|
||||
sandboxProject.setRootDir(s.cwd);
|
||||
|
||||
// verify that identifier file when running 'meteor bundle' on apps
|
||||
// with no identifier file (eg pre-0.9.0 apps)
|
||||
bundleWithFreshIdentifier(s);
|
||||
identifier = s.read(".meteor/identifier");
|
||||
selftest.expectEqual(!! identifier, true);
|
||||
selftest.expectEqual(identifier.length > 0, true);
|
||||
var sessionId;
|
||||
|
||||
// we just ran 'meteor bundle' so let's test that we actually sent
|
||||
// package usage stats
|
||||
var usage = fetchPackageUsageForApp(identifier);
|
||||
selftest.expectEqual(_.sortBy(usage.packages, "name"),
|
||||
_.sortBy(stats.packageList(), "name"));
|
||||
// verify that identifier file exists for new apps
|
||||
var identifier = s.read(".meteor/identifier");
|
||||
selftest.expectEqual(!! identifier, true);
|
||||
selftest.expectEqual(identifier.length > 0, true);
|
||||
|
||||
// verify that the stats server recorded that with no userId
|
||||
var appPackages = stats.getPackagesForAppIdInTest();
|
||||
selftest.expectEqual(appPackages.appId, identifier);
|
||||
selftest.expectEqual(appPackages.userId, null);
|
||||
selftest.expectEqual(_.sortBy(appPackages.packages, "name"),
|
||||
_.sortBy(stats.packageList(), "name"));
|
||||
// verify that identifier file when running 'meteor bundle' on apps
|
||||
// with no identifier file (eg pre-0.9.0 apps)
|
||||
bundleWithFreshIdentifier(s, sandboxProject);
|
||||
|
||||
// now bundle again while logged in. verify that the stats server
|
||||
// recorded that with the right userId
|
||||
testUtils.login(s, "test", "testtest");
|
||||
bundleWithFreshIdentifier(s);
|
||||
appPackages = stats.getPackagesForAppIdInTest();
|
||||
selftest.expectEqual(appPackages.userId, testUtils.getUserId(s));
|
||||
identifier = s.read(".meteor/identifier");
|
||||
selftest.expectEqual(!! identifier, true);
|
||||
selftest.expectEqual(identifier.length > 0, true);
|
||||
|
||||
// Add the opt-out package, verify that no stats are recorded for the
|
||||
// app.
|
||||
run = s.run("add", "package-stats-opt-out");
|
||||
run.waitSecs(15);
|
||||
run.expectExit(0);
|
||||
bundleWithFreshIdentifier(s);
|
||||
appPackages = stats.getPackagesForAppIdInTest();
|
||||
selftest.expectEqual(appPackages, undefined);
|
||||
// we just ran 'meteor bundle' so let's test that we actually sent
|
||||
// package usage stats
|
||||
var usage = fetchPackageUsageForApp(identifier);
|
||||
selftest.expectEqual(_.sortBy(usage.packages, "name"),
|
||||
_.sortBy(stats.packageList(sandboxProject), "name"));
|
||||
|
||||
// Remove the opt-out package, verify that stats get sent again.
|
||||
run = s.run("remove", "package-stats-opt-out");
|
||||
run.waitSecs(15);
|
||||
run.expectExit(0);
|
||||
bundle(s);
|
||||
appPackages = stats.getPackagesForAppIdInTest();
|
||||
selftest.expectEqual(appPackages.userId, testUtils.getUserId(s));
|
||||
selftest.expectEqual(_.sortBy(appPackages.packages, "name"),
|
||||
_.sortBy(stats.packageList(), "name"));
|
||||
// Check that the direct dependency was recorded as such.
|
||||
_.each(usage.packages, function (package) {
|
||||
if (package.name === "local-package" &&
|
||||
! package.direct) {
|
||||
selftest.fail("local-package is not marked as a direct dependency");
|
||||
}
|
||||
});
|
||||
|
||||
// verify that the stats server recorded that with no userId
|
||||
var appPackages = stats.getPackagesForAppIdInTest(sandboxProject);
|
||||
selftest.expectEqual(appPackages.appId, identifier);
|
||||
selftest.expectEqual(appPackages.userId, null);
|
||||
selftest.expectEqual(_.sortBy(appPackages.packages, "name"),
|
||||
_.sortBy(stats.packageList(sandboxProject), "name"));
|
||||
checkMeta(appPackages, sessionId, useFakeRelease);
|
||||
|
||||
// now bundle again while logged in. verify that the stats server
|
||||
// recorded that with the right userId and meta information
|
||||
testUtils.login(s, "test", "testtest");
|
||||
sessionId = auth.getSessionId(config.getAccountsDomain(),
|
||||
JSON.parse(s.readSessionFile()));
|
||||
|
||||
bundleWithFreshIdentifier(s, sandboxProject);
|
||||
appPackages = stats.getPackagesForAppIdInTest(sandboxProject);
|
||||
selftest.expectEqual(appPackages.userId, testUtils.getUserId(s));
|
||||
checkMeta(appPackages, sessionId, useFakeRelease);
|
||||
|
||||
// Log out, and then test that our session id still gets recorded.
|
||||
testUtils.logout(s);
|
||||
run = s.run("run");
|
||||
run.waitSecs(5);
|
||||
run.match("BLAH");
|
||||
appPackages = stats.getPackagesForAppIdInTest(sandboxProject);
|
||||
selftest.expectEqual(appPackages.userId, null);
|
||||
checkMeta(appPackages, sessionId, useFakeRelease);
|
||||
run.stop();
|
||||
|
||||
testUtils.login(s, "test", "testtest");
|
||||
|
||||
// Add the opt-out package, verify that no stats are recorded for the
|
||||
// app.
|
||||
//
|
||||
// XXX The app has a local package-stats-opt-out package in it, so
|
||||
// that we can add the opt-out package without needing to be using
|
||||
// a release that knows about the opt-out package. (Our sandbox
|
||||
// release only has the tool package and no others.) In the near
|
||||
// future we should just have a way to simulate a release in a
|
||||
// sandbox that knows about all the packages in the meteor release
|
||||
// or checkout that is running 'meteor self-test'. That will
|
||||
// simplify this test a lot.
|
||||
run = s.run("add", "package-stats-opt-out");
|
||||
run.waitSecs(15);
|
||||
run.expectExit(0);
|
||||
bundleWithFreshIdentifier(s, sandboxProject);
|
||||
appPackages = stats.getPackagesForAppIdInTest(sandboxProject);
|
||||
selftest.expectEqual(appPackages, undefined);
|
||||
|
||||
// Remove the opt-out package, verify that stats get sent again.
|
||||
run = s.run("remove", "package-stats-opt-out");
|
||||
run.waitSecs(15);
|
||||
run.expectExit(0);
|
||||
bundle(s, sandboxProject);
|
||||
appPackages = stats.getPackagesForAppIdInTest(sandboxProject);
|
||||
selftest.expectEqual(appPackages.userId, testUtils.getUserId(s));
|
||||
selftest.expectEqual(_.sortBy(appPackages.packages, "name"),
|
||||
_.sortBy(stats.packageList(sandboxProject), "name"));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Bundle the app in the current working directory after deleting its
|
||||
// identifier file (meaning a new one will be created).
|
||||
// @param s {Sandbox}
|
||||
var bundleWithFreshIdentifier = function (s) {
|
||||
var bundleWithFreshIdentifier = function (s, sandboxProject) {
|
||||
s.unlink(".meteor/identifier");
|
||||
bundle(s);
|
||||
bundle(s, sandboxProject);
|
||||
};
|
||||
|
||||
// Bundle the app in the current working directory.
|
||||
// @param s {Sandbox}
|
||||
var bundle = function (s) {
|
||||
var bundle = function (s, sandboxProject) {
|
||||
var run = s.run("bundle", "foo.tar.gz");
|
||||
run.waitSecs(30);
|
||||
run.expectExit(0);
|
||||
@@ -91,7 +206,7 @@ var bundle = function (s) {
|
||||
// XXX not sure why this is necessary (i.e. why project can't detect
|
||||
// that .meteor/identifier or .meteor/packages has changed and figure
|
||||
// out that it needs to reload itself)
|
||||
project.project.reload();
|
||||
sandboxProject.reload();
|
||||
};
|
||||
|
||||
// Contact the package stats server and look for a given app
|
||||
@@ -125,3 +240,8 @@ var fetchPackageUsageForApp = function (identifier) {
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
var getClientAddress = function () {
|
||||
var stats = testUtils.ddpConnect(testStatsServer);
|
||||
return stats.call("getClientAddress");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user