Skip to content

Commit c2e7fea

Browse files
HSARBogdan Gavril
authored andcommitted
Assert dir creation and file copy in Maven and Gradle code analysis tests
1 parent f8bb984 commit c2e7fea

File tree

12 files changed

+385
-40
lines changed

12 files changed

+385
-40
lines changed

Tasks/Gradle/CodeAnalysis/Common/CodeAnalysisResultPublisher.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {AnalysisResult} from './AnalysisResult'
2+
import {FileSystemInteractions} from './FileSystemInteractions';
23

34
import path = require('path');
45
import fs = require('fs');
@@ -31,20 +32,20 @@ export class CodeAnalysisResultPublisher {
3132
tl.debug('[CA] Preparing to upload artifacts');
3233

3334
let artifactBaseDir = path.join(this.stagingDir, 'CA');
34-
tl.mkdirP(artifactBaseDir);
35+
FileSystemInteractions.createDirectory(artifactBaseDir);
3536

3637
for (var analysisResult of this.analysisResults) {
3738

3839
// Group artifacts in folders representing the module name
3940
let destinationDir = path.join(artifactBaseDir, analysisResult.moduleName);
40-
tl.mkdirP(destinationDir);
41+
FileSystemInteractions.createDirectory(destinationDir);
4142

4243
for (var resultFile of analysisResult.resultFiles) {
4344
let extension = path.extname(resultFile);
4445
let reportName = path.basename(resultFile, extension);
4546

4647
let artifactName = `${prefix}_${reportName}_${analysisResult.toolName}${extension}`;
47-
tl.cp('-f', resultFile, path.join(destinationDir, artifactName));
48+
FileSystemInteractions.copyFile(resultFile, path.join(destinationDir, artifactName));
4849
}
4950
}
5051

@@ -86,7 +87,7 @@ export class CodeAnalysisResultPublisher {
8687

8788
private uploadMdSummary(content: string):void {
8889
var buildSummaryFilePath: string = path.join(this.stagingDir, 'CodeAnalysisBuildSummary.md');
89-
tl.mkdirP(this.stagingDir);
90+
FileSystemInteractions.createDirectory(this.stagingDir);
9091
fs.writeFileSync(buildSummaryFilePath, content);
9192

9293
tl.debug('[CA] Uploading build summary from ' + buildSummaryFilePath);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import shell = require('shelljs');
2+
import path = require('path');
3+
import fs = require('fs');
4+
5+
import tl = require('vsts-task-lib/task');
6+
7+
export class FileSystemInteractions {
8+
public static copyFile(sourcePath:string, destinationPath:string):void {
9+
shell.cp('-f', sourcePath, destinationPath);
10+
11+
this.checkShell('cp', false);
12+
}
13+
14+
/**
15+
* Create a directory at the specified path, including any folders in between.
16+
* @param directoryPath Path to create.
17+
*/
18+
public static createDirectory(directoryPath:string):void {
19+
// build a stack of directories to create
20+
let stack: string[] = [ ];
21+
let testDir: string = directoryPath;
22+
while (true) {
23+
// validate the loop is not out of control
24+
if (stack.length >= 1000) {
25+
// let the framework throw
26+
fs.mkdirSync(directoryPath);
27+
return;
28+
}
29+
30+
tl.debug(`testing directory '${testDir}'`);
31+
let stats: fs.Stats;
32+
try {
33+
stats = fs.statSync(testDir);
34+
} catch (err) {
35+
if (err.code == 'ENOENT') {
36+
// validate the directory is not the drive root
37+
let parentDir = path.dirname(testDir);
38+
if (testDir == parentDir) {
39+
throw new Error(tl.loc('LIB_MkdirFailedInvalidDriveRoot', directoryPath, testDir)); // Unable to create directory '{p}'. Root directory does not exist: '{testDir}'
40+
}
41+
42+
// push the dir and test the parent
43+
stack.push(testDir);
44+
testDir = parentDir;
45+
continue;
46+
}
47+
else if (err.code == 'UNKNOWN') {
48+
throw new Error(tl.loc('LIB_MkdirFailedInvalidShare', directoryPath, testDir)) // Unable to create directory '{p}'. Unable to verify the directory exists: '{testDir}'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.
49+
}
50+
else {
51+
throw err;
52+
}
53+
}
54+
55+
if (!stats.isDirectory()) {
56+
throw new Error(tl.loc('LIB_MkdirFailedFileExists', directoryPath, testDir)); // Unable to create directory '{p}'. Conflicting file exists: '{testDir}'
57+
}
58+
59+
// testDir exists
60+
break;
61+
}
62+
63+
// create each directory
64+
while (stack.length) {
65+
let dir = stack.pop();
66+
tl.debug(`mkdir '${dir}'`);
67+
try {
68+
fs.mkdirSync(dir);
69+
} catch (err) {
70+
throw new Error(tl.loc('LIB_MkdirFailed', directoryPath, err.message)); // Unable to create directory '{p}'. {err.message}
71+
}
72+
}
73+
}
74+
75+
public static checkShell(cmd: string, continueOnError?: boolean) {
76+
var se = shell.error();
77+
78+
if (se) {
79+
tl.debug(cmd + ' failed');
80+
81+
if (!continueOnError) {
82+
throw new Error(se);
83+
}
84+
}
85+
}
86+
}

Tasks/Gradle/CodeAnalysis/SonarQube/vsts-server-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {SonarQubeEndpoint} from './endpoint';
1111
import {SonarQubeRunSettings} from './run-settings';
1212
import {SonarQubeReportBuilder} from './report-builder';
1313
import {SonarQubeMetrics} from './metrics';
14+
import {FileSystemInteractions} from '../Common/FileSystemInteractions';
1415

1516
/**
1617
* Class provides functions for effecting change on the VSTS serverside.
@@ -145,7 +146,7 @@ export class VstsServerUtils {
145146
*/
146147
private static getOrCreateSonarQubeStagingDirectory(): string {
147148
var sqStagingDir = path.join(tl.getVariable('build.artifactStagingDirectory'), ".sqAnalysis");
148-
tl.mkdirP(sqStagingDir);
149+
FileSystemInteractions.createDirectory(sqStagingDir);
149150
return sqStagingDir;
150151
}
151152

Tasks/Gradle/task.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"version": {
1313
"Major": 1,
1414
"Minor": 0,
15-
"Patch": 59
15+
"Patch": 60
1616
},
1717
"demands": [
1818
"java"

Tasks/Gradle/task.loc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"version": {
1313
"Major": 1,
1414
"Minor": 0,
15-
"Patch": 59
15+
"Patch": 60
1616
},
1717
"demands": [
1818
"java"

Tasks/Maven/CodeAnalysis/Common/CodeAnalysisResultPublisher.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {AnalysisResult} from './AnalysisResult'
2+
import {FileSystemInteractions} from './FileSystemInteractions';
23

34
import path = require('path');
45
import fs = require('fs');
@@ -20,7 +21,7 @@ export class CodeAnalysisResultPublisher {
2021

2122
/**
2223
* Uploads the artifacts. It groups them by module
23-
*
24+
*
2425
* @param {string} prefix - used to discriminate between artifacts comming from different builds of the same projects (e.g. the build number)
2526
*/
2627
public uploadArtifacts(prefix: string):void {
@@ -31,20 +32,20 @@ export class CodeAnalysisResultPublisher {
3132
tl.debug('[CA] Preparing to upload artifacts');
3233

3334
let artifactBaseDir = path.join(this.stagingDir, 'CA');
34-
tl.mkdirP(artifactBaseDir);
35+
FileSystemInteractions.createDirectory(artifactBaseDir);
3536

3637
for (var analysisResult of this.analysisResults) {
3738

3839
// Group artifacts in folders representing the module name
3940
let destinationDir = path.join(artifactBaseDir, analysisResult.moduleName);
40-
tl.mkdirP(destinationDir);
41+
FileSystemInteractions.createDirectory(destinationDir);
4142

4243
for (var resultFile of analysisResult.resultFiles) {
4344
let extension = path.extname(resultFile);
4445
let reportName = path.basename(resultFile, extension);
4546

4647
let artifactName = `${prefix}_${reportName}_${analysisResult.toolName}${extension}`;
47-
tl.cp('-f', resultFile, path.join(destinationDir, artifactName));
48+
FileSystemInteractions.copyFile(resultFile, path.join(destinationDir, artifactName));
4849
}
4950
}
5051

@@ -56,17 +57,17 @@ export class CodeAnalysisResultPublisher {
5657

5758
/**
5859
* Creates and uploads a build summary that looks like:
59-
* Looks like: PMD found 13 violations in 4 files.
60-
* FindBugs found 10 violations in 8 files.
61-
*
62-
* Code analysis results can be found in the 'Artifacts' tab.
60+
* Looks like: PMD found 13 violations in 4 files.
61+
* FindBugs found 10 violations in 8 files.
62+
*
63+
* Code analysis results can be found in the 'Artifacts' tab.
6364
*/
6465
public uploadBuildSummary():void {
6566

6667
if (this.analysisResults.length === 0) {
6768
return;
6869
}
69-
70+
7071
tl.debug('[CA] Preparing a build summary');
7172
let content: string = this.createSummaryContent();
7273
this.uploadMdSummary(content);
@@ -86,7 +87,7 @@ export class CodeAnalysisResultPublisher {
8687

8788
private uploadMdSummary(content: string):void {
8889
var buildSummaryFilePath: string = path.join(this.stagingDir, 'CodeAnalysisBuildSummary.md');
89-
tl.mkdirP(this.stagingDir);
90+
FileSystemInteractions.createDirectory(this.stagingDir);
9091
fs.writeFileSync(buildSummaryFilePath, content);
9192

9293
tl.debug('[CA] Uploading build summary from ' + buildSummaryFilePath);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import shell = require('shelljs');
2+
import path = require('path');
3+
import fs = require('fs');
4+
5+
import tl = require('vsts-task-lib/task');
6+
7+
export class FileSystemInteractions {
8+
public static copyFile(sourcePath:string, destinationPath:string):void {
9+
shell.cp('-f', sourcePath, destinationPath);
10+
11+
this.checkShell('cp', false);
12+
}
13+
14+
/**
15+
* Create a directory at the specified path, including any folders in between.
16+
* @param directoryPath Path to create.
17+
*/
18+
public static createDirectory(directoryPath:string):void {
19+
// build a stack of directories to create
20+
let stack: string[] = [ ];
21+
let testDir: string = directoryPath;
22+
while (true) {
23+
// validate the loop is not out of control
24+
if (stack.length >= 1000) {
25+
// let the framework throw
26+
fs.mkdirSync(directoryPath);
27+
return;
28+
}
29+
30+
tl.debug(`testing directory '${testDir}'`);
31+
let stats: fs.Stats;
32+
try {
33+
stats = fs.statSync(testDir);
34+
} catch (err) {
35+
if (err.code == 'ENOENT') {
36+
// validate the directory is not the drive root
37+
let parentDir = path.dirname(testDir);
38+
if (testDir == parentDir) {
39+
throw new Error(tl.loc('LIB_MkdirFailedInvalidDriveRoot', directoryPath, testDir)); // Unable to create directory '{p}'. Root directory does not exist: '{testDir}'
40+
}
41+
42+
// push the dir and test the parent
43+
stack.push(testDir);
44+
testDir = parentDir;
45+
continue;
46+
}
47+
else if (err.code == 'UNKNOWN') {
48+
throw new Error(tl.loc('LIB_MkdirFailedInvalidShare', directoryPath, testDir)) // Unable to create directory '{p}'. Unable to verify the directory exists: '{testDir}'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.
49+
}
50+
else {
51+
throw err;
52+
}
53+
}
54+
55+
if (!stats.isDirectory()) {
56+
throw new Error(tl.loc('LIB_MkdirFailedFileExists', directoryPath, testDir)); // Unable to create directory '{p}'. Conflicting file exists: '{testDir}'
57+
}
58+
59+
// testDir exists
60+
break;
61+
}
62+
63+
// create each directory
64+
while (stack.length) {
65+
let dir = stack.pop();
66+
tl.debug(`mkdir '${dir}'`);
67+
try {
68+
fs.mkdirSync(dir);
69+
} catch (err) {
70+
throw new Error(tl.loc('LIB_MkdirFailed', directoryPath, err.message)); // Unable to create directory '{p}'. {err.message}
71+
}
72+
}
73+
}
74+
75+
public static checkShell(cmd: string, continueOnError?: boolean) {
76+
var se = shell.error();
77+
78+
if (se) {
79+
tl.debug(cmd + ' failed');
80+
81+
if (!continueOnError) {
82+
throw new Error(se);
83+
}
84+
}
85+
}
86+
}

Tasks/Maven/CodeAnalysis/SonarQube/vsts-server-utils.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {SonarQubeEndpoint} from './endpoint';
1111
import {SonarQubeRunSettings} from './run-settings';
1212
import {SonarQubeReportBuilder} from './report-builder';
1313
import {SonarQubeMetrics} from './metrics';
14+
import {FileSystemInteractions} from '../Common/FileSystemInteractions';
1415

1516
/**
1617
* Class provides functions for effecting change on the VSTS serverside.
@@ -59,14 +60,14 @@ export class VstsServerUtils {
5960
* @returns {Promise<void>} Promise resolved when action completes.
6061
*/
6162
public static processSonarQubeBuildSummary(sqRunSettings:SonarQubeRunSettings, sqMetrics:SonarQubeMetrics):Q.Promise<void> {
62-
// During a pull request build, data necessary to create SQRunSettings is not available
63-
if (sqRunSettings == null || VstsServerUtils.isPrBuild()) {
64-
console.log(tl.loc('sqAnalysis_IsPullRequest_SkippingBuildSummary'));
65-
return Q.when<void>(null);
66-
}
63+
// During a pull request build, data necessary to create SQRunSettings is not available
64+
if (sqRunSettings == null || VstsServerUtils.isPrBuild()) {
65+
console.log(tl.loc('sqAnalysis_IsPullRequest_SkippingBuildSummary'));
66+
return Q.when<void>(null);
67+
}
6768

68-
// Necessary data is not available during a pull request build
69-
return VstsServerUtils.createSonarQubeBuildSummary(sqRunSettings, sqMetrics)
69+
// Necessary data is not available during a pull request build
70+
return VstsServerUtils.createSonarQubeBuildSummary(sqRunSettings, sqMetrics)
7071
.then((buildSummaryContents:string) => {
7172
return VstsServerUtils.saveSonarQubeBuildSummary(buildSummaryContents);
7273
})
@@ -145,7 +146,7 @@ export class VstsServerUtils {
145146
*/
146147
private static getOrCreateSonarQubeStagingDirectory(): string {
147148
var sqStagingDir = path.join(tl.getVariable('build.artifactStagingDirectory'), ".sqAnalysis");
148-
tl.mkdirP(sqStagingDir);
149+
FileSystemInteractions.createDirectory(sqStagingDir);
149150
return sqStagingDir;
150151
}
151152

Tasks/Maven/task.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"version": {
1717
"Major": 1,
1818
"Minor": 0,
19-
"Patch": 66
19+
"Patch": 67
2020
},
2121
"minimumAgentVersion": "1.89.0",
2222
"instanceNameFormat": "Maven $(mavenPOMFile)",

Tasks/Maven/task.loc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"version": {
1717
"Major": 1,
1818
"Minor": 0,
19-
"Patch": 66
19+
"Patch": 67
2020
},
2121
"minimumAgentVersion": "1.89.0",
2222
"instanceNameFormat": "ms-resource:loc.instanceNameFormat",

0 commit comments

Comments
 (0)