Merge branch 'devel' into blaze-refactor

Conflicts:
	packages/webapp/webapp_server.js
This commit is contained in:
David Greenspan
2014-07-01 13:29:04 -07:00
19 changed files with 399 additions and 175 deletions

View File

@@ -87,3 +87,7 @@ Interested in contributing to Meteor?
* Core framework design mailing list: https://groups.google.com/group/meteor-core
* Contribution guidelines: https://github.com/meteor/meteor/tree/devel/Contributing.md
We are hiring! Visit https://www.meteor.com/jobs/working-at-meteor to
learn more about working full-time on the Meteor project.

View File

@@ -12,3 +12,4 @@ jquery-waypoints
less
spiderable
appcache
reload-safetybelt

View File

@@ -92,15 +92,17 @@ different collections. We hope to lift this restriction in a future release.
];
});
Alternatively, a publish function can directly control its published
record set by calling the functions [`added`](#publish_added) (to add a
new document to the published record set), [`changed`](#publish_changed)
(to change or clear some fields on a document already in the published
record set), and [`removed`](#publish_removed) (to remove documents from
the published record set). Publish functions that use these functions
should also call [`ready`](#publish_ready) once the initial record set
is complete. These methods are provided by `this` in your publish
function.
Alternatively, a publish function can directly control its published record set
by calling the functions [`added`](#publish_added) (to add a new document to the
published record set), [`changed`](#publish_changed) (to change or clear some
fields on a document already in the published record set), and
[`removed`](#publish_removed) (to remove documents from the published record
set). These methods are provided by `this` in your publish function.
If a publish function does not return a cursor or array of cursors, it is
assumed to be using the low-level `added`/`changed`/`removed` interface, and it
**must also call [`ready`](#publish_ready) once the initial record set is
complete**.
Example:
@@ -156,6 +158,19 @@ Example:
Counts.findOne(Session.get("roomId")).count +
" messages.");
// server: sometimes publish a query, sometimes publish nothing
Meteor.publish("secretData", function () {
if (this.userId === 'superuser') {
return SecretData.find();
} else {
// Declare that no data is being published. If you leave this line
// out, Meteor will never consider the subscription ready because
// it thinks you're using the added/changed/removed interface where
// you have to explicitly call this.ready().
return [];
}
});
{{#warning}}
Meteor will emit a warning message if you call `Meteor.publish` in a
project that includes the `autopublish` package. Your publish function

View File

@@ -1087,7 +1087,7 @@ Template.api.loginWithPassword = {
{
name: "password",
type: "String",
descr: "The user's password. This is __not__ sent in plain text over the wire — it is secured with [SRP](http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol)."
descr: "The user's password."
},
{
name: "callback",

2
meteor
View File

@@ -1,6 +1,6 @@
#!/bin/bash
BUNDLE_VERSION=0.3.38
BUNDLE_VERSION=0.3.38 # warning! 0.3.39-40 used on packaging branch
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.

View File

@@ -41,9 +41,9 @@ Meteor.loginWithPassword = function (selector, password, callback) {
}, callback);
}
else if (error) {
callback(error);
callback && callback(error);
} else {
callback();
callback && callback();
}
}
});
@@ -69,9 +69,9 @@ var srpUpgradePath = function (options, callback) {
details = EJSON.parse(options.upgradeError.details);
} catch (e) {}
if (!(details && details.format === 'srp')) {
callback(new Meteor.Error(400,
"Password is old. Please reset your " +
"password."));
callback && callback(
new Meteor.Error(400, "Password is old. Please reset your " +
"password."));
} else {
Accounts.callLoginMethod({
methodArguments: [{
@@ -133,7 +133,7 @@ Accounts.changePassword = function (oldPassword, newPassword, callback) {
plaintextPassword: oldPassword
}, function (err) {
if (err) {
callback(err);
callback && callback(err);
} else {
// Now that we've successfully migrated from srp to
// bcrypt, try changing the password again.

View File

@@ -127,7 +127,7 @@ ObserveSequence = {
});
diffArray(lastSeqArray, seqArray, callbacks);
} else if (isMinimongoCursor(seq)) {
} else if (isStoreCursor(seq)) {
var cursor = seq;
seqArray = [];
@@ -188,7 +188,7 @@ ObserveSequence = {
return [];
} else if (seq instanceof Array) {
return seq;
} else if (isMinimongoCursor(seq)) {
} else if (isStoreCursor(seq)) {
return seq.fetch();
} else {
throw badSequenceError();
@@ -201,9 +201,9 @@ var badSequenceError = function () {
"arrays, cursors or falsey values.");
};
var isMinimongoCursor = function (seq) {
var minimongo = Package.minimongo;
return !!minimongo && (seq instanceof minimongo.LocalCollection.Cursor);
var isStoreCursor = function (cursor) {
return cursor && _.isObject(cursor) &&
_.isFunction(cursor.observe) && _.isFunction(cursor.fetch);
};
// Calculates the differences between `lastSeqArray` and

1
packages/reload-safetybelt/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.build*

View File

@@ -0,0 +1,16 @@
Package.describe({
summary: "Reload safety belt for multi-server deployments",
internal: true
});
Package.on_use(function (api) {
api.use("webapp", "server");
api.add_files("reload-safety-belt.js", "server");
api.add_files("safetybelt.js", "server", { isAsset: true });
});
Package.on_test(function (api) {
api.add_files("safetybelt.js", "server", { isAsset: true });
api.use(["reload-safetybelt", "tinytest", "http", "webapp"]);
api.add_files("reload-safety-belt-tests.js", "server");
});

View File

@@ -0,0 +1,10 @@
var script = Assets.getText("safetybelt.js");
Tinytest.add("reload-safetybelt - safety belt is added", function (test) {
test.isTrue(_.some(
WebAppInternals.additionalStaticJs,
function (js, pathname) {
return js === script;
}
));
});

View File

@@ -0,0 +1,6 @@
// The reload safetybelt is some js that will be loaded after everything else in
// the HTML. In some multi-server deployments, when you update, you have a
// chance of hitting an old server for the HTML and the new server for the JS or
// CSS. This prevents you from displaying the page in that case, and instead
// reloads it, presumably all on the new version now.
WebAppInternals.addStaticJs(Assets.getText("safetybelt.js"));

View File

@@ -0,0 +1,6 @@
if (typeof Package === 'undefined' ||
! Package.webapp ||
! Package.webapp.WebApp ||
! Package.webapp.WebApp._isCssLoaded()) {
document.location.reload();
}

View File

@@ -9,11 +9,17 @@
{{/if}}
{{#each js}} <script type="text/javascript" src="{{../bundledJsCssPrefix}}{{url}}"></script>
{{/each}}
{{#if inlineScriptsAllowed}}
<script type='text/javascript'>{{{reloadSafetyBelt}}}</script>
{{else}}
<script type='text/javascript' src='{{rootUrlPathPrefix}}/meteor_reload_safetybelt.js'></script>
{{/if}}
{{#each additionalStaticJs}}
{{#if ../inlineScriptsAllowed}}
<script type='text/javascript'>
{{contents}}
</script>
{{else}}
<script type='text/javascript'
src='{{rootUrlPathPrefix}}{{pathname}}'></script>
{{/if}}
{{/each}}
{{{head}}}
</head>
<body>

View File

@@ -19,18 +19,6 @@ WebAppInternals = {};
var bundledJsCssPrefix;
// The reload safetybelt is some js that will be loaded after everything else in
// the HTML. In some multi-server deployments, when you update, you have a
// chance of hitting an old server for the HTML and the new server for the JS or
// CSS. This prevents you from displaying the page in that case, and instead
// reloads it, presumably all on the new version now.
var RELOAD_SAFETYBELT = "\n" +
"if (typeof Package === 'undefined' ||\n" +
" ! Package.webapp ||\n" +
" ! Package.webapp.WebApp ||\n" +
" ! Package.webapp.WebApp._isCssLoaded())\n" +
" document.location.reload(); \n";
// Keepalives so that when the outer server dies unceremoniously and
// doesn't kill us, we quit ourselves. A little gross, but better than
// pidfiles.
@@ -235,6 +223,158 @@ WebApp._timeoutAdjustmentRequestCallback = function (req, res) {
_.each(finishListeners, function (l) { res.on('finish', l); });
};
// Will be updated by main before we listen.
var boilerplateFunc = null;
var boilerplateBaseData = null;
var memoizedBoilerplate = {};
// Given a request (as returned from `categorizeRequest`), return the
// boilerplate HTML to serve for that request. Memoizes on HTML
// attributes (used by, eg, appcache) and whether inline scripts are
// currently allowed.
var getBoilerplate = function (request) {
var htmlAttributes = getHtmlAttributes(request);
// The only thing that changes from request to request (for now) are
// the HTML attributes (used by, eg, appcache) and whether inline
// scripts are allowed, so we can memoize based on that.
var boilerplateKey = JSON.stringify({
inlineScriptsAllowed: inlineScriptsAllowed,
htmlAttributes: htmlAttributes
});
if (! _.has(memoizedBoilerplate, boilerplateKey)) {
var boilerplateData = _.extend({
htmlAttributes: htmlAttributes,
inlineScriptsAllowed: WebAppInternals.inlineScriptsAllowed()
}, boilerplateBaseData);
memoizedBoilerplate[boilerplateKey] = "<!DOCTYPE html>\n" +
Blaze.toHTML(Blaze.With(boilerplateData, boilerplateFunc));
}
return memoizedBoilerplate[boilerplateKey];
};
// Serve static files from the manifest or added with
// `addStaticJs`. Exported for tests.
// Options are:
// - staticFiles: object mapping pathname of file in manifest -> {
// path, cacheable, sourceMapUrl, type }
// - clientDir: root directory for static files from client manifest
WebAppInternals.staticFilesMiddleware = function (options, req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) {
next();
return;
}
var pathname = connect.utils.parseUrl(req).pathname;
var staticFiles = options.staticFiles;
var clientDir = options.clientDir;
try {
pathname = decodeURIComponent(pathname);
} catch (e) {
next();
return;
}
var serveStaticJs = function (s) {
res.writeHead(200, {
'Content-type': 'application/javascript; charset=UTF-8'
});
res.write(s);
res.end();
};
if (pathname === "/meteor_runtime_config.js" &&
! WebAppInternals.inlineScriptsAllowed()) {
serveStaticJs("__meteor_runtime_config__ = " +
JSON.stringify(__meteor_runtime_config__) + ";");
return;
} else if (_.has(additionalStaticJs, pathname) &&
! WebAppInternals.inlineScriptsAllowed()) {
serveStaticJs(additionalStaticJs[pathname]);
return;
}
if (!_.has(staticFiles, pathname)) {
next();
return;
}
// We don't need to call pause because, unlike 'static', once we call into
// 'send' and yield to the event loop, we never call another handler with
// 'next'.
var info = staticFiles[pathname];
// Cacheable files are files that should never change. Typically
// named by their hash (eg meteor bundled js and css files).
// We cache them ~forever (1yr).
//
// We cache non-cacheable files anyway. This isn't really correct, as users
// can change the files and changes won't propagate immediately. However, if
// we don't cache them, browsers will 'flicker' when rerendering
// images. Eventually we will probably want to rewrite URLs of static assets
// to include a query parameter to bust caches. That way we can both get
// good caching behavior and allow users to change assets without delay.
// https://github.com/meteor/meteor/issues/773
var maxAge = info.cacheable
? 1000 * 60 * 60 * 24 * 365
: 1000 * 60 * 60 * 24;
// Set the X-SourceMap header, which current Chrome understands.
// (The files also contain '//#' comments which FF 24 understands and
// Chrome doesn't understand yet.)
//
// Eventually we should set the SourceMap header but the current version of
// Chrome and no version of FF supports it.
//
// To figure out if your version of Chrome should support the SourceMap
// header,
// - go to chrome://version. Let's say the Chrome version is
// 28.0.1500.71 and the Blink version is 537.36 (@153022)
// - go to http://src.chromium.org/viewvc/blink/branches/chromium/1500/Source/core/inspector/InspectorPageAgent.cpp?view=log
// where the "1500" is the third part of your Chrome version
// - find the first revision that is no greater than the "153022"
// number. That's probably the first one and it probably has
// a message of the form "Branch 1500 - blink@r149738"
// - If *that* revision number (149738) is at least 151755,
// then Chrome should support SourceMap (not just X-SourceMap)
// (The change is https://codereview.chromium.org/15832007)
//
// You also need to enable source maps in Chrome: open dev tools, click
// the gear in the bottom right corner, and select "enable source maps".
//
// Firefox 23+ supports source maps but doesn't support either header yet,
// so we include the '//#' comment for it:
// https://bugzilla.mozilla.org/show_bug.cgi?id=765993
// In FF 23 you need to turn on `devtools.debugger.source-maps-enabled`
// in `about:config` (it is on by default in FF 24).
if (info.sourceMapUrl)
res.setHeader('X-SourceMap', info.sourceMapUrl);
if (info.type === "js") {
res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
} else if (info.type === "css") {
res.setHeader("Content-Type", "text/css; charset=UTF-8");
}
send(req, path.join(clientDir, info.path))
.maxage(maxAge)
.hidden(true) // if we specified a dotfile in the manifest, serve it
.on('error', function (err) {
Log.error("Error serving static file " + err);
res.writeHead(500);
res.end();
})
.on('directory', function () {
Log.error("Unexpected directory " + info.path);
res.writeHead(500);
res.end();
})
.pipe(res);
};
var runWebAppServer = function () {
var shuttingDown = false;
// read the control for the client we'll be serving up
@@ -318,115 +458,10 @@ var runWebAppServer = function () {
// Serve static files from the manifest.
// This is inspired by the 'static' middleware.
app.use(function (req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) {
next();
return;
}
var pathname = connect.utils.parseUrl(req).pathname;
try {
pathname = decodeURIComponent(pathname);
} catch (e) {
next();
return;
}
var serveStaticJs = function (s) {
res.writeHead(200, {
'Content-type': 'application/javascript; charset=UTF-8'
});
res.write(s);
res.end();
};
if (pathname === "/meteor_runtime_config.js" &&
! WebAppInternals.inlineScriptsAllowed()) {
serveStaticJs("__meteor_runtime_config__ = " +
JSON.stringify(__meteor_runtime_config__) + ";");
return;
} else if (pathname === "/meteor_reload_safetybelt.js" &&
! WebAppInternals.inlineScriptsAllowed()) {
serveStaticJs(RELOAD_SAFETYBELT);
return;
}
if (!_.has(staticFiles, pathname)) {
next();
return;
}
// We don't need to call pause because, unlike 'static', once we call into
// 'send' and yield to the event loop, we never call another handler with
// 'next'.
var info = staticFiles[pathname];
// Cacheable files are files that should never change. Typically
// named by their hash (eg meteor bundled js and css files).
// We cache them ~forever (1yr).
//
// We cache non-cacheable files anyway. This isn't really correct, as users
// can change the files and changes won't propagate immediately. However, if
// we don't cache them, browsers will 'flicker' when rerendering
// images. Eventually we will probably want to rewrite URLs of static assets
// to include a query parameter to bust caches. That way we can both get
// good caching behavior and allow users to change assets without delay.
// https://github.com/meteor/meteor/issues/773
var maxAge = info.cacheable
? 1000 * 60 * 60 * 24 * 365
: 1000 * 60 * 60 * 24;
// Set the X-SourceMap header, which current Chrome understands.
// (The files also contain '//#' comments which FF 24 understands and
// Chrome doesn't understand yet.)
//
// Eventually we should set the SourceMap header but the current version of
// Chrome and no version of FF supports it.
//
// To figure out if your version of Chrome should support the SourceMap
// header,
// - go to chrome://version. Let's say the Chrome version is
// 28.0.1500.71 and the Blink version is 537.36 (@153022)
// - go to http://src.chromium.org/viewvc/blink/branches/chromium/1500/Source/core/inspector/InspectorPageAgent.cpp?view=log
// where the "1500" is the third part of your Chrome version
// - find the first revision that is no greater than the "153022"
// number. That's probably the first one and it probably has
// a message of the form "Branch 1500 - blink@r149738"
// - If *that* revision number (149738) is at least 151755,
// then Chrome should support SourceMap (not just X-SourceMap)
// (The change is https://codereview.chromium.org/15832007)
//
// You also need to enable source maps in Chrome: open dev tools, click
// the gear in the bottom right corner, and select "enable source maps".
//
// Firefox 23+ supports source maps but doesn't support either header yet,
// so we include the '//#' comment for it:
// https://bugzilla.mozilla.org/show_bug.cgi?id=765993
// In FF 23 you need to turn on `devtools.debugger.source-maps-enabled`
// in `about:config` (it is on by default in FF 24).
if (info.sourceMapUrl)
res.setHeader('X-SourceMap', info.sourceMapUrl);
if (info.type === "js") {
res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
} else if (info.type === "css") {
res.setHeader("Content-Type", "text/css; charset=UTF-8");
}
send(req, path.join(clientDir, info.path))
.maxage(maxAge)
.hidden(true) // if we specified a dotfile in the manifest, serve it
.on('error', function (err) {
Log.error("Error serving static file " + err);
res.writeHead(500);
res.end();
})
.on('directory', function () {
Log.error("Unexpected directory " + info.path);
res.writeHead(500);
res.end();
})
.pipe(res);
return WebAppInternals.staticFilesMiddleware({
staticFiles: staticFiles,
clientDir: clientDir
}, req, res, next);
});
// Packages and apps can add handlers to this via WebApp.connectHandlers.
@@ -447,10 +482,6 @@ var runWebAppServer = function () {
res.end("An error message");
});
// Will be updated by main before we listen.
var boilerplateFunc = null;
var boilerplateBaseData = null;
var boilerplateByAttributes = {};
app.use(function (req, res, next) {
if (! appUrl(req.url))
return next();
@@ -460,7 +491,6 @@ var runWebAppServer = function () {
if (!boilerplateBaseData)
throw new Error("boilerplateBaseData should be set before listening!");
var headers = {
'Content-Type': 'text/html; charset=utf-8'
};
@@ -480,27 +510,18 @@ var runWebAppServer = function () {
return undefined;
}
var htmlAttributes = getHtmlAttributes(request);
// The only thing that changes from request to request (for now) are the
// HTML attributes (used by, eg, appcache), so we can memoize based on that.
var attributeKey = JSON.stringify(htmlAttributes);
if (!_.has(boilerplateByAttributes, attributeKey)) {
try {
var boilerplateData = _.extend({htmlAttributes: htmlAttributes},
boilerplateBaseData);
boilerplateByAttributes[attributeKey] = "<!DOCTYPE html>\n" +
Blaze.toHTML(Blaze.With(boilerplateData, boilerplateFunc));
} catch (e) {
Log.error("Error running template: " + e.stack);
res.writeHead(500, headers);
res.end();
return undefined;
}
var boilerplate;
try {
boilerplate = getBoilerplate(request);
} catch (e) {
Log.error("Error running template: " + e);
res.writeHead(500, headers);
res.end();
return undefined;
}
res.writeHead(200, headers);
res.write(boilerplateByAttributes[attributeKey]);
res.write(boilerplate);
res.end();
return undefined;
});
@@ -599,13 +620,23 @@ var runWebAppServer = function () {
var expectKeepalives = _.contains(argv, '--keepalive');
boilerplateBaseData = {
// 'htmlAttributes' and 'inlineScriptsAllowed' are set at render
// time, because they are allowed to change from request to
// request.
css: [],
js: [],
head: '',
body: '',
inlineScriptsAllowed: WebAppInternals.inlineScriptsAllowed(),
additionalStaticJs: _.map(
additionalStaticJs,
function (contents, pathname) {
return {
pathname: pathname,
contents: contents
};
}
),
meteorRuntimeConfig: JSON.stringify(__meteor_runtime_config__),
reloadSafetyBelt: RELOAD_SAFETYBELT,
rootUrlPathPrefix: __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '',
bundledJsCssPrefix: bundledJsCssPrefix ||
__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''
@@ -1002,3 +1033,16 @@ WebAppInternals.setInlineScriptsAllowed = function (value) {
WebAppInternals.setBundledJsCssPrefix = function (prefix) {
bundledJsCssPrefix = prefix;
};
// Packages can call `WebAppInternals.addStaticJs` to specify static
// JavaScript to be included in the app. This static JS will be inlined,
// unless inline scripts have been disabled, in which case it will be
// served under `/<sha1 of contents>`.
var additionalStaticJs = {};
WebAppInternals.addStaticJs = function (contents) {
additionalStaticJs["/" + sha1(contents) + ".js"] = contents;
};
// Exported for tests
WebAppInternals.getBoilerplate = getBoilerplate;
WebAppInternals.additionalStaticJs = additionalStaticJs;

View File

@@ -1,4 +1,48 @@
var url = Npm.require("url");
var crypto = Npm.require("crypto");
var http = Npm.require("http");
var additionalScript = "(function () { var foo = 1; })";
WebAppInternals.addStaticJs(additionalScript);
var hash = crypto.createHash('sha1');
hash.update(additionalScript);
var additionalScriptPathname = hash.digest('hex') + ".js";
// Mock the 'res' object that gets passed to connect handlers. This mock
// just records any utf8 data written to the response and returns it
// when you call `mockResponse.getBody()`.
var MockResponse = function () {
this.buffer = "";
this.statusCode = null;
};
MockResponse.prototype.writeHead = function (statusCode) {
this.statusCode = statusCode;
};
MockResponse.prototype.setHeader = function (name, value) {
// nothing
};
MockResponse.prototype.write = function (data, encoding) {
if (! encoding || encoding === "utf8") {
this.buffer = this.buffer + data;
}
};
MockResponse.prototype.end = function (data, encoding) {
if (! encoding || encoding === "utf8") {
if (data) {
this.buffer = this.buffer + data;
}
}
};
MockResponse.prototype.getBody = function () {
return this.buffer;
};
Tinytest.add("webapp - content-type header", function (test) {
var cssResource = _.find(
@@ -21,3 +65,64 @@ Tinytest.add("webapp - content-type header", function (test) {
test.equal(resp.headers["content-type"].toLowerCase(),
"application/javascript; charset=utf-8");
});
Tinytest.add("webapp - additional static javascript", function (test) {
var origInlineScriptsAllowed = WebAppInternals.inlineScriptsAllowed();
var staticFilesOpts = {
staticFiles: {},
clientDir: "/"
};
// It's okay to set this global state because we're not going to yield
// before settng it back to what it was originally.
WebAppInternals.setInlineScriptsAllowed(true);
Meteor._noYieldsAllowed(function () {
var boilerplate = WebAppInternals.getBoilerplate({
browser: "doesn't-matter",
url: "also-doesnt-matter"
});
// When inline scripts are allowed, the script should be inlined.
test.isTrue(boilerplate.indexOf(additionalScript) !== -1);
// And the script should not be served as its own separate resource,
// meaning that the static file handler should pass on this request.
var res = new MockResponse();
var req = new http.IncomingMessage();
req.headers = {};
req.method = "GET";
req.url = "/" + additionalScriptPathname;
var nextCalled = false;
WebAppInternals.staticFilesMiddleware(
staticFilesOpts, req, res, function () {
nextCalled = true;
});
test.isTrue(nextCalled);
// When inline scripts are disallowed, the script body should not be
// inlined, and the script should be included in a <script src="..">
// tag.
WebAppInternals.setInlineScriptsAllowed(false);
boilerplate = WebAppInternals.getBoilerplate({
browser: "doesn't-matter",
url: "also-doesnt-matter"
});
// The script contents itself should not be present; the pathname
// where the script is served should be.
test.isTrue(boilerplate.indexOf(additionalScript) === -1);
test.isTrue(boilerplate.indexOf(additionalScriptPathname) !== -1);
// And the static file handler should serve the script at that pathname.
res = new MockResponse();
WebAppInternals.staticFilesMiddleware(staticFilesOpts, req, res,
function () { });
var resBody = res.getBody();
test.isTrue(resBody.indexOf(additionalScript) !== -1);
test.equal(res.statusCode, 200);
});
WebAppInternals.setInlineScriptsAllowed(origInlineScriptsAllowed);
});

View File

@@ -37,5 +37,12 @@ echo Found build $DIRNAME
s3cmd ls s3://com.meteor.jenkins/$DIRNAME/ | \
perl -nle 'if (/\.tar\.gz/) { ++$TAR } else { die "something weird" } END { exit !($TAR == 3) }'
for FILE in $(s3cmd ls s3://com.meteor.jenkins/$DIRNAME/ | perl -nlaF/ -e 'print $F[-1]'); do
if s3cmd info $TARGET$FILE >/dev/null 2>&1; then
echo "$TARGET$FILE already exists (maybe from another branch?)"
exit 1
fi
done
echo Copying to $TARGET
s3cmd -P cp -r s3://com.meteor.jenkins/$DIRNAME/ $TARGET

View File

@@ -79,7 +79,7 @@ INSTALL_TMPDIR="$HOME/.meteor-install-tmp"
rm -rf "$INSTALL_TMPDIR"
mkdir "$INSTALL_TMPDIR"
echo "Downloading Meteor distribution"
curl --progress-bar --fail "$TARBALL_URL" | tar -xzf - -C "$INSTALL_TMPDIR"
curl --progress-bar --fail "$TARBALL_URL" | tar -xzf - -C "$INSTALL_TMPDIR" -o
# bomb out if it didn't work, eg no net
test -x "${INSTALL_TMPDIR}/.meteor/meteor"
mv "${INSTALL_TMPDIR}/.meteor" "$HOME"

View File

@@ -1158,6 +1158,10 @@ _.extend(JsImage.prototype, {
);
var sourceMapFileName = path.basename(loadItem.sourceMap);
// Remove any existing sourceMappingURL line. (eg, if roundtripping
// through JsImage.readFromDisk, don't end up with two!)
item.source = item.source.replace(
/\n\/\/# sourceMappingURL=.+\n?$/, '');
item.source += "\n//# sourceMappingURL=" + sourceMapFileName + "\n";
loadItem.sourceMapRoot = item.sourceMapRoot;
}

View File

@@ -3,7 +3,6 @@ var selftest = require('../selftest.js');
var testUtils = require('../test-utils.js');
var files = require('../files.js');
var Sandbox = selftest.Sandbox;
var httpHelpers = require('../http-helpers.js');
var commandTimeoutSecs = testUtils.accountsCommandTimeoutSecs;