// 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. TemplateResolver.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); }
internal static TemplateUsageInformation?GetTemplateUsageInformation(ITemplateInfo templateInfo, IEngineEnvironmentSettings environmentSettings, INewCommandInput commandInput, IHostSpecificDataLoader hostDataLoader, TemplateCreator templateCreator) { IParameterSet allParams; IReadOnlyList <string> userParamsWithInvalidValues; HashSet <string> userParamsWithDefaultValues; bool hasPostActionScriptRunner; ITemplate?template = environmentSettings.SettingsLoader.LoadTemplate(templateInfo, commandInput.BaselineName); if (template == null) { return(null); } TemplateResolver.ParseTemplateArgs(templateInfo, hostDataLoader, commandInput); allParams = templateCreator.SetupDefaultParamValuesFromTemplateAndHost(template, template.DefaultName ?? "testName", out IReadOnlyList <string> defaultParamsWithInvalidValues); templateCreator.ResolveUserParameters(template, allParams, commandInput.InputTemplateParams, out userParamsWithInvalidValues); hasPostActionScriptRunner = CheckIfTemplateHasScriptRunningPostActions(template, environmentSettings, commandInput, templateCreator); templateCreator.ReleaseMountPoints(template); List <InvalidParameterInfo> invalidParameters = new List <InvalidParameterInfo>(); if (userParamsWithInvalidValues.Any()) { // Lookup the input param formats - userParamsWithInvalidValues has canonical. foreach (string canonical in userParamsWithInvalidValues) { commandInput.InputTemplateParams.TryGetValue(canonical, out string specifiedValue); string inputFormat = commandInput.TemplateParamInputFormat(canonical); InvalidParameterInfo invalidParam = new InvalidParameterInfo(InvalidParameterInfo.Kind.InvalidParameterValue, inputFormat, specifiedValue, canonical); invalidParameters.Add(invalidParam); } } if (templateCreator.AnyParametersWithInvalidDefaultsUnresolved(defaultParamsWithInvalidValues, userParamsWithInvalidValues, commandInput.InputTemplateParams, out IReadOnlyList <string> defaultsWithUnresolvedInvalidValues)) { IParameterSet templateParams = template.Generator.GetParametersForTemplate(environmentSettings, template); foreach (string defaultParamName in defaultsWithUnresolvedInvalidValues) { ITemplateParameter param = templateParams.ParameterDefinitions.FirstOrDefault(x => string.Equals(x.Name, defaultParamName, StringComparison.Ordinal)); if (param != null) { // Get the best input format available. IReadOnlyList <string> inputVariants = commandInput.VariantsForCanonical(param.Name); string displayName = inputVariants.FirstOrDefault(x => x.Contains(param.Name)) ?? inputVariants.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur) ?? param.Name; InvalidParameterInfo invalidParam = new InvalidParameterInfo(InvalidParameterInfo.Kind.InvalidDefaultValue, displayName, param.DefaultValue, displayName); invalidParameters.Add(invalidParam); } } } // get all the flags // get all the user input params that have the default value Dictionary <string, IReadOnlyList <string> > inputFlagVariants = new Dictionary <string, IReadOnlyList <string> >(); userParamsWithDefaultValues = new HashSet <string>(); foreach (string paramName in allParams.ParameterDefinitions.Select(x => x.Name)) { inputFlagVariants[paramName] = commandInput.VariantsForCanonical(paramName); if (commandInput.TemplateParamHasValue(paramName) && string.IsNullOrEmpty(commandInput.TemplateParamValue(paramName))) { userParamsWithDefaultValues.Add(paramName); } } IReadOnlyDictionary <string, IReadOnlyList <string> > variantsForCanonicals = inputFlagVariants; return(new TemplateUsageInformation { InvalidParameters = invalidParameters, AllParameters = allParams, UserParametersWithInvalidValues = userParamsWithInvalidValues, UserParametersWithDefaultValues = userParamsWithDefaultValues, VariantsForCanonicals = variantsForCanonicals, HasPostActionScriptRunner = hasPostActionScriptRunner }); }