1 |
|
2 | (function () {
|
3 | 'use strict';
|
4 |
|
5 | module.exports = function (grunt) {
|
6 | var glob = require('glob'),
|
7 | fs = require('fs-extra'),
|
8 | path = require('path'),
|
9 | async = require('async'),
|
10 | _ = require('lodash'),
|
11 | xunit = require('./xunit.js')(grunt),
|
12 | coverage = require('./coverage.js')(grunt),
|
13 | defaultOptions = {
|
14 | dynamicAnalysis: 'reuseReports',
|
15 | sourceEncoding: 'UTF-8',
|
16 | language: 'js',
|
17 | defaultOutputDir: '.tmp/sonar/',
|
18 | scmDisabled: true,
|
19 | excludedProperties: []
|
20 | };
|
21 |
|
22 | |
23 |
|
24 |
|
25 |
|
26 | function copyFiles(g, defaultOutputDir, targetDir) {
|
27 | var files = glob.sync(g.src.toString(), {cwd: g.cwd, root: '/'});
|
28 | files.forEach(function (file) {
|
29 | var destinationDirectory = defaultOutputDir + path.sep + targetDir;
|
30 | var fileDirectory = path.dirname(file);
|
31 | if (fileDirectory !== '.') {
|
32 | destinationDirectory = destinationDirectory + path.sep + fileDirectory;
|
33 | }
|
34 | fs.mkdirpSync(destinationDirectory);
|
35 | var source = path.resolve(g.cwd, file);
|
36 | var extension = path.extname(file);
|
37 | var destination;
|
38 | if (targetDir === 'test') {
|
39 | var base = path.basename(file, extension);
|
40 | if(extension === '.js') {
|
41 | destination = destinationDirectory + path.sep + path.basename(base.replace(/\./g, '_') + extension);
|
42 | } else if(extension === '.feature') {
|
43 | destination = destinationDirectory + path.sep + path.basename(base.concat(extension).replace(/\./g, '_') + '.js');
|
44 | }
|
45 | } else {
|
46 | destination = destinationDirectory + path.sep + path.basename(file);
|
47 | }
|
48 |
|
49 | fs.copySync(source, destination, {replace: true});
|
50 | });
|
51 | }
|
52 |
|
53 | |
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | function mergeJson(original, override) {
|
60 | return _.merge(original, override, function (a, b, key, aParent, bParent) {
|
61 | if (_.isUndefined(b)) {
|
62 | aParent[key] = undefined;
|
63 | return;
|
64 | }
|
65 | });
|
66 | }
|
67 |
|
68 | |
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | function buildArgs(sonarOptions, data) {
|
76 |
|
77 | var args = [
|
78 | '-Dsonar.sources=src',
|
79 | '-Dsonar.tests=test',
|
80 | '-Dsonar.javascript.jstestdriver.reportsPath=results',
|
81 | '-Dsonar.javascript.jstestdriver.itReportsPath=results',
|
82 | '-Dsonar.javascript.lcov.reportPath=' + 'results' + path.sep + 'coverage_report.lcov',
|
83 | '-Dsonar.javascript.lcov.itReportPath=' + 'results' + path.sep + 'it_coverage_report.lcov'
|
84 | ];
|
85 |
|
86 |
|
87 | function addParameter(prop, object, key) {
|
88 | var value = _.result(object, key);
|
89 |
|
90 | if (value !== undefined && sonarOptions.excludedProperties.indexOf(prop) === -1) {
|
91 | args.push('-D' + prop + '=' + value);
|
92 | }
|
93 | }
|
94 |
|
95 | addParameter('sonar.host.url', sonarOptions.instance, 'hostUrl');
|
96 | addParameter('sonar.jdbc.url', sonarOptions.instance, 'jdbcUrl');
|
97 | addParameter('sonar.jdbc.username', sonarOptions.instance, 'jdbcUsername');
|
98 | addParameter('sonar.jdbc.password', sonarOptions.instance, 'jdbcPassword');
|
99 | addParameter('sonar.login', sonarOptions.instance, 'login');
|
100 | addParameter('sonar.password', sonarOptions.instance, 'password');
|
101 |
|
102 | addParameter('sonar.sourceEncoding', sonarOptions, 'sourceEncoding');
|
103 | addParameter('sonar.language', sonarOptions, 'language');
|
104 | addParameter('sonar.dynamicAnalysis', sonarOptions, 'dynamicAnalysis');
|
105 | addParameter('sonar.projectBaseDir', sonarOptions, 'defaultOutputDir');
|
106 | addParameter('sonar.scm.disabled', sonarOptions, 'scmDisabled');
|
107 |
|
108 | addParameter('sonar.projectKey', data.project, 'key');
|
109 | addParameter('sonar.projectName', data.project, 'name');
|
110 | addParameter('sonar.projectVersion', data.project, 'version');
|
111 |
|
112 | addParameter('sonar.exclusions', data, 'exclusions');
|
113 | return args;
|
114 | }
|
115 |
|
116 | function logArguments(message, opts) {
|
117 | grunt.log.writeln(message, opts.args);
|
118 | _.each(opts.args, function (arg) {
|
119 | grunt.log.writeln(arg);
|
120 | });
|
121 | }
|
122 |
|
123 | return {
|
124 | run: function (configuration) {
|
125 | var data = configuration.data;
|
126 |
|
127 | if (typeof data.project === 'undefined') {
|
128 | grunt.fail.fatal('No project information has been specified.');
|
129 | }
|
130 |
|
131 | if (typeof data.project.key === 'undefined') {
|
132 | grunt.fail.fatal('Missing project key. Allowed characters are alphanumeric, \'-\', \'_\', \'.\' and \':\', with at least one non-digit.');
|
133 | }
|
134 |
|
135 | if (typeof data.project.name === 'undefined') {
|
136 | grunt.fail.fatal('Missing project name. Please provide one.');
|
137 | }
|
138 |
|
139 | if (typeof data.paths === 'undefined' || data.paths.length === 0) {
|
140 | grunt.fail.fatal('No paths provided. Please provide at least one.');
|
141 | }
|
142 |
|
143 | var sonarOptions = mergeJson(defaultOptions, configuration.options({})),
|
144 | done = configuration.async(),
|
145 | resultsDir = sonarOptions.defaultOutputDir + path.sep + 'results' + path.sep,
|
146 | jUnitResultFile = resultsDir + 'TESTS-xunit.xml',
|
147 | itJUnitResultFile = resultsDir + 'ITESTS-xunit.xml',
|
148 | coverageResultFile = resultsDir + 'coverage_report.lcov',
|
149 | itCoverageResultFile = resultsDir + 'it_coverage_report.lcov';
|
150 |
|
151 | async.series({
|
152 |
|
153 | mergeJUnitReports: function (callback) {
|
154 | grunt.verbose.writeln('Merging JUnit reports');
|
155 | xunit.merge(data.paths, jUnitResultFile, 'unit');
|
156 | callback(null, 200);
|
157 | },
|
158 |
|
159 | mergeItJUnitReports: function (callback) {
|
160 | grunt.verbose.writeln('Merging Integration JUnit reports');
|
161 | xunit.merge(data.paths, itJUnitResultFile, 'itUnit');
|
162 | callback(null, 200);
|
163 | },
|
164 |
|
165 | mergeCoverageReports: function (callback) {
|
166 | grunt.verbose.writeln('Merging Coverage reports');
|
167 | coverage.merge(data.paths, coverageResultFile, 'coverage');
|
168 |
|
169 | callback(null, 200);
|
170 | },
|
171 |
|
172 | mergeItCoverageReports: function (callback) {
|
173 | grunt.verbose.writeln('Merging Integration Coverage reports');
|
174 | coverage.merge(data.paths, itCoverageResultFile, 'itCoverage');
|
175 |
|
176 | callback(null, 200);
|
177 | },
|
178 |
|
179 | copy: function (callback) {
|
180 | grunt.verbose.writeln('Copying files to working directory [' + sonarOptions.defaultOutputDir + ']');
|
181 | var sourceGlobs = [],
|
182 | testGlobs = [];
|
183 |
|
184 | data.paths.forEach(function (p) {
|
185 | var cwd = p.cwd ? p.cwd : '.';
|
186 | sourceGlobs.push({cwd: cwd + path.sep + p.src, src: '**/*.js'});
|
187 | testGlobs.push({cwd: cwd + path.sep + p.test, src: '**/*.js'});
|
188 | testGlobs.push({cwd: cwd + path.sep + p.test, src: '**/*.feature'});
|
189 | });
|
190 |
|
191 | sourceGlobs.forEach(function (g) {
|
192 | copyFiles(g, sonarOptions.defaultOutputDir, 'src');
|
193 | });
|
194 |
|
195 | testGlobs.forEach(function (g) {
|
196 | copyFiles(g, sonarOptions.defaultOutputDir, 'test');
|
197 | });
|
198 | callback(null, 200);
|
199 | },
|
200 |
|
201 |
|
202 | publish: function (callback) {
|
203 | var extension = (/^win/.test(process.platform) ? '.bat': '');
|
204 | var opts = {
|
205 | cmd: 'sonar-runner' + extension,
|
206 | args: buildArgs(sonarOptions, data),
|
207 | opts: {
|
208 | stdio: 'inherit'
|
209 | }
|
210 | };
|
211 |
|
212 | var libDir = path.join(__dirname, '..', 'lib');
|
213 | if (grunt.file.exists(libDir)) {
|
214 |
|
215 | glob.sync('**/bin/sonar-runner' + extension, {cwd: libDir, root: '/'}).forEach(function (file) {
|
216 | opts.cmd = libDir + path.sep + file;
|
217 | });
|
218 | }
|
219 |
|
220 |
|
221 | if (sonarOptions.runnerProperties) {
|
222 | Object.keys(sonarOptions.runnerProperties).forEach(function (prop) {
|
223 | opts.args.push('-D' + prop + '=' + sonarOptions.runnerProperties[prop]);
|
224 | });
|
225 | }
|
226 |
|
227 |
|
228 | if (sonarOptions.debug) {
|
229 | opts.args.push('-X');
|
230 | }
|
231 |
|
232 | if (!sonarOptions.dryRun) {
|
233 | if (sonarOptions.debug) {
|
234 | logArguments('Sonar will run with the following sonar properties:', opts);
|
235 | }
|
236 | grunt.util.spawn(opts, function (error, result, code) {
|
237 | if (code !== 0) {
|
238 | return grunt.warn('The following error occured while trying to upload to sonar: ' + error);
|
239 | } else {
|
240 | grunt.log.writeln('Uploaded information to sonar.');
|
241 | }
|
242 | callback(null, 200);
|
243 | });
|
244 | } else {
|
245 | grunt.log.subhead('Dry-run');
|
246 | logArguments('Sonar would have been triggered with the following sonar properties:', opts);
|
247 | callback(null, 200);
|
248 | }
|
249 | }
|
250 | },
|
251 | function (err) {
|
252 | if (err !== undefined && err !== null) {
|
253 | grunt.fail.fatal(err);
|
254 | }
|
255 | done();
|
256 | });
|
257 | }
|
258 | };
|
259 | };
|
260 | })();
|