Skip to content

Commit

Permalink
JsonPropertyPostAction: allow to create file and path (#41959)
Browse files Browse the repository at this point in the history
  • Loading branch information
Evangelink authored Jul 9, 2024
2 parents 17304ec + 3b4ce6b commit dfe3107
Show file tree
Hide file tree
Showing 17 changed files with 426 additions and 61 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 28 additions & 25 deletions src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@
</data>
<data name="TemplatePackageCoordinator_VulnerabilitySeverity_Moderate" xml:space="preserve">
<value>Moderate</value>
<comment>represents nuget api severity of level 1.</comment>
<comment>represents nuget api severity of level 1.</comment>
</data>
<data name="TemplatePackageCoordinator_VulnerabilitySeverity_High" xml:space="preserve">
<value>High</value>
Expand Down Expand Up @@ -859,67 +859,64 @@ The header is followed by the list of parameters and their errors (might be seve
<data name="Generic_CommandHints_List" xml:space="preserve">
<value>To list installed templates, run:</value>
</data>
<data name="DetailsCommand_Argument_PackageIdentifier">
<data name="DetailsCommand_Argument_PackageIdentifier" xml:space="preserve">
<value>Package identifier</value>
</data>
<data name="DetailsCommand_Option_Version">
<data name="DetailsCommand_Option_Version" xml:space="preserve">
<value>Specifies a concrete version for displaying details. If not specified the last is taken.</value>
</data>
<data name="DetailsCommand_Property_Authors">
<data name="DetailsCommand_Property_Authors" xml:space="preserve">
<value>Authors</value>
</data>
<data name="DetailsCommand_Property_Description">
<data name="DetailsCommand_Property_Description" xml:space="preserve">
<value>Details</value>
</data>
<data name="DetailsCommand_Property_SourceFeed">
<data name="DetailsCommand_Property_SourceFeed" xml:space="preserve">
<value>Source Feed</value>
</data>
<data name="DetailsCommand_Property_Version">
<data name="DetailsCommand_Property_Version" xml:space="preserve">
<value>Package version</value>
</data>
<data name="DetailsCommand_Property_PrefixReserved">
<data name="DetailsCommand_Property_PrefixReserved" xml:space="preserve">
<value>Reserved</value>
</data>
<data name="DetailsCommand_Property_Languages">
<data name="DetailsCommand_Property_Languages" xml:space="preserve">
<value>Languages</value>
<comment></comment>
</data>
<data name="DetailsCommand_Property_LicenseMetadata">
<data name="DetailsCommand_Property_LicenseMetadata" xml:space="preserve">
<value>License Metadata</value>
</data>
<data name="DetailsCommand_Property_LicenseExpression">
<data name="DetailsCommand_Property_LicenseExpression" xml:space="preserve">
<value>License Expression</value>
</data>
<data name="DetailsCommand_Property_License">
<data name="DetailsCommand_Property_License" xml:space="preserve">
<value>License</value>
</data>
<data name="DetailsCommand_Property_LicenseUrl">
<data name="DetailsCommand_Property_LicenseUrl" xml:space="preserve">
<value>License Url</value>
</data>
<data name="DetailsCommand_Property_Owners">
<data name="DetailsCommand_Property_Owners" xml:space="preserve">
<value>Owners</value>
</data>
<data name="DetailsCommand_Property_RepoUrl">
<data name="DetailsCommand_Property_RepoUrl" xml:space="preserve">
<value>Repository Url</value>
</data>
<data name="DetailsCommand_Property_ShortNames">
<data name="DetailsCommand_Property_ShortNames" xml:space="preserve">
<value>Short Names</value>
<comment></comment>
</data>
<data name="DetailsCommand_Property_Tags">
<data name="DetailsCommand_Property_Tags" xml:space="preserve">
<value>Tags</value>
<comment></comment>
</data>
<data name="DetailsCommand_Property_Templates">
<data name="DetailsCommand_Property_Templates" xml:space="preserve">
<value>Templates</value>
</data>
<data name="DetailsCommand_NoNuGetSources">
<data name="DetailsCommand_NoNuGetSources" xml:space="preserve">
<value>No NuGet sources are defined or enabled</value>
</data>
<data name="DetailsCommand_UnableToLoadResources">
<data name="DetailsCommand_UnableToLoadResources" xml:space="preserve">
<value>Failed to load NuGet sources configured for the folder {0}</value>
</data>
<data name="DetailsCommand_UnableToLoadResource">
<data name="DetailsCommand_UnableToLoadResource" xml:space="preserve">
<value>Could not parse NuGet source '{0}', so it was discarded</value>
</data>
<data name="OperationCancelled" xml:space="preserve">
Expand Down Expand Up @@ -947,4 +944,10 @@ The header is followed by the list of parameters and their errors (might be seve
<value>Trusted</value>
<comment>information about NuGet package origin; if a package has PrefixReserved indicator </comment>
</data>
</root>
<data name="PostAction_ModifyJson_Error_ArgumentNotBoolean" xml:space="preserve">
<value>Post action argument '{0}' is not a valid boolean value.</value>
</data>
<data name="PostAction_ModifyJson_Verbose_AttemptingToFindJsonFile" xml:space="preserve">
<value>Attempting to find json file '{0}' in '{1}'</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;

using Microsoft.DotNet.Cli.Utils;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Abstractions.PhysicalFileSystem;
Expand All @@ -12,6 +13,8 @@ namespace Microsoft.TemplateEngine.Cli.PostActionProcessors
{
internal class AddJsonPropertyPostActionProcessor : PostActionProcessorBase
{
private const string AllowFileCreationArgument = "allowFileCreation";
private const string AllowPathCreationArgument = "allowPathCreation";
private const string JsonFileNameArgument = "jsonFileName";
private const string ParentPropertyPathArgument = "parentPropertyPath";
private const string NewJsonPropertyNameArgument = "newJsonPropertyName";
Expand Down Expand Up @@ -46,12 +49,25 @@ protected override bool ProcessInternal(
return false;
}

IReadOnlyList<string> jsonFiles = FindFilesInCurrentProjectOrSolutionFolder(environment.Host.FileSystem, outputBasePath, matchPattern: jsonFileName, maxAllowedAboveDirectories: 1);
IReadOnlyList<string> jsonFiles = FindFilesInCurrentFolderOrParentFolder(environment.Host.FileSystem, outputBasePath, jsonFileName);

if (jsonFiles.Count == 0)
{
Reporter.Error.WriteLine(LocalizableStrings.PostAction_ModifyJson_Error_NoJsonFile);
return false;
if (!bool.TryParse(action.Args.GetValueOrDefault(AllowFileCreationArgument, "false"), out bool createFile))
{
Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, AllowFileCreationArgument));
return false;
}

if (!createFile)
{
Reporter.Error.WriteLine(LocalizableStrings.PostAction_ModifyJson_Error_NoJsonFile);
return false;
}

string newJsonFilePath = Path.Combine(outputBasePath, jsonFileName);
environment.Host.FileSystem.WriteAllText(newJsonFilePath, "{}");
jsonFiles = new List<string> { newJsonFilePath };
}

if (jsonFiles.Count > 1)
Expand All @@ -73,7 +89,8 @@ protected override bool ProcessInternal(
newJsonElementProperties!.ParentProperty,
":",
newJsonElementProperties.NewJsonPropertyName,
newJsonElementProperties.NewJsonPropertyValue);
newJsonElementProperties.NewJsonPropertyValue,
action);

if (newJsonContent == null)
{
Expand All @@ -87,7 +104,7 @@ protected override bool ProcessInternal(
return true;
}

private static JsonNode? AddElementToJson(IPhysicalFileSystem fileSystem, string targetJsonFile, string? propertyPath, string propertyPathSeparator, string newJsonPropertyName, string newJsonPropertyValue)
private static JsonNode? AddElementToJson(IPhysicalFileSystem fileSystem, string targetJsonFile, string? propertyPath, string propertyPathSeparator, string newJsonPropertyName, string newJsonPropertyValue, IPostAction action)
{
JsonNode? jsonContent = JsonNode.Parse(fileSystem.ReadAllText(targetJsonFile), nodeOptions: null, documentOptions: DeserializerOptions);

Expand All @@ -96,7 +113,13 @@ protected override bool ProcessInternal(
return null;
}

JsonNode? parentProperty = FindJsonNode(jsonContent, propertyPath, propertyPathSeparator);
if (!bool.TryParse(action.Args.GetValueOrDefault(AllowPathCreationArgument, "false"), out bool createPath))
{
Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, AllowPathCreationArgument));
return false;
}

JsonNode? parentProperty = FindJsonNode(jsonContent, propertyPath, propertyPathSeparator, createPath);

if (parentProperty == null)
{
Expand All @@ -116,7 +139,7 @@ protected override bool ProcessInternal(
return jsonContent;
}

private static JsonNode? FindJsonNode(JsonNode content, string? nodePath, string pathSeparator)
private static JsonNode? FindJsonNode(JsonNode content, string? nodePath, string pathSeparator, bool createPath)
{
if (nodePath == null)
{
Expand All @@ -134,18 +157,22 @@ protected override bool ProcessInternal(
return null;
}

node = node[property];
JsonNode? childNode = node[property];
if (childNode is null && createPath)
{
node[property] = childNode = new JsonObject();
}

node = childNode;
}

return node;
}

private static IReadOnlyList<string> FindFilesInCurrentProjectOrSolutionFolder(
private static string[] FindFilesInCurrentFolderOrParentFolder(
IPhysicalFileSystem fileSystem,
string startPath,
string matchPattern,
Func<string, bool>? secondaryFilter = null,
int maxAllowedAboveDirectories = 250)
string matchPattern)
{
string? directory = fileSystem.DirectoryExists(startPath) ? startPath : Path.GetDirectoryName(startPath);

Expand All @@ -158,22 +185,20 @@ private static IReadOnlyList<string> FindFilesInCurrentProjectOrSolutionFolder(

do
{
List<string> filesInDir = fileSystem.EnumerateFileSystemEntries(directory, matchPattern, SearchOption.AllDirectories).ToList();
List<string> matches = new();

matches = secondaryFilter == null ? filesInDir : filesInDir.Where(x => secondaryFilter(x)).ToList();
Reporter.Verbose.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Verbose_AttemptingToFindJsonFile, matchPattern, directory));
string[] filesInDir = fileSystem.EnumerateFileSystemEntries(directory, matchPattern, SearchOption.AllDirectories).ToArray();

if (matches.Count > 0)
if (filesInDir.Length > 0)
{
return matches;
return filesInDir;
}

directory = Path.GetPathRoot(directory) != directory ? Directory.GetParent(directory)?.FullName : null;
numberOfUpLevels++;
}
while (directory != null && numberOfUpLevels <= maxAllowedAboveDirectories);
while (directory != null && numberOfUpLevels <= 1);

return new List<string>();
return Array.Empty<string>();
}

private class JsonContentParameters
Expand Down
10 changes: 10 additions & 0 deletions src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit dfe3107

Please sign in to comment.