Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

Commit

Permalink
Drop use of MSBuild API directly and use indirect project evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
Nate McMaster committed Oct 11, 2016
1 parent ef63762 commit 27fc53e
Show file tree
Hide file tree
Showing 17 changed files with 414 additions and 106 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ project.lock.json
.testPublish/
.build/
/.vs/
.vscode/
testWorkDir/
*.nuget.props
*.nuget.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(Project)" />
<Target Name="_FindUserSecretsProperty">
<WriteLinesToFile File="$(OutputFile)" Lines="$(UserSecretsId)" />
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.IO;
using System.Reflection;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.CommandLineUtils;

namespace Microsoft.Extensions.SecretManager.Tools.Internal
Expand All @@ -13,6 +14,7 @@ public class CommandLineOptions
public bool IsVerbose { get; set; }
public bool IsHelp { get; set; }
public string Project { get; set; }
public string Configuration { get; set; }
internal ICommand Command { get; set; }

public static CommandLineOptions Parse(string[] args, TextWriter output)
Expand All @@ -34,6 +36,9 @@ public static CommandLineOptions Parse(string[] args, TextWriter output)
var optionProject = app.Option("-p|--project <PROJECT>", "Path to project, default is current directory",
CommandOptionType.SingleValue, inherited: true);

var optionConfig = app.Option("-c|--configuration <CONFIGURATION>", $"The project configuration to use. Defaults to {Constants.DefaultConfiguration}",
CommandOptionType.SingleValue, inherited: true);

// the escape hatch if project evaluation fails, or if users want to alter a secret store other than the one
// in the current project
var optionId = app.Option("--id", "The user secret id to use.",
Expand All @@ -58,6 +63,7 @@ public static CommandLineOptions Parse(string[] args, TextWriter output)
options.IsHelp = app.IsShowingInformation;
options.IsVerbose = optionVerbose.HasValue();
options.Project = optionProject.Value();
options.Configuration = optionConfig.Value();

return options;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;

namespace Microsoft.Extensions.SecretManager.Tools.Internal
{
internal class MsBuildProjectFinder
{
private readonly string _directory;

public MsBuildProjectFinder(string directory)
{
if (string.IsNullOrEmpty(directory))
{
throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(directory));
}

_directory = directory;
}

public string FindMsBuildProject(string project)
{
var projectPath = project ?? _directory;

if (!Path.IsPathRooted(projectPath))
{
projectPath = Path.Combine(_directory, projectPath);
}

if (Directory.Exists(projectPath))
{
var projects = Directory.EnumerateFileSystemEntries(projectPath, "*.*proj", SearchOption.TopDirectoryOnly)
.Where(f => !".xproj".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
.ToList();

if (projects.Count > 1)
{
throw new GracefulException(Resources.FormatError_MultipleProjectsFound(projectPath));
}

if (projects.Count == 0)
{
throw new GracefulException(Resources.FormatError_NoProjectsFound(projectPath));
}

return projects[0];
}

if (!File.Exists(projectPath))
{
throw new GracefulException(Resources.FormatError_ProjectPath_NotFound(projectPath));
}

return projectPath;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.SecretManager.Tools.Internal
{
public class ProjectIdResolver : IDisposable
{
private const string TargetsFileName = "FindUserSecretsProperty.targets";
private readonly ILogger _logger;
private readonly string _workingDirectory;
private readonly List<string> _tempFiles = new List<string>();

public ProjectIdResolver(ILogger logger, string workingDirectory)
{
_workingDirectory = workingDirectory;
_logger = logger;
}

public string Resolve(string project, string configuration = Constants.DefaultConfiguration)
{
var finder = new MsBuildProjectFinder(_workingDirectory);
var projectFile = finder.FindMsBuildProject(project);

_logger.LogDebug(Resources.Message_Project_File_Path, projectFile);

var targetFile = GetTargetFile();
var outputFile = Path.GetTempFileName();
_tempFiles.Add(outputFile);

var commandOutput = new List<string>();
var commandResult = Command.CreateDotNet("msbuild",
new[] {
targetFile,
"/nologo",
"/t:_FindUserSecretsProperty",
$"/p:Project={projectFile}",
$"/p:OutputFile={outputFile}",
$"/p:Configuration={configuration}"
})
.CaptureStdErr()
.CaptureStdOut()
.OnErrorLine(l => commandOutput.Add(l))
.OnOutputLine(l => commandOutput.Add(l))
.Execute();

if (commandResult.ExitCode != 0)
{
_logger.LogDebug(string.Join(Environment.NewLine, commandOutput));
throw new GracefulException(Resources.FormatError_ProjectFailedToLoad(projectFile));
}

var id = File.ReadAllText(outputFile)?.Trim();
if (string.IsNullOrEmpty(id))
{
throw new GracefulException(Resources.FormatError_ProjectMissingId(projectFile));
}

return id;
}

public void Dispose()
{
foreach (var file in _tempFiles)
{
TryDelete(file);
}
}

private string GetTargetFile()
{
var assemblyDir = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location);

// targets should be in one of these locations, depending on test setup and tools installation
var searchPaths = new[]
{
AppContext.BaseDirectory,
assemblyDir, // next to assembly
Path.Combine(assemblyDir, "../../tools"), // inside the nupkg
};

var foundFile = searchPaths
.Select(dir => Path.Combine(dir, TargetsFileName))
.Where(File.Exists)
.FirstOrDefault();

if (foundFile != null)
{
return foundFile;
}

// This should only really happen during testing. Current build system doesn't give us a good way to ensure the
// test project has an always-up to date version of the targets file.
// TODO cleanup after we switch to an MSBuild system in which can specify "CopyToOutputDirectory: Always" to resolve this issue
var outputPath = Path.GetTempFileName();
using (var resource = GetType().GetTypeInfo().Assembly.GetManifestResourceStream(TargetsFileName))
using (var stream = new FileStream(outputPath, FileMode.Create))
{
resource.CopyTo(stream);
}

// cleanup
_tempFiles.Add(outputPath);

return outputPath;
}

private static void TryDelete(string file)
{
try
{
if (File.Exists(file))
{
File.Delete(file);
}
}
catch
{
// whatever
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
<dependencies>
<group targetFramework=".NETCoreApp1.0">
<!-- MUST BE alphabetical -->
<dependency id="Microsoft.Build.Runtime" version="$dep_1$" />
<dependency id="Microsoft.DotNet.Cli.Utils" version="$dep_2$" />
<dependency id="Microsoft.Extensions.CommandLineUtils" version="$dep_3$" />
<dependency id="Microsoft.Extensions.Configuration.UserSecrets" version="$dep_4$" />
<dependency id="Microsoft.Extensions.Logging" version="$dep_5$" />
<dependency id="Microsoft.NETCore.App" version="$dep_6$" />
<dependency id="NuGet.Versioning" version="$dep_7$" />
<dependency id="Microsoft.DotNet.Cli.Utils" version="$dep_1$" />
<dependency id="Microsoft.Extensions.CommandLineUtils" version="$dep_2$" />
<dependency id="Microsoft.Extensions.Configuration.UserSecrets" version="$dep_3$" />
<dependency id="Microsoft.Extensions.Logging" version="$dep_4$" />
<dependency id="Microsoft.NETCore.App" version="$dep_5$" />
</group>
</dependencies>
</metadata>
<files>
<file src="FindUserSecretsProperty.targets" target="tools\" />
<file src="bin/$configuration$/netcoreapp1.0/dotnet-user-secrets.dll" target="lib\netcoreapp1.0\" />
<file src="bin/$configuration$/netcoreapp1.0/dotnet-user-secrets.runtimeconfig.json" target="lib/netcoreapp1.0\" />
</files>
Expand Down
Loading

0 comments on commit 27fc53e

Please sign in to comment.