diff --git a/packages/autoupdate/autoupdate_client.js b/packages/autoupdate/autoupdate_client.js index 79512758b8..000f5a2da6 100644 --- a/packages/autoupdate/autoupdate_client.js +++ b/packages/autoupdate/autoupdate_client.js @@ -30,19 +30,16 @@ var autoupdateVersionRefreshable = // The collection of acceptable client versions. ClientVersions = new Meteor.Collection("meteor_autoupdate_clientVersions"); -ClientVersionsRefreshable = - new Meteor.Collection("meteor_autoupdate_clientVersions_refreshable"); Autoupdate = {}; Autoupdate.newClientAvailable = function () { - return !! ClientVersions.findOne( - { current: true, - _id: {$ne: autoupdateVersion} - }) || !! ClientVersionsRefreshable.findOne( - { current: true, - _id: {$ne: autoupdateVersionRefreshable} - }); + return !! ClientVersions.findOne({ + refreshable: false, + version: {$ne: autoupdateVersion} }) || + !! ClientVersions.findOne({ + refreshable: true, + version: {$ne: autoupdateVersionRefreshable} }); }; var knownToSupportCssOnLoad = false; @@ -79,73 +76,71 @@ Autoupdate._retrySubscription = function () { }, onReady: function () { if (Package.reload) { - var handle = ClientVersions.find().observeChanges({ - added: function (id, fields) { - var self = this; - if (id !== autoupdateVersion) { - if (handle) { - handle.stop(); - Package.reload.Reload._reload(); + var checkNewVersionDocument = function (id, fields) { + var self = this; + var isRefreshable = id === 'version-refreshable'; + if (isRefreshable && + fields.version !== autoupdateVersionRefreshable) { + autoupdateVersionRefreshable = fields.version; + // Switch out old css links for the new css links. Inspired by: + // https://github.com/guard/guard-livereload/blob/master/js/livereload.js#L710 + var newCss = fields.assets.allCss; + var oldLinks = []; + _.each(document.getElementsByTagName('link'), function (link) { + if (link.className === '__meteor-css__') { + oldLinks.push(link); } - } - } - }); + }); - ClientVersionsRefreshable.find().observeChanges({ - added: function (id, fields) { - if (id !== autoupdateVersionRefreshable) { - autoupdateVersionRefreshable = id; + var waitUntilCssLoads = function (link, callback) { + var executeCallback = _.once(callback); + link.onload = function () { + knownToSupportCssOnLoad = true; + executeCallback(); + }; + if (! knownToSupportCssOnLoad) { + var id = Meteor.setInterval(function () { + if (link.sheet) { + executeCallback(); + Meteor.clearInterval(id); + } + }, 50); + } + }; - // Switch out old css links for the new css links. Inspired by: - // https://github.com/guard/guard-livereload/blob/master/js/livereload.js#L710 - var newCss = fields.assets.allCss; - var oldLinks = []; - _.each(document.getElementsByTagName('link'), function (link) { - if (link.className === '__meteor-css__') { - oldLinks.push(link); - } + var attachStylesheetLink = function (newLink) { + var removeOldLinks = _.after(newCss.length, function () { + _.each(oldLinks, function (oldLink) { + oldLink.parentNode.removeChild(oldLink); + }); }); - var waitUntilCssLoads = function (link, callback) { - var executeCallback = _.once(callback); - link.onload = function () { - knownToSupportCssOnLoad = true; - executeCallback(); - }; - if (! knownToSupportCssOnLoad) { - var id = Meteor.setInterval(function () { - if (link.sheet) { - executeCallback(); - Meteor.clearInterval(id); - } - }, 50); - } - }; + document.getElementsByTagName("head").item(0).appendChild(newLink); - var attachStylesheetLink = function (newLink) { - var removeOldLinks = _.after(newCss.length, function () { - _.each(oldLinks, function (oldLink) { - oldLink.parentNode.removeChild(oldLink); - }); - }); - - document.getElementsByTagName("head").item(0).appendChild(newLink); - - waitUntilCssLoads(newLink, function () { - Meteor.setTimeout(removeOldLinks, 200); - }); - }; - - _.each(newCss, function (css) { - var newLink = document.createElement("link"); - newLink.setAttribute("rel", "stylesheet"); - newLink.setAttribute("type", "text/css"); - newLink.setAttribute("class", "__meteor-css__"); - newLink.setAttribute("href", css.url); - attachStylesheetLink(newLink); + waitUntilCssLoads(newLink, function () { + Meteor.setTimeout(removeOldLinks, 200); }); - } + }; + + _.each(newCss, function (css) { + var newLink = document.createElement("link"); + newLink.setAttribute("rel", "stylesheet"); + newLink.setAttribute("type", "text/css"); + newLink.setAttribute("class", "__meteor-css__"); + newLink.setAttribute("href", css.url); + attachStylesheetLink(newLink); + }); } + else if (! isRefreshable && + fields.version !== autoupdateVersionRefreshable && handle) { + handle.stop(); + Package.reload.Reload._reload(); + } + }; + + var handle = ClientVersions.find().observeChanges({ + added: checkNewVersionDocument, + changed: checkNewVersionDocument }); } } diff --git a/packages/autoupdate/autoupdate_server.js b/packages/autoupdate/autoupdate_server.js index a04f154325..56de4ea266 100644 --- a/packages/autoupdate/autoupdate_server.js +++ b/packages/autoupdate/autoupdate_server.js @@ -43,9 +43,6 @@ Autoupdate = {}; // The collection of acceptable client versions. ClientVersions = new Meteor.Collection("meteor_autoupdate_clientVersions", { connection: null }); -ClientVersionsRefreshable = - new Meteor.Collection("meteor_autoupdate_clientVersions_refreshable", - { connection: null }); // The client hash includes __meteor_runtime_config__, so wait until // all packages have loaded and have had a chance to populate the @@ -61,9 +58,6 @@ var startupVersion = null; // updateVersions can only be called after the server has fully loaded. var updateVersions = function (shouldReloadClientProgram) { syncQueue.runTask(function () { - var oldVersion = Autoupdate.autoupdateVersion; - var oldVersionRefreshable = Autoupdate.autoupdateVersionRefreshable; - // Step 1: load the current client program on the server and update the // hash values in __meteor_runtime_config__. if (shouldReloadClientProgram) { @@ -90,25 +84,30 @@ var updateVersions = function (shouldReloadClientProgram) { WebAppInternals.generateBoilerplate(); } - if (Autoupdate.autoupdateVersion !== oldVersion) { - if (oldVersion) { - ClientVersions.remove(oldVersion); - } - + if (! ClientVersions.findOne({_id: "version"})) { ClientVersions.insert({ - _id: Autoupdate.autoupdateVersion, - current: true + _id: "version", + refreshable: false, + version: Autoupdate.autoupdateVersion, }); + } else { + ClientVersions.update("version", { $set: { + version: Autoupdate.autoupdateVersion, + }}); } - if (Autoupdate.autoupdateVersionRefreshable !== oldVersionRefreshable) { - if (oldVersionRefreshable) { - ClientVersionsRefreshable.remove(oldVersionRefreshable); - } - ClientVersionsRefreshable.insert({ - _id: Autoupdate.autoupdateVersionRefreshable, + if (! ClientVersions.findOne({_id: "version-refreshable"})) { + ClientVersions.insert({ + _id: "version-refreshable", + version: Autoupdate.autoupdateVersionRefreshable, + refreshable: true, assets: WebAppInternals.refreshableAssets }); + } else { + ClientVersions.update("version-refreshable", { $set: { + version: Autoupdate.autoupdateVersionRefreshable, + assets: WebAppInternals.refreshableAssets + }}); } }); }; @@ -125,7 +124,7 @@ Meteor.startup(function () { Meteor.publish( "meteor_autoupdate_clientVersions", function () { - return [ ClientVersions.find(), ClientVersionsRefreshable.find() ]; + return ClientVersions.find(); }, {is_auto: true} ); diff --git a/tools/buildmessage.js b/tools/buildmessage.js index 863bdde618..2c5939dc2d 100644 --- a/tools/buildmessage.js +++ b/tools/buildmessage.js @@ -325,8 +325,8 @@ var exception = function (error) { // XXX this may be the wrong place to do this, but it makes syntax errors in // files loaded via unipackage.load have context. if (error instanceof files.FancySyntaxError) { - error.message = "Syntax error: " + error.message + " at " + - error.file + ":" + error.line + ":" + error.column; + error = new Error("Syntax error: " + error.message + " at " + + error.file + ":" + error.line + ":" + error.column); } throw error; } diff --git a/tools/bundler.js b/tools/bundler.js index d78b940d8b..69bcd8b282 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -1648,8 +1648,6 @@ exports.bundle = function (options) { var buildOptions = options.buildOptions || {}; var appDir = project.project.rootDir; - if (! release.usingRightReleaseForApp(appDir)) - throw new Error("running wrong release for app?"); var serverArch = buildOptions.serverArch || archinfo.host(); var webArchs = buildOptions.webArchs || [ "web.browser" ]; @@ -1668,6 +1666,9 @@ exports.bundle = function (options) { var messages = buildmessage.capture({ title: "building the application" }, function () { + if (! release.usingRightReleaseForApp(appDir)) + throw new Error("running wrong release for app?"); + var packageLoader = project.project.getPackageLoader(); var downloaded = tropohouse.default.downloadMissingPackages( project.project.dependencies, { serverArch: serverArch }); diff --git a/tools/catalog-base.js b/tools/catalog-base.js index 3e787c08a4..6be67c20c9 100644 --- a/tools/catalog-base.js +++ b/tools/catalog-base.js @@ -105,6 +105,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { // the inevitable rather than slowing down normal operations) _recordOrRefresh: function (recordFinder) { var self = this; + buildmessage.assertInCapture(); var record = recordFinder(); // If we cannot find it maybe refresh. if (!record) { @@ -124,6 +125,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { // release track, or null if there is no such release track. getReleaseTrack: function (name) { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); return self._recordOrRefresh(function () { return _.findWhere(self.releaseTracks, { name: name }); @@ -138,6 +140,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { // serious refactoring. getReleaseVersion: function (track, version, notInitialized) { var self = this; + buildmessage.assertInCapture(); if (!notInitialized) self._requireInitialized(); return self._recordOrRefresh(function () { return _.findWhere(self.releaseVersions, @@ -186,6 +189,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { // package, or null if there is no such package. getPackage: function (name) { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); return self._recordOrRefresh(function () { return _.findWhere(self.packages, { name: name }); @@ -213,6 +217,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { getVersion: function (name, version) { var self = this; self._requireInitialized(); + buildmessage.assertInCapture(); var lookupVersion = function () { return _.has(self.versions, name) && @@ -250,6 +255,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { getLatestVersion: function (name) { var self = this; self._requireInitialized(); + buildmessage.assertInCapture(); var versions = self.getSortedVersions(name); if (versions.length === 0) @@ -263,6 +269,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { getBuildsForArches: function (name, version, arches) { var self = this; self._requireInitialized(); + buildmessage.assertInCapture(); var versionInfo = self.getVersion(name, version); if (! versionInfo) @@ -298,6 +305,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { getBuildWithPreciseBuildArchitectures: function (versionRecord, buildArchitectures) { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); return self._recordOrRefresh(function () { @@ -310,6 +318,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { getAllBuilds: function (name, version) { var self = this; self._requireInitialized(); + buildmessage.assertInCapture(); var versionRecord = self.getVersion(name, version); if (!versionRecord) @@ -324,6 +333,7 @@ _.extend(baseCatalog.BaseCatalog.prototype, { // version). getDefaultReleaseVersion: function (track) { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); if (!track) diff --git a/tools/catalog.js b/tools/catalog.js index 1d66a399e1..c44c1275ac 100644 --- a/tools/catalog.js +++ b/tools/catalog.js @@ -77,6 +77,7 @@ _.extend(OfficialCatalog.prototype, { // the in-progress refresh to finish. refresh: function () { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); if (self._refreshFutures) { @@ -212,6 +213,7 @@ _.extend(CompleteCatalog.prototype, { // silently ignored. initialize: function (options) { var self = this; + buildmessage.assertInCapture(); options = options || {}; @@ -259,9 +261,7 @@ _.extend(CompleteCatalog.prototype, { var self = this; opts = opts || {}; self._requireInitialized(); - // XXX for now, just doing the assertion if we have to call project - // stuff. but oh, this will be improved. - opts.ignoreProjectDeps || buildmessage.assertInCapture(); + buildmessage.assertInCapture(); // Kind of a hack, as per specification. We don't have a constraint solver // initialized yet. We are probably trying to build the constraint solver @@ -335,9 +335,12 @@ _.extend(CompleteCatalog.prototype, { // anyway. When we are using hot code push, we may be restarting the app // because of a local package change that impacts that catalog. Don't wait // on the official catalog to refresh data.json, in this case. + // - watchSet: if provided, any files read in reloading packages will be added + // to this set. refresh: function (options) { var self = this; options = options || {}; + buildmessage.assertInCapture(); // We need to limit the rate of refresh, or, at least, prevent any sort of // loops. ForceRefresh will override either one. @@ -355,7 +358,7 @@ _.extend(CompleteCatalog.prototype, { self._insertServerPackages(localData); self._recomputeEffectiveLocalPackages(); - self._addLocalPackageOverrides(); + self._addLocalPackageOverrides({watchSet: options.watchSet}); self.initialized = true; // Rebuild the resolver, since packages may have changed. self._initializeResolver(); @@ -416,8 +419,10 @@ _.extend(CompleteCatalog.prototype, { // first removing any existing packages that have the same name. // // XXX emits buildmessages. are callers expecting that? - _addLocalPackageOverrides: function () { + _addLocalPackageOverrides: function (options) { var self = this; + options = options || {}; + buildmessage.assertInCapture(); // Remove all packages from the catalog that have the same name as // a local package, along with all of their versions and builds. @@ -462,8 +467,19 @@ _.extend(CompleteCatalog.prototype, { if (buildmessage.jobHasMessages()) broken = true; }); + + if (options.watchSet) { + options.watchSet.merge(packageSource.pluginWatchSet); + _.each(packageSource.architectures, function (sourceArch) { + options.watchSet.merge(sourceArch.watchSet); + }); + } + + // Recover by ignoring, but not until after we've augmented the watchSet + // (since we want the watchSet to include files with problems that the + // user may fix!) if (broken) - return; // recover by ignoring + return; packageSources[name] = packageSource; @@ -724,6 +740,7 @@ _.extend(CompleteCatalog.prototype, { // function twice with the same `name`. addLocalPackage: function (name, directory) { var self = this; + buildmessage.assertInCapture(); self._requireInitialized(); var resolvedPath = path.resolve(directory); @@ -741,20 +758,6 @@ _.extend(CompleteCatalog.prototype, { self.refresh(); }, - // Reverse the effect of addLocalPackage. - removeLocalPackage: function (name) { - var self = this; - self._requireInitialized(); - - if (! _.has(self.localPackages, name)) - throw new Error("no such local package?"); - delete self.localPackages[name]; - - // see #CallingRefreshEveryTimeLocalPackagesChange - self._recomputeEffectiveLocalPackages(); - self.refresh(); - }, - // True if `name` is a local package (is to be loaded via // localPackageDirs or addLocalPackage rather than from the package // server) diff --git a/tools/commands-packages.js b/tools/commands-packages.js index 0927f30da9..9661984b2d 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -24,11 +24,13 @@ var compiler = require('./compiler.js'); var catalog = require('./catalog.js'); var stats = require('./stats.js'); var unipackage = require('./unipackage.js'); +var packageLoader = require('./package-loader.js'); // Returns an object with keys: // record : (a package or version record) // release : true if it is a release instead of a package. var getReleaseOrPackageRecord = function(name) { + buildmessage.assertInCapture(); // Too lazy to do string parsing. var rec = catalog.official.getPackage(name); var rel = false; @@ -68,6 +70,96 @@ var formatList = function (items) { return out; }; +// Seriously, this dies if it can't refresh. Only call it if you're sure you're +// OK that the command doesn't work while offline. +var doOrDie = exports.doOrDie = function (f) { + var ret; + var messages = buildmessage.capture(function () { + ret = f(); + }); + if (messages.hasMessages()) { + process.stderr.write(messages.formatMessages()); + throw main.ExitWithCode(1); + } + return ret; +}; + +var refreshOfficialCatalogOrDie = function () { + doOrDie(function () { + catalog.official.refresh(); + }); +}; + + +// Internal use only. Makes sure that your Meteor install is totally good to go +// (is "airplane safe"). Specifically, it: +// - Builds all local packages (including their npm dependencies) +// - Ensures that all packages in your current release are downloaded +// - Ensures that all packages used by your app (if any) are downloaded +// (It also ensures you have the dev bundle downloaded, just like every command +// in a checkout.) +// +// The use case is, for example, cloning an app from github, running this +// command, then getting on an airplane. +// +// This does NOT guarantee a *re*build of all local packages (though it will +// download any new dependencies). If you want to rebuild all local packages, +// call meteor rebuild. That said, rebuild should only be necessary if there's a +// bug in the build tool... otherwise, packages should be rebuilt whenever +// necessary! +main.registerCommand({ + name: '--get-ready' +}, function (options) { + // It is not strictly needed, but it is thematically a good idea to refresh + // the official catalog when we call get-ready, since it is an + // internet-requiring action. + refreshOfficialCatalogOrDie(); + + var loadPackages = function (packagesToLoad, loader) { + buildmessage.assertInCapture(); + loader.downloadMissingPackages(); + _.each(packagesToLoad, function (name) { + // Calling getPackage on the loader will return a unipackage object, which + // means that the package will be compiled/downloaded. That we throw the + // package variable away afterwards is immaterial. + loader.getPackage(name); + }); + }; + + var messages = buildmessage.capture({ + title: 'getting packages ready' + }, function () { + // First, build all accessible *local* packages, whether or not this app + // uses them. Use the "all packages are local" loader. + loadPackages(catalog.complete.getLocalPackageNames(), + new packageLoader.PackageLoader({versions: null})); + + // In an app? Get the list of packages used by this app. Calling getVersions + // on the project will ensureDepsUpToDate which will ensure that all builds + // of everything we need from versions have been downloaded. (Calling + // buildPackages may be redundant, but can't hurt.) + if (options.appDir) { + loadPackages(_.keys(project.getVersions()), project.getPackageLoader()); + } + + // Using a release? Get all the packages in the release. + if (release.current.isProperRelease()) { + var releasePackages = release.current.getPackages(); + loadPackages( + _.keys(releasePackages), + new packageLoader.PackageLoader({versions: releasePackages})); + } + }); + + if (messages.hasMessages()) { + process.stderr.write("\n" + messages.formatMessages()); + return 1; + }; + + console.log("You are ready!"); + return 0; +}); + /////////////////////////////////////////////////////////////////////////////// // publish a package @@ -97,12 +189,15 @@ main.registerCommand({ // Refresh the catalog, caching the remote package data on the server. We can // optimize the workflow by using this data to weed out obviously incorrect // submissions before they ever hit the wire. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); var packageName = path.basename(options.packageDir); // Fail early if the package already exists. if (options.create) { - if (catalog.official.getPackage(packageName)) { + var packageInfo = doOrDie(function () { + return catalog.official.getPackage(packageName); + }); + if (packageInfo) { process.stderr.write("Package already exists. To create a new version of an existing "+ "package, do not use the --create flag! \n"); return 2; @@ -184,7 +279,7 @@ main.registerCommand({ // then exit with the previous error code. conn.close(); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); return ec; }); @@ -207,9 +302,12 @@ main.registerCommand({ var versionString = all[1]; // Refresh the catalog, cacheing the remote package data on the server. - catalog.official.refresh(true); + refreshOfficialCatalogOrDie(); - if (! catalog.complete.getPackage(name)) { + var packageInfo = doOrDie(function () { + return catalog.complete.getPackage(name); + }); + if (! packageInfo) { process.stderr.write( "You can't call `meteor publish-for-arch` on package '" + name + "' without\n" + "publishing it first.\n\n" + @@ -217,7 +315,9 @@ main.registerCommand({ return 1; } - var pkgVersion = catalog.official.getVersion(name, versionString); + var pkgVersion = doOrDie(function () { + return catalog.official.getVersion(name, versionString); + }); if (! pkgVersion) { process.stderr.write( "You can't call `meteor publish-for-arch` on version " + versionString + " of\n" + @@ -308,7 +408,7 @@ main.registerCommand({ return 1; } - catalog.official.refresh(); // XXX buildmessage.capture? + refreshOfficialCatalogOrDie(); return 0; }); @@ -323,7 +423,7 @@ main.registerCommand({ }, function (options) { // Refresh the catalog, cacheing the remote package data on the server. process.stdout.write("Resyncing with package server...\n"); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); try { var conn = packageClient.loggedInPackagesConnection(); @@ -416,7 +516,10 @@ main.registerCommand({ // authorized to publish before we do any complicated/long operations, and // before we publish its packages. if (!options['create-track']) { - var trackRecord = catalog.official.getReleaseTrack(relConf.track); + var trackRecord; + doOrDie(function () { + trackRecord = catalog.official.getReleaseTrack(relConf.track); + }); if (!trackRecord) { process.stderr.write('\n There is no release track named ' + relConf.track + '. If you are creating a new track, use the --create-track flag.\n'); @@ -650,15 +753,16 @@ main.registerCommand({ continue; var prebuilt = toPublish[name]; - var opts = { - new: !catalog.official.getPackage(name) - }; process.stdout.write("Publishing package: " + name + "\n"); var pubEC; // XXX merge with messages? messages = buildmessage.capture({ title: "publishing package " + name }, function () { + var opts = { + new: !catalog.official.getPackage(name) + }; + // If we are creating a new package, dsPS will document this for us, so // we don't need to do this here. Though, in the future, once we are // done bootstrapping package servers, we should consider having some @@ -720,7 +824,7 @@ main.registerCommand({ } // Get it back. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); process.stdout.write("Done creating " + relConf.track + "@" + relConf.version + "!\n"); @@ -763,12 +867,18 @@ main.registerCommand({ return 1; } - catalog.official.refresh(); + // XXX this is dumb, we should be able to search even if we can't + // refresh. let's make sure to differentiate "horrible parse error while + // refreshing" from "can't connect to catalog" + refreshOfficialCatalogOrDie(); if (options.details) { var full = options.args[0].split('@'); var name = full[0]; - var allRecord = getReleaseOrPackageRecord(name); + var allRecord; + doOrDie(function () { + allRecord = getReleaseOrPackageRecord(name); + }); var record = allRecord.record; if (!record) { process.stderr.write("Unknown package or release: " + name + "\n"); @@ -779,11 +889,12 @@ main.registerCommand({ if (!allRecord.isRelease) { label = "package"; var getRelevantRecord = function (version) { - var versionRecord = - catalog.official.getVersion(name, version); - var myBuilds = _.pluck( - catalog.official.getAllBuilds(name, version), - 'buildArchitectures'); + var versionRecord = doOrDie(function () { + return catalog.official.getVersion(name, version); + }); + var myBuilds = _.pluck(doOrDie(function () { + return catalog.official.getAllBuilds(name, version); + }), 'buildArchitectures'); // Does this package only have a cross-platform build? if (myBuilds.length === 1) { var allArches = myBuilds[0].split('+'); @@ -807,12 +918,16 @@ main.registerCommand({ } else { label = "release"; if (full.length > 1) { - versionRecords = [catalog.official.getReleaseVersion(name, full[1])]; + doOrDie(function () { + versionRecords = [catalog.official.getReleaseVersion(name, full[1])]; + }); } else { versionRecords = _.map(catalog.official.getSortedRecommendedReleaseVersions(name, ""), function (v) { - return catalog.official.getReleaseVersion(name, v); + return doOrDie(function () { + return catalog.official.getReleaseVersion(name, v); + }); }); } } @@ -878,7 +993,7 @@ main.registerCommand({ search = new RegExp(options.args[0]); } catch (err) { process.stderr.write(err + "\n"); - process.exit(1); + return 1; } if (options.maintainer) { @@ -891,11 +1006,14 @@ main.registerCommand({ // you update to a new version of meteor is not that dire. selector = function (packageName, isRelease) { var record; - if (isRelease) { - record = catalog.official.getReleaseTrack(packageName); - } else { - record = catalog.official.getPackage(packageName); - } + // XXX make sure search works while offline + doOrDie(function () { + if (isRelease) { + record = catalog.official.getReleaseTrack(packageName); + } else { + record = catalog.official.getPackage(packageName); + } + }); return packageName.match(search) && !!_.findWhere(record.maintainers, {username: username}); }; @@ -907,7 +1025,9 @@ main.registerCommand({ _.each(allPackages, function (pack) { if (selector(pack, false)) { - var vr = catalog.official.getLatestVersion(pack); + var vr = doOrDie(function () { + return catalog.official.getLatestVersion(pack); + }); if (vr) { matchingPackages.push( { name: pack, description: vr.description }); @@ -916,10 +1036,13 @@ main.registerCommand({ }); _.each(allReleases, function (track) { if (selector(track, true)) { - var vr = catalog.official.getDefaultReleaseVersion(track); + var vr = doOrDie(function () { + return catalog.official.getDefaultReleaseVersion(track); + }); if (vr) { - var vrlong = - catalog.official.getReleaseVersion(track, vr.version); + var vrlong = doOrDie(function () { + return catalog.official.getReleaseVersion(track, vr.version); + }); matchingReleases.push( { name: track, description: vrlong.description }); } @@ -1040,7 +1163,9 @@ main.registerCommand({ var couldNotContactServer = false; // Refresh the catalog, cacheing the remote package data on the server. - catalog.official.refresh(true); + // XXX should be able to update even without a refresh, esp to a specific + // server + refreshOfficialCatalogOrDie(); // If you are specifying packaging individually, you probably don't want to // update the release. @@ -1051,7 +1176,7 @@ main.registerCommand({ // Some basic checks to make sure that this command is being used correctly. if (options["packages-only"] && options["patch"]) { process.stderr.write("There is no such thing as a patch update to packages."); - return 1;; + return 1; } if (!options["packages-only"]) { @@ -1079,7 +1204,9 @@ main.registerCommand({ // but it should be a no-op next time (unless there actually was a new latest // release in the interim). if (! release.forced) { - var latestRelease = release.latestDownloaded(releaseTrack); + var latestRelease = doOrDie(function () { + return release.latestDownloaded(releaseTrack); + }); // Are we on some track without ANY recommended releases at all, // and the user ran 'meteor update' without specifying a release? We // really can't do much here. @@ -1179,7 +1306,9 @@ main.registerCommand({ return 1;; } var r = appRelease.split('@'); - var record = catalog.official.getReleaseVersion(r[0], r[1]); + var record = doOrDie(function () { + return catalog.official.getReleaseVersion(r[0], r[1]); + }); var updateTo = record.patchReleaseVersion; if (!updateTo) { process.stderr.write( @@ -1188,11 +1317,15 @@ main.registerCommand({ } releaseVersionsToTry = [updateTo]; } else if (release.forced) { - releaseVersionsToTry = [release.current.getReleaseVersion()]; + doOrDie(function () { + releaseVersionsToTry = [release.current.getReleaseVersion()]; + }); } else { // XXX clean up all this splitty stuff - var appReleaseInfo = catalog.official.getReleaseVersion( - appRelease.split('@')[0], appRelease.split('@')[1]); + var appReleaseInfo = doOrDie(function () { + return catalog.official.getReleaseVersion( + appRelease.split('@')[0], appRelease.split('@')[1]); + }); var appOrderKey = (appReleaseInfo && appReleaseInfo.orderKey) || null; releaseVersionsToTry = catalog.official.getSortedRecommendedReleaseVersions( releaseTrack, appOrderKey); @@ -1220,21 +1353,35 @@ main.registerCommand({ return 1; } var solutionReleaseVersion = _.find(releaseVersionsToTry, function (versionToTry) { - var releaseRecord = catalog.complete.getReleaseVersion(releaseTrack, versionToTry); + var releaseRecord = doOrDie(function () { + return catalog.complete.getReleaseVersion(releaseTrack, versionToTry); + }); if (!releaseRecord) throw Error("missing release record?"); - var constraints = project.calculateCombinedConstraints(releaseRecord.packages); + var constraints = doOrDie(function () { + return project.calculateCombinedConstraints(releaseRecord.packages); + }); try { - solutionPackageVersions = catalog.complete.resolveConstraints( - constraints, - { previousSolution: previousVersions }, - { ignoreProjectDeps: true }); + var messages = buildmessage.capture(function () { + solutionPackageVersions = catalog.complete.resolveConstraints( + constraints, + { previousSolution: previousVersions }, + { ignoreProjectDeps: true }); + }); + if (messages.hasMessages()) { + if (process.env.METEOR_UPDATE_DEBUG) { + process.stderr.write( + "Update to release " + releaseTrack + "@" + versionToTry + + " is impossible:\n" + messages.formatMessages()); + } + return false; + } } catch (e) { - // XXX we should make the error handling explicitly detectable, and not - // actually mention failures that are recoverable - process.stderr.write( - "Update to release " + releaseTrack + - "@" + versionToTry + " impossible: " + e.message + "\n"); + if (process.env.METEOR_UPDATE_DEBUG) { + process.stderr.write( + "Update to release " + releaseTrack + + "@" + versionToTry + " impossible: " + e.message + "\n"); + } return false; } return true; @@ -1322,13 +1469,21 @@ main.registerCommand({ // Call the constraint solver. This should not fail, since we are not adding // any constraints that we didn't have before. - var newVersions = catalog.complete.resolveConstraints(allPackages, { - previousSolution: versions, - breaking: !options.minor, - upgrade: upgradePackages - }, { - ignoreProjectDeps: true + var newVersions; + var messages = buildmessage.capture(function () { + newVersions = catalog.complete.resolveConstraints(allPackages, { + previousSolution: versions, + breaking: !options.minor, + upgrade: upgradePackages + }, { + ignoreProjectDeps: true + }); }); + if (messages.hasMessages()) { + process.stderr.write("Error resolving constraints for packages:\n" + + messages.formatMessages()); + return 1; + } // Just for the sake of good messages, check to see if anything changed. if (_.isEqual(newVersions, versions)) { @@ -1378,7 +1533,8 @@ main.registerCommand({ var failed = false; // Refresh the catalog, cacheing the remote package data on the server. - catalog.official.refresh(); + // XXX ensure this works while offline + refreshOfficialCatalogOrDie(); // Read in existing package dependencies. var packages = project.getConstraints(); @@ -1405,17 +1561,19 @@ main.registerCommand({ }); _.each(constraints, function (constraint) { // Check that the package exists. - if (! catalog.complete.getPackage(constraint.name)) { - process.stderr.write(constraint.name + ": no such package\n"); - failed = true; - return; - } + doOrDie(function () { + if (! catalog.complete.getPackage(constraint.name)) { + process.stderr.write(constraint.name + ": no such package\n"); + failed = true; + return; + } + }); // If the version was specified, check that the version exists. if (constraint.version !== null) { - var versionInfo = catalog.complete.getVersion( - constraint.name, - constraint.version); + var versionInfo = doOrDie(function () { + return catalog.complete.getVersion(constraint.name, constraint.version); + }); if (! versionInfo) { process.stderr.write( constraint.name + "@" + constraint.version + ": no such version\n"); @@ -1519,7 +1677,9 @@ main.registerCommand({ process.stdout.write("\n"); _.each(constraints, function (constraint) { var version = newVersions[constraint.name]; - var versionRecord = catalog.complete.getVersion(constraint.name, version); + var versionRecord = doOrDie(function () { + return catalog.complete.getVersion(constraint.name, version); + }); if (constraint.constraintString !== null && version !== constraint.version) { process.stdout.write("Added " + constraint.name + " at version " + version + @@ -1548,7 +1708,8 @@ main.registerCommand({ // server. Technically, we don't need to do this, since it is unlikely that // new data will change our constraint solver decisions. But as a user, I // would expect this command to update the local catalog. - catalog.official.refresh(true); + // XXX what if we're offline? + refreshOfficialCatalogOrDie(); // Read in existing package dependencies. var packages = project.getConstraints(); @@ -1621,7 +1782,7 @@ main.registerCommand({ }, function (options) { // We want the most recent information. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); var name = options.args[0]; // Yay, checking that options are correct. @@ -1637,7 +1798,10 @@ main.registerCommand({ } // Now let's get down to business! Fetching the thing. - var fullRecord = getReleaseOrPackageRecord(name); + var fullRecord; + doOrDie(function () { + fullRecord = getReleaseOrPackageRecord(name); + }); var record = fullRecord.record; if (!options.list) { @@ -1669,7 +1833,7 @@ main.registerCommand({ process.stderr.write("\n" + err + "\n"); } conn.close(); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); } process.stdout.write("\n The maintainers for " + name + " are:\n"); @@ -1682,6 +1846,212 @@ main.registerCommand({ return 0; }); + /////////////////////////////////////////////////////////////////////////////// +// admin make-bootstrap-tarballs +/////////////////////////////////////////////////////////////////////////////// + +main.registerCommand({ + name: 'admin make-bootstrap-tarballs', + minArgs: 2, + maxArgs: 2, + hidden: true +}, function (options) { + var releaseNameAndVersion = options.args[0]; + var outputDirectory = options.args[1]; + + // In this function, we want to use the official catalog everywhere, because + // we assume that all packages have been published (along with the release + // obviously) and we want to be sure to only bundle the published versions. + catalog.official.refresh(); + + var parsed = utils.splitConstraint(releaseNameAndVersion); + if (!parsed.constraint) + throw new main.ShowUsage; + + var release = doOrDie(function () { + return catalog.official.getReleaseVersion( + parsed.package, parsed.constraint); + }); + if (!release) { + // XXX this could also mean package unknown. + process.stderr.write('Release unknown: ' + releaseNameAndVersion + '\n'); + return 1; + } + + var toolPkg = release.tool && utils.splitConstraint(release.tool); + if (! (toolPkg && toolPkg.constraint)) + throw new Error("bad tool in release: " + toolPkg); + var toolPkgBuilds = doOrDie(function () { + return catalog.official.getAllBuilds( + toolPkg.package, toolPkg.constraint); + }); + if (!toolPkgBuilds) { + // XXX this could also mean package unknown. + process.stderr.write('Tool version unknown: ' + release.tool + '\n'); + return 1; + } + if (!toolPkgBuilds.length) { + process.stderr.write('Tool version has no builds: ' + release.tool + '\n'); + return 1; + } + + // XXX check to make sure this is the three arches that we want? it's easier + // during 0.9.0 development to allow it to just decide "ok, i just want to + // build the OSX tarball" though. + var buildArches = _.pluck(toolPkgBuilds, 'buildArchitectures'); + var osArches = _.map(buildArches, function (buildArch) { + var subArches = buildArch.split('+'); + var osArches = _.filter(subArches, function (subArch) { + return subArch.substr(0, 3) === 'os.'; + }); + if (osArches.length !== 1) { + throw Error("build architecture " + buildArch + " lacks unique os.*"); + } + return osArches[0]; + }); + + process.stderr.write( + 'Building bootstrap tarballs for architectures ' + + osArches.join(', ') + '\n'); + // Before downloading anything, check that the catalog contains everything we + // need for the OSes that the tool is built for. + var messages = buildmessage.capture(function () { + _.each(osArches, function (osArch) { + _.each(release.packages, function (pkgVersion, pkgName) { + buildmessage.enterJob({ + title: "looking up " + pkgName + "@" + pkgVersion + " on " + osArch + }, function () { + if (!catalog.official.getBuildsForArches(pkgName, pkgVersion, [osArch])) { + buildmessage.error("missing build of " + pkgName + "@" + pkgVersion + + " for " + osArch); + } + }); + }); + }); + }); + + if (messages.hasMessages()) { + process.stderr.write("\n" + messages.formatMessages()); + return 1; + }; + + files.mkdir_p(outputDirectory); + + // Get a copy of the data.json. + var dataTmpdir = files.mkdtemp(); + var tmpDataJson = path.join(dataTmpdir, 'data.json'); + + var savedData = packageClient.updateServerPackageData(null, { + packageStorageFile: tmpDataJson + }).data; + if (!savedData) { + // will have already printed an error + return 2; + } + + _.each(osArches, function (osArch) { + var tmpdir = files.mkdtemp(); + // We're going to build and tar up a tropohouse in a temporary directory; we + // don't want to use any of our local packages, so we use catalog.official + // instead of catalog. + // XXX update to '.meteor' when we combine houses + var tmpTropo = new tropohouse.Tropohouse( + path.join(tmpdir, '.meteor'), catalog.official); + var messages = buildmessage.capture(function () { + buildmessage.enterJob({ + title: "downloading tool package " + toolPkg.package + "@" + + toolPkg.constraint + }, function () { + tmpTropo.maybeDownloadPackageForArchitectures({ + packageName: toolPkg.package, + version: toolPkg.constraint, + architectures: [osArch] // XXX 'web.browser' too? + }); + }); + _.each(release.packages, function (pkgVersion, pkgName) { + buildmessage.enterJob({ + title: "downloading package " + pkgName + "@" + pkgVersion + }, function () { + tmpTropo.maybeDownloadPackageForArchitectures({ + packageName: pkgName, + version: pkgVersion, + architectures: [osArch] // XXX 'web.browser' too? + }); + }); + }); + }); + if (messages.hasMessages()) { + process.stderr.write("\n" + messages.formatMessages()); + return 1; + } + + // Install the data.json file we synced earlier. + files.copyFile(tmpDataJson, config.getPackageStorage(tmpTropo)); + + // Create the top-level 'meteor' symlink, which links to the latest tool's + // meteor shell script. + var toolUnipackagePath = + tmpTropo.packagePath(toolPkg.package, toolPkg.constraint); + var toolUnipackage = new unipackage.Unipackage; + toolUnipackage.initFromPath(toolPkg.package, toolUnipackagePath); + var toolRecord = _.findWhere(toolUnipackage.toolsOnDisk, {arch: osArch}); + if (!toolRecord) + throw Error("missing tool for " + osArch); + fs.symlinkSync( + path.join( + tmpTropo.packagePath(toolPkg.package, toolPkg.constraint, true), + toolRecord.path, + 'meteor'), + path.join(tmpTropo.root, 'meteor')); + + files.createTarball( + tmpTropo.root, + path.join(outputDirectory, 'meteor-bootstrap-' + osArch + '.tar.gz')); + }); + + return 0; +}); + +// We will document how to set banners on things in a later release. +main.registerCommand({ + name: 'admin set-banners', + minArgs: 1, + maxArgs: 1, + hidden: true +}, function (options) { + var bannersFile = options.args[0]; + try { + var bannersData = fs.readFileSync(bannersFile, 'utf8'); + bannersData = JSON.parse(bannersData); + } catch (e) { + process.stderr.write("Could not parse banners file: "); + process.stderr.write(e.message + "\n"); + return 1; + } + if (!bannersData.track) { + process.stderr.write("Banners file should have a 'track' key.\n"); + return 1; + } + if (!bannersData.banners) { + process.stderr.write("Banners file should have a 'banners' key.\n"); + return 1; + } + + try { + var conn = packageClient.loggedInPackagesConnection(); + } catch (err) { + packageClient.handlePackageServerConnectionError(err); + return 1; + } + + conn.call('setBannersOnReleases', bannersData.track, + bannersData.banners); + + // Refresh afterwards. + refreshOfficialCatalogOrDie(); + return 0; +}); + main.registerCommand({ name: 'admin recommend-release', minArgs: 1, @@ -1692,7 +2062,7 @@ main.registerCommand({ }, function (options) { // We want the most recent information. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); var release = options.args[0].split('@'); var name = release[0]; var version = release[1]; @@ -1702,7 +2072,10 @@ main.registerCommand({ } // Now let's get down to business! Fetching the thing. - var record = catalog.official.getReleaseTrack(name); + var record; + doOrDie(function () { + record = catalog.official.getReleaseTrack(name); + }); if (!record) { process.stderr.write('\n There is no release track named ' + name + '\n'); return 1; @@ -1731,7 +2104,7 @@ main.registerCommand({ process.stderr.write("\n" + err + "\n"); } conn.close(); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); return 0; }); @@ -1744,7 +2117,7 @@ main.registerCommand({ }, function (options) { // We want the most recent information. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); var package = options.args[0].split('@'); var name = package[0]; var version = package[1]; @@ -1755,7 +2128,9 @@ main.registerCommand({ var ecv = options.args[1]; // Now let's get down to business! Fetching the thing. - var record = catalog.official.getPackage(name); + var record = doOrDie(function () { + return catalog.official.getPackage(name); + }); if (!record) { process.stderr.write('\n There is no package named ' + name + '\n'); return 1; @@ -1780,7 +2155,7 @@ main.registerCommand({ process.stderr.write("\n" + err + "\n"); } conn.close(); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); return 0; }); @@ -1793,12 +2168,14 @@ main.registerCommand({ }, function (options) { // We want the most recent information. - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); var name = options.args[0]; var url = options.args[1]; // Now let's get down to business! Fetching the thing. - var record = catalog.official.getPackage(name); + var record = doOrDie(function () { + return catalog.official.getPackage(name); + }); if (!record) { process.stderr.write('\n There is no package named ' + name + '\n'); return 1; @@ -1821,7 +2198,7 @@ main.registerCommand({ process.stderr.write("\n" + err + "\n"); } conn.close(); - catalog.official.refresh(); + refreshOfficialCatalogOrDie(); return 0; }); diff --git a/tools/commands.js b/tools/commands.js index 402cb3d018..cb2ad41c47 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -19,12 +19,12 @@ var httpHelpers = require('./http-helpers.js'); var archinfo = require('./archinfo.js'); var tropohouse = require('./tropohouse.js'); var packageCache = require('./package-cache.js'); -var packageLoader = require('./package-loader.js'); var PackageSource = require('./package-source.js'); var compiler = require('./compiler.js'); var catalog = require('./catalog.js'); var stats = require('./stats.js'); var unipackage = require('./unipackage.js'); +var commandsPackages = require('./commands-packages.js'); // The architecture used by Galaxy servers; it's the architecture used // by 'meteor deploy'. @@ -60,6 +60,7 @@ var hostedWithGalaxy = function (site) { // version record for that package. var getLocalPackages = function () { var ret = {}; + buildmessage.assertInCapture(); var names = catalog.complete.getAllPackageNames(); _.each(names, function (name) { @@ -71,6 +72,7 @@ var getLocalPackages = function () { return ret; }; + /////////////////////////////////////////////////////////////////////////////// // options that act like commands /////////////////////////////////////////////////////////////////////////////// @@ -138,75 +140,6 @@ main.registerCommand({ return 0; }); -// Internal use only. Makes sure that your Meteor install is totally good to go -// (is "airplane safe"). Specifically, it: -// - Builds all local packages (including their npm dependencies) -// - Ensures that all packages in your current release are downloaded -// - Ensures that all packages used by your app (if any) are downloaded -// (It also ensures you have the dev bundle downloaded, just like every command -// in a checkout.) -// -// The use case is, for example, cloning an app from github, running this -// command, then getting on an airplane. -// -// This does NOT guarantee a *re*build of all local packages (though it will -// download any new dependencies). If you want to rebuild all local packages, -// call meteor rebuild. That said, rebuild should only be necessary if there's a -// bug in the build tool... otherwise, packages should be rebuilt whenever -// necessary! -main.registerCommand({ - name: '--get-ready' -}, function (options) { - // It is not strictly needed, but it is thematically a good idea to refresh - // the official catalog when we call get-ready, since it is an - // internet-requiring action. - catalog.official.refresh(); - - var loadPackages = function (packagesToLoad, loader) { - buildmessage.assertInCapture(); - loader.downloadMissingPackages(); - _.each(packagesToLoad, function (name) { - // Calling getPackage on the loader will return a unipackage object, which - // means that the package will be compiled/downloaded. That we throw the - // package variable away afterwards is immaterial. - loader.getPackage(name); - }); - }; - - var messages = buildmessage.capture({ - title: 'getting packages ready' - }, function () { - // First, build all accessible *local* packages, whether or not this app - // uses them. Use the "all packages are local" loader. - loadPackages(catalog.complete.getLocalPackageNames(), - new packageLoader.PackageLoader({versions: null})); - - // In an app? Get the list of packages used by this app. Calling getVersions - // on the project will ensureDepsUpToDate which will ensure that all builds - // of everything we need from versions have been downloaded. (Calling - // buildPackages may be redundant, but can't hurt.) - if (options.appDir) { - loadPackages(_.keys(project.getVersions()), project.getPackageLoader()); - } - - // Using a release? Get all the packages in the release. - if (release.current.isProperRelease()) { - var releasePackages = release.current.getPackages(); - loadPackages( - _.keys(releasePackages), - new packageLoader.PackageLoader({versions: releasePackages})); - } - }); - - if (messages.hasMessages()) { - process.stderr.write("\n" + messages.formatMessages()); - return 1; - }; - - console.log("You are ready!"); - return 0; -}); - /////////////////////////////////////////////////////////////////////////////// // run /////////////////////////////////////////////////////////////////////////////// @@ -343,7 +276,9 @@ main.registerCommand({ var relString; if (release.current.isCheckout()) { xn = xn.replace(/~cc~/g, "//"); - var rel = catalog.complete.getDefaultReleaseVersion(); + var rel = commandsPackages.doOrDie(function () { + return catalog.complete.getDefaultReleaseVersion(); + }); var relString = rel.track + "@" + rel.version; } else { xn = xn.replace(/~cc~/g, ""); @@ -381,10 +316,12 @@ main.registerCommand({ // (In particular, it's not sufficient to create the new app with // this version of the tools, and then stamp on the correct release // at the end.) - if (! release.current.isCheckout() && - release.current.name !== release.latestDownloaded() && - ! release.forced) { - throw new main.SpringboardToLatestRelease; + if (! release.current.isCheckout() && !release.forced) { + var needToSpringboard = commandsPackages.doOrDie(function () { + return release.current.name !== release.latestDownloaded(); + }); + if (needToSpringboard) + throw new main.SpringboardToLatestRelease; } var exampleDir = path.join(__dirname, '..', 'examples'); @@ -532,7 +469,7 @@ main.registerCommand({ process.stderr.write("Invalid architecture: " + options.architecture + "\n"); process.stderr.write( "Please use one of the following: " + VALID_ARCHITECTURES + "\n"); - process.exit(1); + return 1; } var bundleArch = options.architecture || archinfo.host(); @@ -979,7 +916,9 @@ main.registerCommand({ if (options.args.length === 0) { // Only test local packages if no package is specified. // XXX should this use the new getLocalPackageNames? - var packageList = getLocalPackages(); + var packageList = commandsPackages.doOrDie(function () { + return getLocalPackages(); + }); if (! packageList) { // Couldn't load the package list, probably because some package // has a parse error. Bail out -- this kind of sucks; we would @@ -1071,12 +1010,18 @@ main.registerCommand({ // compute them for us. This means that right now, we are testing all packages // as they work together. var tests = []; - _.each(testPackages, function(name) { - var versionRecord = catalog.complete.getLatestVersion(name); - if (versionRecord && versionRecord.testName) { - tests.push(versionRecord.testName); - } + var messages = buildmessage.capture(function () { + _.each(testPackages, function(name) { + var versionRecord = catalog.complete.getLatestVersion(name); + if (versionRecord && versionRecord.testName) { + tests.push(versionRecord.testName); + } + }); }); + if (messages.hasMessages()) { + process.stderr.write(messages.formatMessages()); + return 1; + } project.forceEditPackages(tests, 'add'); @@ -1114,10 +1059,6 @@ main.registerCommand({ }); } - _.each(localPackageNames, function (name) { - catalog.complete.removeLocalPackage(name); - }); - return ret; }); @@ -1204,212 +1145,6 @@ main.registerCommand({ return auth.whoAmICommand(options); }); -/////////////////////////////////////////////////////////////////////////////// -// admin make-bootstrap-tarballs -/////////////////////////////////////////////////////////////////////////////// - -main.registerCommand({ - name: 'admin make-bootstrap-tarballs', - minArgs: 2, - maxArgs: 2, - hidden: true, -}, function (options) { - var releaseNameAndVersion = options.args[0]; - var outputDirectory = options.args[1]; - - // In this function, we want to use the official catalog everywhere, because - // we assume that all packages have been published (along with the release - // obviously) and we want to be sure to only bundle the published versions. - catalog.official.refresh(); - - var parsed = utils.splitConstraint(releaseNameAndVersion); - if (!parsed.constraint) - throw new main.ShowUsage; - - var release = catalog.official.getReleaseVersion(parsed.package, - parsed.constraint); - if (!release) { - // XXX this could also mean package unknown. - process.stderr.write('Release unknown: ' + releaseNameAndVersion + '\n'); - return 1; - } - - var toolPkg = release.tool && utils.splitConstraint(release.tool); - if (! (toolPkg && toolPkg.constraint)) - throw new Error("bad tool in release: " + toolPkg); - var toolPkgBuilds = catalog.official.getAllBuilds( - toolPkg.package, toolPkg.constraint); - if (!toolPkgBuilds) { - // XXX this could also mean package unknown. - process.stderr.write('Tool version unknown: ' + release.tool + '\n'); - return 1; - } - if (!toolPkgBuilds.length) { - process.stderr.write('Tool version has no builds: ' + release.tool + '\n'); - return 1; - } - - // XXX check to make sure this is the three arches that we want? it's easier - // during 0.9.0 development to allow it to just decide "ok, i just want to - // build the OSX tarball" though. - var buildArches = _.pluck(toolPkgBuilds, 'buildArchitectures'); - var osArches = _.map(buildArches, function (buildArch) { - var subArches = buildArch.split('+'); - var osArches = _.filter(subArches, function (subArch) { - return subArch.substr(0, 3) === 'os.'; - }); - if (osArches.length !== 1) { - throw Error("build architecture " + buildArch + " lacks unique os.*"); - } - return osArches[0]; - }); - - process.stderr.write( - 'Building bootstrap tarballs for architectures ' + - osArches.join(', ') + '\n'); - // Before downloading anything, check that the catalog contains everything we - // need for the OSes that the tool is built for. - var messages = buildmessage.capture(function () { - _.each(osArches, function (osArch) { - _.each(release.packages, function (pkgVersion, pkgName) { - buildmessage.enterJob({ - title: "looking up " + pkgName + "@" + pkgVersion + " on " + osArch - }, function () { - if (!catalog.official.getBuildsForArches(pkgName, pkgVersion, [osArch])) { - buildmessage.error("missing build of " + pkgName + "@" + pkgVersion + - " for " + osArch); - } - }); - }); - }); - }); - - if (messages.hasMessages()) { - process.stderr.write("\n" + messages.formatMessages()); - return 1; - }; - - files.mkdir_p(outputDirectory); - - // Get a copy of the data.json. - var dataTmpdir = files.mkdtemp(); - var tmpDataJson = path.join(dataTmpdir, 'data.json'); - - var savedData = packageClient.updateServerPackageData(null, { - packageStorageFile: tmpDataJson - }).data; - if (!savedData) { - // will have already printed an error - process.exit(2); - } - - _.each(osArches, function (osArch) { - var tmpdir = files.mkdtemp(); - // We're going to build and tar up a tropohouse in a temporary directory; we - // don't want to use any of our local packages, so we use catalog.official - // instead of catalog. - // XXX update to '.meteor' when we combine houses - var tmpTropo = new tropohouse.Tropohouse( - path.join(tmpdir, '.meteor'), catalog.official); - var messages = buildmessage.capture(function () { - buildmessage.enterJob({ - title: "downloading tool package " + toolPkg.package + "@" + - toolPkg.constraint - }, function () { - tmpTropo.maybeDownloadPackageForArchitectures({ - packageName: toolPkg.package, - version: toolPkg.constraint, - architectures: [osArch] // XXX 'web.browser' too? - }); - }); - _.each(release.packages, function (pkgVersion, pkgName) { - buildmessage.enterJob({ - title: "downloading package " + pkgName + "@" + pkgVersion - }, function () { - tmpTropo.maybeDownloadPackageForArchitectures({ - packageName: pkgName, - version: pkgVersion, - architectures: [osArch] // XXX 'web.browser' too? - }); - }); - }); - }); - if (messages.hasMessages()) { - process.stderr.write("\n" + messages.formatMessages()); - return 1; - } - - // Install the data.json file we synced earlier. - files.copyFile(tmpDataJson, config.getPackageStorage(tmpTropo)); - - // Create the top-level 'meteor' symlink, which links to the latest tool's - // meteor shell script. - var toolUnipackagePath = - tmpTropo.packagePath(toolPkg.package, toolPkg.constraint); - var toolUnipackage = new unipackage.Unipackage; - toolUnipackage.initFromPath(toolPkg.package, toolUnipackagePath); - var toolRecord = _.findWhere(toolUnipackage.toolsOnDisk, {arch: osArch}); - if (!toolRecord) - throw Error("missing tool for " + osArch); - fs.symlinkSync( - path.join( - tmpTropo.packagePath(toolPkg.package, toolPkg.constraint, true), - toolRecord.path, - 'meteor'), - path.join(tmpTropo.root, 'meteor')); - - files.createTarball( - tmpTropo.root, - path.join(outputDirectory, 'meteor-bootstrap-' + osArch + '.tar.gz')); - }); - - return 0; -}); - -/////////////////////////////////////////////////////////////////////////////// -// admin set-banners -/////////////////////////////////////////////////////////////////////////////// - -// We will document how to set banners on things in a later release. -main.registerCommand({ - name: 'admin set-banners', - minArgs: 1, - maxArgs: 1, - hidden: true, -}, function (options) { - var bannersFile = options.args[0]; - try { - var bannersData = fs.readFileSync(bannersFile, 'utf8'); - bannersData = JSON.parse(bannersData); - } catch (e) { - process.stderr.write("Could not parse banners file: "); - process.stderr.write(e.message + "\n"); - return 1; - } - if (!bannersData.track) { - process.stderr.write("Banners file should have a 'track' key.\n"); - return 1; - } - if (!bannersData.banners) { - process.stderr.write("Banners file should have a 'banners' key.\n"); - return 1; - } - - try { - var conn = packageClient.loggedInPackagesConnection(); - } catch (err) { - packageClient.handlePackageServerConnectionError(err); - return 1; - } - - conn.call('setBannersOnReleases', bannersData.track, - bannersData.banners); - - // Refresh afterwards. - catalog.official.refresh(); - return 0; -}); - /////////////////////////////////////////////////////////////////////////////// // self-test /////////////////////////////////////////////////////////////////////////////// diff --git a/tools/compiler.js b/tools/compiler.js index 373710b515..c34826313d 100644 --- a/tools/compiler.js +++ b/tools/compiler.js @@ -137,7 +137,7 @@ var determineBuildTimeDependencies = function (packageSource, constraintSolverOpts) { var ret = {}; constraintSolverOpts = constraintSolverOpts || {}; - constraintSolverOpts.ignoreProjectDeps || buildmessage.assertInCapture(); + buildmessage.assertInCapture(); // There are some special cases where we know that the package has no source // files, which means it can't have any interesting build-time @@ -898,6 +898,7 @@ compiler.compile = function (packageSource, options) { // so any dependencies that contains plugins have real versions in the // catalog already. Still, this seems very brittle and we should fix it. var getPluginProviders = function (versions) { + buildmessage.assertInCapture(); var result = {}; _.each(versions, function (version, name) { // Direct dependencies only create a build-order constraint if @@ -918,7 +919,7 @@ var getPluginProviders = function (versions) { compiler.getBuildOrderConstraints = function ( packageSource, constraintSolverOpts) { constraintSolverOpts = constraintSolverOpts || {}; - constraintSolverOpts.ignoreProjectDeps || buildmessage.assertInCapture(); + buildmessage.assertInCapture(); var versions = {}; // map from package name to version to true var addVersion = function (version, name) { diff --git a/tools/main.js b/tools/main.js index 1f4a4da66b..9c909f02b8 100644 --- a/tools/main.js +++ b/tools/main.js @@ -76,6 +76,14 @@ var messages = {}; // usage information. main.ShowUsage = function () {}; +// Exception to throw from a helper function inside a command which is identical +// to returning the given exit code from the command. ONLY USE THIS IN HELPERS +// THAT ARE ONLY CALLED DIRECTLY FROM COMMANDS! DON'T BE LAZY AND PUT THROW OF +// THIS IN RANDOM LIBRARY CODE! +main.ExitWithCode = function (code) { + this.code = code; +}; + // Exception to throw to skip the process.exit call. main.WaitForExit = function () {}; @@ -771,7 +779,14 @@ Fiber(function () { } else { // Run outside an app dir with no --release flag. Use the latest // release we know about (in the default track). - releaseName = release.latestDownloaded(); + var messages = buildmessage.capture(function () { + releaseName = release.latestDownloaded(); + }); + if (messages.hasMessages()) { + process.stderr.write("=> Errors while determining latest release:\n" + + messages.formatMessages()); + process.exit(1); + } } } @@ -789,7 +804,20 @@ Fiber(function () { } try { - var rel = release.load(releaseName); + var rel; + var messages = buildmessage.capture(function () { + rel = release.load(releaseName); + }); + if (messages.hasMessages()) { + // XXX The errors that trigger this are likely things like failure to + // load livedata when trying to refresh, or maybe failure to build some + // local packages, or something. They probably aren't "release doesn't + // exist"? But who knows? + process.stderr.write("=> Errors while loading release:\n" + + messages.formatMessages()); + process.exit(1); + } + } catch (e) { var name = releaseName; if (e instanceof files.OfflineError) { @@ -1115,8 +1143,15 @@ commandName + ": You're not in a Meteor project directory.\n" + // Commands that require you to be in a package directory add that package // as a local package to the catalog. Other random commands don't (but if we // see a reason for them to, we can change this rule). - catalog.complete.addLocalPackage(path.basename(options.packageDir), - options.packageDir); + messages = buildmessage.capture(function () { + catalog.complete.addLocalPackage(path.basename(options.packageDir), + options.packageDir); + }); + if (messages.hasMessages()) { + process.stderr.write("=> Errors while scanning current package:\n\n"); + process.stderr.write(messages.formatMessages()); + process.exit(1); + } } if (command.requiresRelease && ! release.current) { @@ -1148,24 +1183,35 @@ commandName + ": You're not in a Meteor project directory.\n" + var ret = command.func(options); } catch (e) { if (e === main.ShowUsage || e === main.WaitForExit || - e === main.SpringboardToLatestRelease) + e === main.SpringboardToLatestRelease || + e === main.WaitForExit) { throw new Error( "you meant 'throw new main.Foo', not 'throw main.Foo'"); - if (e instanceof main.ShowUsage) { + } else if (e instanceof main.ShowUsage) { process.stderr.write(longHelp(commandName) + "\n"); process.exit(1); - } - if (e instanceof main.SpringboardToLatestRelease) { + } else if (e instanceof main.SpringboardToLatestRelease) { // Load the latest release's metadata so that we can figure out // the tools version that it uses. We should only do this if // we know there is some latest release on this track. - var latestRelease = release.load(release.latestDownloaded(e.track)); + var latestRelease; + var messages = buildmessage.capture(function () { + latestRelease = release.load(release.latestDownloaded(e.track)); + }); + if (messages.hasMessages()) { + process.stderr.write("=> Errors while loading latest release:\n\n"); + process.stderr.write(messages.formatMessages()); + process.exit(1); + } springboard(latestRelease, latestRelease.name); // (does not return) - } - if (e instanceof main.WaitForExit) + } else if (e instanceof main.WaitForExit) { return; - throw e; + } else if (e instanceof main.ExitWithCode) { + process.exit(e.code); + } else { + throw e; + } } // Exit. (We will not get here if the command threw an exception diff --git a/tools/package-loader.js b/tools/package-loader.js index 55e76766b9..0f8871b3d8 100644 --- a/tools/package-loader.js +++ b/tools/package-loader.js @@ -58,28 +58,6 @@ _.extend(exports.PackageLoader.prototype, { name, loadPath, self.constraintSolverOpts); }, - containsPlugins: function (name) { - var self = this; - - // We don't want to ever look at the catalog in the uniload case. We - // shouldn't ever care about plugins anyway, since uniload should never - // compile real packages from source (it sorta compiles the wrapper "load" - // package, which should avoid calling this function). - if (self.uniloadDir) - throw Error("called containsPlugins for uniload?"); - - var versionRecord; - if (self.versions === null) { - versionRecord = catalog.complete.getLatestVersion(name); - } else if (_.has(self.versions, name)) { - versionRecord = catalog.complete.getVersion(name, self.versions[name]); - } else { - throw new Error("no version specified for package " + name); - } - - return versionRecord.containsPlugins; - }, - // As getPackage, but returns the path of the package that would be // loaded rather than loading the package, and does not take any // options. Returns null if the package is not available. diff --git a/tools/package-source.js b/tools/package-source.js index c125800b70..278273f3e6 100644 --- a/tools/package-source.js +++ b/tools/package-source.js @@ -406,6 +406,7 @@ _.extend(PackageSource.prototype, { // guideline for a repeatable build. initFromPackageDir: function (name, dir, options) { var self = this; + buildmessage.assertInCapture(); var isPortable = true; options = options || {}; diff --git a/tools/project.js b/tools/project.js index 13d030412f..f44546bc8e 100644 --- a/tools/project.js +++ b/tools/project.js @@ -233,6 +233,8 @@ _.extend(Project.prototype, { // getCurrentCombinedConstraints. calculateCombinedConstraints : function (releasePackages) { var self = this; + buildmessage.assertInCapture(); + var allDeps = []; // First, we process the contents of the .meteor/packages file. The // self.constraints variable is always up to date. diff --git a/tools/release.js b/tools/release.js index 59a31753b2..77decb2138 100644 --- a/tools/release.js +++ b/tools/release.js @@ -5,6 +5,7 @@ var project = require('./project.js').project; var warehouse = require('./warehouse.js'); var catalog = require('./catalog.js'); var utils = require('./utils.js'); +var buildmessage = require('./buildmessage.js'); var release = exports; @@ -91,6 +92,7 @@ _.extend(Release.prototype, { // (XXX: Or maybe just return "checkout" or something?) getCurrentToolsVersion: function () { var self = this; + buildmessage.assertInCapture(); if (release.current.name) { return self._manifest.tool; @@ -178,6 +180,7 @@ release.explicit = null; // in the current project. (taking into account release.forced and whether we're // currently running from a checkout). release.usingRightReleaseForApp = function () { + buildmessage.assertInCapture(); if (release.current === null) throw new Error("no release?"); @@ -195,6 +198,7 @@ release.usingRightReleaseForApp = function () { // for use. May not be called when running from a checkout. // 'track' is optional (it defaults to the default track). release.latestDownloaded = function (track) { + buildmessage.assertInCapture(); if (! files.usesWarehouse()) throw new Error("called from checkout?"); // For self-test only. @@ -233,6 +237,7 @@ release.latestDownloaded = function (track) { // in the world (confirmed with server). release.load = function (name, options) { options = options || {}; + buildmessage.assertInCapture(); if (! name) { return new Release({ name: null }); @@ -285,6 +290,7 @@ release.setCurrent = function (releaseObject, forced, explicit) { // XXX hack release._setCurrentForOldTest = function () { + buildmessage.assertInCapture(); if (process.env.METEOR_SPRINGBOARD_RELEASE) { release.setCurrent(release.load(process.env.METEOR_SPRINGBOARD_RELEASE), true); diff --git a/tools/run-app.js b/tools/run-app.js index e05bd152d7..003534cfc9 100644 --- a/tools/run-app.js +++ b/tools/run-app.js @@ -397,7 +397,22 @@ _.extend(AppRunner.prototype, { // tell the catalog to reload local package sources (since their // dependencies may have changed), and then we should recompute the project // constraints. - catalog.complete.refresh({ forceRefresh: true }); + // XXX the catalog refresh seems overly conservative, but who knows + var refreshWatchSet = new watch.WatchSet; + var refreshMessages = buildmessage.capture(function () { + catalog.complete.refresh({ forceRefresh: true, + watchSet: refreshWatchSet}); + }); + if (refreshMessages.hasMessages()) { + return { + outcome: 'bundle-fail', + bundleResult: { + errors: refreshMessages, + serverWatchSet: refreshWatchSet + } + }; + } + project.reload(); runLog.clearLog(); @@ -411,11 +426,22 @@ _.extend(AppRunner.prototype, { // release.current), but we still want to detect the mismatch if // you are testing packages from an app and you 'meteor update' // that app. - if (self.appDirForVersionCheck && - ! release.usingRightReleaseForApp()) { - return { outcome: 'wrong-release', - releaseNeeded: - project.getMeteorReleaseVersion() }; + if (self.appDirForVersionCheck) { + var wrongRelease; + var rightReleaseMessages = buildmessage.capture(function () { + wrongRelease = ! release.usingRightReleaseForApp(); + }); + if (rightReleaseMessages.hasMessages()) { + return { + outcome: 'bundle-fail', + bundleResult: { errors: rightReleaseMessages } + }; + } + if (wrongRelease) { + return { outcome: 'wrong-release', + releaseNeeded: project.getMeteorReleaseVersion() + }; + } } // Bundle up the app @@ -425,6 +451,7 @@ _.extend(AppRunner.prototype, { stats.recordPackages(self.appDir); }); if (statsMessages.hasMessages()) { + // XXX so this happens any time you're offline? process.stdout.write("Error talking to stats server:\n" + statsMessages.formatMessages()); // ... but continue; @@ -698,8 +725,10 @@ _.extend(AppRunner.prototype, { self.watchFuture = new Future; var watchSet = new watch.WatchSet(); - watchSet.merge(runResult.bundleResult.serverWatchSet); - watchSet.merge(runResult.bundleResult.clientWatchSet); + if (runResult.bundleResult.serverWatchSet) + watchSet.merge(runResult.bundleResult.serverWatchSet); + if (runResult.bundleResult.clientWatchSet) + watchSet.merge(runResult.bundleResult.clientWatchSet); var watcher = new watch.Watcher({ watchSet: watchSet, onChange: function () { diff --git a/tools/selftest.js b/tools/selftest.js index 1366ca6e22..f419a9a60b 100644 --- a/tools/selftest.js +++ b/tools/selftest.js @@ -84,13 +84,15 @@ var execFileSync = function (binary, args) { })().wait(); }; -var captureAndThrow = function (f) { +var doOrThrow = function (f) { + var ret; var messages = buildmessage.capture(function () { - f(); + ret = f(); }); if (messages.hasMessages()) { throw Error(messages.formatMessages()); } + return ret; }; /////////////////////////////////////////////////////////////////////////////// @@ -634,7 +636,7 @@ _.extend(Sandbox.prototype, { // build apps that contain core packages). var toolPackage, toolPackageDirectory; - captureAndThrow(function () { + doOrThrow(function () { toolPackage = getToolsPackage(); toolPackageDirectory = '.' + toolPackage.version + '.XXX++' + toolPackage.buildArchitectures(); @@ -700,22 +702,31 @@ _.extend(Sandbox.prototype, { // should be OK. var oldOffline = catalog.official.offline; catalog.official.offline = true; - catalog.official.refresh(); + doOrThrow(function () { + catalog.official.refresh(); + }); _.each( ['autopublish', 'standard-app-packages', 'insecure'], function (name) { - var versionRec = catalog.official.getLatestVersion(name); + var versionRec = doOrThrow(function () { + return catalog.official.getLatestVersion(name); + }); if (!versionRec) { catalog.official.offline = false; - catalog.official.refresh(); + doOrThrow(function () { + catalog.official.refresh(); + }); catalog.official.offline = true; - versionRec = catalog.official.getLatestVersion(name); + versionRec = doOrThrow(function () { + return catalog.official.getLatestVersion(name); + }); if (!versionRec) { throw new Error(" hack fails for " + name); } } - var buildRec = catalog.official.getAllBuilds( - name, versionRec.version)[0]; + var buildRec = doOrThrow(function () { + return catalog.official.getAllBuilds(name, versionRec.version)[0]; + }); // Insert into packages. stubCatalog.collections.packages.push({ @@ -1582,6 +1593,6 @@ _.extend(exports, { expectThrows: expectThrows, getToolsPackage: getToolsPackage, execFileSync: execFileSync, - captureAndThrow: captureAndThrow, + doOrThrow: doOrThrow, testPackageServerUrl: 'https://test-packages.meteor.com' }); diff --git a/tools/tests/hot-code-push.js b/tools/tests/hot-code-push.js index 489295a089..b2e17def48 100644 --- a/tools/tests/hot-code-push.js +++ b/tools/tests/hot-code-push.js @@ -133,15 +133,25 @@ selftest.define("javascript hot code push", function (options) { s.mkdir("server"); s.write("server/test.js", "jsVar = 'bar'"); run.match("server restarted"); + + // Setting the autoupdateVersion to a different string should also + // force the client to restart. + s.write("server/test.js", + "Package.autoupdate.Autoupdate.autoupdateVersion = 'random'"); + run.match("server restarted"); + run.match("client connected: 0"); + run.match("jsVar: undefined"); + + s.unlink("server/test.js"); + run.match("server restarted"); + s.write("client/empty.js", ""); run.match("client connected: 0"); // We should not be able to access a server variable from the client. run.match("jsVar: undefined"); - s.unlink("server/test.js"); - run.match("server restarted"); s.unlink("client/empty.js"); - run.match("client connected: 0"); + run.match("client connected: 1"); run.match("jsVar: undefined"); // Break the HTML file. This should kill the server, and print errors. @@ -169,6 +179,7 @@ selftest.define("javascript hot code push", function (options) { s.write("client/test.js", "jsVar = 'baz'"); run.match("client connected: 3"); run.match("jsVar: baz"); + s.unlink("client/test.js"); run.stop(); diff --git a/tools/tests/old/app-with-private/.meteor/versions b/tools/tests/old/app-with-private/.meteor/versions index 6d8d7e812c..ecc60c1245 100644 --- a/tools/tests/old/app-with-private/.meteor/versions +++ b/tools/tests/old/app-with-private/.meteor/versions @@ -1,6 +1,6 @@ application-configuration@1.0.0 autopublish@1.0.0 -autoupdate@1.0.0 +autoupdate@1.0.1 binary-heap@1.0.0 blaze-tools@1.0.0 blaze@1.0.1 @@ -20,7 +20,7 @@ jquery@1.0.0 json@1.0.0 livedata@1.0.1 logging@1.0.0 -meteor@1.0.1 +meteor@1.0.2 minifiers@1.0.0 minimongo@1.0.1 mongo-livedata@1.0.0 @@ -32,10 +32,10 @@ reload@1.0.0 retry@1.0.0 routepolicy@1.0.0 session@1.0.0 -spacebars-compiler@1.0.0 +spacebars-compiler@1.0.1 spacebars@1.0.0 standard-app-packages@1.0.0 -templating@1.0.1 +templating@1.0.2 test-package@1.0.0 ui@1.0.0 underscore@1.0.0 diff --git a/tools/tests/old/app-with-private/packages/test-package/versions.json b/tools/tests/old/app-with-private/packages/test-package/versions.json index 09cd0ef7d5..aa6e7e44bf 100644 --- a/tools/tests/old/app-with-private/packages/test-package/versions.json +++ b/tools/tests/old/app-with-private/packages/test-package/versions.json @@ -2,7 +2,7 @@ "dependencies": [ [ "meteor", - "1.0.1" + "1.0.2" ], [ "underscore", @@ -15,6 +15,6 @@ {} ] ], - "toolVersion": "meteor-tool@1.0.7", + "toolVersion": "meteor-tool@1.0.8", "format": "1.0" } \ No newline at end of file diff --git a/tools/tests/old/app-with-public/.meteor/versions b/tools/tests/old/app-with-public/.meteor/versions index a91a0cadb7..c93bc952fb 100644 --- a/tools/tests/old/app-with-public/.meteor/versions +++ b/tools/tests/old/app-with-public/.meteor/versions @@ -1,6 +1,6 @@ application-configuration@1.0.0 autopublish@1.0.0 -autoupdate@1.0.0 +autoupdate@1.0.1 binary-heap@1.0.0 blaze-tools@1.0.0 blaze@1.0.1 @@ -20,7 +20,7 @@ jquery@1.0.0 json@1.0.0 livedata@1.0.1 logging@1.0.0 -meteor@1.0.1 +meteor@1.0.2 minifiers@1.0.0 minimongo@1.0.1 mongo-livedata@1.0.0 @@ -33,10 +33,10 @@ reload@1.0.0 retry@1.0.0 routepolicy@1.0.0 session@1.0.0 -spacebars-compiler@1.0.0 +spacebars-compiler@1.0.1 spacebars@1.0.0 standard-app-packages@1.0.0 -templating@1.0.1 +templating@1.0.2 ui@1.0.0 underscore@1.0.0 webapp@1.0.0 diff --git a/tools/tests/old/empty-app/.meteor/versions b/tools/tests/old/empty-app/.meteor/versions index 813cdc51ed..787bb1ae56 100644 --- a/tools/tests/old/empty-app/.meteor/versions +++ b/tools/tests/old/empty-app/.meteor/versions @@ -1,5 +1,5 @@ application-configuration@1.0.0 -autoupdate@1.0.0 +autoupdate@1.0.1 binary-heap@1.0.0 blaze-tools@1.0.0 blaze@1.0.1 @@ -18,7 +18,7 @@ jquery@1.0.0 json@1.0.0 livedata@1.0.1 logging@1.0.0 -meteor@1.0.1 +meteor@1.0.2 minifiers@1.0.0 minimongo@1.0.1 mongo-livedata@1.0.0 @@ -30,10 +30,10 @@ reload@1.0.0 retry@1.0.0 routepolicy@1.0.0 session@1.0.0 -spacebars-compiler@1.0.0 +spacebars-compiler@1.0.1 spacebars@1.0.0 standard-app-packages@1.0.0 -templating@1.0.1 +templating@1.0.2 ui@1.0.0 underscore@1.0.0 webapp@1.0.0 diff --git a/tools/tests/old/test-bundler-assets.js b/tools/tests/old/test-bundler-assets.js index e4c8d18177..70edeebeb2 100644 --- a/tools/tests/old/test-bundler-assets.js +++ b/tools/tests/old/test-bundler-assets.js @@ -9,6 +9,7 @@ var uniload = require('../../uniload.js'); var release = require('../../release.js'); var project = require('../../project.js'); var catalog = require('../../catalog.js'); +var buildmessage = require('../../buildmessage.js'); var appWithPublic = path.join(__dirname, 'app-with-public'); var appWithPrivate = path.join(__dirname, 'app-with-private'); @@ -29,11 +30,24 @@ var setAppDir = function (appDir) { files.getCurrentToolsDir(), 'packages')); } - catalog.complete.initialize({ - localPackageDirs: localPackageDirs + doOrThrow(function () { + catalog.complete.initialize({ + localPackageDirs: localPackageDirs + }); }); }; +var doOrThrow = function (f) { + var ret; + var messages = buildmessage.capture(function () { + ret = f(); + }); + if (messages.hasMessages()) { + throw Error(messages.formatMessages()); + } + return ret; +}; + // These tests make some assumptions about the structure of stars: that there // are client and server programs inside programs/. @@ -143,7 +157,9 @@ var runTest = function () { var Fiber = require('fibers'); Fiber(function () { - release._setCurrentForOldTest(); + doOrThrow(function () { + release._setCurrentForOldTest(); + }); try { runTest(); diff --git a/tools/tests/old/test-bundler-npm.js b/tools/tests/old/test-bundler-npm.js index 3ebfdfc0f2..d9cc7ae6a1 100644 --- a/tools/tests/old/test-bundler-npm.js +++ b/tools/tests/old/test-bundler-npm.js @@ -9,6 +9,7 @@ var bundler = require('../../bundler.js'); var release = require('../../release.js'); var project = require('../../project.js'); var catalog = require('../../catalog.js'); +var buildmessage = require('../../buildmessage.js'); var meteorNpm = require('../../meteor-npm.js'); var lastTmpDir = null; @@ -27,11 +28,24 @@ var setAppDir = function (appDir) { files.getCurrentToolsDir(), 'packages')); } - catalog.complete.initialize({ - localPackageDirs: localPackageDirs + doOrThrow(function () { + catalog.complete.initialize({ + localPackageDirs: localPackageDirs + }); }); }; +var doOrThrow = function (f) { + var ret; + var messages = buildmessage.capture(function () { + ret = f(); + }); + if (messages.hasMessages()) { + throw Error(messages.formatMessages()); + } + return ret; +}; + /// /// TEST PACKAGE DIR /// @@ -39,7 +53,9 @@ var tmpPackageDirContainer = tmpDir(); var testPackageDir = path.join(tmpPackageDirContainer, 'test-package'); var reloadPackages = function () { - catalog.complete.refresh(); + doOrThrow(function () { + catalog.complete.refresh(); + }); }; var updateTestPackage = function (npmDependencies) { @@ -383,7 +399,9 @@ var runTest = function () { var Fiber = require('fibers'); Fiber(function () { setAppDir(appWithPackageDir); - release._setCurrentForOldTest(); + doOrThrow(function () { + release._setCurrentForOldTest(); + }); meteorNpm._printNpmCalls = true; try { diff --git a/tools/tests/old/test-bundler-options.js b/tools/tests/old/test-bundler-options.js index 1cc05b2325..c119c2b70c 100644 --- a/tools/tests/old/test-bundler-options.js +++ b/tools/tests/old/test-bundler-options.js @@ -29,11 +29,24 @@ var setAppDir = function (appDir) { files.getCurrentToolsDir(), 'packages')); } - catalog.complete.initialize({ - localPackageDirs: localPackageDirs + doOrThrow(function () { + catalog.complete.initialize({ + localPackageDirs: localPackageDirs + }); }); }; +var doOrThrow = function (f) { + var ret; + var messages = buildmessage.capture(function () { + ret = f(); + }); + if (messages.hasMessages()) { + throw Error(messages.formatMessages()); + } + return ret; +}; + var runTest = function () { var readManifest = function (tmpOutputDir) { return JSON.parse(fs.readFileSync( @@ -143,7 +156,9 @@ var runTest = function () { var Fiber = require('fibers'); Fiber(function () { - release._setCurrentForOldTest(); + doOrThrow(function () { + release._setCurrentForOldTest(); + }); try { runTest(); diff --git a/tools/tests/package-tests.js b/tools/tests/package-tests.js index cae3e2e030..4f9c161493 100644 --- a/tools/tests/package-tests.js +++ b/tools/tests/package-tests.js @@ -440,7 +440,7 @@ selftest.define("release track defaults to METEOR-CORE", var run = s.run("publish", "--create"); run.waitSecs(20); run.matchErr("Unknown release METEOR-CORE@" + releaseVersion); - run.expectExit(8); + run.expectExit(1); }); }); diff --git a/tools/tests/releases.js b/tools/tests/releases.js index 1ee2248cc4..603c28e9f6 100644 --- a/tools/tests/releases.js +++ b/tools/tests/releases.js @@ -20,7 +20,7 @@ selftest.define("springboard", ['checkout', 'net'], function () { var run; var toolsPackage; - selftest.captureAndThrow(function() { + selftest.doOrThrow(function() { toolsPackage = selftest.getToolsPackage(); }); var toolsVersion = toolsPackage.name + '@' + @@ -125,7 +125,7 @@ selftest.define("springboard", ['checkout', 'net'], function () { // hackhackhack. If we clean up the hackhackhackhack, then this does not need // the internets. (Or, to be more specific: our warehouse code tries to fetch // the packages from the internet. If we could fool it into using local packages -// instead, or think that it alreayd has the packages, it would be ok). +// instead, or think that it already has the packages, it would be ok). selftest.define("writing versions file", ['checkout', 'net'], function () { var s = new Sandbox({ warehouse: { @@ -136,8 +136,8 @@ selftest.define("writing versions file", ['checkout', 'net'], function () { var run; var toolsPackage; - selftest.captureAndThrow(function() { - toolsPackage = selftest.getToolsPackage(); + selftest.doOrThrow(function() { + toolsPackage = selftest.getToolsPackage(); }); var toolsVersion = toolsPackage.name + '@' + toolsPackage.version; @@ -167,9 +167,7 @@ selftest.define("writing versions file", ['checkout', 'net'], function () { run.expectExit(0); versions = s.read('.meteor/versions'); if (versions) { - selftest.expectEqual( - "Versions file written with --release.", - "Versions file NOT written with --release."); + selftest.fail("Versions file written with --release."); } // Update with --release. @@ -179,9 +177,7 @@ selftest.define("writing versions file", ['checkout', 'net'], function () { // version file should exist. versions = s.read('.meteor/versions'); if (!versions) { - selftest.expectEqual( - "Versions file NOT written after update", - "Versions file written after update."); + selftest.fail("Versions file NOT written after update"); } }); diff --git a/tools/tests/report-stats.js b/tools/tests/report-stats.js index c79dcd3ceb..4123289d13 100644 --- a/tools/tests/report-stats.js +++ b/tools/tests/report-stats.js @@ -36,7 +36,7 @@ var checkMeta = function (appPackages, sessionId, useFakeRelease) { if (useFakeRelease) { var toolsPackage; - selftest.captureAndThrow(function() { + selftest.doOrThrow(function() { toolsPackage = selftest.getToolsPackage(); }); expectedUserAgentInfo.meteorReleaseTrack = diff --git a/tools/tests/selftest-test.js b/tools/tests/selftest-test.js index 928a4865e3..9fab7003ef 100644 --- a/tools/tests/selftest-test.js +++ b/tools/tests/selftest-test.js @@ -12,7 +12,7 @@ selftest.define("selftest-from-warehouse", ['checkout'], function () { var run; var toolsPackage; - selftest.captureAndThrow(function() { + selftest.doOrThrow(function() { toolsPackage = selftest.getToolsPackage(); }); var toolsVersion = toolsPackage.name + '@' + diff --git a/tools/updater.js b/tools/updater.js index ae4b13a4e0..05a52c561c 100644 --- a/tools/updater.js +++ b/tools/updater.js @@ -33,16 +33,25 @@ exports.tryToDownloadUpdate = function (options) { }; var checkForUpdate = function (showBanner) { - // XXX we should ignore errors here, right? but still do the "can we update - // this app with a locally available release" check. - catalog.official.refresh(); + var messages = buildmessage.capture(function () { + catalog.official.refresh(); - if (!release.current.isProperRelease()) + if (!release.current.isProperRelease()) + return; + + updateMeteorToolSymlink(); + + maybeShowBanners(); + }); + + if (messages.hasMessages()) { + // Ignore, since running in the background. + // XXX unfortunately the "can't refresh" message still prints :( + // XXX But maybe if it's just a "we're offline" message we should keep + // going? In case we want to present the "hey there's a locally + // available recommended release? return; - - updateMeteorToolSymlink(); - - maybeShowBanners(); + } }; var maybeShowBanners = function () { @@ -120,6 +129,8 @@ var maybeShowBanners = function () { // Update ~/.meteor/meteor to point to the tool binary from the tools of the // latest recommended release on the default release track. var updateMeteorToolSymlink = function () { + buildmessage.assertInCapture(); + // Get the latest release version of METEOR-CORE. (*Always* of the default // track, not of whatever we happen to be running: we always want the tool // symlink to go to the default track.)