diff --git a/app/assets/javascripts/raven/index.js b/app/assets/javascripts/raven/index.js index 5325e495815..f82824c0940 100644 --- a/app/assets/javascripts/raven/index.js +++ b/app/assets/javascripts/raven/index.js @@ -6,6 +6,10 @@ const index = function index() { currentUserId: gon.current_user_id, whitelistUrls: [gon.gitlab_url], isProduction: process.env.NODE_ENV, + release: process.env.HEAD_COMMIT_SHA, + tags: { + HEAD_COMMIT_SHA: process.env.HEAD_COMMIT_SHA, + }, }); return RavenConfig; diff --git a/app/assets/javascripts/raven/raven_config.js b/app/assets/javascripts/raven/raven_config.js index c7fe1cacf49..da3fb7a6744 100644 --- a/app/assets/javascripts/raven/raven_config.js +++ b/app/assets/javascripts/raven/raven_config.js @@ -57,6 +57,8 @@ const RavenConfig = { configure() { Raven.config(this.options.sentryDsn, { + release: this.options.release, + tags: this.options.tags, whitelistUrls: this.options.whitelistUrls, environment: this.options.isProduction ? 'production' : 'development', ignoreErrors: this.IGNORE_ERRORS, diff --git a/config/webpack.config.js b/config/webpack.config.js index 7bc225968de..b28543cab21 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -2,6 +2,7 @@ var fs = require('fs'); var path = require('path'); +var execSync = require('child_process').execSync; var webpack = require('webpack'); var StatsPlugin = require('stats-webpack-plugin'); var CompressionPlugin = require('compression-webpack-plugin'); @@ -197,7 +198,33 @@ var config = { } } +function getHeadCommitSHA() { + // Simple SHA validation. + // Match 5-40 numbers or lowercase letters between a and f. + const SHA_REGEX = /^\b[0-9a-f]{5,40}\b$/; + let stdout; + + try { + stdout = execSync('git rev-parse HEAD'); + } catch (error) { + throw error; + } + + const headCommitSHA = stdout.trim(); + if (!SHA_REGEX.test(headCommitSHA)) { + throw new Error(`\`git rev-parse HEAD\` output is not a valid SHA1: ${headCommitSHA}`); + } + + return headCommitSHA; +} + if (IS_PRODUCTION) { + const processEnv = { + NODE_ENV: JSON.stringify('production'), + }; + + processEnv.HEAD_COMMIT_SHA = getHeadCommitSHA(); + config.devtool = 'source-map'; config.plugins.push( new webpack.NoEmitOnErrorsPlugin(), @@ -209,7 +236,7 @@ if (IS_PRODUCTION) { sourceMap: true }), new webpack.DefinePlugin({ - 'process.env': { NODE_ENV: JSON.stringify('production') } + 'process.env': processEnv, }), new CompressionPlugin({ asset: '[path].gz[query]', diff --git a/spec/javascripts/raven/index_spec.js b/spec/javascripts/raven/index_spec.js index b5662cd0331..e8fe6b32c43 100644 --- a/spec/javascripts/raven/index_spec.js +++ b/spec/javascripts/raven/index_spec.js @@ -2,18 +2,14 @@ import RavenConfig from '~/raven/raven_config'; import index from '~/raven/index'; describe('RavenConfig options', () => { - let sentryDsn; - let currentUserId; - let gitlabUrl; - let isProduction; + const sentryDsn = 'sentryDsn'; + const currentUserId = 'currentUserId'; + const gitlabUrl = 'gitlabUrl'; + const isProduction = 'isProduction'; + const headCommitSHA = 'headCommitSHA'; let indexReturnValue; beforeEach(() => { - sentryDsn = 'sentryDsn'; - currentUserId = 'currentUserId'; - gitlabUrl = 'gitlabUrl'; - isProduction = 'isProduction'; - window.gon = { sentry_dsn: sentryDsn, current_user_id: currentUserId, @@ -21,6 +17,7 @@ describe('RavenConfig options', () => { }; process.env.NODE_ENV = isProduction; + process.env.HEAD_COMMIT_SHA = headCommitSHA; spyOn(RavenConfig, 'init'); @@ -33,6 +30,10 @@ describe('RavenConfig options', () => { currentUserId, whitelistUrls: [gitlabUrl], isProduction, + release: headCommitSHA, + tags: { + HEAD_COMMIT_SHA: headCommitSHA, + }, }); }); diff --git a/spec/javascripts/raven/raven_config_spec.js b/spec/javascripts/raven/raven_config_spec.js index a2d720760fc..d2f4f9ac6fb 100644 --- a/spec/javascripts/raven/raven_config_spec.js +++ b/spec/javascripts/raven/raven_config_spec.js @@ -25,17 +25,9 @@ describe('RavenConfig', () => { }); describe('init', () => { - let options; + const options = {}; beforeEach(() => { - options = { - sentryDsn: '//sentryDsn', - ravenAssetUrl: '//ravenAssetUrl', - currentUserId: 1, - whitelistUrls: ['//gitlabUrl'], - isProduction: true, - }; - spyOn(RavenConfig, 'configure'); spyOn(RavenConfig, 'bindRavenErrors'); spyOn(RavenConfig, 'setUser'); @@ -84,6 +76,10 @@ describe('RavenConfig', () => { sentryDsn: '//sentryDsn', whitelistUrls: ['//gitlabUrl'], isProduction: true, + release: 'release', + tags: { + HEAD_COMMIT_SHA: 'headCommitSha', + }, }; ravenConfig = jasmine.createSpyObj('ravenConfig', ['shouldSendSample']); @@ -100,6 +96,8 @@ describe('RavenConfig', () => { it('should call Raven.config', () => { expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, { + release: options.release, + tags: options.tags, whitelistUrls: options.whitelistUrls, environment: 'production', ignoreErrors: ravenConfig.IGNORE_ERRORS,