mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
merging packaging into uvf
This commit is contained in:
@@ -3,19 +3,14 @@ var Fiber = Npm.require('fibers');
|
||||
var Future = Npm.require(path.join('fibers', 'future'));
|
||||
|
||||
Meteor._noYieldsAllowed = function (f) {
|
||||
// "Fiber" and "yield" are both in the global namespace. The yield function is
|
||||
// at both "yield" and "Fiber.yield". (It's also at require('fibers').yield
|
||||
// but that is because require('fibers') === Fiber.)
|
||||
var savedYield = Fiber.yield;
|
||||
Fiber.yield = function () {
|
||||
throw new Error("Can't call yield in a noYieldsAllowed block!");
|
||||
};
|
||||
global.yield = Fiber.yield;
|
||||
try {
|
||||
return f();
|
||||
} finally {
|
||||
Fiber.yield = savedYield;
|
||||
global.yield = savedYield;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ _.extend(CompleteCatalog.prototype, {
|
||||
// project root path has not been initialized, we are probably running
|
||||
// outside of a project, and have nothing to look at for guidance.
|
||||
if (opts.ignoreProjectDeps || !project.rootDir) {
|
||||
console.log("ignore project deps & resolve");
|
||||
return self.resolver.resolve(deps, constr, resolverOpts);
|
||||
}
|
||||
|
||||
|
||||
@@ -308,8 +308,9 @@ main.registerCommand({
|
||||
// at the end.)
|
||||
if (! release.current.isCheckout() &&
|
||||
release.current.name !== release.latestDownloaded() &&
|
||||
! release.forced)
|
||||
! release.forced) {
|
||||
throw new main.SpringboardToLatestRelease;
|
||||
}
|
||||
|
||||
var exampleDir = path.join(__dirname, '..', 'examples');
|
||||
var examples = _.reject(fs.readdirSync(exampleDir), function (e) {
|
||||
@@ -425,18 +426,28 @@ main.registerCommand({
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Unless --release was passed (meaning that either the user asked for a
|
||||
// particular release, or that we _just did_ this and springboarded at
|
||||
// #UpdateSpringboard), go get the latest release and switch to it. (We
|
||||
// already know what the latest release is because we refreshed the catalog
|
||||
// above.)
|
||||
// This is the release track we'll end up on --- either because it's
|
||||
// the explicitly specified (with --release) track; or because we
|
||||
// didn't specify a release and it's the app's current release (if we're
|
||||
// in an app dir), since non-forced updates don't change the track.
|
||||
// XXX better error checking on release.current.name
|
||||
// XXX add a method to release.current
|
||||
var releaseTrack = release.current.getReleaseTrack();
|
||||
|
||||
// Unless --release was passed (in which case we ought to already have
|
||||
// springboarded to that release), go get the latest release and switch to
|
||||
// it. (We already know what the latest release is because we refreshed the
|
||||
// catalog above.) Note that after springboarding, we will hit this again
|
||||
// (because springboarding to a specific release does NOT set release.forced),
|
||||
// but it should be a no-op next time (unless there actually was a new latest
|
||||
// release in the interim).
|
||||
if (! release.forced) {
|
||||
if (! release.current ||
|
||||
release.current.name !== release.latestDownloaded()) {
|
||||
release.current.name !== release.latestDownloaded(releaseTrack)) {
|
||||
// The user asked for the latest release (well, they "asked for it" by not
|
||||
// passing --release). We're not currently running the latest release (we
|
||||
// may have even just learned about it). #UpdateSpringboard
|
||||
throw new main.SpringboardToLatestRelease;
|
||||
// passing --release). We're not currently running the latest release on
|
||||
// this track (we may have even just learned about it). #UpdateSpringboard
|
||||
throw new main.SpringboardToLatestRelease(releaseTrack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +460,7 @@ main.registerCommand({
|
||||
// If we're not in an app, then we're done (other than maybe printing some
|
||||
// stuff).
|
||||
if (! options.appDir) {
|
||||
if (release.forced) {
|
||||
if (release.forced || process.env.METEOR_SPRINGBOARD_RELEASE) {
|
||||
// We get here if:
|
||||
// 1) the user ran 'meteor update' and we found a new version
|
||||
// 2) the user ran 'meteor update --release xyz' (regardless of
|
||||
@@ -457,7 +468,9 @@ main.registerCommand({
|
||||
//
|
||||
// In case (1), we downloaded and installed the update and then
|
||||
// we springboarded (at #UpdateSpringboard above), causing
|
||||
// release.forced to be true.
|
||||
// $METEOR_SPRINGBOARD_RELEASE to be true.
|
||||
// XXX probably should have a better interface than looking directly
|
||||
// at the env var here
|
||||
//
|
||||
// In case (2), we downloaded, installed, and springboarded to
|
||||
// the requested release in the initialization code, before the
|
||||
@@ -487,12 +500,6 @@ main.registerCommand({
|
||||
// Otherwise, we have to upgrade the app too, if the release changed.
|
||||
var appRelease = project.getMeteorReleaseVersion();
|
||||
if (appRelease !== null && appRelease === release.current.name) {
|
||||
// Note that in this case, release.forced is true iff --release was actually
|
||||
// passed on the command-line: #UpdateSpringboard can't have occured. Why?
|
||||
// Well, if the user didn't specify --release but we're in an app, then
|
||||
// release.current.name must have been taken from the app, and
|
||||
// #UpdateSpringboard only happens if needs to try to change the app
|
||||
// release, and this is the message for not needing to change the release.
|
||||
var maybeTheLatestRelease = release.forced ? "" : ", the latest release";
|
||||
var maybeOnThisComputer =
|
||||
couldNotContactServer ? "\ninstalled on this computer" : "";
|
||||
@@ -504,6 +511,7 @@ main.registerCommand({
|
||||
|
||||
|
||||
// OK, let's figure out what release fits with our package constraints!
|
||||
<<<<<<< HEAD
|
||||
// XXX this will actually be a loop over possible releases in the non-force
|
||||
// case
|
||||
// XXX better error checking on name
|
||||
@@ -534,76 +542,148 @@ main.registerCommand({
|
||||
// XXX did we have to change some package versions? we should probably
|
||||
// mention that fact.
|
||||
// XXX error handling.
|
||||
|
||||
// Find upgraders (in order) necessary to upgrade the app for the new
|
||||
// release (new metadata file formats, etc, or maybe even updating renamed
|
||||
// APIs).
|
||||
//
|
||||
// * If this is a pre-engine app with no .meteor/release file, run
|
||||
// all upgraders.
|
||||
// * If the app didn't have a release because it was created by a
|
||||
// checkout, don't run any upgraders.
|
||||
//
|
||||
// We're going to need the list of upgraders from the old release
|
||||
// (the release the app was using previously) to decide what
|
||||
// upgraders to run. It's possible that we don't have it downloaded
|
||||
// yet (if they checked out the project and immediately ran 'meteor
|
||||
// update --release foo'), so it's important to do this before we
|
||||
// actually update the project.
|
||||
var upgradersToRun;
|
||||
if (appRelease === "none") {
|
||||
upgradersToRun = [];
|
||||
=======
|
||||
var releaseVersionsToTry;
|
||||
if (release.forced) {
|
||||
releaseVersionsToTry = [release.current.getReleaseVersion()];
|
||||
} else {
|
||||
var oldUpgraders;
|
||||
|
||||
if (appRelease === null) {
|
||||
oldUpgraders = [];
|
||||
} else {
|
||||
try {
|
||||
var oldRelease = release.load(appRelease);
|
||||
} catch (e) {
|
||||
if (e instanceof files.OfflineError) {
|
||||
process.stderr.write(
|
||||
"You need to be online to do this. Please check your internet connection.\n");
|
||||
return 1;
|
||||
}
|
||||
if (e instanceof warehouse.NoSuchReleaseError) {
|
||||
// In this situation it's tempting to just print a warning and
|
||||
// skip the updaters, but I can't figure out how to explain
|
||||
// what's happening to the user, so let's just do this.
|
||||
process.stderr.write(
|
||||
"This project says that it uses version " + appRelease + " of Meteor, but you\n" +
|
||||
"don't have that version of Meteor installed and the Meteor update servers\n" +
|
||||
"don't have it either. Please edit the .meteor/release file in the project\n" +
|
||||
"project and change it to a valid Meteor release.\n");
|
||||
return 1;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
oldUpgraders = oldRelease.getUpgraders();
|
||||
// XXX clean up all this splitty stuff
|
||||
var appReleaseInfo = catalog.getReleaseVersion(
|
||||
appRelease.split('@')[0], appRelease.split('@')[1]);
|
||||
var appOrderKey = (appReleaseInfo && appReleaseInfo.orderKey) || null;
|
||||
releaseVersionsToTry = catalog.getSortedRecommendedReleaseVersions(
|
||||
releaseTrack, appOrderKey);
|
||||
if (!releaseVersionsToTry.length) {
|
||||
// XXX make error better, and make sure that the "already there" error
|
||||
// above truly does cover every other case
|
||||
var maybeOnThisComputer =
|
||||
couldNotContactServer ? "\ninstalled on this computer" : "";
|
||||
console.log(
|
||||
"This project is already at Meteor %s, which is newer than the latest release%s.",
|
||||
appRelease, maybeOnThisComputer);
|
||||
return;
|
||||
}
|
||||
|
||||
upgradersToRun = _.difference(release.current.getUpgraders(), oldUpgraders);
|
||||
}
|
||||
|
||||
// Write the release to .meteor/release.
|
||||
project.writeMeteorReleaseVersion(release.current.name);
|
||||
|
||||
// Now run the upgraders.
|
||||
_.each(upgradersToRun, function (upgrader) {
|
||||
require("./upgraders.js").runUpgrader(upgrader, options.appDir);
|
||||
var solutionPackageVersions = null;
|
||||
var directDependencies = project.getDirectDependencies(options.appDir);
|
||||
var previousVersions = project.getIndirectDependencies(options.appDir);
|
||||
var solutionReleaseVersion = _.find(releaseVersionsToTry, function (versionToTry) {
|
||||
var releaseRecord = catalog.getReleaseVersion(releaseTrack, versionToTry);
|
||||
if (!releaseRecord)
|
||||
throw Error("missing release record?");
|
||||
var constraints = project.combinedConstraints(
|
||||
directDependencies, releaseRecord.packages);
|
||||
try {
|
||||
solutionPackageVersions = catalog.resolveConstraints(
|
||||
constraints, { previousSolution: previousVersions });
|
||||
} catch (e) {
|
||||
// XXX we should make the error handling explicitly detectable, and not
|
||||
// actually mention failures that are recoverable
|
||||
process.stderr.write(
|
||||
"XXX Update to release " + releaseTrack +
|
||||
"@" + versionToTry + " impossible: " + e.message + "\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!solutionReleaseVersion) {
|
||||
// XXX put an error here when we stop doing an error on every failure above
|
||||
return 1;
|
||||
}
|
||||
|
||||
var solutionReleaseName = releaseTrack + '@' + solutionReleaseVersion;
|
||||
|
||||
// We could at this point springboard to solutionRelease (which is no newer
|
||||
// than the release we are currently running), but there's no clear advantage
|
||||
// to this yet. The main reason might be if we decide to delete some
|
||||
// backward-compatibility code which knows how to deal with an older release,
|
||||
// but if we actually do that, we can change this code to add the extra
|
||||
// springboard at that time.
|
||||
|
||||
|
||||
// XXX did we have to change some package versions? we should probably
|
||||
// mention that fact.
|
||||
|
||||
// XXX reimplement upgraders (or don't, until we need them).
|
||||
// // Find upgraders (in order) necessary to upgrade the app for the new
|
||||
// // release (new metadata file formats, etc, or maybe even updating renamed
|
||||
// // APIs).
|
||||
// //
|
||||
// // * If this is a pre-engine app with no .meteor/release file, run
|
||||
// // all upgraders.
|
||||
// // * If the app didn't have a release because it was created by a
|
||||
// // checkout, don't run any upgraders.
|
||||
// //
|
||||
// // We're going to need the list of upgraders from the old release
|
||||
// // (the release the app was using previously) to decide what
|
||||
// // upgraders to run. It's possible that we don't have it downloaded
|
||||
// // yet (if they checked out the project and immediately ran 'meteor
|
||||
// // update --release foo'), so it's important to do this before we
|
||||
// // actually update the project.
|
||||
// var upgradersToRun;
|
||||
// if (appRelease === "none") {
|
||||
// upgradersToRun = [];
|
||||
// } else {
|
||||
// var oldUpgraders;
|
||||
|
||||
// if (appRelease === null) {
|
||||
// oldUpgraders = [];
|
||||
// } else {
|
||||
// try {
|
||||
// var oldRelease = release.load(appRelease);
|
||||
// } catch (e) {
|
||||
// if (e instanceof files.OfflineError) {
|
||||
// process.stderr.write(
|
||||
// "You need to be online to do this. Please check your internet connection.\n");
|
||||
// return 1;
|
||||
// }
|
||||
// if (e instanceof warehouse.NoSuchReleaseError) {
|
||||
// // In this situation it's tempting to just print a warning and
|
||||
// // skip the updaters, but I can't figure out how to explain
|
||||
// // what's happening to the user, so let's just do this.
|
||||
// process.stderr.write(
|
||||
// "This project says that it uses version " + appRelease + " of Meteor, but you\n" +
|
||||
// "don't have that version of Meteor installed and the Meteor update servers\n" +
|
||||
// "don't have it either. Please edit the .meteor/release file in the project\n" +
|
||||
// "project and change it to a valid Meteor release.\n");
|
||||
// return 1;
|
||||
// }
|
||||
// throw e;
|
||||
// }
|
||||
// oldUpgraders = oldRelease.getUpgraders();
|
||||
// }
|
||||
|
||||
// // XXX release.current needs to be replaced with solutionReleaseName
|
||||
// upgradersToRun = _.difference(release.current.getUpgraders(), oldUpgraders);
|
||||
// }
|
||||
|
||||
// Write the new versions to .meteor/packages and .meteor/versions.
|
||||
project.setDependencies(options.appDir, directDependencies.appDeps,
|
||||
solutionPackageVersions);
|
||||
|
||||
// Write the release to .meteor/release.
|
||||
project.writeMeteorReleaseVersion(solutionReleaseName);
|
||||
|
||||
// XXX redo upgrader support
|
||||
// // Now run the upgraders.
|
||||
// _.each(upgradersToRun, function (upgrader) {
|
||||
// require("./upgraders.js").runUpgrader(upgrader, options.appDir);
|
||||
// });
|
||||
|
||||
// This is the right spot to do any other changes we need to the app in
|
||||
// order to update it for the new release.
|
||||
|
||||
console.log("%s: updated to Meteor %s.",
|
||||
path.basename(options.appDir), release.current.name);
|
||||
path.basename(options.appDir), solutionReleaseName);
|
||||
|
||||
// Print any notices relevant to this upgrade.
|
||||
// XXX This doesn't include package-specific notices for packages that
|
||||
// are included transitively (eg, packages used by app packages).
|
||||
var packages = project.getConstraints();
|
||||
|
||||
// var packages = project.getPackages(options.appDir);
|
||||
// XXX reimplement "notices" for tropohouse
|
||||
// warehouse.printNotices(appRelease, release.current.name, packages);
|
||||
});
|
||||
@@ -839,6 +919,7 @@ main.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Get the contents of our versions file, we will want them in order to remove
|
||||
// to the user what we removed.
|
||||
var versions = project.getVersions();
|
||||
|
||||
@@ -124,9 +124,7 @@ _.extend(exports, {
|
||||
return process.env.METEOR_PACKAGE_SERVER_URL;
|
||||
var host = config.getPackageServerDomain();
|
||||
|
||||
console.log("XXX: currently using http, not https for package server.");
|
||||
return "http://" + host;
|
||||
// return addScheme(host);
|
||||
return addScheme(host);
|
||||
},
|
||||
|
||||
getPackageServerDomain: function () {
|
||||
|
||||
@@ -76,3 +76,16 @@ exports.waitForOne = function (/* futures */) {
|
||||
|
||||
return combinedFuture.wait();
|
||||
};
|
||||
|
||||
|
||||
exports.noYieldsAllowed = function (f) {
|
||||
var savedYield = Fiber.yield;
|
||||
Fiber.yield = function () {
|
||||
throw new Error("Can't call yield in a noYieldsAllowed block!");
|
||||
};
|
||||
try {
|
||||
return f();
|
||||
} finally {
|
||||
Fiber.yield = savedYield;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -595,6 +595,14 @@ files.writeFileAtomically = function (filename, contents) {
|
||||
fs.renameSync(tmpFile, filename);
|
||||
};
|
||||
|
||||
// Like fs.symlinkSync, but creates a temporay link and renames it over the
|
||||
// file; this means it works even if the file already exists.
|
||||
files.symlinkOverSync = function (linkText, file) {
|
||||
var tmpSymlink = file + ".tmp" + utils.randomToken();
|
||||
fs.symlinkSync(linkText, tmpSymlink);
|
||||
fs.renameSync(tmpSymlink, file);
|
||||
};
|
||||
|
||||
// Run a program synchronously and, assuming it returns success (0),
|
||||
// return whatever it wrote to stdout, as a string. Otherwise (if it
|
||||
// did not exit gracefully and return 0) return null. As node has
|
||||
|
||||
@@ -80,7 +80,11 @@ main.WaitForExit = function () {};
|
||||
|
||||
// Exception to throw from a command to exit, restart, and reinvoke
|
||||
// the command with the latest available (downloaded) Meteor release.
|
||||
main.SpringboardToLatestRelease = function () {};
|
||||
// If track is specified, it uses the latest available in the given
|
||||
// track instead of the default track.
|
||||
main.SpringboardToLatestRelease = function (track) {
|
||||
this.track = track;
|
||||
};
|
||||
|
||||
// Register a command-line command.
|
||||
//
|
||||
@@ -632,6 +636,7 @@ Fiber(function () {
|
||||
// #ImprovingCrossVersionOptionParsing.
|
||||
|
||||
var releaseOverride = null;
|
||||
var releaseForced = false;
|
||||
if (_.has(rawOptions, '--release')) {
|
||||
if (rawOptions['--release'].length > 1) {
|
||||
process.stderr.write(
|
||||
@@ -640,6 +645,7 @@ Fiber(function () {
|
||||
process.exit(1);
|
||||
}
|
||||
releaseOverride = rawOptions['--release'][0];
|
||||
releaseForced = true;
|
||||
if (! releaseOverride) {
|
||||
process.stderr.write(
|
||||
"The --release option needs a value.\n" +
|
||||
@@ -650,6 +656,12 @@ Fiber(function () {
|
||||
}
|
||||
if (_.has(process.env, 'METEOR_SPRINGBOARD_RELEASE')) {
|
||||
// See #SpringboardEnvironmentVar
|
||||
// Note that this does *NOT* cause release.forced to be true.
|
||||
// release.forced should only be set when the user actually
|
||||
// ran with --release, not just because (eg) they ran
|
||||
// 'meteor update' and we springboarded to the latest release.
|
||||
// (It's important that 'meteor update' be able to tell these
|
||||
// conditions apart even after the springboard!)
|
||||
releaseOverride = process.env['METEOR_SPRINGBOARD_RELEASE'];
|
||||
}
|
||||
|
||||
@@ -702,7 +714,7 @@ Fiber(function () {
|
||||
}
|
||||
} else {
|
||||
// Run outside an app dir with no --release flag. Use the latest
|
||||
// release we know about.
|
||||
// release we know about (in the default track).
|
||||
releaseName = release.latestDownloaded();
|
||||
}
|
||||
}
|
||||
@@ -743,7 +755,7 @@ Fiber(function () {
|
||||
throw e;
|
||||
}
|
||||
|
||||
release.setCurrent(rel, /* forced */ !! releaseOverride);
|
||||
release.setCurrent(rel, releaseForced);
|
||||
}
|
||||
|
||||
// If we're not running the correct version of the tools for this
|
||||
@@ -1054,7 +1066,7 @@ commandName + ": You're not in a Meteor package directory.\n");
|
||||
if (e instanceof main.SpringboardToLatestRelease) {
|
||||
// Load the latest release's metadata so that we can figure out
|
||||
// the tools version that it uses.
|
||||
var latestRelease = release.load(release.latestDownloaded());
|
||||
var latestRelease = release.load(release.latestDownloaded(e.track));
|
||||
springboard(latestRelease, latestRelease.name);
|
||||
// (does not return)
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ var Project = function () {
|
||||
self._depsUpToDate = false;
|
||||
};
|
||||
|
||||
<<<<<<< HEAD
|
||||
_.extend(Project.prototype, {
|
||||
// Set a given root directory as the project's root directory. Figure out all
|
||||
// relevant file paths and read in data that is independent of the constraint
|
||||
|
||||
@@ -37,6 +37,20 @@ _.extend(Release.prototype, {
|
||||
return this.name === null;
|
||||
},
|
||||
|
||||
getReleaseTrack: function () {
|
||||
var self = this;
|
||||
if (! self.isProperRelease())
|
||||
throw new Error("not a proper release?");
|
||||
return self.name.split('@')[0];
|
||||
},
|
||||
|
||||
getReleaseVersion: function () {
|
||||
var self = this;
|
||||
if (! self.isProperRelease())
|
||||
throw new Error("not a proper release?");
|
||||
return self.name.split('@')[1];
|
||||
},
|
||||
|
||||
// Return the package name for the command-line tools that this release
|
||||
// uses. Valid only for proper releases.
|
||||
getToolsPackage: function () {
|
||||
@@ -161,14 +175,17 @@ release.usingRightReleaseForApp = function () {
|
||||
|
||||
// Return the name of the latest release that is downloaded and ready
|
||||
// for use. May not be called when running from a checkout.
|
||||
release.latestDownloaded = function () {
|
||||
// 'track' is optional (it defaults to the default track).
|
||||
release.latestDownloaded = function (track) {
|
||||
if (! files.usesWarehouse())
|
||||
throw new Error("called from checkout?");
|
||||
// For self-test only.
|
||||
if (process.env.METEOR_TEST_LATEST_RELEASE)
|
||||
return process.env.METEOR_TEST_LATEST_RELEASE;
|
||||
|
||||
|
||||
var defaultRelease = catalog.official.getDefaultReleaseVersion();
|
||||
|
||||
if (!defaultRelease) {
|
||||
throw new Error("no latest release available?");
|
||||
}
|
||||
|
||||
@@ -167,7 +167,6 @@ selftest.define("add packages", function () {
|
||||
run = s.run("--once");
|
||||
|
||||
run = s.run("add", "say-something@1.0.0", "--offline-catalog");
|
||||
|
||||
run.match("Successfully added");
|
||||
|
||||
checkPackages(s,
|
||||
|
||||
@@ -184,5 +184,17 @@ _.extend(exports.Tropohouse.prototype, {
|
||||
unipackage.saveToPath(packageDir);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
latestMeteorSymlink: function () {
|
||||
var self = this;
|
||||
var path = path.join(self.root, 'meteor');
|
||||
return fs.readlinkSync(path);
|
||||
},
|
||||
|
||||
replaceLatestMeteorSymlink: function (linkText) {
|
||||
var self = this;
|
||||
var path = path.join(self.root, 'meteor');
|
||||
files.symlinkOverSync(linkText, path);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -336,7 +336,7 @@ _.extend(Unipackage.prototype, {
|
||||
// recover by returning by no builds
|
||||
return null;
|
||||
}
|
||||
return _.where(self.builds, { arch: chosenArch })[0];
|
||||
return _.findWhere(self.builds, { arch: chosenArch });
|
||||
},
|
||||
|
||||
// Load this package's plugins into memory, if they haven't already
|
||||
|
||||
178
tools/updater.js
178
tools/updater.js
@@ -1,31 +1,15 @@
|
||||
var path = require('path');
|
||||
var inFiber = require('./fiber-helpers.js').inFiber;
|
||||
var files = require('./files.js');
|
||||
var warehouse = require('./warehouse.js');
|
||||
var tropohouse = require('./tropohouse.js');
|
||||
var httpHelpers = require('./http-helpers.js');
|
||||
var config = require('./config.js');
|
||||
var release = require('./release.js');
|
||||
var runLog = require('./run-log.js');
|
||||
|
||||
/**
|
||||
* Downloads the current manifest file and returns it. Throws
|
||||
* files.OfflineError if we are offline, or throws some other
|
||||
* exception if the server turned down our request.
|
||||
*/
|
||||
exports.getManifest = function () {
|
||||
// Automated self-test support. You can set an environment variable
|
||||
// to stub out the manifest fetch with a particular value, or to
|
||||
// throw OfflineError.
|
||||
if (process.env.METEOR_TEST_UPDATE_MANIFEST === "offline")
|
||||
throw new files.OfflineError(new Error("scripted failure for tests"));
|
||||
if (process.env.METEOR_TEST_UPDATE_MANIFEST)
|
||||
return JSON.parse(process.env.METEOR_TEST_UPDATE_MANIFEST);
|
||||
|
||||
return httpHelpers.getUrl({
|
||||
url: config.getUpdateManifestUrl(),
|
||||
json: true,
|
||||
useSessionHeader: true
|
||||
});
|
||||
};
|
||||
var catalog = require('./catalog.js');
|
||||
var archinfo = require('./archinfo.js');
|
||||
var unipackage = require('./unipackage.js');
|
||||
var utils = require('./utils.js');
|
||||
|
||||
/**
|
||||
* Check to see if an update is available. If so, download and install
|
||||
@@ -42,69 +26,107 @@ exports.tryToDownloadUpdate = function (options) {
|
||||
if (checkInProgress)
|
||||
return;
|
||||
checkInProgress = true;
|
||||
check(!!options.showBanner);
|
||||
checkForUpdate(!!options.showBanner);
|
||||
checkInProgress = false;
|
||||
};
|
||||
|
||||
var check = function (showBanner) {
|
||||
var manifest = null;
|
||||
try {
|
||||
manifest = exports.getManifest();
|
||||
} catch (e) {
|
||||
// Ignore error (eg, offline), but still do the "can we update this app
|
||||
// with a locally available release" check.
|
||||
}
|
||||
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.serverCatalog.refresh(true);
|
||||
|
||||
if (!files.usesWarehouse())
|
||||
if (!release.isProperRelease())
|
||||
return;
|
||||
|
||||
// XXX in the future support release channels other than stable
|
||||
var manifestLatestRelease =
|
||||
manifest && manifest.releases && manifest.releases.stable &&
|
||||
manifest.releases.stable.version;
|
||||
var localLatestRelease = warehouse.latestRelease();
|
||||
if (manifestLatestRelease && manifestLatestRelease !== localLatestRelease) {
|
||||
// The manifest is telling us about a release that isn't our latest
|
||||
// release! First, print a banner... but only if we've never printed a
|
||||
// banner for this release before. (Or, well... only if this release isn't
|
||||
// the last release which has had a banner printed.)
|
||||
if (manifest.releases.stable.banner &&
|
||||
warehouse.lastPrintedBannerRelease() !== manifestLatestRelease) {
|
||||
if (showBanner) {
|
||||
runLog.log("");
|
||||
runLog.log(manifest.releases.stable.banner);
|
||||
runLog.log("");
|
||||
}
|
||||
warehouse.writeLastPrintedBannerRelease(manifestLatestRelease);
|
||||
} else {
|
||||
// Already printed this banner, or maybe there is no banner.
|
||||
if (showBanner) {
|
||||
runLog.log("=> Meteor " + manifestLatestRelease +
|
||||
" is being downloaded in the background.");
|
||||
}
|
||||
}
|
||||
warehouse.fetchLatestRelease();
|
||||
// We should now have fetched the latest release, which *probably* is
|
||||
// manifestLatestRelease. As long as it's changed from the one it was
|
||||
// before we tried to fetch it, print that out.
|
||||
var newLatestRelease = warehouse.latestRelease();
|
||||
if (showBanner && newLatestRelease !== localLatestRelease) {
|
||||
runLog.log(
|
||||
"=> Meteor " + newLatestRelease +
|
||||
" is available. Update this project with 'meteor update'.");
|
||||
}
|
||||
var currentReleaseTrack = release.current.getTrack();
|
||||
var latestRelease = catalog.serverCatalog.getDefaultRelease(
|
||||
currentReleaseTrack);
|
||||
// Maybe you're on some random track with nothing recommended. That's OK.
|
||||
if (!latestRelease)
|
||||
return;
|
||||
var latestReleaseToolParts = latestRelease.tool.split('@');
|
||||
var latestReleaseToolPackage = latestReleaseToolParts[0];
|
||||
var latestReleaseToolVersion = latestReleaseToolParts[1];
|
||||
var relativeToolPath = tropohouse.default.packagePath(
|
||||
latestReleaseToolPackage, latestReleaseToolVersion, true);
|
||||
|
||||
var localLatestReleaseLink = tropohouse.default.latestMeteorSymlink();
|
||||
if (utils.startsWith(localLatestReleaseLink, relativeToolPath + path.sep)) {
|
||||
// The latest release from the catalog is not where the ~/.meteor0/meteor
|
||||
// symlink points to. Let's make sure we have that release on disk,
|
||||
// and then update the symlink.
|
||||
// XXX download the packages too?
|
||||
tropohouse.default.maybeDownloadPackageForArchitectures(
|
||||
{packageName: latestReleaseToolPackage,
|
||||
version: latestReleaseToolVersion},
|
||||
[archinfo.host()]);
|
||||
|
||||
var toolUnipackage = new unipackage.Unipackage;
|
||||
toolUnipackage.initFromPath(
|
||||
latestReleaseToolPackage,
|
||||
tropohouse.default.packagePath(latestReleaseToolPackage,
|
||||
latestReleaseToolVersion));
|
||||
var toolRecord = _.findWhere(toolUnipackage.toolsOnDisk,
|
||||
{arch: archinfo.host()});
|
||||
// XXX maybe we shouldn't throw from this background thing
|
||||
if (!toolRecord)
|
||||
throw Error("latest release has no tool?");
|
||||
|
||||
console.log("XXX updating tool symlink for",
|
||||
latestRelease.track + "@" + latestRelease.version);
|
||||
|
||||
tropohouse.default.replaceLatestMeteorSymlink(
|
||||
path.join(relativeToolPath, toolRecord.path, 'meteor'));
|
||||
}
|
||||
|
||||
// We didn't do a global update (or we're not online), but do we need to
|
||||
// update this app? Specifically: is our local latest release something
|
||||
// other than this app's release, and the user didn't specify a specific
|
||||
// release at the command line with --release?
|
||||
if (showBanner &&
|
||||
localLatestRelease !== release.current.name &&
|
||||
! release.forced) {
|
||||
runLog.log(
|
||||
"=> Meteor " + localLatestRelease +
|
||||
" is available. Update this project with 'meteor update'.");
|
||||
}
|
||||
// XXX print banners
|
||||
|
||||
// var manifestLatestRelease =
|
||||
// manifest && manifest.releases && manifest.releases.stable &&
|
||||
// manifest.releases.stable.version;
|
||||
// var localLatestRelease = warehouse.latestRelease();
|
||||
// if (manifestLatestRelease && manifestLatestRelease !== localLatestRelease) {
|
||||
// // The manifest is telling us about a release that isn't our latest
|
||||
// // release! First, print a banner... but only if we've never printed a
|
||||
// // banner for this release before. (Or, well... only if this release isn't
|
||||
// // the last release which has had a banner printed.)
|
||||
// if (manifest.releases.stable.banner &&
|
||||
// warehouse.lastPrintedBannerRelease() !== manifestLatestRelease) {
|
||||
// if (showBanner) {
|
||||
// runLog.log("");
|
||||
// runLog.log(manifest.releases.stable.banner);
|
||||
// runLog.log("");
|
||||
// }
|
||||
// warehouse.writeLastPrintedBannerRelease(manifestLatestRelease);
|
||||
// } else {
|
||||
// // Already printed this banner, or maybe there is no banner.
|
||||
// if (showBanner) {
|
||||
// runLog.log("=> Meteor " + manifestLatestRelease +
|
||||
// " is being downloaded in the background.");
|
||||
// }
|
||||
// }
|
||||
// warehouse.fetchLatestRelease();
|
||||
// // We should now have fetched the latest release, which *probably* is
|
||||
// // manifestLatestRelease. As long as it's changed from the one it was
|
||||
// // before we tried to fetch it, print that out.
|
||||
// var newLatestRelease = warehouse.latestRelease();
|
||||
// if (showBanner && newLatestRelease !== localLatestRelease) {
|
||||
// runLog.log(
|
||||
// "=> Meteor " + newLatestRelease +
|
||||
// " is available. Update this project with 'meteor update'.");
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // We didn't do a global update (or we're not online), but do we need to
|
||||
// // update this app? Specifically: is our local latest release something
|
||||
// // other than this app's release, and the user didn't specify a specific
|
||||
// // release at the command line with --release?
|
||||
// if (showBanner &&
|
||||
// localLatestRelease !== release.current.name &&
|
||||
// ! release.forced) {
|
||||
// runLog.log(
|
||||
// "=> Meteor " + localLatestRelease +
|
||||
// " is available. Update this project with 'meteor update'.");
|
||||
// }
|
||||
};
|
||||
|
||||
@@ -273,4 +273,9 @@ exports.isDirectory = function (dir) {
|
||||
return false;
|
||||
}
|
||||
return stats.isDirectory();
|
||||
|
||||
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)
|
||||
exports.startsWith = function(str, starts) {
|
||||
return str.length >= starts.length &&
|
||||
str.substring(0, starts.length) === starts;
|
||||
};
|
||||
|
||||
@@ -34,14 +34,6 @@ var fiberHelpers = require('./fiber-helpers.js');
|
||||
|
||||
var WAREHOUSE_URLBASE = 'https://warehouse.meteor.com';
|
||||
|
||||
// Like fs.symlinkSync, but creates a temporay link and renames it over the
|
||||
// file; this means it works even if the file already exists.
|
||||
var symlinkOverSync = function (linkText, file) {
|
||||
var tmpSymlink = file + ".tmp" + utils.randomToken();
|
||||
fs.symlinkSync(linkText, tmpSymlink);
|
||||
fs.renameSync(tmpSymlink, file);
|
||||
};
|
||||
|
||||
var warehouse = exports;
|
||||
_.extend(warehouse, {
|
||||
// An exception meaning that you asked for a release that doesn't
|
||||
@@ -122,37 +114,6 @@ _.extend(warehouse, {
|
||||
}
|
||||
},
|
||||
|
||||
// returns true if we updated the latest symlink
|
||||
// XXX make errors prettier
|
||||
fetchLatestRelease: function (options) {
|
||||
options = options || {};
|
||||
var manifest = updater.getManifest();
|
||||
|
||||
// XXX in the future support release channels other than stable
|
||||
var releaseName = manifest && manifest.releases &&
|
||||
manifest.releases.stable && manifest.releases.stable.version;
|
||||
if (! releaseName)
|
||||
throw new Error("no stable release found?");
|
||||
|
||||
var latestReleaseManifest = warehouse._populateWarehouseForRelease(
|
||||
releaseName, !!options.showInstalling);
|
||||
|
||||
// First, make sure the latest tools symlink reflects the latest installed
|
||||
// release.
|
||||
if (latestReleaseManifest.tools !== warehouse.latestTools()) {
|
||||
symlinkOverSync(latestReleaseManifest.tools,
|
||||
warehouse._latestToolsSymlinkPath());
|
||||
}
|
||||
|
||||
var storedLatestRelease = warehouse.latestRelease();
|
||||
if (storedLatestRelease === releaseName)
|
||||
return false;
|
||||
|
||||
symlinkOverSync(releaseName + '.release.json',
|
||||
warehouse._latestReleaseSymlinkPath());
|
||||
return true;
|
||||
},
|
||||
|
||||
packageExistsInWarehouse: function (name, version) {
|
||||
// A package exists if its directory exists. (We used to look for a
|
||||
// particular file name ("package.js") inside the directory, but since we
|
||||
|
||||
Reference in New Issue
Block a user