Example #1
0
        // 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);
        }
Example #2
0
 // Attempts to match templates against the inputs.
 private TemplateListResolutionResult QueryForTemplateMatches()
 {
     return(TemplateListResolver.GetTemplateResolutionResult(_settingsLoader.UserTemplateCache.TemplateInfo, _hostDataLoader, _commandInput, _defaultLanguage));
 }
Example #3
0
        // 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);
        }
Example #4
0
        // 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);
        }
Example #5
0
        // 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);
        }
Example #7
0
        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());
        }
Example #8
0
        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));
            }
        }