Merge pull request #618 from mozilla/issue-530-ver-txt

feature(server): add /ver.json endpoint and route definition processing.
This commit is contained in:
Zach Carter 2014-02-28 13:58:54 -08:00
Родитель bf216593d4 8167684695
Коммит ec09cdc634
7 изменённых файлов: 190 добавлений и 4 удалений

Просмотреть файл

@ -108,7 +108,8 @@
"0.0.7": "9088ab5ae1e861f4d81b176b4a8046080703deed"
},
"bluebird": {
"1.0.5": "ef7d5f01bb741938a7cac5b4fb6cfc0622759b9e"
"1.0.5": "ef7d5f01bb741938a7cac5b4fb6cfc0622759b9e",
"1.0.7": "55b97e965b3f9ca6f2b3cb1fd432e1830fb00ed1"
},
"boolbase": {
"1.0.0": "68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"

Просмотреть файл

@ -22,6 +22,7 @@
"author": "Mozilla (https://mozilla.org/)",
"license": "MPL 2.0",
"dependencies": {
"bluebird": "1.0.7",
"bower": "1.2.8",
"connect-cachify": "0.0.15",
"connect-fonts": "0.0.12",

Просмотреть файл

@ -7,7 +7,45 @@
var url = require('url');
var dns = require('dns');
var path = require('path');
var fs = require('fs');
var config = require('./configuration');
var logger = require('intel').getLogger('server.routes');
/**
* Steal a concept from Persona and load routes from definition
* files in the `routes` subdirectory. Each definition must contain
* 3 attributes, method, path and process.
* method is one of `GET`, `POST`, etc.
* path is a string or regular expression that express uses to match a route.
* process is a function that is called with req and res to handle the route.
*/
function isValidRoute(route) {
return !! route.method && route.path && route.process;
}
function loadRouteDefinitions(routesPath) {
var routes = [];
fs.readdirSync(routesPath).forEach(function (file) {
// skip files that don't have a .js suffix or start with a dot
if (path.extname(file) !== '.js' || /^\./.test(file)) {
return logger.info('route definition not loaded: %s', file);
}
var route = require(path.join(routesPath, file));
if (! isValidRoute(route)) {
return logger.error('route definition invalid: %s', file);
}
routes.push(route);
});
return routes;
}
var routesPath = path.join(__dirname, 'routes');
var routes = loadRouteDefinitions(routesPath);
module.exports = function (fxAccountUrl, templates) {
@ -57,7 +95,7 @@ module.exports = function (fxAccountUrl, templates) {
if (config.get('env') === 'development') {
app.get('/tests/index.html', function (req, res) {
var checkCoverage = 'coverage' in req.query &&
req.query['coverage'] !== 'false';
req.query.coverage !== 'false';
return res.render('mocha', {
check_coverage: checkCoverage
});
@ -95,10 +133,14 @@ module.exports = function (fxAccountUrl, templates) {
});
});
app.get('/', function(req, res) {
res.render('index');
});
routes.forEach(function (route) {
app[route.method](route.path, route.process);
});
};
};

Просмотреть файл

@ -0,0 +1,104 @@
/* 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/. */
/**
* Return version info based on package.json and the git hash
*
* We figure out the Git hash in the following order:
*
* (1) read config/version.json if exists (ie. staging, production)
* (2) figure it out from git (either regular '.git', or
* '/home/app/git' for AwsBox)
*/
'use strict';
var fs = require('fs');
var path = require('path');
var util = require('util');
var child_process = require('child_process');
var Promise = require('bluebird');
var logger = require('intel').getLogger('server.ver.json');
var version = require('../../../package.json').version;
exports.method = 'get';
exports.path = '/ver.json';
function getCommitHashFromVersionJson() {
return Promise.attempt(function () {
var configFile = path.join(__dirname, '..', 'config', 'version.json');
if (fs.existsSync(configFile)) {
var commitHash;
try {
commitHash = require(configFile).version.hash;
} catch (e) {
logger.error('could not read version.hash from version.json');
}
return commitHash;
}
});
}
function getGitDir() {
if (! fs.existsSync(path.join(__dirname, '..', '..', '..', '.git'))) {
// try at '/home/app/git' for AwsBox deploys
return path.sep + path.join('home', 'app', 'git');
}
}
function getCommitHashFromGit() {
var deferred = Promise.defer();
var gitDir = getGitDir();
var cmd = util.format('git %s rev-parse HEAD', gitDir ? '--git-dir=' + gitDir : '');
child_process.exec(cmd, function (err, stdout) {
deferred.resolve(stdout.replace(/\s+/, ''));
});
return deferred.promise;
}
var promise;
function getVersionInfo() {
// only resolve once, the data does not need to be re-calculated.
if (promise) {
return promise;
}
// (1) read config/version.json if exists (ie. staging, production)
promise = getCommitHashFromVersionJson()
.then(function (commitHash) {
if (commitHash) {
return commitHash;
}
// (2) figure it out from git (either regular '.git',
// or '/home/app/git' for AwsBox)
return getCommitHashFromGit();
})
.then(function (commitHash) {
logger.info('version set to: %s', version);
logger.info('commit hash set to: %s', commitHash);
return {
version: version,
commit: commitHash
};
});
return promise;
}
// seed the info on startup.
getVersionInfo();
exports.process = function (req, res) {
getVersionInfo()
.then(function (versionInfo) {
// charset must be set on json responses.
res.charset = 'utf8';
res.json(versionInfo);
});
};

Просмотреть файл

@ -11,7 +11,11 @@ define([
intern.webdriver = {};
intern.environments = [];
intern.functionalSuites = [];
intern.suites = [ 'tests/server/templates', 'tests/server/routes' ];
intern.suites = [
'tests/server/templates',
'tests/server/routes',
'tests/server/ver.json.js'
];
return intern;
});

Просмотреть файл

@ -40,6 +40,7 @@ define([
'/force_auth': 200,
'/tests/index.html': 200,
'/tests/index.html?coverage': 200,
'/ver.json': 200,
'/non_existent': 404
};

33
tests/server/ver.json.js Normal file
Просмотреть файл

@ -0,0 +1,33 @@
/* 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/. */
define([
'intern!object',
'intern/chai!assert',
'intern/dojo/node!../../server/lib/configuration',
'intern/dojo/node!request'
], function (registerSuite, assert, config, request) {
'use strict';
var serverUrl = config.get('public_url');
var suite = {
name: 'ver.json'
};
suite['#get ver.json'] = function () {
var dfd = this.async(1000);
request(serverUrl + '/ver.json', dfd.callback(function (err, res) {
assert.equal(res.statusCode, 200);
assert.equal(res.headers['content-type'], 'application/json; charset=utf8');
var body = JSON.parse(res.body);
assert.ok('version' in body);
assert.ok('commit' in body);
}, dfd.reject.bind(dfd)));
};
registerSuite(suite);
});