Skip to content

Commit

Permalink
Issue mozilla#2423: New Embedded WebExtension add-on
Browse files Browse the repository at this point in the history
  • Loading branch information
lmorchard committed Aug 1, 2017
1 parent 11d3b98 commit d862825
Show file tree
Hide file tree
Showing 50 changed files with 2,371 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ media/
node_modules/
static/
tmp/
addon/build/
addon/dist/
addon/locale/

# Files to ignore within specific directories
addon/*.rdf
addon/*.xpi
addon/locale/
legal-copy/*html
373 changes: 373 additions & 0 deletions addon/LICENSE

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions addon/bin/sign
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env node
/*
* This Source Code is subject to the terms of the Mozilla Public License
* version 2.0 (the 'License'). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/.
*/
/*
* This script is for signing, packaging, and distributing the testpilot
* add-on. If the current package version has already been signed, we generate
* an update.rdf, download the signed xpi from amo, and move the files into
* the testpilot project.
* If the package version has not been signed, we build the xpi and update.rdf,
* sign the xpi, and move them into the testpilot project.
*
*/
var fs = require('fs');
var path = require('path');
var request = require('request');
var jwt = require('jsonwebtoken');

var manifestFilename = __dirname + '/../build/package.json';
var manifest = require(manifestFilename);
var version = manifest.version;
var apiKey = process.env['AMO_USER'];
var apiSecret = process.env['AMO_SECRET'];

var authToken = jwt.sign({iss: apiKey}, apiSecret, {
algorithm: "HS256",
expiresIn: 60
});

var signedOpts = {
url: 'https://addons.mozilla.org/api/v3/addons/@' + manifest.name + '/versions/'+version+ '/',
headers: {
'Authorization': 'JWT ' + authToken
}
};

request(signedOpts, signCb);

function signCb(err, resp, body) {
if (!err && resp.statusCode == 200) {
var info = JSON.parse(body);
if (info.files.length) {
var ws = fs.createWriteStream('addon.xpi');
signedOpts.url = info.files[0].download_url;
request(signedOpts).pipe(ws);
}
} else distAddon();
}

function distAddon() {
signAddon('addon.xpi', function(err, signedXpiPath) {
if (err) return console.error(err);
checkExistsAndMv(signedXpiPath, 'addon.xpi', function(err) {
if (err) return console.error(err);
console.log('addon.xpi written to addon.xpi');
});
});
}

function signAddon(xpiPath, cb) {
require('jpm/lib/sign').sign({
apiKey: apiKey,
apiSecret: apiSecret,
xpi: xpiPath
}).then(function(result) {
if (result.success) cb(null, result.downloadedFiles[0]);
}).catch(cb);
}

function checkExistsAndMv(fromFilePath, toFilePath, cb) {
fs.stat(fromFilePath, function(err) {
if (err) return cb(err);
fs.rename(fromFilePath, toFilePath, function(err) {
if (err) return cb(err);
else cb();
});
});
}
43 changes: 43 additions & 0 deletions addon/bin/update-version
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

var fs = require('fs');
var util = require('util');
var childProcess = require('child_process');

// Load the current package.json
var packageFilename = __dirname + '/../build/package.json';
var package = require(packageFilename);

// Come up with a version tag suffix based on CIRCLE_TAG, or fall back to
// looking up the current git commit hash.
var tag;
if (process.env['CIRCLE_TAG']) {
tag = 'tag-' + process.env['CIRCLE_TAG'];
} else {
// Note: Must use short git hash, because AMO rejects versions > 32 chars
tag = 'dev-' + childProcess.execSync('git rev-parse --short HEAD')
.toString('utf-8')
.trim();
}

// Update the version tag suffix.
var versionParts = package.version.split('-');
var origVersion = package.version;
package.version = versionParts[0] + '-' + tag;

// Write the modified package.json
var packageJSON = JSON.stringify(package, null, ' ');
fs.writeFileSync(packageFilename, packageJSON);

var manifestFilename = __dirname + '/../build/webextension/manifest.json';
var manifest = require(manifestFilename);
manifest.version = package.version;
var manifestJSON = JSON.stringify(manifest, null, ' ');
fs.writeFileSync(manifestFilename, manifestJSON);

var installFilename = __dirname + '/../build/install.rdf';
var install = fs.readFileSync(installFilename, { encoding: 'utf8' });
fs.writeFileSync(installFilename, install.replace(
/<em:version>([\d\.]+)<\/em:version>/,
`<em:version>${package.version}</em:version>`
));
11 changes: 11 additions & 0 deletions addon/lib/webpack-after-build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = class AfterBuildPlugin {
constructor (cb) {
this.cb = cb;
}

apply (compiler) {
compiler.plugin('done', () => {
this.cb();
});
}
};
15 changes: 15 additions & 0 deletions addon/lib/webpack-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = class ThrowErrorPlugin {
apply(compiler) {
compiler.plugin('done', stats => {
if (
process.env.NODE_ENV !== 'development' &&
stats.compilation.errors &&
stats.compilation.errors.length
) {
// eslint-disable-next-line no-console
console.error(stats.compilation.errors);
process.exit(1);
}
});
}
};
63 changes: 63 additions & 0 deletions addon/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"title": "Test Pilot",
"name": "testpilot-addon",
"id": "@testpilot-addon",
"version": "2.0.0",
"private": true,
"description": "Test Pilot is a privacy-sensitive user research program focused on getting new features into Firefox faster.",
"repository": "mozilla/testpilot",
"homepage": "https://testpilot.firefox.com/",
"updateURL": "https://testpilot.firefox.com/static/addon/update.rdf",
"updateLink": "https://testpilot.firefox.com/static/addon/latest",
"bugs": {
"url": "https://github.com/mozilla/testpilot/issues"
},
"author": "Mozilla (https://mozilla.org/)",
"engines": {
"firefox": ">=51",
"fennec": ">=51"
},
"permissions": {
"multiprocess": true
},
"main": "./bootstrap.js",
"license": "MPL-2.0",
"devDependencies": {
"babel-plugin-syntax-async-functions": "6.13.0",
"babel-plugin-syntax-object-rest-spread": "6.13.0",
"babel-plugin-transform-object-rest-spread": "6.23.0",
"chai": "4.0.2",
"copy-webpack-plugin": "4.0.1",
"cross-env": "5.0.1",
"eslint": "3.17.1",
"eslint-config-airbnb": "10.0.1",
"eslint-config-react": "1.1.7",
"eslint-plugin-flowtype": "2.30.0",
"eslint-plugin-import": "2.2.0",
"eslint-plugin-jsx-a11y": "5.0.3",
"eslint-plugin-react": "7.0.1",
"jpm": "1.3.1",
"mocha": "3.2.0",
"mocha-junit-reporter": "1.13.0",
"nyc": "11.0.2",
"pontoon-to-webext": "^1.0.2",
"shelljs": "0.7.8",
"webpack": "3.1.0"
},
"dependencies": {
"babel-loader": "7.1.1",
"babel-polyfill": "6.23.0",
"pubsub-js": "1.5.7",
"uuid": "3.1.0"
},
"scripts": {
"start": "npm run watch",
"watch": "cross-env NODE_ENV=development webpack --watch",
"package": "cross-env NODE_ENV=production webpack",
"package:dev": "cross-env NODE_ENV=development webpack",
"sign": "cross-env NODE_ENV=production webpack && node bin/sign",
"lint": "eslint -c ../.eslintrc src",
"test": "mocha --require babel-register --require ./test/test-setup.js --recursive",
"test:watch": "npm test -- --watch"
}
}
65 changes: 65 additions & 0 deletions addon/src/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* global APP_STARTUP */

import PubSub from 'pubsub-js';

import { debug, log, setAddonMetadata } from './lib/utils';
import { startupDone, startupPromise, startupObserver } from './lib/appStartup';
import { startupLegacyStorage, shutdownLegacyStorage } from './lib/legacyStorage';
import { startupPrefsObserver, shutdownPrefsObserver } from './lib/prefs';
import { startupFrameScripts, shutdownFrameScripts } from './lib/frameScripts';
import { startupWebExtension, shutdownWebExtension } from './lib/webExtension';
import { startupEvents, shutdownEvents } from './lib/events';
import { startupChannels, shutdownChannels } from './lib/channels';
import { startupTelemetry, shutdownTelemetry } from './lib/telemetry';
import { startupAddonManager, shutdownAddonManager } from './lib/addonManager';

PubSub.immediateExceptions = true;

export function startup(data, reason) {
setAddonMetadata(data);

if (reason === APP_STARTUP) {
startupObserver.register();
} else {
startupDone();
}

return startupPromise
.then(setupDebug)
.then(startupLegacyStorage)
.then(() => startupTelemetry(data, reason))
.then(startupAddonManager)
.then(startupPrefsObserver)
.then(startupEvents)
.then(startupChannels)
.then(startupFrameScripts)
.then(() => startupWebExtension(data, reason))
.catch(err => log('startup error', err));
}

export function shutdown(data, reason) {
try {
shutdownWebExtension(data, reason);
shutdownFrameScripts();
shutdownChannels();
shutdownEvents();
shutdownPrefsObserver();
shutdownAddonManager();
shutdownTelemetry(data, reason);
shutdownLegacyStorage();
PubSub.clearAllSubscriptions();
} catch (err) {
log('shutdown error', err);
}
}

async function setupDebug() {
if (!debug) return;
['bootstrap', 'webExtension'].forEach(root =>
PubSub.subscribe(root, (message, data) => log('pubsub', message, data))
);
}
1 change: 1 addition & 0 deletions addon/src/chrome.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
content testpilot-addon-scripts ./chrome/scripts/
Loading

0 comments on commit d862825

Please sign in to comment.