This repository was archived by the owner on Nov 20, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 75
Add an init command for adding a UserSecretsId #500
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cbb8e81
add init call tests
liamdawson 33cbf5e
add init command triggering secrets id error case
liamdawson 24e0cea
fix init case to support cases without secrets id
liamdawson 3c97868
correctly resolve project file path
liamdawson f3b2203
add basically functional init command
liamdawson 197039e
Update init invocation logic
liamdawson ac6f992
add invalid ID init test
liamdawson 76c86ac
fix invalid ID case, use resources for strings
liamdawson 5cb03dd
fix bug preventing id and project passing to init
liamdawson d14d917
Merge remote-tracking branch 'upstream/master' into add-init-command
liamdawson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// 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 System.Xml.Linq; | ||
using System.Xml.XPath; | ||
using Microsoft.Extensions.CommandLineUtils; | ||
|
||
namespace Microsoft.Extensions.SecretManager.Tools.Internal | ||
{ | ||
// Workaround used to handle the fact that the options have not been parsed at configuration time | ||
public class InitCommandFactory : ICommand | ||
{ | ||
public CommandLineOptions Options { get; } | ||
|
||
internal static void Configure(CommandLineApplication command, CommandLineOptions options) | ||
{ | ||
command.Description = "Set a user secrets ID to enable secret storage"; | ||
command.HelpOption(); | ||
|
||
command.OnExecute(() => | ||
{ | ||
options.Command = new InitCommandFactory(options); | ||
}); | ||
} | ||
|
||
public InitCommandFactory(CommandLineOptions options) | ||
{ | ||
Options = options; | ||
} | ||
|
||
public void Execute(CommandContext context) | ||
{ | ||
new InitCommand(Options.Id, Options.Project).Execute(context); | ||
} | ||
|
||
public void Execute(CommandContext context, string workingDirectory) | ||
{ | ||
new InitCommand(Options.Id, Options.Project).Execute(context, workingDirectory); | ||
} | ||
} | ||
|
||
public class InitCommand : ICommand | ||
{ | ||
public string OverrideId { get; } | ||
public string ProjectPath { get; } | ||
public string WorkingDirectory { get; private set; } = Directory.GetCurrentDirectory(); | ||
|
||
public InitCommand(string id, string project) | ||
{ | ||
OverrideId = id; | ||
ProjectPath = project; | ||
} | ||
|
||
public void Execute(CommandContext context, string workingDirectory) | ||
{ | ||
WorkingDirectory = workingDirectory; | ||
Execute(context); | ||
} | ||
|
||
public void Execute(CommandContext context) | ||
{ | ||
var projectPath = ResolveProjectPath(ProjectPath, WorkingDirectory); | ||
|
||
// Load the project file as XML | ||
var projectDocument = XDocument.Load(projectPath); | ||
|
||
// Accept the `--id` CLI option to the main app | ||
string newSecretsId = string.IsNullOrWhiteSpace(OverrideId) | ||
? Guid.NewGuid().ToString() | ||
: OverrideId; | ||
|
||
// Confirm secret ID does not contain invalid characters | ||
if (Path.GetInvalidPathChars().Any(invalidChar => newSecretsId.Contains(invalidChar))) | ||
{ | ||
throw new ArgumentException(Resources.FormatError_InvalidSecretsId(newSecretsId)); | ||
} | ||
|
||
var existingUserSecretsId = projectDocument.XPathSelectElements("//UserSecretsId").FirstOrDefault(); | ||
|
||
// Check if a UserSecretsId is already set | ||
if (existingUserSecretsId != default) | ||
{ | ||
// Only set the UserSecretsId if the user specified an explicit value | ||
if (string.IsNullOrWhiteSpace(OverrideId)) | ||
{ | ||
context.Reporter.Output(Resources.FormatMessage_ProjectAlreadyInitialized(projectPath)); | ||
return; | ||
} | ||
|
||
existingUserSecretsId.SetValue(newSecretsId); | ||
} | ||
else | ||
{ | ||
// Find the first non-conditional PropertyGroup | ||
var propertyGroup = projectDocument.Root.DescendantNodes() | ||
.FirstOrDefault(node => node is XElement el | ||
&& el.Name == "PropertyGroup" | ||
&& el.Attributes().All(attr => | ||
attr.Name != "Condition")) as XElement; | ||
|
||
// No valid property group, create a new one | ||
if (propertyGroup == null) | ||
{ | ||
propertyGroup = new XElement("PropertyGroup"); | ||
projectDocument.Root.AddFirst(propertyGroup); | ||
} | ||
|
||
// Add UserSecretsId element | ||
propertyGroup.Add(new XElement("UserSecretsId", newSecretsId)); | ||
} | ||
|
||
projectDocument.Save(projectPath); | ||
|
||
context.Reporter.Output(Resources.FormatMessage_SetUserSecretsIdForProject(newSecretsId, projectPath)); | ||
} | ||
|
||
private static string ResolveProjectPath(string name, string path) | ||
{ | ||
var finder = new MsBuildProjectFinder(path); | ||
return finder.FindMsBuildProject(name); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In retrospec, Command.Execute should probably have returned
int
so the command can set an exit code. I'd be happy to also take a PR that made this change.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay - I looked at that but ran into the fact the interface had the expectation. At first glance though, I don't know how I'd safely modify the interface to allow a status code return without causing a breaking change (but that's a separate conversation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have my blessing to make a breaking change on
ICommand
. For projects like this, theICommand
type is an internal implementation detail of these console tools. No one should be compiling an app againstdotnet-user-secrets.dll
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ICommand
in this instance is from CommandLineUtils, so not part of this tool - do you still want that change?