From d48f2abc7c460b4ee45f79e0d6276945dd69ddce Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 7 Dec 2016 14:32:41 -0800 Subject: [PATCH] Use MSBuild project extensions instead of importing the users project Implicit imports prevents using on a project file that has the Sdk attribute. This change instead generates a file in the MSBuildProjectExtensionsPath to inject targets require to find the UserSecretsId property in a project. Resolves #242 --- .../Internal/ProjectIdResolver.cs | 116 +++++++++--------- ...soft.Extensions.SecretManager.Tools.nuspec | 1 - .../Program.cs | 6 +- .../project.json | 11 +- .../resources/ProjectIdResolverTargets.xml | 5 + .../FindUserSecretsProperty.targets | 6 - .../UserSecretsTestFixture.cs | 8 +- 7 files changed, 70 insertions(+), 83 deletions(-) create mode 100644 src/Microsoft.Extensions.SecretManager.Tools/resources/ProjectIdResolverTargets.xml delete mode 100644 src/Microsoft.Extensions.SecretManager.Tools/toolassets/FindUserSecretsProperty.targets diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/ProjectIdResolver.cs b/src/Microsoft.Extensions.SecretManager.Tools/Internal/ProjectIdResolver.cs index 2c891a3755..a2d86da7ae 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/ProjectIdResolver.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Internal/ProjectIdResolver.cs @@ -5,19 +5,16 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.SecretManager.Tools.Internal { - public class ProjectIdResolver : IDisposable + public class ProjectIdResolver { - private const string TargetsFileName = "FindUserSecretsProperty.targets"; private const string DefaultConfig = "Debug"; private readonly IReporter _reporter; private readonly string _workingDirectory; - private readonly List _tempFiles = new List(); public ProjectIdResolver(IReporter reporter, string workingDirectory) { @@ -29,83 +26,82 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal { var finder = new MsBuildProjectFinder(_workingDirectory); var projectFile = finder.FindMsBuildProject(project); + EnsureProjectExtensionTargetsExist(projectFile); _reporter.Verbose(Resources.FormatMessage_Project_File_Path(projectFile)); - var targetFile = GetTargetFile(); - var outputFile = Path.GetTempFileName(); - _tempFiles.Add(outputFile); - configuration = !string.IsNullOrEmpty(configuration) ? configuration : DefaultConfig; - var args = new[] + var outputFile = Path.GetTempFileName(); + try { - "msbuild", - targetFile, - "/nologo", - "/t:_FindUserSecretsProperty", - $"/p:Project={projectFile}", - $"/p:OutputFile={outputFile}", - $"/p:Configuration={configuration}" - }; - var psi = new ProcessStartInfo - { - FileName = DotNetMuxer.MuxerPathOrDefault(), - Arguments = ArgumentEscaper.EscapeAndConcatenate(args), - RedirectStandardOutput = true, - RedirectStandardError = true - }; + var args = new[] + { + "msbuild", + projectFile, + "/nologo", + "/t:_ExtractUserSecretsMetadata", // defined in ProjectIdResolverTargets.xml + $"/p:_UserSecretsMetadataFile={outputFile}", + $"/p:Configuration={configuration}" + }; + var psi = new ProcessStartInfo + { + FileName = DotNetMuxer.MuxerPathOrDefault(), + Arguments = ArgumentEscaper.EscapeAndConcatenate(args), + RedirectStandardOutput = true, + RedirectStandardError = true + }; #if DEBUG - _reporter.Verbose($"Invoking '{psi.FileName} {psi.Arguments}'"); + _reporter.Verbose($"Invoking '{psi.FileName} {psi.Arguments}'"); #endif - var process = Process.Start(psi); - process.WaitForExit(); + var process = Process.Start(psi); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + _reporter.Verbose(process.StandardOutput.ReadToEnd()); + _reporter.Verbose(process.StandardError.ReadToEnd()); + throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile)); + } + + var id = File.ReadAllText(outputFile)?.Trim(); + if (string.IsNullOrEmpty(id)) + { + throw new InvalidOperationException(Resources.FormatError_ProjectMissingId(projectFile)); + } + return id; - if (process.ExitCode != 0) - { - _reporter.Verbose(process.StandardOutput.ReadToEnd()); - _reporter.Verbose(process.StandardError.ReadToEnd()); - throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile)); } - - var id = File.ReadAllText(outputFile)?.Trim(); - if (string.IsNullOrEmpty(id)) + finally { - throw new InvalidOperationException(Resources.FormatError_ProjectMissingId(projectFile)); + TryDelete(outputFile); } - - return id; } - private string GetTargetFile() + private void EnsureProjectExtensionTargetsExist(string projectFile) { - var assemblyDir = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location); + // relies on MSBuildProjectExtensionsPath and Microsoft.Common.targets to import this file + // into the target project + var projectExtensionsPath = Path.Combine( + Path.GetDirectoryName(projectFile), + "obj", + $"{Path.GetFileName(projectFile)}.usersecrets.targets"); - // targets should be in one of these locations, depending on test setup and tools installation - var searchPaths = new[] + Directory.CreateDirectory(Path.GetDirectoryName(projectExtensionsPath)); + + // should overwrite the file always. Hypothetically, another version of the user-secrets tool + // could have already put a file here. We want to ensure the target file matches the currently + // running tool + using (var resource = GetType().GetTypeInfo().Assembly.GetManifestResourceStream("ProjectIdResolverTargets.xml")) + using (var stream = new FileStream(projectExtensionsPath, FileMode.Create)) + using (var writer = new StreamWriter(stream)) { - AppContext.BaseDirectory, - assemblyDir, // next to assembly - Path.Combine(assemblyDir, "../../toolassets"), // inside the nupkg - Path.Combine(assemblyDir, "toolassets"), // for local builds - Path.Combine(AppContext.BaseDirectory, "../../toolassets"), // relative to packaged deps.json - }; - - return searchPaths - .Select(dir => Path.Combine(dir, TargetsFileName)) - .Where(File.Exists) - .First(); - } - - public void Dispose() - { - foreach (var file in _tempFiles) - { - TryDelete(file); + writer.WriteLine(""); + resource.CopyTo(stream); } } diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec b/src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec index 5d47660dd5..d528291728 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec +++ b/src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec @@ -23,7 +23,6 @@ - diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs index 5ff2a8e228..667c16b27f 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs @@ -124,10 +124,8 @@ namespace Microsoft.Extensions.SecretManager.Tools return options.Id; } - using (var resolver = new ProjectIdResolver(reporter, _workingDirectory)) - { - return resolver.Resolve(options.Project, options.Configuration); - } + var resolver = new ProjectIdResolver(reporter, _workingDirectory); + return resolver.Resolve(options.Project, options.Configuration); } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.SecretManager.Tools/project.json b/src/Microsoft.Extensions.SecretManager.Tools/project.json index 9b198d29f1..e33c4f90e0 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/project.json +++ b/src/Microsoft.Extensions.SecretManager.Tools/project.json @@ -5,16 +5,17 @@ "emitEntryPoint": true, "warningsAsErrors": true, "keyFile": "../../tools/Key.snk", - "copyToOutput": "toolassets/*.targets", + "embed": { + "mappings": { + "ProjectIdResolverTargets.xml": "./resources/ProjectIdResolverTargets.xml" + } + }, "compile": { "include": "../Shared/**/*.cs" } }, "publishOptions": { - "include": [ - "toolassets/*.targets", - "prefercliruntime" - ] + "include": "prefercliruntime" }, "dependencies": { "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", diff --git a/src/Microsoft.Extensions.SecretManager.Tools/resources/ProjectIdResolverTargets.xml b/src/Microsoft.Extensions.SecretManager.Tools/resources/ProjectIdResolverTargets.xml new file mode 100644 index 0000000000..a30a87d34a --- /dev/null +++ b/src/Microsoft.Extensions.SecretManager.Tools/resources/ProjectIdResolverTargets.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Microsoft.Extensions.SecretManager.Tools/toolassets/FindUserSecretsProperty.targets b/src/Microsoft.Extensions.SecretManager.Tools/toolassets/FindUserSecretsProperty.targets deleted file mode 100644 index 694dc25008..0000000000 --- a/src/Microsoft.Extensions.SecretManager.Tools/toolassets/FindUserSecretsProperty.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/test/Microsoft.Extensions.SecretManager.Tools.Tests/UserSecretsTestFixture.cs b/test/Microsoft.Extensions.SecretManager.Tools.Tests/UserSecretsTestFixture.cs index a3ff3dfbb1..569ee1c6c3 100644 --- a/test/Microsoft.Extensions.SecretManager.Tools.Tests/UserSecretsTestFixture.cs +++ b/test/Microsoft.Extensions.SecretManager.Tools.Tests/UserSecretsTestFixture.cs @@ -32,9 +32,7 @@ namespace Microsoft.Extensions.Configuration.UserSecrets.Tests return GetTempSecretProject(out userSecretsId); } - private const string ProjectTemplate = @" - - + private const string ProjectTemplate = @" Exe netcoreapp1.0 @@ -43,12 +41,8 @@ namespace Microsoft.Extensions.Configuration.UserSecrets.Tests - - - - "; public string GetTempSecretProject(out string userSecretsId)