update XXX-comment improve

This commit is contained in:
ekatek
2014-06-11 13:36:02 -07:00
parent 6dee09d8d4
commit 5da7a763a9

View File

@@ -711,3 +711,270 @@ main.registerCommand({
}
process.stdout.write(formatList(items));
});
///////////////////////////////////////////////////////////////////////////////
// update
///////////////////////////////////////////////////////////////////////////////
main.registerCommand({
name: 'update',
options: {
patch: { type: Boolean, required: false },
"packages-only": { type: Boolean, required: false }
},
// We have to be able to work without a release, since 'meteor
// update' is how you fix apps that don't have a release.
requiresRelease: false,
minArgs: 0,
maxArgs: Infinity,
}, function (options) {
// XXX clean this up if we don't end up using it, but we probably should be
// using it on the refresh call
var couldNotContactServer = false;
// Refresh the catalog, cacheing the remote package data on the server.
catalog.official.refresh(true);
// If you are specifying packaging individually, you probably don't want to
// update the release.
if (options.args.length > 0) {
options["packages-only"] = true;
}
if (!options["packages-only"]) {
// refuse to update the release if we're in a git checkout.
if (! files.usesWarehouse()) {
process.stderr.write(
"update: can only be run from official releases, not from checkouts\n");
return 1;
}
// 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) {
var latestRelease = 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.
if (!latestRelease) {
// XXX is there a command to get to the latest METEOR-CORE@? Should we
// recommend it here?
process.stderr.write(
"There are no recommended releases on release track " +
releaseTrack + ".\n");
return 1;
}
if (! release.current || release.current.name !== latestRelease) {
// The user asked for the latest release (well, they "asked for it" by not
// 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);
}
}
// At this point we should have a release. (If we didn't to start
// with, #UpdateSpringboard fixed that.) And it can't be a checkout,
// because we checked for that at the very beginning.
if (! release.current || ! release.current.isProperRelease())
throw new Error("don't have a proper release?");
// If we're not in an app, then we're done (other than maybe printing some
// stuff).
if (! options.appDir) {
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
// whether we found a new release)
//
// In case (1), we downloaded and installed the update and then
// we springboarded (at #UpdateSpringboard above), causing
// $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
// command even ran. They could equivalently have run 'meteor
// help --release xyz'.
console.log(
"Installed. Run 'meteor update' inside of a particular project\n" +
"directory to update that project to Meteor %s.", release.current.name);
} else {
// We get here if the user ran 'meteor update' and we didn't
// find a new version.
if (couldNotContactServer) {
// We already printed an error message about our inability to
// ask the server if we're up to date.
} else {
console.log(
"The latest version of Meteor, %s, is already installed on this\n" +
"computer. Run 'meteor update' inside of a particular project\n" +
"directory to update that project to Meteor %s.",
release.current.name, release.current.name);
}
}
return;
}
// Otherwise, we have to upgrade the app too, if the release changed.
var appRelease = project.getMeteorReleaseVersion();
if (appRelease !== null && appRelease === release.current.name) {
var maybeTheLatestRelease = release.forced ? "" : ", the latest release";
var maybeOnThisComputer =
couldNotContactServer ? "\ninstalled on this computer" : "";
console.log(
"This project is already at Meteor %s%s%s.",
appRelease, maybeTheLatestRelease, maybeOnThisComputer);
return;
}
// XXX: also while we are at it, we should consider disallowing both
// options.patch and release.forced. Otherwise, the behavior is... what I had
// to use to test this, actually ( update --patch --release
// ekate-meteor@5.0.13 updated me to ekate-meteor@5.0.13.1) but that's way too
// confusing to make sense.
// XXX did we have to change some package versions? we should probably
// mention that fact.
// XXX error handling.
var releaseVersionsToTry;
if (options.patch) {
// XXX: something something something current release
if (appRelease == null) {
console.log(
"Cannot patch update unless a release is set.");
process.exit(1);
}
var r = appRelease.split('@');
var record = catalog.official.getReleaseVersion(r[0], r[1]);
var updateTo = record.patchReleaseVersion;
if (!updateTo) {
console.log(
"You are at the latest patch version.");
process.exit(1);
}
releaseVersionsToTry = [updateTo];
} else if (release.forced) {
releaseVersionsToTry = [release.current.getReleaseVersion()];
} else {
// XXX clean up all this splitty stuff
var appReleaseInfo = catalog.official.getReleaseVersion(
appRelease.split('@')[0], appRelease.split('@')[1]);
var appOrderKey = (appReleaseInfo && appReleaseInfo.orderKey) || null;
releaseVersionsToTry = catalog.official.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;
}
}
var solutionPackageVersions = null;
var directDependencies = project.getConstraints();
var previousVersions = project.getVersions();
var solutionReleaseVersion = _.find(releaseVersionsToTry, function (versionToTry) {
var releaseRecord = catalog.complete.getReleaseVersion(releaseTrack, versionToTry);
if (!releaseRecord)
throw Error("missing release record?");
var constraints = project.calculateCombinedConstraints(
directDependencies, releaseRecord.packages);
try {
solutionPackageVersions = catalog.complete.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.
var upgraders = require('./upgraders.js');
var upgradersToRun = upgraders.upgradersToRun();
// XXX did we have to change some package versions? we should probably
// mention that fact.
// Write the new versions to .meteor/packages and .meteor/versions.
project.setVersions(solutionPackageVersions);
// Write the release to .meteor/release.
project.writeMeteorReleaseVersion(solutionReleaseName);
console.log("%s: updated to Meteor %s.",
path.basename(options.appDir), solutionReleaseName);
// Now run the upgraders.
// XXX should we also run upgraders on other random commands, in case there
// was a crash after changing .meteor/release but before running them?
_.each(upgradersToRun, function (upgrader) {
upgraders.runUpgrader(upgrader);
project.appendFinishedUpgrader(upgrader);
});
}
// Update the packages to the latest version. We don't do this for patch
// releases.
//
// XXX: Can we figure out if we got here with update --release foo,
// or just with update?
if (options['packages-only'] && !options['patch']) {
// We can't update packages when we are not in a release.
if (!options.appDir) return 0;
// Let's update packages to the latest version. That's easy.
var versions = project.getVersions();
var allPackages = project.getCurrentCombinedConstraints();
// XXX: Updating individual packages and the constraint solver
var newVersions = catalog.complete.resolveConstraints(allPackages, {
previousSolution: versions,
breaking: !options.minor,
upgrade: true
});
project.setVersions(newVersions);
process.exit(0);
}
});