January 18th, 2014
Automatic updates for Chrome extension hosted in private Bitbucket repository
A while back at work I wrote a Chrome extension, and we wanted it to automatically update itself straight from the Git repository. I ran into two problems doing this however:
- The extension did not send the browser’s cookie along with the request for the updates.xml, and therefore the request was denied by Bitbucket. Without the cookie, you are logged out as far as Bitbucket is concerned, and for a private repository, that means no access!
- Bitbucket annoyingly returned the
X-Content-Type-Options: nosniff
header which stops Chrome extensions from proceeding with the update.
Luckily the Chrome extension API provides support for reading cookies and modifying web requests + responses. Which is everything we need to have our plugin trick itself into updating!
To do this, you’ll need to enable the following permissions in your manifest.json
:
["background",
"webRequest",
"webRequestBlocking",
"cookies",
"*://*/"]
You’ll need to intercept relevant responses and strip out the X-Content-Type-Options
header:
chrome.webRequest.onHeadersReceived.addListener(function (details) {
details.responseHeaders.forEach(function (header, i) {
if (header.name === "X-Content-Type-Options") {
details.responseHeaders.splice(i, 1);
}
});
return {
responseHeaders: details.responseHeaders
};
}, { urls: ["https://bitbucket.org/kzar/chrome-extension-update-example/raw/HEAD/*"] },
["responseHeaders", "blocking"]
);
and, if you want this to work from a private repository, you’ll need to shove the Bitbucket cookie into relevant requests:
var bitbucket_cookie = "";
chrome.cookies.getAll({ url : "https://bitbucket.org" }, function (cookies) {
cookies.forEach(function (cookie) {
bitbucket_cookie += cookie.name + "=" + cookie.value + "; ";
});
bitbucket_cookie = bitbucket_cookie.slice(0, -2);
});
chrome.webRequest.onBeforeSendHeaders.addListener(function (details) {
details.requestHeaders.push({ name: 'Cookie', value: bitbucket_cookie });
return {
requestHeaders: details.requestHeaders
};
}, { urls: ["https://bitbucket.org/kzar/chrome-extension-update-example/raw/HEAD/*"] },
["requestHeaders", "blocking"]
);
Of course, you’ll need to have a updates.xml
file in your repository that points to the latest packaged .crx
for your extension in your repository as well. Also, you’ll have to adjust the URLs.
It’s worth doing though, I found being able to push a new version of your code along with updates to updates.xml
, manifest.json
and the plugin .crx
file and have your users updated automatically with zero infrastructure was really handy! Much easier than nagging them to upgrade every time you fix something.
Now for those of you with an astute eye, the example snippets have a URL that looks plausibly like a real Bitbucket repository. Good news, it is! You can even try it right now for yourself, download old-chrome-extension-update-example.crx, install it, click to perform Chrome extension updates and you should see it upgrade from version 0.1 to 0.2. If you want to have a look at how it works “Check it out” (sorry!) and see for yourself. (Please note it’s a public repo for obvious reasons. The example should work for private repositories as well assuming your users have access to it.)
Cheers, Dave.