Commit e6a0522e authored by Nick Stenning's avatar Nick Stenning Committed by Robert Knight

Allow uploading sourcemaps to multiple Sentry projects

This commit reworks scripts/gulp/upload-to-sentry.js to allow for
uploading to multiple Sentry projects at once. This allow us to build
the assets once, and upload them to multiple Sentry projects within the
same gulp invocation.
parent d85ed229
......@@ -258,11 +258,14 @@ gulp.task('upload-sourcemaps',
['build-app-js',
'build-extension-js'], function () {
var uploadToSentry = require('./scripts/gulp/upload-to-sentry');
gulp.src(['build/scripts/*.js', 'build/scripts/*.map'])
.pipe(uploadToSentry({
apiKey: getEnv('SENTRY_API_KEY'),
release: getEnv('SENTRY_RELEASE_VERSION'),
organization: getEnv('SENTRY_ORGANIZATION'),
project: getEnv('SENTRY_PROJECT'),
}));
var opts = {
key: getEnv('SENTRY_API_KEY'),
organization: getEnv('SENTRY_ORGANIZATION'),
};
var projects = getEnv('SENTRY_PROJECTS').split(',');
var release = getEnv('SENTRY_RELEASE_VERSION');
return gulp.src(['build/scripts/*.js', 'build/scripts/*.map'])
.pipe(uploadToSentry(opts, projects, release));
});
'use strict';
var fs = require('fs');
var gulpUtil = require('gulp-util');
var path = require('path');
var gulpUtil = require('gulp-util');
var request = require('request');
var through = require('through2');
var SENTRY_API_ROOT = 'https://app.getsentry.com/api/0'
/**
* interface SentryOptions {
* /// The Sentry API key
* apiKey: string;
* /// The release string for the release to create
* release: string;
* /// The organization slug to use when constructing the API URL
* key: string;
* /// The Sentry organisation to upload the release to
* organization: string;
* /// The project name slug to use when constructing the API URL
* project: string;
* }
*/
/** Wrapper around request() that returns a Promise. */
function httpRequest(opts) {
return new Promise(function (resolve, reject) {
......@@ -27,7 +26,7 @@ function httpRequest(opts) {
reject(err);
} else {
resolve({
status: response.statusCode,
response: response,
body: body,
});
}
......@@ -35,6 +34,55 @@ function httpRequest(opts) {
});
}
/** Create a release in Sentry. Returns a Promise. */
function createRelease(opts, project, release) {
return httpRequest({
uri: `${SENTRY_API_ROOT}/projects/${opts.organization}/${project}/releases/`,
method: 'POST',
auth: {
user: opts.key,
password: '',
},
body: {
version: release,
},
json: true,
}).then(function (result) {
var success = (result.response.statusCode === 201);
var alreadyCreated = (result.response.statusCode === 400 &&
result.body.detail.match(/already exists/));
if (success || alreadyCreated) {
return;
}
throw new Error(`unable to create release '${release}' in project '${project}'`);
});
}
/** Upload a named file to a release in Sentry. Returns a Promise. */
function uploadReleaseFile(opts, project, release, file) {
return httpRequest({
uri: `${SENTRY_API_ROOT}/projects/${opts.organization}/${project}/releases/${release}/files/`,
method: 'POST',
auth: {
user: opts.key,
password: '',
},
formData: {
file: fs.createReadStream(file.path),
name: path.basename(file.path),
}
}).then(function (result) {
if (result.response.statusCode === 201) {
return;
}
throw new Error(`Uploading file failed: ${result.response.statusCode}: ${result.body}`);
});
}
/**
* Upload a stream of Vinyl files as a Sentry release.
*
......@@ -43,71 +91,33 @@ function httpRequest(opts) {
* files as artefacts for the release.
*
* @param {SentryOptions} opts
* @param {Array[String]} projects - A list of projects to which to upload
* files.
* @param {String} release - The name of the release.
* @return {NodeJS.ReadWriteStream} - A stream into which Vinyl files from
* gulp.src() etc. can be piped.
*/
module.exports = function uploadToSentry(opts) {
// A map of already-created release versions.
// Once the release has been successfully created, this is used
// to avoid creating it again.
var createdReleases = {};
module.exports = function uploadToSentry(opts, projects, release) {
// Create releases in every project
var releases = projects.map(function (project) {
gulpUtil.log(`Creating release '${release}' in project '${project}'`);
return createRelease(opts, project, release);
});
return through.obj(function (file, enc, callback) {
enc = enc;
gulpUtil.log(`Uploading ${file.path} to Sentry`);
var sentryURL =
`https://app.getsentry.com/api/0/projects/${opts.organization}/${opts.project}/releases`;
var releaseCreated;
if (createdReleases[opts.release]) {
releaseCreated = Promise.resolve();
} else {
releaseCreated = httpRequest({
uri: `${sentryURL}/`,
method: 'POST',
auth: {
user: opts.apiKey,
password: '',
},
body: {
version: opts.release,
},
json: true,
}).then(function (result) {
if (result.status === 201 ||
(result.status === 400 &&
result.body.detail.match(/already exists/))) {
createdReleases[opts.release] = true;
return;
}
});
}
releaseCreated.then(function () {
return httpRequest({
uri: `${sentryURL}/${opts.release}/files/`,
method: 'POST',
auth: {
user: opts.apiKey,
password: '',
},
formData: {
file: fs.createReadStream(file.path),
name: path.basename(file.path),
},
Promise.all(releases)
.then(function () {
gulpUtil.log(`Uploading ${path.basename(file.path)}`);
var uploads = projects.map(function (project) {
return uploadReleaseFile(opts, project, release, file);
});
}).then(function (result) {
if (result.status === 201) {
callback();
return;
}
var message =
`Uploading file failed: ${result.status}: ${result.body}`;
throw new Error(message);
}).catch(function (err) {
return Promise.all(uploads);
})
.then(function () {
callback();
})
.catch(function (err) {
gulpUtil.log('Sentry upload failed: ', err);
throw err;
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment