diff --git a/Tasks/DotNetCoreCLIV2/Strings/resources.resjson/en-US/resources.resjson b/Tasks/DotNetCoreCLIV2/Strings/resources.resjson/en-US/resources.resjson index d0d9d231d4d6..5686558c03d5 100644 --- a/Tasks/DotNetCoreCLIV2/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/DotNetCoreCLIV2/Strings/resources.resjson/en-US/resources.resjson @@ -103,7 +103,7 @@ "loc.messages.dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", "loc.messages.noProjectFilesFound": "Project file(s) matching the specified pattern were not found.", "loc.messages.noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s.", - "loc.messages.noWebProjctFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.", + "loc.messages.noWebProjectFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file, wwwroot folder in the directory, or by the usage of Microsoft.Net.Web.Sdk in your project file. You can set Publish Web Projects property to false (publishWebProjects: false in yml) if your project doesn't follow this convention or if you want to publish projects other than web projects.", "loc.messages.zipFailed": "Zip failed with error: %s", "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", diff --git a/Tasks/DotNetCoreCLIV2/Tests/L0.ts b/Tasks/DotNetCoreCLIV2/Tests/L0.ts index adc86a690a3d..2bef4b77d2b0 100644 --- a/Tasks/DotNetCoreCLIV2/Tests/L0.ts +++ b/Tasks/DotNetCoreCLIV2/Tests/L0.ts @@ -312,6 +312,20 @@ describe('DotNetCoreExe Suite', function () { done(); }); + + it('publish works with publishWebProjects option if .csproj have Microsoft.Net.Sdk.Web', (done: MochaDone) => { + process.env["__projects__"] = "validateWebProject.csproj"; + process.env["workingDirectory"] = "."; + process.env["__publishWebProject__"] = "true"; + let tp = path.join(__dirname, 'validateWebProject.js'); + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + tr.run(); + + assert(tr.invokedToolCount == 1, 'should have invoked been invoked once'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + it('publish updates the output with the project name appended', (done: MochaDone) => { process.env["__projects__"] = "*customoutput/project.json"; process.env["__publishWebProjects__"] = "false"; diff --git a/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.csproj b/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.csproj new file mode 100644 index 000000000000..8fc9f6a49bc2 --- /dev/null +++ b/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.1 + + + + + + + + + + + + + diff --git a/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.ts b/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.ts new file mode 100644 index 000000000000..0c9044dc1f00 --- /dev/null +++ b/Tasks/DotNetCoreCLIV2/Tests/validateWebProject.ts @@ -0,0 +1,42 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +let taskPath = path.join(__dirname, '..', 'dotnetcore.js'); +let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tmr.setInput('command', "publish"); +tmr.setInput('projects', process.env["__projects__"]); +tmr.setInput('publishWebProjects', process.env["__publishWebProjects__"] && process.env["__publishWebProjects__"] == "true" ? "true" : "false"); +tmr.setInput('arguments', process.env["__arguments__"] ? process.env["__arguments__"] : ""); +tmr.setInput('modifyOutputPath', process.env["modifyOutput"] == "false" ? "false" : "true"); +tmr.setInput('zipAfterPublish', process.env["zipAfterPublish"] ? process.env["zipAfterPublish"] : "false"); +tmr.setInput('workingDirectory', process.env["workingDirectory"] ? process.env["workingDirectory"] : ""); + +process.env['TASK_TEST_TRACE'] = "true"; + +var projectFile = path.join(__dirname, process.env["__projects__"]); +var execCommand = "dotnet publish " + projectFile + +let a: ma.TaskLibAnswers = { + "which": { + "dotnet": "dotnet", + }, + "checkPath": { "dotnet": true }, + "exec": {}, + "findMatch": { + "**/*.csproj\n**/*.vbproj\n**/*.fsproj": [projectFile] + } +} + +a['exec'][execCommand] = { + "code": 0, + "stdout": "published", + "stderr": "" +} + +process.env["MOCK_NORMALIZE_SLASHES"] = "true"; +tmr.setAnswers(a) +tmr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); + +tmr.run(); \ No newline at end of file diff --git a/Tasks/DotNetCoreCLIV2/dotnetcore.ts b/Tasks/DotNetCoreCLIV2/dotnetcore.ts index d30c33978713..ce3ab486bfd4 100644 --- a/Tasks/DotNetCoreCLIV2/dotnetcore.ts +++ b/Tasks/DotNetCoreCLIV2/dotnetcore.ts @@ -2,6 +2,7 @@ import tl = require("azure-pipelines-task-lib/task"); import tr = require("azure-pipelines-task-lib/toolrunner"); import path = require("path"); import fs = require("fs"); +import ltx = require("ltx"); var archiver = require('archiver'); import * as packCommand from './packcommand'; @@ -325,22 +326,50 @@ export class dotNetExe { } var projectFiles = utility.getProjectFiles(projectPattern); + var resolvedProjectFiles: string[] = []; if (searchWebProjects) { - projectFiles = projectFiles.filter(function (file, index, files): boolean { + resolvedProjectFiles = projectFiles.filter(function (file, index, files): boolean { var directory = path.dirname(file); return tl.exist(path.join(directory, "web.config")) || tl.exist(path.join(directory, "wwwroot")); }); - if (!projectFiles.length) { - tl.error(tl.loc("noWebProjctFound")); - } + if (!resolvedProjectFiles.length) { + var projectFilesUsingWebSdk = projectFiles.filter(this.isWebSdkUsed); + if(!projectFilesUsingWebSdk.length) { + tl.error(tl.loc("noWebProjectFound")); + } + return projectFilesUsingWebSdk; + } + return resolvedProjectFiles; } - return projectFiles; } + private isWebSdkUsed(projectfile: string): boolean { + if (projectfile.endsWith('.vbproj')) return false + + try { + var fileBuffer: Buffer = fs.readFileSync(projectfile); + var webConfigContent: string; + + var fileEncodings = ['utf8', 'utf16le']; + + for(var i = 0; i < fileEncodings.length; i++) { + tl.debug("Trying to decode with " + fileEncodings[i]); + webConfigContent = fileBuffer.toString(fileEncodings[i]); + try { + var projectSdkUsed: string = ltx.parse(webConfigContent).getAttr("sdk") || ltx.parse(webConfigContent).getAttr("Sdk"); + return projectSdkUsed && projectSdkUsed.toLowerCase() == "microsoft.net.sdk.web"; + } catch (error) {} + } + } catch(error) { + tl.warning(error); + } + return false; + } + private isPublishCommand(): boolean { return this.command === "publish"; } diff --git a/Tasks/DotNetCoreCLIV2/package-lock.json b/Tasks/DotNetCoreCLIV2/package-lock.json index 7c8244da0df6..4cd4affe6b77 100644 --- a/Tasks/DotNetCoreCLIV2/package-lock.json +++ b/Tasks/DotNetCoreCLIV2/package-lock.json @@ -107,20 +107,19 @@ } }, "azure-devops-node-api": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-6.6.3.tgz", - "integrity": "sha512-94wSu4O6CSSXoqYWg7Rzt2/IqbW2xVNu2qOtx6e7lnXxnDOcAu4eRzi8tgVNHsXTIGOVEsTqgMvGvFThKr9Pig==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-8.0.0.tgz", + "integrity": "sha512-QkIzphuE3y/hZVMB6ONN0Dev5r9+CIAiopWulwoYx1Er0kYcsbXsKXKynuLSxsVPocMppbr4YPhTsX2eHY/Mjw==", "requires": { - "os": "0.1.1", "tunnel": "0.0.4", - "typed-rest-client": "1.0.9", + "typed-rest-client": "1.2.0", "underscore": "1.8.3" }, "dependencies": { "typed-rest-client": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.0.9.tgz", - "integrity": "sha512-iOdwgmnP/tF6Qs+oY4iEtCf/3fnCDl7Gy9LGPJ4E3M4Wj3uaSko15FVwbsaBmnBqTJORnXBWVY5306D4HH8oiA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.2.0.tgz", + "integrity": "sha512-FrUshzZ1yxH8YwGR29PWWnfksLEILbWJydU7zfIRkyH7kAEzB62uMAl2WY6EyolWpLpVHeJGgQm45/MaruaHpw==", "requires": { "tunnel": "0.0.4", "underscore": "1.8.3" @@ -313,17 +312,20 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "ip-address": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.0.tgz", - "integrity": "sha512-+4yKpEyent8IpjuDQVkIpzIDbxSlCHTPdmaXCRLH0ttt3YsrbNxuZJ6h+1wLPx10T7gWsLN7M6BXIHV2vZNOGw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.2.tgz", + "integrity": "sha512-7aeFm/7oqo0mMhubTSjZ2Juw/F+WJ3hyfCScNVRQdz5RSRhw1Rj4ZlBFsmEajeKgQDI8asqVs31h8DpxEv7IfQ==", "requires": { "jsbn": "1.1.0", - "lodash.find": "^4.6.0", - "lodash.max": "^4.0.1", - "lodash.merge": "^4.6.1", - "lodash.padstart": "^4.6.1", - "lodash.repeat": "^4.1.0", - "sprintf-js": "1.1.1" + "lodash": "^4.17.11", + "sprintf-js": "1.1.2" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + } } }, "isarray": { @@ -358,31 +360,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" - }, - "lodash.max": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", - "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" - }, - "lodash.merge": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "lodash.repeat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", - "integrity": "sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ=" - }, "ltx": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/ltx/-/ltx-2.8.1.tgz", @@ -420,11 +397,6 @@ "wrappy": "1" } }, - "os": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", - "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" - }, "packaging-common": { "version": "file:../../_build/Tasks/Common/packaging-common-1.0.1.tgz", "requires": { @@ -435,14 +407,14 @@ "@types/node": "10.12.9", "@types/q": "1.5.2", "adm-zip": "^0.4.11", - "azure-devops-node-api": "^6.6.0", + "azure-devops-node-api": "8.0.0", "azure-pipelines-task-lib": "2.8.0", "azure-pipelines-tool-lib": "0.12.0", "ini": "^1.3.4", "ip-address": "^5.8.9", "ltx": "^2.6.2", "q": "^1.5.0", - "typed-rest-client": "0.12.0" + "typed-rest-client": "1.2.0" } }, "path-is-absolute": { @@ -500,9 +472,9 @@ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" }, "sprintf-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, "string_decoder": { "version": "1.0.3", @@ -529,9 +501,9 @@ "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" }, "typed-rest-client": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-0.12.0.tgz", - "integrity": "sha1-Y3b1Un9CfaEh3K/f1+QeEyHgcgw=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.2.0.tgz", + "integrity": "sha512-FrUshzZ1yxH8YwGR29PWWnfksLEILbWJydU7zfIRkyH7kAEzB62uMAl2WY6EyolWpLpVHeJGgQm45/MaruaHpw==", "requires": { "tunnel": "0.0.4", "underscore": "1.8.3" @@ -561,16 +533,6 @@ "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" - }, - "vso-node-api": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.5.0.tgz", - "integrity": "sha512-hFjPLMJkq02zF8U+LhZ4airH0ivaiKzGdlNAQlYFB3lWuGH/UANUrl63DVPUQOyGw+7ZNQ+ufM44T6mWN92xyg==", - "requires": { - "tunnel": "0.0.4", - "typed-rest-client": "^0.12.0", - "underscore": "1.8.3" - } } } }, @@ -580,13 +542,24 @@ "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" }, "vso-node-api": { - "version": "6.0.1-preview", - "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.0.1-preview.tgz", - "integrity": "sha1-RBprv5s8aNpiTbAeo1y6jwpMLKs=", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.5.0.tgz", + "integrity": "sha512-hFjPLMJkq02zF8U+LhZ4airH0ivaiKzGdlNAQlYFB3lWuGH/UANUrl63DVPUQOyGw+7ZNQ+ufM44T6mWN92xyg==", "requires": { - "q": "^1.0.1", "tunnel": "0.0.4", - "underscore": "^1.8.3" + "typed-rest-client": "^0.12.0", + "underscore": "1.8.3" + }, + "dependencies": { + "typed-rest-client": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-0.12.0.tgz", + "integrity": "sha1-Y3b1Un9CfaEh3K/f1+QeEyHgcgw=", + "requires": { + "tunnel": "0.0.4", + "underscore": "1.8.3" + } + } } }, "vsts-task-lib": { diff --git a/Tasks/DotNetCoreCLIV2/task.json b/Tasks/DotNetCoreCLIV2/task.json index 070a9bd4c396..58c1b8b8937f 100644 --- a/Tasks/DotNetCoreCLIV2/task.json +++ b/Tasks/DotNetCoreCLIV2/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 153, - "Patch": 2 + "Patch": 3 }, "minimumAgentVersion": "2.115.0", "instanceNameFormat": "dotnet $(command)", @@ -529,7 +529,7 @@ "dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", "noProjectFilesFound": "Project file(s) matching the specified pattern were not found.", "noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s.", - "noWebProjctFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.", + "noWebProjectFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file, wwwroot folder in the directory, or by the usage of Microsoft.Net.Web.Sdk in your project file. You can set Publish Web Projects property to false (publishWebProjects: false in yml) if your project doesn't follow this convention or if you want to publish projects other than web projects.", "zipFailed": "Zip failed with error: %s", "Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", "Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", diff --git a/Tasks/DotNetCoreCLIV2/task.loc.json b/Tasks/DotNetCoreCLIV2/task.loc.json index ee46e8d26476..a33e671ae7ba 100644 --- a/Tasks/DotNetCoreCLIV2/task.loc.json +++ b/Tasks/DotNetCoreCLIV2/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 153, - "Patch": 2 + "Patch": 3 }, "minimumAgentVersion": "2.115.0", "instanceNameFormat": "ms-resource:loc.instanceNameFormat",