// Attempts to invoke the template. // Warning: The _commandInput cannot be assumed to be in a state that is parsed for the template being invoked. // So be sure to only get template-agnostic information from it. Anything specific to the template must be gotten from the ITemplateMatchInfo // Or do a reparse if necessary (currently occurs in one error case). private async Task <CreationResultStatus> CreateTemplateAsync(ITemplateMatchInfo templateMatchDetails) { ITemplateInfo template = templateMatchDetails.Info; char[] invalidChars = Path.GetInvalidFileNameChars(); if (_commandInput?.Name != null && _commandInput.Name.IndexOfAny(invalidChars) > -1) { string printableChars = string.Join(", ", invalidChars.Where(x => !char.IsControl(x)).Select(x => $"'{x}'")); string nonPrintableChars = string.Join(", ", invalidChars.Where(char.IsControl).Select(x => $"char({(int)x})")); Reporter.Error.WriteLine(string.Format(LocalizableStrings.InvalidNameParameter, printableChars, nonPrintableChars).Bold().Red()); return(CreationResultStatus.CreateFailed); } string fallbackName = new DirectoryInfo( !string.IsNullOrWhiteSpace(_commandInput.OutputPath) ? _commandInput.OutputPath : Directory.GetCurrentDirectory()) .Name; if (string.IsNullOrEmpty(fallbackName) || string.Equals(fallbackName, "/", StringComparison.Ordinal)) { // DirectoryInfo("/").Name on *nix returns "/", as opposed to null or "". fallbackName = null; } // Name returns <disk letter>:\ for root disk folder on Windows - replace invalid chars else if (fallbackName.IndexOfAny(invalidChars) > -1) { Regex pattern = new Regex($"[{Regex.Escape(new string(invalidChars))}]"); fallbackName = pattern.Replace(fallbackName, ""); if (string.IsNullOrWhiteSpace(fallbackName)) { fallbackName = null; } } TemplateCreationResult instantiateResult; try { instantiateResult = await _templateCreator.InstantiateAsync(template, _commandInput.Name, fallbackName, _commandInput.OutputPath, templateMatchDetails.GetValidTemplateParameters(), _commandInput.SkipUpdateCheck, _commandInput.IsForceFlagSpecified, _commandInput.BaselineName, _commandInput.IsDryRun) .ConfigureAwait(false); } catch (ContentGenerationException cx) { Reporter.Error.WriteLine(cx.Message.Bold().Red()); if (cx.InnerException != null) { Reporter.Error.WriteLine(cx.InnerException.Message.Bold().Red()); } return(CreationResultStatus.CreateFailed); } catch (TemplateAuthoringException tae) { Reporter.Error.WriteLine(tae.Message.Bold().Red()); return(CreationResultStatus.CreateFailed); } string resultTemplateName = string.IsNullOrEmpty(instantiateResult.TemplateFullName) ? _commandInput.TemplateName : instantiateResult.TemplateFullName; switch (instantiateResult.Status) { case CreationResultStatus.Success: if (!_commandInput.IsDryRun) { Reporter.Output.WriteLine(string.Format(LocalizableStrings.CreateSuccessful, resultTemplateName)); } else { Reporter.Output.WriteLine(LocalizableStrings.FileActionsWouldHaveBeenTaken); foreach (IFileChange change in instantiateResult.CreationEffects.FileChanges) { Reporter.Output.WriteLine($" {change.ChangeKind}: {change.TargetRelativePath}"); } } if (!string.IsNullOrEmpty(template.ThirdPartyNotices)) { Reporter.Output.WriteLine(string.Format(LocalizableStrings.ThirdPartyNotices, template.ThirdPartyNotices)); } HandlePostActions(instantiateResult); break; case CreationResultStatus.CreateFailed: Reporter.Error.WriteLine(string.Format(LocalizableStrings.CreateFailed, resultTemplateName, instantiateResult.Message).Bold().Red()); break; case CreationResultStatus.MissingMandatoryParam: if (string.Equals(instantiateResult.Message, "--name", StringComparison.Ordinal)) { Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, instantiateResult.Message, resultTemplateName).Bold().Red()); } else { // TODO: rework to avoid having to reparse. // The canonical info could be in the ITemplateMatchInfo, but currently isn't. TemplateListResolver.ParseTemplateArgs(template, _hostDataLoader, _commandInput); IReadOnlyList <string> missingParamNamesCanonical = instantiateResult.Message.Split(new[] { ',' }) .Select(x => _commandInput.VariantsForCanonical(x.Trim()) .DefaultIfEmpty(x.Trim()).First()) .ToList(); string fixedMessage = string.Join(", ", missingParamNamesCanonical); Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, fixedMessage, resultTemplateName).Bold().Red()); } break; case CreationResultStatus.OperationNotSpecified: break; case CreationResultStatus.NotFound: Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingTemplateContentDetected, _commandName).Bold().Red()); break; case CreationResultStatus.InvalidParamValues: TemplateUsageInformation?usageInformation = TemplateUsageHelp.GetTemplateUsageInformation(template, _environment, _commandInput, _hostDataLoader, _templateCreator); if (usageInformation != null) { string invalidParamsError = InvalidParameterInfo.InvalidParameterListToString(usageInformation.Value.InvalidParameters); Reporter.Error.WriteLine(invalidParamsError.Bold().Red()); Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunHelpForInformationAboutAcceptedParameters, $"{_commandName} {_commandInput.TemplateName}").Bold().Red()); } else { Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingTemplateContentDetected, _commandName).Bold().Red()); return(CreationResultStatus.NotFound); } break; default: break; } return(instantiateResult.Status); }
// Attempts to match templates against the inputs. private TemplateListResolutionResult QueryForTemplateMatches() { return(TemplateListResolver.GetTemplateResolutionResult(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage)); }
// Attempts to invoke the template. // Warning: The _commandInput cannot be assumed to be in a state that is parsed for the template being invoked. // So be sure to only get template-agnostic information from it. Anything specific to the template must be gotten from the ITemplateMatchInfo // Or do a reparse if necessary (currently occurs in one error case). private async Task <CreationResultStatus> CreateTemplateAsync(ITemplateMatchInfo templateMatchDetails) { ITemplateInfo template = templateMatchDetails.Info; string fallbackName = new DirectoryInfo(_commandInput.OutputPath ?? Directory.GetCurrentDirectory()).Name; if (string.IsNullOrEmpty(fallbackName) || string.Equals(fallbackName, "/", StringComparison.Ordinal)) { // DirectoryInfo("/").Name on *nix returns "/", as opposed to null or "". fallbackName = null; } TemplateCreationResult instantiateResult; try { instantiateResult = await _templateCreator.InstantiateAsync(template, _commandInput.Name, fallbackName, _commandInput.OutputPath, templateMatchDetails.GetValidTemplateParameters(), _commandInput.SkipUpdateCheck, _commandInput.IsForceFlagSpecified, _commandInput.BaselineName).ConfigureAwait(false); } catch (ContentGenerationException cx) { Reporter.Error.WriteLine(cx.Message.Bold().Red()); if (cx.InnerException != null) { Reporter.Error.WriteLine(cx.InnerException.Message.Bold().Red()); } return(CreationResultStatus.CreateFailed); } catch (TemplateAuthoringException tae) { Reporter.Error.WriteLine(tae.Message.Bold().Red()); return(CreationResultStatus.CreateFailed); } string resultTemplateName = string.IsNullOrEmpty(instantiateResult.TemplateFullName) ? TemplateName : instantiateResult.TemplateFullName; switch (instantiateResult.Status) { case CreationResultStatus.Success: Reporter.Output.WriteLine(string.Format(LocalizableStrings.CreateSuccessful, resultTemplateName)); if (!string.IsNullOrEmpty(template.ThirdPartyNotices)) { Reporter.Output.WriteLine(string.Format(LocalizableStrings.ThirdPartyNotices, template.ThirdPartyNotices)); } HandlePostActions(instantiateResult); break; case CreationResultStatus.CreateFailed: Reporter.Error.WriteLine(string.Format(LocalizableStrings.CreateFailed, resultTemplateName, instantiateResult.Message).Bold().Red()); break; case CreationResultStatus.MissingMandatoryParam: if (string.Equals(instantiateResult.Message, "--name", StringComparison.Ordinal)) { Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, instantiateResult.Message, resultTemplateName).Bold().Red()); } else { // TODO: rework to avoid having to reparse. // The canonical info could be in the ITemplateMatchInfo, but currently isn't. TemplateListResolver.ParseTemplateArgs(template, _hostDataLoader, _commandInput); IReadOnlyList <string> missingParamNamesCanonical = instantiateResult.Message.Split(new[] { ',' }) .Select(x => _commandInput.VariantsForCanonical(x.Trim()) .DefaultIfEmpty(x.Trim()).First()) .ToList(); string fixedMessage = string.Join(", ", missingParamNamesCanonical); Reporter.Error.WriteLine(string.Format(LocalizableStrings.MissingRequiredParameter, fixedMessage, resultTemplateName).Bold().Red()); } break; case CreationResultStatus.OperationNotSpecified: break; case CreationResultStatus.InvalidParamValues: TemplateUsageInformation usageInformation = TemplateUsageHelp.GetTemplateUsageInformation(template, EnvironmentSettings, _commandInput, _hostDataLoader, _templateCreator); string invalidParamsError = InvalidParameterInfo.InvalidParameterListToString(usageInformation.InvalidParameters); Reporter.Error.WriteLine(invalidParamsError.Bold().Red()); Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunHelpForInformationAboutAcceptedParameters, $"{CommandName} {TemplateName}").Bold().Red()); break; default: break; } return(instantiateResult.Status); }
// TODO: make sure help / usage works right in these cases. private CreationResultStatus EnterMaintenanceFlow() { if (!TemplateListResolver.ValidateRemainingParameters(_commandInput, out IReadOnlyList <string> invalidParams)) { HelpForTemplateResolution.DisplayInvalidParameters(invalidParams); if (_commandInput.IsHelpFlagSpecified) { _telemetryLogger.TrackEvent(CommandName + "-Help"); HelpForTemplateResolution.ShowUsageHelp(_commandInput); } else { Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunHelpForInformationAboutAcceptedParameters, CommandName).Bold().Red()); } return(CreationResultStatus.InvalidParamValues); } if (_commandInput.ToUninstallList != null) { if (_commandInput.ToUninstallList.Count > 0 && _commandInput.ToUninstallList[0] != null) { IEnumerable <string> failures = Installer.Uninstall(_commandInput.ToUninstallList); foreach (string failure in failures) { Console.WriteLine(LocalizableStrings.CouldntUninstall, failure); } } else { Console.WriteLine(LocalizableStrings.CommandDescription); Console.WriteLine(); Console.WriteLine(LocalizableStrings.InstalledItems); foreach (string value in _settingsLoader.InstallUnitDescriptorCache.InstalledItems.Values) { Console.WriteLine($" {value}"); } return(CreationResultStatus.Success); } } if (_commandInput.ToInstallList != null && _commandInput.ToInstallList.Count > 0 && _commandInput.ToInstallList[0] != null) { CreationResultStatus installResult = EnterInstallFlow(); if (installResult == CreationResultStatus.Success) { _settingsLoader.Reload(); TemplateListResolutionResult resolutionResult = QueryForTemplateMatches(); HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(resolutionResult, EnvironmentSettings, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage); } return(installResult); } //No other cases specified, we've fallen through to "Usage help + List" HelpForTemplateResolution.ShowUsageHelp(_commandInput); TemplateListResolutionResult templateResolutionResult = QueryForTemplateMatches(); HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(templateResolutionResult, EnvironmentSettings, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage); return(CreationResultStatus.Success); }
// TODO: make sure help / usage works right in these cases. private CreationResultStatus EnterMaintenanceFlow() { if (!TemplateListResolver.ValidateRemainingParameters(_commandInput, out IReadOnlyList <string> invalidParams)) { HelpForTemplateResolution.DisplayInvalidParameters(invalidParams); if (_commandInput.IsHelpFlagSpecified) { // this code path doesn't go through the full help & usage stack, so needs it's own call to ShowUsageHelp(). HelpForTemplateResolution.ShowUsageHelp(_commandInput, _telemetryLogger); } else { Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunHelpForInformationAboutAcceptedParameters, CommandName).Bold().Red()); } return(CreationResultStatus.InvalidParamValues); } if (_commandInput.ToUninstallList != null) { if (_commandInput.ToUninstallList.Count > 0 && _commandInput.ToUninstallList[0] != null) { IEnumerable <string> failures = Installer.Uninstall(_commandInput.ToUninstallList); foreach (string failure in failures) { Console.WriteLine(LocalizableStrings.CouldntUninstall, failure); } } else { Console.WriteLine(LocalizableStrings.CommandDescription); Console.WriteLine(); Console.WriteLine(LocalizableStrings.InstalledItems); foreach (KeyValuePair <Guid, string> entry in _settingsLoader.InstallUnitDescriptorCache.InstalledItems) { Console.WriteLine($" {entry.Value}"); if (_settingsLoader.InstallUnitDescriptorCache.Descriptors.TryGetValue(entry.Value, out IInstallUnitDescriptor descriptor)) { if (descriptor.Details != null && descriptor.Details.TryGetValue("Version", out string versionValue)) { Console.WriteLine($" {LocalizableStrings.Version} {versionValue}"); } } HashSet <string> displayStrings = new HashSet <string>(StringComparer.Ordinal); foreach (TemplateInfo info in _settingsLoader.UserTemplateCache.TemplateInfo.Where(x => x.ConfigMountPointId == entry.Key)) { string str = $" {info.Name} ({info.ShortName})"; if (info.Tags != null && info.Tags.TryGetValue("language", out ICacheTag languageTag)) { str += " " + string.Join(", ", languageTag.ChoicesAndDescriptions.Select(x => x.Key)); } displayStrings.Add(str); } if (displayStrings.Count > 0) { Console.WriteLine($" {LocalizableStrings.Templates}:"); foreach (string displayString in displayStrings) { Console.WriteLine(displayString); } } } return(CreationResultStatus.Success); } } if (_commandInput.ToInstallList != null && _commandInput.ToInstallList.Count > 0 && _commandInput.ToInstallList[0] != null) { CreationResultStatus installResult = EnterInstallFlow(); if (installResult == CreationResultStatus.Success) { _settingsLoader.Reload(); TemplateListResolutionResult resolutionResult = QueryForTemplateMatches(); HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(resolutionResult, EnvironmentSettings, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage); } return(installResult); } //No other cases specified, we've fallen through to "Usage help + List" TemplateListResolutionResult templateResolutionResult = QueryForTemplateMatches(); HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(templateResolutionResult, EnvironmentSettings, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage); return(CreationResultStatus.Success); }
public bool TryGetSingularInvokableMatch(out ITemplateMatchInfo template, out SingularInvokableMatchCheckStatus resultStatus) { IReadOnlyList <ITemplateMatchInfo> invokableMatches = _coreMatchedTemplates.Where(x => x.IsInvokableMatch()).ToList(); IReadOnlyList <ITemplateMatchInfo> languageFilteredInvokableMatches; if (_hasUserInputLanguage) { languageFilteredInvokableMatches = invokableMatches; } else { // check for templates with the default language languageFilteredInvokableMatches = invokableMatches.Where(x => x.DispositionOfDefaults.Any(y => y.Location == MatchLocation.DefaultLanguage && y.Kind == MatchKind.Exact)).ToList(); // no candidate templates matched the default language, continue with the original candidates. if (languageFilteredInvokableMatches.Count == 0) { languageFilteredInvokableMatches = invokableMatches; } } if (languageFilteredInvokableMatches.Count == 1) { template = languageFilteredInvokableMatches[0]; resultStatus = SingularInvokableMatchCheckStatus.SingleMatch; return(true); } // if multiple templates in the group have single starts with matches on the same parameter, it's ambiguous. // For the case where one template has single starts with, and another has ambiguous - on the same param: // The one with single starts with is chosen as invokable because if the template with an ambiguous match // was not installed, the one with the singluar invokable would be chosen. HashSet <string> singleStartsWithParamNames = new HashSet <string>(); foreach (ITemplateMatchInfo checkTemplate in languageFilteredInvokableMatches) { IList <string> singleStartParamNames = checkTemplate.MatchDisposition.Where(x => x.Location == MatchLocation.OtherParameter && x.Kind == MatchKind.SingleStartsWith).Select(x => x.InputParameterName).ToList(); foreach (string paramName in singleStartParamNames) { if (!singleStartsWithParamNames.Add(paramName)) { template = null; resultStatus = SingularInvokableMatchCheckStatus.AmbiguousChoice; return(false); } } } ITemplateMatchInfo highestInGroupIfSingleGroup = TemplateListResolver.FindHighestPrecedenceTemplateIfAllSameGroupIdentity(languageFilteredInvokableMatches, out bool ambiguousGroupIdResult); if (highestInGroupIfSingleGroup != null) { template = highestInGroupIfSingleGroup; resultStatus = SingularInvokableMatchCheckStatus.SingleMatch; return(true); } else if (ambiguousGroupIdResult) { template = null; resultStatus = SingularInvokableMatchCheckStatus.AmbiguousPrecedence; return(false); } template = null; resultStatus = SingularInvokableMatchCheckStatus.NoMatch; return(false); }
private async Task <CreationResultStatus> EnterTemplateManipulationFlowAsync() { if (_commandInput.IsListFlagSpecified || _commandInput.IsHelpFlagSpecified) { ListOrHelpTemplateListResolutionResult listingTemplateResolutionResult = TemplateListResolver.GetTemplateResolutionResultForListOrHelp(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage); return(HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(listingTemplateResolutionResult, EnvironmentSettings, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage, showUsageHelp: _commandInput.IsHelpFlagSpecified)); } TemplateListResolutionResult templateResolutionResult = TemplateListResolver.GetTemplateResolutionResult(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage); TemplateInvocationAndAcquisitionCoordinator invocationCoordinator = new TemplateInvocationAndAcquisitionCoordinator(_settingsLoader, _commandInput, _templateCreator, _hostDataLoader, _telemetryLogger, _defaultLanguage, CommandName, _inputGetter, _callbacks); return(await invocationCoordinator.CoordinateInvocationOrAcquisitionAsync()); }
public async Task <CreationResultStatus> CoordinateInvocationOrAcquisitionAsync() { EnsureTemplateResolutionResult(); if (_templateToInvoke != null) { // invoke and then check for updates CreationResultStatus creationResult = await InvokeTemplateAsync(); // check for updates on this template (pack) await CheckForTemplateUpdateAsync(); return(creationResult); } else { // The command didn't resolve to an installed template. Search for something that does. bool anySearchMatches = await SearchForTemplateMatchesAsync(); if (!anySearchMatches) { ListOrHelpTemplateListResolutionResult listingTemplateListResolutionResult = TemplateListResolver.GetTemplateResolutionResultForListOrHelp(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage); return(HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(listingTemplateListResolutionResult, _environment, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage, false)); } else { return(CreationResultStatus.Success); } } }
public async Task <CreationResultStatus> CoordinateInvocationOrAcquisitionAsync() { EnsureTemplateResolutionResult(); if (_templateToInvoke != null) { // invoke and then check for updates CreationResultStatus creationResult = await InvokeTemplateAsync(); // check for updates on this template (pack) await CheckForTemplateUpdateAsync(); return(creationResult); } else { ListOrHelpTemplateListResolutionResult listingTemplateListResolutionResult = TemplateListResolver.GetTemplateResolutionResultForListOrHelp(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage); return(HelpForTemplateResolution.CoordinateHelpAndUsageDisplay(listingTemplateListResolutionResult, _environment, _commandInput, _hostDataLoader, _telemetryLogger, _templateCreator, _defaultLanguage, false)); } }